LoginSignup
23
25

More than 5 years have passed since last update.

Javaでオセロを書いてみました。

Last updated at Posted at 2013-07-14

コマンドプロンプト上で動作します。Othello_CUI.javaという名前で保存して

$ javac Othello_CUI.java
$ java Othello_CUI

で実際に実行することができます。

package sandbox;

import java.util.Scanner;
public class Main {

    private static final int  L       = 10, // オセロ盤の周囲の「壁」を含めた、オセロ盤の縦と横のマスの数。オセロ版は8×8だが、周囲に壁を設けると10×10になる。
                                 X       =  0, // X軸の値が格納されている配列の位置。二次元配列dirを参照する際に用いる。
                                 Y       =  1, // Y軸の値が格納されている配列の位置。二次元配列dirを参照する際に用いる。
                                 EMPTY   =  0, // オセロ盤のマスに何も置かれていない事を表す。
                                 BLACK   =  1, // オセロ盤のマスに黒石が置かれていること、黒の手番であること、黒番のプレイヤーのこと
                                 WHITE   =  2, // オセロ盤のマスに白石が置かれていること、白の手番であること、白番のプレイヤーのこと
                                 DRAW    =  3,// 引き分けを表す。
                                 WALL    =  9, // オセロ盤の周囲に仮想的に用意した「壁」を表す。
                                 PASS    =  1, // 手番をパスするコマンド
                                 GIVE_UP =  3,// 投了するコマンド
                                 EXIT    =  2;// ゲームをやめるコマンド

    /** オセロのマスに置いた石の周囲8方向を表す座標の組。 */
    private static final int[][] dir     = {{-1,-1},// 左下
                                            { 0,-1},// 下
                                            { 1,-1},// 右下
                                            { 1, 0},//右
                                            { 1, 1},//右上
                                            { 0, 1},//上
                                            {-1, 1},//左上
                                            {-1, 0}};//左

    /** オセロの盤面を座標と見做したときのx軸、y軸の値 */
    private static       int    x,y;

    /** コマンドプロンプト上に描画するメッセージ */
    private static final String LINE       = "--------------------------\n",
                                TITLE      = "\n" + LINE + "---       オセロ       ---\n" + LINE + "\n",
                                USAGE      = "  ---    遊び方    ---    \n  縦 5,横 3 のマスに置く => 「5 3」と入力。\n パス: pass \n 投了:give up\n ゲームの終了:exit\n",
                                URGES      = "\n駒を置いて下さい。=>  ",
                                RANGE      = "[1-8]",
                                S_BLACK    = "黒",
                                S_WHITE    = "白",
                                S_TURN     = "番",
                                S_DRAW     = "\n   ---   引き分け   ---   \n",
                                PRE_LINE   = "\n   ---   ",
                                POST_LINE  = "   ---       \n\n",
                                BLACK_TURN = PRE_LINE + S_BLACK + S_TURN + POST_LINE,
                                WHITE_TURN = PRE_LINE + S_WHITE + S_TURN + POST_LINE,
                                S_VICTORY  = "の勝ち  ---   \n",
                                S_ERROR    = "駒が置けません。別のマスを選択して下さい。\n",
                                THANKS     = "Thank you for playing. Good by the next time.";

    /** オセロ盤の盤面 */
    private static int[][] board   = new int[L][L];

    /* オセロの手番。黒番を先手とする。 */
    private static int     turn    = BLACK,

    /* 勝者を表す。引き分けの場合もある */
                           victory ;

    /** 手番クラス */
    static class Turn{

        /** 手番を表示する。*/
        private static void show(){
            if    (turn==BLACK){print(BLACK_TURN);}
            else               {print(WHITE_TURN);}
        }

        /** 手番を入れ替える */
        private static void shift(){
            turn = 3 - turn;
        }
    }

    /** オセロ盤クラス */
    static class Board{

        /** オセロ盤を初期配置にする */
        public static void init(){
            for(int i=0;i<L;i++){
                board[0][i] =
                board[9][i] = WALL;
            }
            for(int i=1;i<L-1;i++){
                board[i][0] =
                board[i][9] = WALL;
            }
            board[4][4] =
            board[5][5] = WHITE;
            board[4][5] =
            board[5][4] = BLACK;
        }

        /** オセロ盤をコマンドプロンプト上に表示する */
        public static void show(){

            print(LINE);
            print("     ");
            for(int i=1;i<L-1;i++){
                print(i);
                if(i<L-2){print(" ");}
            }

            for(int i=0;i<L;i++){
                if(0<i && i<L-1){
                    print("\n %d ",i);
                }else {
                    print("\n   ");
                }
                for(int j=0;j<L;j++){
                    switch(board[i][j]){
                        case WALL  : {print("+"); break;}
                        case EMPTY : {print("_"); break;}
                        case WHITE : {print("o"); break;}
                        case BLACK : {print("*");       }
                    }
                    print(" ");
                }
                if(i<L-1){print(" ");}
                else     {print("\n") ;}
            }
            print(LINE);
        }
    }

    /** ユーザーの入力を標準入力から読み込む */
    private static Scanner  sc = new Scanner(System.in);

    /** エントリポイント */
    public static void main(String[] args){

        Board.init(); // オセロ盤を初期化する

        print(TITLE); // タイトルと使い方を表示
        print(USAGE);

        out:while(true){
            Turn.show();
            Board.show();
            print(URGES);
            switch(input()){
                case EXIT    : {print(THANKS);       // ゲームをやめる
                                break out;          }
                case GIVE_UP : {victory = 3 - turn ; // 投了する
                                show_result();
                                break out;          }
                case PASS    : {Turn.shift();          // 手番をPASSする
                                continue;           }
            }
            if(update()){            // 盤面を更新
                if(judge()){         // 勝敗を判定
                    show_result();  // 勝敗を表示
                    break;
                }
                Turn.shift();        // 手番を交代する
            } else {
                print(S_ERROR);     // 石が置けなかったとき
            }
        }
    }

    /**
     * 勝敗を判定する
     * @return 勝敗が決まったとき、true
     */
    private static boolean judge(){
        int black = 0,
            white = 0;

        // 石を数える
        for(int i=1;i<L-1;i++){
            for(int j=1;j<L-1;j++){
                if(board[i][j]==BLACK){
                    black++;
                } else if(board[i][j]==WHITE){
                    white++;
                }
            }
        }

        // 盤面が石で埋まっているとき、石の数の多い方を勝者とする。
        if(black+white==8*8){
            if(black<white){
                victory = WHITE;
            } else if(black==white){
                victory = DRAW;
            } else {
                victory = BLACK;
            }
            return true;
        }
        return false;
    }

    /**
     * 盤に石を置き、返せる相手の石を引っ繰り返す
     * @return 石を置けるとき、true
     */
    private static boolean update(){
        if(chk_cell()){
            flip();
            return true;
        } else {
            return false;
        }
    }

    /**
     * 置こうと試みたマスに石が置けるかどうか判定する。
     * @return 石が置けるとき、true
     */
    private static boolean chk_cell(){
        if(board[y][x]!=EMPTY){return false;} // 石を置こうとしたマスが空でないときは、石を置けない。
        boolean result = false;
        out:for(int i=0;i<dir.length;i++){ // 石の周囲を八方向全てチェックする。
            int j=x,k=y;
            j += dir[i][X]; // j,kに石の周囲8マスのうちいずれかひとつの座標を取る。
            k += dir[i][Y];
            if(board[k][j] == 3 - turn){ // 石を置こうとしたマスの周りに相手の石がある。
                while(true){
                    j += dir[i][X]; // 更に先のマスをチェックしてみる。
                    k += dir[i][Y];
                    if(board[k][j]==turn){ // 相手の石のさらに先に自分の石があるので、相手の石を裏返せる。
                        result = true; // 石が置けることが分かったので、trueを返してメソッドを抜ける。
                        break out;
                    } else if(board[k][j]== 3 - turn){ // 相手の石がまだ続いているので、更に先をチェック。
                        continue;
                    }
                    break; // 相手の石の先が空のマスか壁なので、この方向は石を置ける状態にない。
                }
            }
        }
        return result; // 判定結果を返却する。
    }

    /** 相手の石を引っ繰り返す */
    private static void flip(){
        board[y][x] = turn;
        for(int i=0;i<dir.length;i++){
            int j=x,k=y;
            j += dir[i][X];k += dir[i][Y];
            if(board[k][j] == 3 - turn){
                out:while(true){
                    j += dir[i][X];k += dir[i][Y];
                    if(board[k][j]==turn){
                        while(true){
                            j -= dir[i][X]; k -= dir[i][Y];
                            if(board[k][j]==turn){break out;}
                            board[k][j] = turn;
                        }
                    } else if(board[k][j]== 3 - turn){
                        continue;
                    }
                    break;
                }
            }
        }
    }

    /** 勝敗の結果をコマンドプロンプト上に表示する */
    private static void show_result(){
        String ret;
        if   (victory == DRAW) {ret = S_DRAW ;}
        else{
            String v;
            if    (victory == BLACK){v = S_BLACK ;}
            else                    {v = S_WHITE ;}
            ret = PRE_LINE + v + S_VICTORY ;
        }
        print(ret);
        Board.show();
    }

    /** 文字列出力のための簡易メソッド */
    private static void print(String s,Object... i){System.out.printf(s,i);}

    /** 数値を文字列出力するための簡易メソッド */
    private static void print(int i){System.out.print(i);}

    /** 標準入力からユーザーの入力を読み込む */
    private static int input(){
        int ret = 0;
        while(true){
            String[] s = sc.nextLine().split("\\s");
            if(s.length==1){
                if     (s[0].equals("pass")){ret = PASS ; break;}
                else if(s[0].equals("exit")){ret = EXIT ; break;}
            } else if(s.length==2){
                if     (s[0].equals("give") && s[1].equals("up")){ret = GIVE_UP ; break;}
                else if(s[0].matches(RANGE) && s[1].matches(RANGE)){
                    y = Integer.parseInt(s[0]);
                    x = Integer.parseInt(s[1]);
                    break;
                }
            }
            print(S_ERROR);
            print(USAGE);
            print(URGES);
        }
        return ret;
    }
}
23
25
17

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
25