i added some more stuff:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
enum Stone {
vacant, black, white; // maybe add edge?
Stone otherColor() {
if (this == black) return white;
else if (this == white) return black;
else
return vacant;
}
}
interface Board {
int size();
Stone at(int x, int y);
}
interface MutableBoard extends Board {
int size();
Stone at(int x, int y);
void setAt(int x, int y, Stone color);
}
class BoardImpl implements MutableBoard {
BoardImpl(int n) {
this.n = n;
stones = new Stone[n][n];
clear(n);
}
private void clear(int n) {
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
stones[i][j] = Stone.vacant;
}
@Override public int size() {
return n;
}
@Override public Stone at(int x, int y) {
return stones[x][y];
}
@Override public void setAt(int x, int y, Stone color) {
stones[x][y] = color;
}
final int n;
private final Stone[][] stones;
}
class Block {
Block(Board b, int i, int j, boolean[][] processed) {
this.b = b;
this.processed = processed;
n = b.size();
this.who = b.at(i, j);
points = new LinkedList<Point>();
grow(i, j);
countLiberties();
}
int liberties() {
return l;
}
Stone color() {
return who;
}
public String toString() {
return who + " " + points.size() + " stone(s), " + l + " liberties" + points;
}
static List<List<Block>> findBlocks(Board b) {
int n = b.size();
boolean[][] processed = new boolean[n][n];
List<Block> blackBlocks = new LinkedList<Block>();
List<Block> whiteBlocks = new LinkedList<Block>();
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
Stone who = b.at(i, j);
if (!who.equals(Stone.vacant) && processed[i][j] == false) {
Block g = new Block(b, i, j, processed);
switch (who) {
case black:
blackBlocks.add(g);
break;
case white:
whiteBlocks.add(g);
break;
default:
System.out.println("findGroups " + b + g + who);
}
}
}
List<List<Block>> blocks = new LinkedList<List<Block>>();
blocks.add(blackBlocks);
blocks.add(whiteBlocks);
return blocks;
}
private void grow(int i, int j) {
if (!(0 <= i && i < n && 0 <= j && j < n)) return;
if (!processed[i][j]) {
Stone who = b.at(i, j);
if (who.equals(this.who) || who.equals(Stone.vacant)) processed[i][j] = true;
if (who == this.who) {
points.add(new Point(i, j));
grow(i - 1, j);
grow(i, j - 1);
grow(i, j + 1);
grow(i + 1, j);
}
}
}
private void countLiberties() {
l = 0;
processed = new boolean[n][n];
for (int k = 0; k < points.size(); k++) {
Point p = (Point) points.get(k);
count(p.x - 1, p.y);
count(p.x, p.y - 1);
count(p.x, p.y + 1);
count(p.x + 1, p.y);
}
}
private void count(int i, int j) {
if (0 <= i && i < n && 0 <= j && j < n && !processed[i][j]) {
if (b.at(i, j) == Stone.vacant) l++;
processed[i][j] = true;
}
}
static List<Block> capturedStones(List<List<Block>> blockLists) {
List<Block> dead = new LinkedList<Block>();
List<Block> blocks = blockLists.get(0);
if (blocks != null) for (int i = 0; i < blocks.size(); i++) {
Block g = (Block) blocks.get(i);
// System.out.println(g);
if (g.liberties() == 0) dead.add(g);
}
blocks = blockLists.get(1);
if (blocks != null) for (int i = 0; i < blocks.size(); i++) {
Block g = (Block) blocks.get(i);
// System.out.println(g);
if (g.liberties() == 0) dead.add(g);
}
return dead;
}
private Stone who;
List<Point> points;
private int n;
private int l;
transient private Board b;
transient private boolean[][] processed;
}
class Move {
Move(Stone stone, Point point) {
if (stone.equals(Stone.vacant)) throw new RuntimeException("bad move");
this.stone = stone;
this.point = point;
}
Move(Stone stone, int x, int y) {
this(stone, new Point(x, y));
}
void make(MutableBoard board) {
board.setAt(point.x, point.y, stone);
List<List<Block>> blockLists = Block.findBlocks(board);
List<Block> capturedBlocks = Block.capturedStones(blockLists);
if (capturedBlocks != null && capturedBlocks.size() > 0) {
Block fromThisMove = null;
for (Block block : capturedBlocks)
if (block.points.contains(point) && block.points.size() == 1) {
fromThisMove = block;
break;
}
if (fromThisMove != null) capturedBlocks.remove(fromThisMove);
for (Block block : capturedBlocks) {
captured = capturedBlocks;
for (Point point2 : block.points)
board.setAt(point2.x, point2.y, Stone.vacant);
}
}
}
void unmake(MutableBoard board) {
if (captured != null) for (Block block : captured) {
Stone who = block.color();
for (Point point : block.points)
board.setAt(point.x, point.y, who);
}
board.setAt(point.x, point.y, Stone.vacant);
}
final Point point;
final Stone stone;
List<Block> captured;
}
class Line {
Line(Point p1, Point p2) {
this.p1 = p1;
this.p2 = p2;
}
public void paint(Graphics g) {
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
final Point p1, p2;
}
class Gui extends JPanel {
Gui(TopPanel game, MutableBoard board, int width, int height) {
this.game = game;
this.board = board;
x0 = 10;
y0 = 10;
this.n = board.size();
rectangle = new Rectangle(1, 1, n, n);
dx = width / n;
dy = height / n;
for (int i = 0; i < n; i++)
addLine(x0, y0 + i * dy, x0 + (n - 1) * dx, y0 + i * dy);
for (int j = 0; j < n; j++)
addLine(x0 + j * dx, y0, x0 + j * dx, y0 + (n - 1) * dy);
addMouseListener(new MouseListener() {
@Override public void mouseReleased(MouseEvent e) {}
@Override public void mousePressed(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseClicked(MouseEvent e) {
Point2D.Float point = toBoardCoordinates(e.getPoint());
Point closest = new Point(Math.round(point.x), Math.round(point.y));
if (ispointOnBoard(closest)) {
double distance = point.distance(closest);
if (distance < .35) if (Gui.this.board.at(closest.x, closest.y) == Stone.vacant) Gui.this.game.makeMove(closest);
else
System.out.println(closest + " is occupied!");
} else
System.out.println(closest + " is off the board!");
}
});
}
private Point2D.Float toBoardCoordinates(Point screen) {
return new Point2D.Float((screen.x - x0) / (float) dx + 1, (screen.y - y0) / (float) dy + 1);
}
private Point toScreenCoordinates(Point board) {
return new Point(x0 + (board.x - 1) * dx, y0 + (board.y - 1) * dy);
}
private boolean ispointOnBoard(Point point) {
return rectangle.contains(point);
}
Move createMove(Stone stone, Point point) {
if (ispointOnBoard(point)) return new Move(stone, point);
else
throw new RuntimeException(point + " is off the board!");
}
void makeMove(Move move) { // currently we do not allow vacant moves
repaint(); // use the long form!
}
void unmove(Move move) {
repaint();
}
private void addLine(int x1, int y1, int x2, int y2) {
this.lines.add(new Line(new Point(x1, y1), new Point(x2, y2)));
}
private void addLine(Point p1, Point p2) {
this.lines.add(new Line(p1, p2));
}
private Image blackStone(int width, int height, Color color) {
Image img = createImage(width, height);
Graphics g = img.getGraphics();
g.setColor(color);
g.fillRect(0, 0, width, height);
g.setColor(Color.black);
g.fillOval(0, 0, width - 1, height - 1);
g.drawOval(0, 0, width - 1, height - 1);
// these two are different from
// either g.fillOval(-1, -1, width+1, height+1);
// or g.fillOval(0, 0, width-1, height-1);
g.setColor(Color.white);
g.drawArc(width / 5, height / 5, width * 3 / 5, height * 3 / 5, -20, -60);
return img;
}
private Image whiteStone(int width, int height, Color color) {
Image img = createImage(width, height);
Graphics g = img.getGraphics();
g.setColor(color);
g.fillRect(0, 0, width, height);
g.setColor(Color.white);
g.fillOval(0, 0, width - 1, height - 1);
g.setColor(Color.black);
g.drawOval(0, 0, width - 1, height - 1);
g.drawArc(width / 5, height / 5, width * 3 / 5, height * 3 / 5, -20, -60);
return img;
}
private void paintStone(Graphics g, int i, int j) {
if (board.at(i, j) != Stone.vacant) {
if (board.at(i, j) == Stone.black) g.setColor(Color.black);
else if (board.at(i, j) == Stone.white) g.setColor(Color.white);
else
throw new RuntimeException("oops");
Point screen = toScreenCoordinates(new Point(i, j));
if (black == null || white == null) g.fillOval(screen.x - dx / 2, screen.y - dy / 2, dx, dx);
else {
if (board.at(i, j) == Stone.black) g.drawImage(black, screen.x - dx / 2, screen.y - dy / 2, dx, dy, null);
else
g.drawImage(white, screen.x - dx / 2, screen.y - dy / 2, dx, dy, null);
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (game != null) {
black = blackStone(dx, dy, getBackground());
white = whiteStone(dx, dy, getBackground());
Color color = g.getColor();
for (Line line : lines) {
line.paint(g);
}
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
paintStone(g, i, j);
g.setColor(color);
}
}
private final TopPanel game;
private final int n;
final MutableBoard board;
private final int x0, y0;
private final int dx, dy;
private final Rectangle rectangle;
private final Set<Line> lines = new LinkedHashSet<Line>();
private Image black, white;
private static final long serialVersionUID = 1L;
}
class Game {
Game(int n) {
board = new BoardImpl(n);
}
Move makeMove(Point point) {
Move move = new Move(turn, point);
move.make(board);
moves.add(move);
turn = turn.otherColor();
return move;
}
Move unmove() {
if (moves.size() > 0) {
Move move = moves.remove(moves.size() - 1);
move.unmake(board);
turn = turn.otherColor();
return move;
} else {
System.out.println("no moves to undo!");
return null;
}
}
final MutableBoard board;
Stone turn = Stone.black;
final java.util.List<Move> moves = new LinkedList<Move>();
}
class TopPanel extends JPanel {
static final String unmove = "Unmove";
static final String newgame = "New Game";
TopPanel(Game game) {
init();
this.game = game;
gui = new Gui(this, game.board, 500, 520);
Dimension d = new Dimension(510, 530);
gui.setSize(d);
gui.setPreferredSize(d);
gui.setMaximumSize(d);
gui.setMinimumSize(d);
gui.setOpaque(true);
gui.setBackground(Color.orange);
add(gui);
}
void makeMove(Point point) {
Move move = game.makeMove(point);
if (move != null) gui.makeMove(move);
}
void unmove() {
Move move = game.unmove();
if (move != null) gui.unmove(move);
else
System.out.println("no moves to undo!");
}
private void init() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JButton jButton = new JButton(newgame);
jButton.setName(newgame);
add(jButton);
jButton = new JButton(unmove);
jButton.setName(unmove);
add(jButton);
jButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
Object object = e.getSource();
if (object instanceof JButton) if (((JButton) object).getName().equals(unmove)) unmove();
else if (((JButton) object).getName().equals(newgame)) {
//
}
}
});
}
final Gui gui;
Game game;
private static final long serialVersionUID = 1L;
}
class Main {
private static void build(Container c) {
c.add(new TopPanel(new Game(19)));
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Board");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
build(frame.getContentPane());
frame.setPreferredSize(new Dimension(520, 600));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}