public class Main { public static void main(String[] args) { Othello othello = new Othello(); OthelloGui othelloGui = new OthelloGui(othello); othello.addOthelloObserver(othelloGui); Player p0 = new HumanPlayer(othelloGui,PieceColor.BLACK,"先手"); // Player p0 = new RandomPlayer(PieceColor.BLACK,"先手"); Player p1 = new RandomPlayer(PieceColor.WHITE,"後手"); othello.setTurnInterval(50); othello.play(p0, p1); } }
public class Othello { private Board board = new Board(); private Player[] players = new Player[2]; // 0: black, 1: white private int turnCount; private boolean isMoved; private Player currentPlayer; private long turnInterval; public void setTurnInterval(long msec) { this.turnInterval = msec; } public void play(Player p1, Player p2) { players[0] = p1; players[1] = p2; board.initBoard(); currentPlayer = p1; turnCount = 0; gameStart(); while (!board.isGameOver()) { currentPlayer = players[turnCount % players.length]; turnStart(); if (board.canPlace(currentPlayer.getColor())) { Position pos = currentPlayer.nextMove(board); board.placeAt(pos.getX(), pos.getY(), currentPlayer.getColor()); isMoved = true; } else { isMoved = false; // passed } turnEnd(); turnCount++; sleep(turnInterval); } gameEnd(); } public Board getBoard() { return board; } public Player getCurrentPlayer() { return currentPlayer; } public Player getPlayer1() { return players[0]; } public Player getPlayer2() { return players[1]; } public boolean isMoved() { return isMoved; } public boolean isPassed() { return !isMoved; } public Player getWinner() { int c0 = getPieceCount(players[0]); int c1 = getPieceCount(players[1]); if (c0 > c1) { return players[0]; } else if (c0 < c1) { return players[1]; } else { return null; } } public int getPieceCount(Player player) { return board.getPieceCount(player.getColor()); } private void sleep(long msec) { try { Thread.sleep(msec); } catch (InterruptedException e) { } } private List<OthelloObserver> observers = new ArrayList<OthelloObserver>(); public void addOthelloObserver(OthelloObserver o) { observers.add(o); } private void gameStart() { for (Iterator<OthelloObserver> it = observers.iterator(); it.hasNext(); ) { it.next().gameStarted(this); } } private void gameEnd() { for (Iterator<OthelloObserver> it = observers.iterator(); it.hasNext(); ) { it.next().gameEnded(this); } } private void turnStart() { for (Iterator<OthelloObserver> it = observers.iterator(); it.hasNext(); ) { it.next().turnStarted(this); } } private void turnEnd() { for (Iterator<OthelloObserver> it = observers.iterator(); it.hasNext(); ) { it.next().turnEnded(this); } } }
public abstract class BasicPlayer implements Player { private int color; private String name; public BasicPlayer(int color) { this(color,"Player"); } public BasicPlayer(int color, String name) { this.color = color; this.name = name; } public String getName() { return name; } public int getColor() { return color; } @Override public String toString() { String c = PieceColor.toColorString(color); return name+"("+c+")"; } public abstract Position nextMove(Board board); }
import java.awt.Font;
import javax.swing.JLabel;
class BigLabel extends JLabel { public BigLabel(String text) { super(text); setHorizontalAlignment(CENTER); } public void setFontSize(float size) { Font font = getFont(); Font bigFont = font.deriveFont(size); setFont(bigFont); } }
public class Board { private int width = 8; private int height = 8; /* * boardの各要素の値は * EMPTY,BLACK,WHITE * のいずれか。 */ private int[][] board; public Board() { board = new int[width][height]; initBoard(); } public void initBoard() { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { board[x][y] = PieceColor.EMPTY; } } int cx = width / 2 - 1; int cy = height / 2 - 1; board[cx][cy] = PieceColor.WHITE; board[cx + 1][cy] = PieceColor.BLACK; board[cx][cy + 1] = PieceColor.BLACK; board[cx + 1][cy + 1] = PieceColor.WHITE; } public int getHeight() { return height; } public int getWidth() { return width; } public int getPieceColor(int x, int y) { return board[x][y]; } private boolean isOnBoard(int x, int y) { return isOnBoardX(x) && isOnBoardY(y); } private boolean isOnBoardX(int x) { return 0 <= x && x < width; } private boolean isOnBoardY(int y) { return 0 <= y && y < height; } public boolean isGameOver() { return !canPlace(PieceColor.BLACK) && !canPlace(PieceColor.WHITE); } public int getPieceCount(int color) { int count = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (board[x][y] == color) { count++; } } } return count; } public void placeAt(int x, int y,int myColor) { if (canPlaceAt(x, y, myColor)) { board[x][y] = myColor; reverse(x, y, myColor); } } public boolean canPlace(int myColor) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if(canPlaceAt(x,y,myColor)) { return true; } } } return false;
} public boolean canPlaceAt(int x, int y,int myColor) { // return isOnBoard(x, y) && board[x][y] == PieceColor.EMPTY; if (x >= width || y >= width) { return false; } if (board[x][y] != PieceColor.EMPTY) { return false; } if (canPlaceAt(x, y, -1, 1, myColor)) { return true; } if (canPlaceAt(x, y, 1, -1, myColor)) { return true; } if (canPlaceAt(x, y, -1,-1, myColor)) { return true; } if (canPlaceAt(x, y, 1, 1, myColor)) { return true; } if (canPlaceAt(x, y, 0,-1, myColor)) { return true; } if (canPlaceAt(x, y, -1, 0, myColor)) { return true; } if (canPlaceAt(x, y, 0, 1, myColor)) { return true; } if (canPlaceAt(x, y, 1, 0, myColor)) { return true; } return false; } public boolean canPlaceAt(int x, int y, int s, int t, int myColor) { x += s; y += t;
if(x < 0 || x >= width || y < 0 || y >= width) { return false; } if(board[x][y] == myColor) { return false; } if (board[x][y] == PieceColor.EMPTY) { return false; } x += s; y += t; while (x >= 0 && x < width && y >= 0 && y < width) { if (board[x][y] == PieceColor.EMPTY) { return false; } if (board[x][y] == myColor) { return true; } x += s; y += t; } return false; } public void reverse(int x, int y, int myColor) { if (canPlaceAt(x, y, -1, 1, myColor)) { reverse(x, y, -1, 1, myColor); } if (canPlaceAt(x, y, 1, -1, myColor)) { reverse(x, y, 1, -1, myColor); } if (canPlaceAt(x, y, -1,-1, myColor)) { reverse(x, y, -1,-1, myColor); } if (canPlaceAt(x, y, 1, 1, myColor)) { reverse(x, y, 1, 1, myColor); } if (canPlaceAt(x, y, 0,-1, myColor)) { reverse(x, y, 0,-1, myColor); } if (canPlaceAt(x, y, -1, 0, myColor)) { reverse(x, y, -1, 0, myColor); } if (canPlaceAt(x, y, 0, 1, myColor)) { reverse(x, y, 0, 1, myColor); } if (canPlaceAt(x, y, 1, 0, myColor)) { reverse(x, y, 1, 0, myColor); } } public void reverse(int x, int y, int s, int t, int myColor) { x += s; y += t; while (board[x][y] != myColor) { board[x][y] = myColor; x += s; y += t; } } }
import java.awt.GridLayout;
import javax.swing.JComponent;
public class BoardView extends JComponent { public BoardView(Othello othello, OthelloGui othelloGui) { Board board = othello.getBoard(); setLayout(new GridLayout(board.getHeight(),board.getWidth())); for (int y = 0; y < board.getHeight(); y++) { for (int x = 0; x < board.getWidth(); x++) { add(new CellView(x, y, othello, othelloGui)); } } }
public class OthelloGui extends JFrame implements UserIO, OthelloObserver { private BoardView boardView; private BigLabel turnLabel; public OthelloGui(Othello othello) { super("Othello"); setDefaultCloseOperation(EXIT_ON_CLOSE); boardView = new BoardView(othello,this); add(boardView, BorderLayout.CENTER); turnLabel = new BigLabel("先手(黒)の番"); turnLabel.setFontSize(20.0f); add(turnLabel,BorderLayout.NORTH); pack(); setVisible(true); } private Position clickPosition; public void userClicked(int x, int y) { clickPosition = new Position(x,y); } public Position readPlacePosition() { // マルチスレッドをまだ勉強していないので、仕方がないからbusy wait clickPosition = null; while (clickPosition == null) { try { Thread.sleep(100); } catch (InterruptedException e) { } } return clickPosition; } public void showMessage(String message) { JOptionPane.showMessageDialog(this, message); } public void gameStarted(Othello othello) { repaint(); } public void gameEnded(Othello othello) { repaint(); Player p1 = othello.getPlayer1(); String s1 = othello.getPieceCount(p1)+"個"; Player p2 = othello.getPlayer2(); String s2 = othello.getPieceCount(p2)+"個";
String result = "引き分け"; Player p = othello.getWinner(); if (p != null) { result = p+"の勝ち"; }
showMessage(result+" "+s1+" : "+s2); } public void turnStarted(Othello othello) { Player p = othello.getCurrentPlayer(); turnLabel.setText(p+"の番"); repaint(); } public void turnEnded(Othello othello) { if (othello.isPassed()) { Player p = othello.getCurrentPlayer(); showMessage(p+" パス"); } repaint(); } }
public class PieceColor { /* * これらの定数は、以下の2箇所で使う * (1) ボードのマスの状態 : EMPTY,BLACK,WHITE * (2) プレイヤーの色またはコマの色 : BLACK,WHITE (この場合EMPTYはない) * * 注) * enumを使いたいところだが、まだやっていないのでstatic final定数で実現。 * * クラス名はColorにしたかったが、packageをうまく使えない場合、 * java.awt.Colorと混乱する恐れがあるので、少し長いけどPieceColorにした。 * * PieceColor.BLACKは長いので、単純名のBLACKを使いたい場合、 * static importを行う * interfaceにしてimplementsする * などの解決策がある。 * しかし、まだやっていないので、長いけどPieceColor.BLACKと書く。 */ public static final int EMPTY = 0; public static final int BLACK = 1; public static final int WHITE = 2; public static int getOpponent(int myColor) { if (myColor == BLACK) { return WHITE; } else { return BLACK; } } public static String toColorString(int color) { if (color == BLACK) { return "黒"; } else if (color == WHITE) { return "白"; } else { return ""; } } }
public interface Player { String getName(); int getColor(); Position nextMove(Board board); }
public class Position { private int x; private int y; public Position(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } @Override public String toString() { return "("+x+","+y+")"; }
}
import java.util.Random;
public class RandomPlayer extends BasicPlayer { public RandomPlayer(int color) { this(color,"Computer Player"); } public RandomPlayer(int color, String name) { super(color,name); } private Random random = new Random(); public Position nextMove(Board board) { int x, y; do { x = random.nextInt(board.getWidth()); y = random.nextInt(board.getHeight()); } while (!board.canPlaceAt(x, y, getColor())); return new Position(x, y); } }
public interface UserIO { Position readPlacePosition(); void showMessage(String message); }