勝敗を判定する
勝敗判定機能をつけます。また黒と白の石の数が上部に表示されるようにします。
石の数を数える
勝敗を判定するにはまず黒と白の石の数を数える必要があります。countStone()で数えています。
private Counter countStone() { Counter counter = new Counter(); for (int y = 0; y < MASU; y++) { for (int x = 0; x < MASU; x++) { if (board[y][x] == BLACK_STONE) counter.blackCount++; if (board[y][x] == WHITE_STONE) counter.whiteCount++; } } return counter; }
盤面を走査していって黒石が打たれていたら黒石の数(counter.blackCount)を1増やし、白石が打たれていたら白石の数(counter.whiteCount)を1増やしています。このメソッドでは黒石と白石の数の両方の値を返したいです。しかし、メソッドの戻り値は1 つであるため2つのint型の値を返すことはできません。こんな場合は2つの値(ここでは黒石と白石の数)をクラスで包み込んで値を設定し、オブジェクトを1つだけ返す手法がよく使われます。黒石と白石の数をを持つクラスがCounterクラスです。
private class Counter { public int blackCount; public int whiteCount; public Counter() { blackCount = 0; whiteCount = 0; } }
簡単なクラスなので内部クラス(クラスの中のクラス)としました。countStone()の戻り値はcounterオブジェクト1つだけです。1 つのオブジェクトでblackCountとwhiteCountの2つの値が返せます。
情報パネル
黒白の石の数を表示する領域を作りました。InfoPanelとして実装し、フレームの上の方に配置しました。
public Othello() { ・・・ Container contentPane = getContentPane(); // 情報パネルを作成する InfoPanel infoPanel = new InfoPanel(); contentPane.add(infoPanel, BorderLayout.NORTH); // メインパネルを作成してフレームに追加 MainPanel mainPanel = new MainPanel(infoPanel); contentPane.add(mainPanel, BorderLayout.CENTER); ・・・ }
MainPanelにInfoPanelを渡している点に注意してください。MainPanelで石の数を数えてInfoPanelへ表示するので MainPanelからInfoPanelへの参照がないと情報パネルを操作できません。MainPanelの中のpaintComponent()でラベルを変更しています。
// 盤面の石の数を数える Counter counter = countStone(); // ラベルに表示 infoPanel.setBlackLabel(counter.blackCount); infoPanel.setWhiteLabel(counter.whiteCount);
盤面の石の数を数えてラベルを変更しているだけです。
勝敗判定
オセロの勝敗は64個の石がすべて打たれたときに決まります。最初に4個の石があるため打たれる石の数は60個です。そこで打たれた石の数を数える変数を用意します。
// 終了時の石の数(オセロは8x8-4=60手で終了する) private static final int END_NUMBER = 60; // 打たれた石の数 private int putNumber;
putNumberが60個になったら終了します。勝敗判定を行うメソッドはmouseClicked()にあるendGame()です。石が打たれた後、毎回勝敗を判定しています。
private void endGame() { // 打たれた石の数が60個(全部埋まった状態)以外は何もしない if (putNumber == END_NUMBER) { // 黒白両方の石を数える Counter counter; counter = countStone(); // 黒が過半数(64/2=32)を取っていたら勝ち // 過半数以下なら負け // 同じ数なら引き分け if (counter.blackCount > 32) { gameState = YOU_WIN; } else if (counter.blackCount < 32) { gameState = YOU_LOSE; } else { gameState = DRAW; } } }
endGame()では打たれた石の数が60個以外のときは何もしません。60個のときは黒と白の石の数を数えます。黒が過半数取っていれば勝ちです。過半数取れなったら負けです。同じ数だった場合は引き分けです。プレイヤーは常に黒だと想定しています。今はどっちも自分で動かしてますが(笑)スタート・ゲームオーバー画面、スタート画面とゲームオーバー画面も簡単ですがつけて見ました。gameStateで状態を管理し、状態に応じて処理を変えるのが常套手段です。
// ゲーム状態 private static final int START = 0; private static final int PLAY = 1; private static final int YOU_WIN = 2; private static final int YOU_LOSE = 3; private static final int DRAW = 4; // ゲーム状態 private int gameState;
paintComponent()ではgameStateに応じて描くものを変えています。
public void paintComponent(Graphics g) { super.paintComponent(g); // 盤面を描く drawBoard(g); switch (gameState) { case START : drawTextCentering(g, "OTHELLO"); break; case PLAY : // 石を描く drawStone(g); // 盤面の石の数を数える Counter counter = countStone(); // ラベルに表示 infoPanel.setBlackLabel(counter.blackCount); infoPanel.setWhiteLabel(counter.whiteCount); break; case YOU_WIN : drawTextCentering(g, "YOU WIN"); break; case YOU_LOSE : drawTextCentering(g, "YOU LOSE"); break; case DRAW : drawTextCentering(g, "DRAW"); break; } }
マウスをクリックしたときの動作も画面に応じて変えています。
public void mouseClicked(MouseEvent e) { switch (gameState) { case START : // START画面でクリックされたらゲーム開始 gameState = PLAY; break; case PLAY : // どこのマスかを調べる int x = e.getX() / GS; int y = e.getY() / GS; // (x, y)に石がおける場合だけ打つ if (canPutDown(x, y)) { // その場所に石を打つ putDownStone(x, y); // ひっくり返す reverse(x, y); // 手番を変える flagForWhite = !flagForWhite; } // 終了したか調べる endGame(); break; case YOU_WIN : case YOU_LOSE : case DRAW : // ゲーム終了時にクリックされたらスターとへ戻る gameState = START; // 盤面初期化 initBoard(); break; } // 再描画する repaint(); }