import java.util.*; import java.io.*; // 手とその評価値を表す抽象クラス abstract class Move { // 評価値は doubleで表す double value; // 評価値の getter double getValue(){ return value; } // 評価値の setter void setValue(double value){ this.value=value; } } // 動かした盤面を元に戻せるように記憶 abstract class TrailStack{ } // 盤面の状態を表す abstract class State{ // 次の可能手の MoveのVectorを返す abstract Vector nextMoves(); // 手にしたがって盤面を変化させる. // TrailStackを返す abstract TrailStack doMove(Move move); // TrailStackに従って盤面を戻す abstract void undoMove(TrailStack trail); // 盤面の評価値 abstract double eval(); } // 次の手を決めるアルゴリズム abstract class Search { // 次の手を返す abstract Move bestMove(State state); } // Depth1Search // 可能手を全部生成して,その盤面の評価値で最良のものを選択する // class Depth1Search extends Search { // return null if there exists no moves Move bestMove(State state){ // 可能な手を全部生成する Vector moves=state.nextMoves(); int size=moves.size(); // 最良の手が複数あるのでそれを管理する Vector bestMoves=new Vector(); Move best=null; // 最良の手の値を負の無限大に設定しておく double bestVal=Double.NEGATIVE_INFINITY; for(int i=0;i< size;i++){ Move move=(Move)moves.elementAt(i); // 1手進めてみる TrailStack trail=state.doMove(move); // 評価値を計算 // 1手進んだ時(相手の番)の評価値なので符号を入れ替える. double moveVal= -state.eval(); // 戻す state.undoMove(trail); move.setValue(moveVal); // 評価値がこれまでのbestを超えた時 if(bestVal< moveVal){ bestVal=moveVal; bestMoves=new Vector(); bestMoves.addElement(move); } // 評価値がこれまでのbestと同じ時 else if(bestVal==moveVal){ bestMoves.addElement(move); } } // bestの中から乱数で選択 int bestSize=bestMoves.size(); int selected=(int)(Math.random()*bestSize); return (Move)bestMoves.elementAt(selected); } } // // MinMax search // class MinMaxSearch extends Search { // どの深さまで読むか? int maxLevel; // インスタンス生成時にレベルを設定 MinMaxSearch(int maxLevel){ this.maxLevel=maxLevel; } // レベルに従った評価値 double eval(State state,int level){ // 末端のレベルでは局面の評価値 if(level==0) return state.eval(); // そうでない時はベストの手の時の評価値 Move best=bestMove(state,level); if(best==null) return state.eval(); return best.getValue(); } // 手の生成 Move bestMove(State state){ return bestMove(state,maxLevel); } // 可能な手がない時は0を返す Move bestMove(State state,int level){ // 可能な手を全部生成する Vector moves=state.nextMoves(); int size=moves.size(); // 最良の手が複数あるのでそれを管理する Vector bestMoves=new Vector(); Move best=null; // 最良の手の値を負の無限大に設定しておく double bestVal=Double.NEGATIVE_INFINITY; for(int i=0;i< size;i++){ Move move=(Move)moves.elementAt(i); // 1手進めてみる TrailStack trail=state.doMove(move); // 評価値を計算 // 1手進んだ時(相手の番)の評価値なので符号を入れ替える. double moveVal= -eval(state,level-1); // 戻す state.undoMove(trail); move.setValue(moveVal); // 評価値がこれまでのbestを超えた時 if(bestVal< moveVal){ bestVal=moveVal; bestMoves=new Vector(); bestMoves.addElement(move); } // 評価値がこれまでのbestと同じ時 else if(bestVal==moveVal){ bestMoves.addElement(move); } } // bestの中から乱数で選択 int bestSize=bestMoves.size(); if(bestSize>0){ int selected=(int)(Math.random()*bestSize); return (Move)bestMoves.elementAt(selected); } else return null; } } // // Alpha-beta Search // class AlphaBetaSearch extends Search { // どの深さまで読むか? int maxLevel; // インスタンス生成時にレベルを設定 AlphaBetaSearch(int maxLevel){ this.maxLevel=maxLevel; } // レベルに従った評価値 // alpha double eval(State state,int level,double alpha, double beta){ // 末端のレベルでは局面の評価値 if(level==0) return state.eval(); // そうでない時はベストの手の時の評価値 Move best=bestMove(state,level,alpha,beta); if(best==null) return state.eval(); return best.getValue(); } // 手の生成 Move bestMove(State state){ return bestMove(state,maxLevel,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY); } // 可能な手がない時は0を返す // beta値よりも大きい値を返す場合は選ばれない Move bestMove(State state,int level,double alpha,double beta){ // 可能な手を全部生成する Vector moves=state.nextMoves(); int size=moves.size(); // 最良の手が複数あるのでそれを管理する Vector bestMoves=new Vector(); Move best=null; // 最良の手の値を負の無限大に設定しておく double bestVal=Double.NEGATIVE_INFINITY; for(int i=0;i< size;i++){ Move move=(Move)moves.elementAt(i); // 1手進めてみる TrailStack trail=state.doMove(move); // 評価値を計算 // 1手進んだ時(相手の番)の評価値なので符号を入れ替える. // bestValよりも小さい値は選ばれない double moveVal= -eval(state,level-1,-beta,-alpha); // 戻す state.undoMove(trail); move.setValue(moveVal); // 評価値がこれまでのbestを超えた時 if(bestVal< moveVal){ if(moveVal>beta){ // この木は刈られる return move; } bestVal=moveVal; if(bestVal>alpha) alpha=bestVal; bestMoves=new Vector(); bestMoves.addElement(move); } // 評価値がこれまでのbestと同じ時 else if(bestVal==moveVal){ bestMoves.addElement(move); } } // bestの中から乱数で選択 int bestSize=bestMoves.size(); if(bestSize>0){ int selected=(int)(Math.random()*bestSize); // return (Move)bestMoves.elementAt(selected); return (Move)bestMoves.elementAt(0); } else return null; } } class OthelloMove extends Move{ int position; OthelloMove(int position,double value){ this.position=position; this.value=value; } OthelloMove(int position){ this(position,0.0); } int getPosition(){ return position; } int getX(){ return position%10; } int getY(){ return position/10; } } class OthelloTrail extends TrailStack{ int position; Vector reverses=new Vector(); OthelloTrail(int position){ this.position=position; } void add(int position){ reverses.addElement(new Integer(position)); } int getPosition(){ return position; } int size(){ return reverses.size(); } int get(int index){ return ((Integer)(reverses.get(index))).intValue(); } } class OthelloState extends State{ static final int Black=1; static final int White=-1; static final int Empty=0; // 盤外かどうかのチェックをさぼるためには以下のチェックが有効 static final int Edge=2; int board[]=new int[10*10]; int turn; // 初期状態のオセロ盤を作る OthelloState(){ int color; for(int x=0;x<10;x++) for(int y=0;y<10;y++){ if(x==0 || x==9 || y==0 || y==9) color=Edge; else if((x==4 && y==4)||(x==5 && y==5)) color=White; else if((x==4 && y==5)||(x==5 && y==4)) color=Black; else color=Empty; board[y*10+x]=color; } turn=Black; } static final int directions[]={-11,-10,-9,-1,1,9,10,11}; int opponent(int turn){ return -turn; } boolean checkDir(int position,int dir){ // System.out.println("checkDir("+position+","+dir+")"); int oturn=opponent(turn),piece; if(board[position+dir]!=oturn) return false; for(position+=dir*2;(piece=board[position])==oturn;position+=dir){ // System.out.println("board["+position+"]="+piece); } // System.out.println("lastpiece="+piece+",turn="+turn); if(piece!=turn) return false; return true; } void reverseDir(int position,int dir,OthelloTrail trail){ int oturn=opponent(turn),piece; for(position+=dir;(piece=board[position])==oturn;position+=dir){ board[position]=turn; trail.add(position); } } boolean legalMove(int position){ if(board[position]!=Empty)return false; for(int dir=0;dir< directions.length;dir++) if(checkDir(position,directions[dir])) return true; return false; } boolean legalMove(int x, int y){ if(1<=x && x<=8 && 1<=y && y<=8 && legalMove(y*10+x)) return true; return false; } Vector nextMoves(){ Vector ret=new Vector(); for(int x=1;x<9;x++) for(int y=1;y<9;y++){ int position=y*10+x; if(legalMove(position)) ret.add(new OthelloMove(position)); } return ret; } boolean canPass(){ for(int x=1;x<9;x++) for(int y=1;y<9;y++){ int position=y*10+x; if(legalMove(position)) return false; } return true; } void doPass(){ turn=opponent(turn); } TrailStack doMove(Move gmove){ OthelloMove move=(OthelloMove)gmove; int position=move.getPosition(); OthelloTrail trail=new OthelloTrail(position); board[position]=turn; for(int dir=0;dir< directions.length;dir++) if(checkDir(position,directions[dir])) reverseDir(position,directions[dir],trail); turn=opponent(turn); return trail; } // void undoMove(TrailStack tstack){ OthelloTrail trail=(OthelloTrail)tstack; board[trail.getPosition()]=Empty; int size=trail.size(); for(int i=0;i< size;i++){ int position=trail.get(i); board[position]=turn; } turn=opponent(turn); } // 一番単純な石の数による評価関数 double eval(){ int sum=0; for(int y=1;y<9;y++) for(int x=1;x<9;x++) sum+=board[y*10+x]; return (double)(turn*sum); } int result(){ int sum=0; for(int y=1;y<9;y++) for(int x=1;x<9;x++) sum+=board[y*10+x]; return sum; } // void showState(){ System.out.println(" abcdefgh"); for(int y=1;y<9;y++){ System.out.print(y); for(int x=1;x<9;x++){ System.out.print("O.@".charAt(board[y*10+x]+1)); } System.out.println(); } } } class HumanSearch extends Search { BufferedReader d; HumanSearch(BufferedReader d){ this.d=d; } Move bestMove(State state){ OthelloState game=(OthelloState)state; try{ while(true){ System.out.println("次の手を入力してください(例 d3)"); String str=d.readLine(); if(str.length()==2){ int x=" abcdefgh".indexOf(str.charAt(0)); int y=" 12345678".indexOf(str.charAt(1)); if(1<=x && x<=8 && 1<=y && y<=8){ if(game.legalMove(x,y)){ return new OthelloMove(y*10+x); } else System.out.println(str+"の地点には打てません"); } else System.out.println("入力の形式が間違っています"+x+","+y); } else System.out.println("入力は半角2文字でなくてはいけません"); } } catch(IOException e){ } return null; } } class Othello { OthelloState game; // コンストラクタ Othello(){ game=new OthelloState(); } // 終局 void gameEnd(){ int result=game.result(); if(result>0) System.out.println("黒が"+result+"個勝ちました"); else if(result<0) System.out.println("白が"+(-result)+"個勝ちました"); else System.out.println("引き分けです"); System.exit(0); } void startGame(Search players[]) throws IOException { for(int player=0;;player=1-player){ game.showState(); if(game.canPass()){ game.doPass(); if(game.canPass()){ gameEnd(); } System.out.println("次に打てる手はないのでパスします"); } else{ int x,y; OthelloMove bestMove=(OthelloMove)players[player].bestMove(game); System.out.println("着手は "+ " abcdefgh".charAt(bestMove.getX())+ " 12345678".charAt(bestMove.getY())+"です."); System.out.println("評価値は "+bestMove.getValue()+"です."); game.doMove(bestMove); } } } // main関数 public static void main(String args[]) throws IOException{ Othello othello=new Othello(); BufferedReader d=new BufferedReader(new InputStreamReader(System.in)); Search players[]=new Search[2]; players[0]=new HumanSearch(d); // players[0]=new Depth1Search(); // players[0]=new MinMaxSearch(6); players[1]=new MinMaxSearch(6); othello.startGame(players); } }