人間の考え方を囲碁プログラムで実現するとき、 最も簡単なのは次のアルゴリズムです。 多くの囲碁プログラムの思考ルーチンも、 このアルゴリズムを基本に作られています。 (強いプログラムはもっといろいろなことを考えていますし、 順番もこんなに単純ではありません。)
まず最初に、各石から距離3以内(二間トビまたはケイマまで)を、 その石の色の地と考えてみます。
(例) 盤面 地(■で表している) +++++++ +++■+++ +++++++ ++■■■++ +++++++ +■■■■■+ +++●+++ ■■■●■■■ +++++++ +■■■■■+ +++++++ ++■■■++ +++++++ +++■+++ +++++++++ +++■+++++ +++++++++ ++■■■■+++ +++++++++ +■■■■■■++ +++●+++++ ■■■●■■■■+ +++++●+++ +■■■■●■■■ +++++++++ ++■■■■■■+ +++++++++ +++■■■■++ +++++++++ +++++■+++相手の石が近くにある場合は、勢力が打ち消しあって、 どちらの地でもないと考えてみます。
(例) +++++++++ +++■+□+++ +++++++++ ++■■+□□++ +++++++++ +■■■+□□□+ +++●+○+++ ■■■●+○□□□ +++++++++ +■■■+□□□+ +++++++++ ++■■+□□++ +++++++++ +++■+□+++これをプログラムで書くと、次のようになります。
/*------------------------------------------------------------------*/ /* 盤面の評価 */ /*------------------------------------------------------------------*/ static int EvalBoard( int board[][BOARD_SIZE] ) { int blackBoard[BOARD_SIZE][BOARD_SIZE]; /* 黒石の影響範囲 */ int whiteBoard[BOARD_SIZE][BOARD_SIZE]; /* 白石の影響範囲 */ int totalBoard[BOARD_SIZE][BOARD_SIZE]; /* 地 */ int x, y, value; /* 初期化 */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ blackBoard[y][x] = FALSE; whiteBoard[y][x] = FALSE; totalBoard[y][x] = SPACE; } } /* 距離3以内なら地とみなす */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( board[y][x] == BLACK ){ SetValue( blackBoard, x-3, y ); SetValue( blackBoard, x-2, y-1 ); SetValue( blackBoard, x-2, y ); SetValue( blackBoard, x-2, y+1 ); SetValue( blackBoard, x-1, y-2 ); SetValue( blackBoard, x-1, y-1 ); SetValue( blackBoard, x-1, y ); SetValue( blackBoard, x-1, y+1 ); SetValue( blackBoard, x-1, y+2 ); SetValue( blackBoard, x, y-3 ); SetValue( blackBoard, x, y-2 ); SetValue( blackBoard, x, y-1 ); SetValue( blackBoard, x, y ); SetValue( blackBoard, x, y+1 ); SetValue( blackBoard, x, y+2 ); SetValue( blackBoard, x, y+3 ); SetValue( blackBoard, x+1, y-2 ); SetValue( blackBoard, x+1, y-1 ); SetValue( blackBoard, x+1, y ); SetValue( blackBoard, x+1, y+1 ); SetValue( blackBoard, x+1, y+2 ); SetValue( blackBoard, x+2, y-1 ); SetValue( blackBoard, x+2, y ); SetValue( blackBoard, x+2, y+1 ); SetValue( blackBoard, x+3, y ); }else if( board[y][x] == WHITE ){ SetValue( whiteBoard, x-3, y ); SetValue( whiteBoard, x-2, y-1 ); SetValue( whiteBoard, x-2, y ); SetValue( whiteBoard, x-2, y+1 ); SetValue( whiteBoard, x-1, y-2 ); SetValue( whiteBoard, x-1, y-1 ); SetValue( whiteBoard, x-1, y ); SetValue( whiteBoard, x-1, y+1 ); SetValue( whiteBoard, x-1, y+2 ); SetValue( whiteBoard, x, y-3 ); SetValue( whiteBoard, x, y-2 ); SetValue( whiteBoard, x, y-1 ); SetValue( whiteBoard, x, y ); SetValue( whiteBoard, x, y+1 ); SetValue( whiteBoard, x, y+2 ); SetValue( whiteBoard, x, y+3 ); SetValue( whiteBoard, x+1, y-2 ); SetValue( whiteBoard, x+1, y-1 ); SetValue( whiteBoard, x+1, y ); SetValue( whiteBoard, x+1, y+1 ); SetValue( whiteBoard, x+1, y+2 ); SetValue( whiteBoard, x+2, y-1 ); SetValue( whiteBoard, x+2, y ); SetValue( whiteBoard, x+2, y+1 ); SetValue( whiteBoard, x+3, y ); } } } value = 0; for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( board[y][x] == BLACK ){ totalBoard[y][x] = BLACK; }else if( board[y][x] == WHITE ){ totalBoard[y][x] = WHITE; }else{ if( blackBoard[y][x] == TRUE ){ if( whiteBoard[y][x] == TRUE ){ /* 黒地と白地が同じ点ならば相殺される */ continue; }else{ /* 黒地としてカウント */ totalBoard[y][x] = BLACK; value++; } }else{ if( whiteBoard[y][x] == TRUE ){ /* 白地としてカウント */ totalBoard[y][x] = WHITE; value--; } } } } } return( value ); } /*------------------------------------------------------------------*/ /* 地の設定 */ /*------------------------------------------------------------------*/ static void SetValue( int board[][BOARD_SIZE], int x, int y ) { if( x > 0 && x < (BOARD_SIZE-1) && y > 0 && y < (BOARD_SIZE-1) ){ board[y][x] = TRUE; } }このプログラムで次の盤面を認識させてみると、次のような結果になりました。
盤面 地 +++++++++++++++++++ +■++□+++□□□□□□□++++ +++++++++++++++○+++ ■++++□+□□+□□□□+○+++ ++●○++++++○++○++●++ ++●○++□□++○□□○++●+■ ++●○++++○++++++●+++ ++●○++□+○++++++●+■■ +++○+++++●+++++++++ +++○+□+++●++++++■■+ +●●++++++++++++●+++ ■●●+++□++++■■+■●■■■ ++++○++++++++++++++ ++++○□□□+■■++■■■■■+ ++○++++++++++++++++ ++○+□□□++■++++■■■++ +++++++++++++++++++ □++□□□++++++++■■■++ +++++++++++++++++++ +□□□□++++++++■■■■■+ +++++++++++++++●+++ ++□□++++++++■■■●■■■ +++++++++++++++++++ ++□□□++++++++■■■■■+ +++++++++++++++++++ +□□□□□++++++++■■■++ +++○+++++++++++++++ □□□○□+□++++++++■+++ +++++++++++++++++++ +□□□++■+++++++■■■++ +++○+++++++++++++++ □□□○+++■+++++■■■■■+ +++++●+++++++++●+++ +□+++●■■■+++■■■●■■■ +++++++++++++++++++ ++□++■■■+++++■■■■■+ +++++++++++++++++++ +++□■■■+++++++■■■++
こんどは、各石から距離3以内(二間トビまたはケイマまで)を、 その石の影響範囲と考えます。 そして、各点において、その点に対して影響力の強い方 (その点に影響を及ぼす黒石の数と白石の数の多い方)の色の地と認識します。
これをプログラムで書くと、次のようになります。
/*------------------------------------------------------------------*/ /* 盤面の評価 */ /*------------------------------------------------------------------*/ static int EvalBoard( int board[][BOARD_SIZE] ) { int blackBoard[BOARD_SIZE][BOARD_SIZE]; /* 黒石の影響範囲 */ int whiteBoard[BOARD_SIZE][BOARD_SIZE]; /* 白石の影響範囲 */ int totalBoard[BOARD_SIZE][BOARD_SIZE]; /* 地 */ int x, y, value; /* 初期化 */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ blackBoard[y][x] = 0; whiteBoard[y][x] = 0; totalBoard[y][x] = SPACE; } } /* 距離3以内なら地とみなす */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( board[y][x] == BLACK ){ SetValue( blackBoard, x-3, y ); SetValue( blackBoard, x-2, y-1 ); SetValue( blackBoard, x-2, y ); SetValue( blackBoard, x-2, y+1 ); SetValue( blackBoard, x-1, y-2 ); SetValue( blackBoard, x-1, y-1 ); SetValue( blackBoard, x-1, y ); SetValue( blackBoard, x-1, y+1 ); SetValue( blackBoard, x-1, y+2 ); SetValue( blackBoard, x, y-3 ); SetValue( blackBoard, x, y-2 ); SetValue( blackBoard, x, y-1 ); SetValue( blackBoard, x, y ); SetValue( blackBoard, x, y+1 ); SetValue( blackBoard, x, y+2 ); SetValue( blackBoard, x, y+3 ); SetValue( blackBoard, x+1, y-2 ); SetValue( blackBoard, x+1, y-1 ); SetValue( blackBoard, x+1, y ); SetValue( blackBoard, x+1, y+1 ); SetValue( blackBoard, x+1, y+2 ); SetValue( blackBoard, x+2, y-1 ); SetValue( blackBoard, x+2, y ); SetValue( blackBoard, x+2, y+1 ); SetValue( blackBoard, x+3, y ); }else if( board[y][x] == WHITE ){ SetValue( whiteBoard, x-3, y ); SetValue( whiteBoard, x-2, y-1 ); SetValue( whiteBoard, x-2, y ); SetValue( whiteBoard, x-2, y+1 ); SetValue( whiteBoard, x-1, y-2 ); SetValue( whiteBoard, x-1, y-1 ); SetValue( whiteBoard, x-1, y ); SetValue( whiteBoard, x-1, y+1 ); SetValue( whiteBoard, x-1, y+2 ); SetValue( whiteBoard, x, y-3 ); SetValue( whiteBoard, x, y-2 ); SetValue( whiteBoard, x, y-1 ); SetValue( whiteBoard, x, y ); SetValue( whiteBoard, x, y+1 ); SetValue( whiteBoard, x, y+2 ); SetValue( whiteBoard, x, y+3 ); SetValue( whiteBoard, x+1, y-2 ); SetValue( whiteBoard, x+1, y-1 ); SetValue( whiteBoard, x+1, y ); SetValue( whiteBoard, x+1, y+1 ); SetValue( whiteBoard, x+1, y+2 ); SetValue( whiteBoard, x+2, y-1 ); SetValue( whiteBoard, x+2, y ); SetValue( whiteBoard, x+2, y+1 ); SetValue( whiteBoard, x+3, y ); } } } value = 0; for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( board[y][x] == BLACK ){ totalBoard[y][x] = BLACK; }else if( board[y][x] == WHITE ){ totalBoard[y][x] = WHITE; }else{ if( blackBoard[y][x] > whiteBoard[y][x] ){ /* 黒地としてカウント */ totalBoard[y][x] = BLACK; value++; }else if( blackBoard[y][x] < whiteBoard[y][x] ){ /* 白地としてカウント */ totalBoard[y][x] = WHITE; value--; } } } } return( value ); } /*------------------------------------------------------------------*/ /* 地の設定 */ /*------------------------------------------------------------------*/ static void SetValue( int board[][BOARD_SIZE], int x, int y ) { if( x > 0 && x < (BOARD_SIZE-1) && y > 0 && y < (BOARD_SIZE-1) ){ board[y][x] += 1; } }この改良をしたプログラムでの認識です。
盤面 地 +++++++++++++++++++ +■■□□+++□□□□□□□■+++ +++++++++++++++○+++ ■■+□□□+□□□□□□□+○■++ ++●○++++++○++○++●++ ■■●○□□□□□□○□□○+■●■■ ++●○++++○++++++●+++ ■■●○□□□+○□□□□+■●■■■ +++○+++++●+++++++++ ■■+○□□□++●□++■■■■■+ +●●++++++++++++●+++ ■■●□□□□++++■■+■●■■■ ++++○++++++++++++++ ■++□○□□□+■■++■■■■■+ ++○++++++++++++++++ +■○□□□□++■++++■■■++ +++++++++++++++++++ □++□□□++++++++■■■++ +++++++++++++++++++ +□□□□++++++++■■■■■+ +++++++++++++++●+++ ++□□++++++++■■■●■■■ +++++++++++++++++++ ++□□□++++++++■■■■■+ +++++++++++++++++++ +□□□□□++++++++■■■++ +++○+++++++++++++++ □□□○□+□++++++++■+++ +++++++++++++++++++ +□□□□□■+++++++■■■++ +++○+++++++++++++++ □□□○□++■+++++■■■■■+ +++++●+++++++++●+++ +□+□+●■■■+++■■■●■■■ +++++++++++++++++++ ++□++■■■+++++■■■■■+ +++++++++++++++++++ +++□■■■+++++++■■■++左上隅は良くなったと思いますが、それでもまだまだ認識結果に不満が残ります。
この部分は、囲碁プログラムの開発者の考え方の特徴が最も出やすい部分だと 言えるでしょう。 みなさんが自由に考えてください。
/* 現在の盤面の評価値を求める */ initValue = EvalBoard( 現在の盤面 ); /* すべての候補手を評価する */ for( すべての候補手 ){ /* (1) その手は合法手でないならば次の候補手の評価へ */ if( 合法手でない ){ continue; } /* (2) その手を評価する */ value = EvalBoard( その手を仮に打った盤面 ); /* (3) 評価が最高のものを残す */ if( maxValue < value ){ maxValue = value; move_x = x; move_y = y; } } /* 現在の盤面の評価値と候補手を仮に打った盤面の評価値とを比較 */ if( maxValue > initValue ){ /* 候補手を打った方が評価値が高いのならば( move_x, move_y )が次の一手 */ }else{ /* 候補手を打つと現在よりも悪くなるならばパスが次の一手 */ }ここで注意するのは、現在の盤面と候補手を打った盤面との比較です。 打つ前より悪くなるなら、パスを選択します。 ダメを詰めることにより、次の相手の手で石が取られてしまう場合、 現在の盤面の方が評価値が高くなります。 また、ダメしか残っていないならば、現在の盤面と候補手を打った盤面では 評価値が同じになるでしょう。このときもパスをします。 そして、黒と白の両者がパスをしたら終局になります。 (パスをしなかったらいつまでたっても終りません。)
実験結果は次の通りです。
数字は、(その点に候補手を打ったときの地 − 現在の地)を表します。
この候補手の評価の実験では、中央付近に打つ手が最善(24目の手)という結果が出ました。
盤面 評価値(地の増分) +++++++++++++++++++ 2 4 6 10 12 12 11 12 13 13 10 7 7 8 7 6 5 4 4 +++++++++++++++○+++ 2 4 8 10 12 13 15 16 16 13 13 12 11 8 5 0 6 5 5 ++●○++++++○++○++●++ 1 4 0 0 11 14 16 18 16 14 0 16 12 0 3 4 0 6 4 ++●○++++○++++++●+++ 1 2 0 0 12 14 17 15 0 16 16 13 11 7 5 0 4 3 4 +++○+++++●+++++++++ 2 2 4 0 11 15 15 16 17 0 15 13 10 8 6 4 0 3 3 +●●++++++++++++●+++ 4 0 0 8 10 13 17 18 18 17 16 14 13 10 5 0 3 4 4 ++++○++++++++++++++ 5 7 8 10 0 15 18 20 19 19 17 16 14 9 5 4 3 5 6 ++○++++++++++++++++ 8 9 0 14 15 17 19 20 20 20 20 17 13 10 7 5 5 5 6 +++++++++++++++++++ 11 13 15 16 18 19 20 21 22 21 21 18 15 10 6 5 5 5 6 +++++++++++++++++++ 13 18 19 18 19 20 22 24 23 22 20 20 16 10 4 3 3 6 7 +++++++++++++++●+++ 15 19 20 20 19 22 24 24 24 22 22 19 16 11 5 0 4 7 7 +++++++++++++++++++ 15 17 20 19 21 22 23 24 24 24 22 20 16 12 5 5 4 8 7 +++++++++++++++++++ 12 16 16 19 20 21 21 22 24 24 24 21 18 11 9 7 8 7 9 +++○+++++++++++++++ 12 13 16 0 18 17 20 20 21 24 24 24 17 13 9 9 8 9 8 +++++++++++++++++++ 10 15 16 16 14 15 16 18 20 21 24 21 18 11 9 7 8 7 9 +++○+++++++++++++++ 12 15 18 0 13 12 13 16 18 21 20 20 16 12 5 5 4 8 7 +++++●+++++++++●+++ 12 16 15 15 12 0 12 13 16 17 19 17 15 10 5 0 4 6 6 +++++++++++++++++++ 12 14 15 12 11 11 8 10 12 16 16 16 12 8 3 3 2 4 4 +++++++++++++++++++ 9 11 11 11 9 7 6 5 9 12 15 12 9 5 4 3 3 2 3
基本編と同じように、サンプルプログラムを置きます。 このプログラムも書き方に工夫すべきところがたくさんありますが、 プログラミングのテクニックについて解説するのが目的ではないので、 そこはみなさんが改良してください。
それから、基本編の対局プログラムと、この思考ルーチンで、 人間とコンピュータが対局できるプログラムを作ることができます。 勿論そのままではダメで、対局プログラムから思考ルーチンを呼ぶようにしたり、 人間とコンピュータのどちらが黒番かを設定するためのインターフェースなど、 手を加えないといけないところがいくつかあります。 そこはみなさんが作成してください。
このプログラムは、"figure"という名前のファイルを読んで、 次の一手を返すプログラムです。"figure"というファイルには、 次の一手を求めたい盤面を描いておきます。
"figure"の例 +++++++++++++++++++ +++++++++++++++O+++ ++*O++++++O++O++*++ ++*O++++O++++++*+++ +++O+++++*+++++++++ +**++++++++++++*+++ ++++O++++++++++++++ ++O++++++++++++++++ +++++++++++++++++++ +++++++++++++++++++ +++++++++++++++*+++ +++++++++++++++++++ +++++++++++++++++++ +++O+++++++++++++++ +++++++++++++++++++ +++O+++++++++++++++ +++++*+++++++++*+++ +++++++++++++++++++ +++++++++++++++++++
/*------------------------------------------------------------------*/ /* ファイルから盤面図を読み込み、黒番の次の一手を返すプログラム */ /*------------------------------------------------------------------*/ #include <stdio.h> #include <string.h> #define TRUE 1 #define FALSE 0 #define SPACE 0 /* 空点(石が置かれていない) */ #define BLACK 1 /* 黒 */ #define WHITE 2 /* 白 */ #define OUT 3 /* 盤外 */ #define BOARD_SIZE 21 /* 碁盤の大きさ */ /* 相手の色 */ #define reverseColor( color ) ( color == BLACK ? WHITE : BLACK ) /*------------------------------------------------------------------*/ /* 関数 */ /*------------------------------------------------------------------*/ /* メイン関数 */ void main( void ); /* 盤面図入力 */ static void InputFigure( int board[][BOARD_SIZE] ); /* 碁盤の初期化 */ static void InitializeBoard( int board[][BOARD_SIZE] ); /* 碁盤を表示する */ static void DisplayBoard( int board[][BOARD_SIZE] ); /* 次の一手を求める */ static void ThinkMove( int board[][BOARD_SIZE], int color, int *x, int *y ); /* 盤面の評価 */ static int EvalBoard( int board[][BOARD_SIZE] ); /* 地の設定 */ static void SetValue( int board[][BOARD_SIZE], int x, int y ); /* 合法手がどうか調べる */ static int CheckLegal( int board[][BOARD_SIZE], int color, int x, int y ); /* 自殺手かどうか調べる */ static int CheckSuicide( int board[][BOARD_SIZE], int color, int x, int y ); /* チェック用の碁盤をクリア */ static void ClearCheckBoard( void ); /* 相手に囲まれているか調べる */ static int DoCheckRemoveStone( int board[][BOARD_SIZE], int color, int x, int y ); /* 碁盤を複写 */ static void CopyBoard( int source[][BOARD_SIZE], int copied[][BOARD_SIZE] ); /* 碁盤に石を置く*/ static void SetStone( int board[][BOARD_SIZE], int color, int x, int y ); /* 囲まれた石を取り除く */ static int RemoveStone( int board[][BOARD_SIZE], int color, int x, int y ); /* 囲まれた石を取り除く */ static int DoRemoveStone( int board[][BOARD_SIZE], int color, int x, int y, int prisoner ); /*------------------------------------------------------------------*/ /* 変数 */ /*------------------------------------------------------------------*/ /* 碁盤の表示のための文字 */ static char stone[] = { '+', '*', 'O', '?' }; /* 合法手かどうか調べるのに使う */ static int checkBoard[BOARD_SIZE][BOARD_SIZE]; /*------------------------------------------------------------------*/ /* メイン関数 */ /*------------------------------------------------------------------*/ void main( void ) { int board[BOARD_SIZE][BOARD_SIZE]; /* 碁盤 */ int xBlack, yBlack; /* 黒の着手位置 */ printf( "Sample Program Start\n" ); /* 盤面図の入力 */ InputFigure( board ); /* 黒の次の一手を求める */ ThinkMove( board, BLACK, &xBlack, &yBlack ); /* 碁盤の表示 */ DisplayBoard( board ); /* 結果の表示 */ printf( "Computer -> (%2d,%2d)\n", xBlack, yBlack); printf( "Sample Program End\n" ); } /*------------------------------------------------------------------*/ /* 盤面図入力 */ /* ファイル名"figure"というファイルに盤面図が書かれている */ /* 劫による着手禁止点はないものとする */ /*------------------------------------------------------------------*/ static void InputFigure( int board[][BOARD_SIZE] ) { FILE *fp; char buf[256]; int x, y, i; /* ファイルを開く */ if( (fp = fopen( "figure", "r" )) == NULL ){ fprintf( stderr, "ERROR: file(figure) open fail\n" ); exit(1); } /* 碁盤の初期化 */ InitializeBoard( board ); /* 各座標を読む */ for( y=1; y < (BOARD_SIZE-1); y++ ){ buf[0] = '\0'; fgets( buf, 256, fp ); for( x=1, i=0; x < (BOARD_SIZE-1); x++ ){ if( buf[i] == 'O' ){ board[y][x] = WHITE; }else if( buf[i] == '*' ){ board[y][x] = BLACK; }else if( buf[i] == '+' ){ board[y][x] = SPACE; }else{ board[y][x] = SPACE; } i++; } } /* ファイルを閉じる */ fclose( fp ); } /*------------------------------------------------------------------*/ /* 碁盤の初期化 */ /*------------------------------------------------------------------*/ static void InitializeBoard( int board[][BOARD_SIZE] ) { int x, y; for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ board[y][x] = SPACE; } } for( y=0; y < BOARD_SIZE; y++ ){ board[y][0]= OUT; board[y][BOARD_SIZE-1] = OUT; board[0][y] = OUT; board[BOARD_SIZE-1][y] = OUT; } } /*------------------------------------------------------------------*/ /* 碁盤を表示する */ /*------------------------------------------------------------------*/ static void DisplayBoard( int board[][BOARD_SIZE] ) { int x, y; printf( " [ 1 2 3 4 5 6 7 8 910111213141516171819]\n" ); for( y=1; y < (BOARD_SIZE-1); y++ ){ printf( "[%2d] ", y ); for( x=1; x < (BOARD_SIZE-1); x++ ){ printf( " %c", stone[board[y][x]] ); } printf( "\n" ); } } /*------------------------------------------------------------------*/ /* 次の一手を求める */ /*------------------------------------------------------------------*/ static void ThinkMove( int board[][BOARD_SIZE], int color, int *xBlack, int *yBlack ) { int tmpBoard[BOARD_SIZE][BOARD_SIZE]; /* 複写用碁盤 */ int evalBoard[BOARD_SIZE][BOARD_SIZE]; /* 評価値格納用碁盤 */ int resultX, resultY; int x, y; int eval, init_eval, max_eval; /* 評価値格納用碁盤の初期化 */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ evalBoard[y][x] = 0; } } /* 盤面の評価 */ init_eval = EvalBoard( board ); /* 候補手を仮りに盤上に打って評価 */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ /* 着手禁止点なら次の点へ */ if( CheckLegal( board, color, x, y ) == FALSE ){ continue; } /* 碁盤を複写 */ CopyBoard( board, tmpBoard ); /* 候補手を盤上に打つ */ SetStone( tmpBoard, color, x, y ); /* 着手後の盤面の評価 */ eval = EvalBoard( tmpBoard ); /* 着手前よりどのくらい良くなったかを評価値格納用碁盤に記録 */ evalBoard[y][x] = eval - init_eval; } } /* 評価値が最高のものを選ぶ */ max_eval = 0; resultX = resultY = 0; for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( max_eval < evalBoard[y][x] ){ max_eval = evalBoard[y][x]; resultX = x; resultY = y; } } } *xBlack = resultX; *yBlack = resultY; } /*------------------------------------------------------------------*/ /* 盤面の評価 */ /*------------------------------------------------------------------*/ static int EvalBoard( int board[][BOARD_SIZE] ) { int blackBoard[BOARD_SIZE][BOARD_SIZE]; /* 黒石の影響範囲 */ int whiteBoard[BOARD_SIZE][BOARD_SIZE]; /* 白石の影響範囲 */ int totalBoard[BOARD_SIZE][BOARD_SIZE]; /* 地 */ int x, y, value; /* 初期化 */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ blackBoard[y][x] = 0; whiteBoard[y][x] = 0; totalBoard[y][x] = SPACE; } } /* 距離3以内なら地とみなす */ for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( board[y][x] == BLACK ){ SetValue( blackBoard, x-3, y ); SetValue( blackBoard, x-2, y-1 ); SetValue( blackBoard, x-2, y ); SetValue( blackBoard, x-2, y+1 ); SetValue( blackBoard, x-1, y-2 ); SetValue( blackBoard, x-1, y-1 ); SetValue( blackBoard, x-1, y ); SetValue( blackBoard, x-1, y+1 ); SetValue( blackBoard, x-1, y+2 ); SetValue( blackBoard, x, y-3 ); SetValue( blackBoard, x, y-2 ); SetValue( blackBoard, x, y-1 ); SetValue( blackBoard, x, y ); SetValue( blackBoard, x, y+1 ); SetValue( blackBoard, x, y+2 ); SetValue( blackBoard, x, y+3 ); SetValue( blackBoard, x+1, y-2 ); SetValue( blackBoard, x+1, y-1 ); SetValue( blackBoard, x+1, y ); SetValue( blackBoard, x+1, y+1 ); SetValue( blackBoard, x+1, y+2 ); SetValue( blackBoard, x+2, y-1 ); SetValue( blackBoard, x+2, y ); SetValue( blackBoard, x+2, y+1 ); SetValue( blackBoard, x+3, y ); }else if( board[y][x] == WHITE ){ SetValue( whiteBoard, x-3, y ); SetValue( whiteBoard, x-2, y-1 ); SetValue( whiteBoard, x-2, y ); SetValue( whiteBoard, x-2, y+1 ); SetValue( whiteBoard, x-1, y-2 ); SetValue( whiteBoard, x-1, y-1 ); SetValue( whiteBoard, x-1, y ); SetValue( whiteBoard, x-1, y+1 ); SetValue( whiteBoard, x-1, y+2 ); SetValue( whiteBoard, x, y-3 ); SetValue( whiteBoard, x, y-2 ); SetValue( whiteBoard, x, y-1 ); SetValue( whiteBoard, x, y ); SetValue( whiteBoard, x, y+1 ); SetValue( whiteBoard, x, y+2 ); SetValue( whiteBoard, x, y+3 ); SetValue( whiteBoard, x+1, y-2 ); SetValue( whiteBoard, x+1, y-1 ); SetValue( whiteBoard, x+1, y ); SetValue( whiteBoard, x+1, y+1 ); SetValue( whiteBoard, x+1, y+2 ); SetValue( whiteBoard, x+2, y-1 ); SetValue( whiteBoard, x+2, y ); SetValue( whiteBoard, x+2, y+1 ); SetValue( whiteBoard, x+3, y ); } } } value = 0; for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ if( board[y][x] == BLACK ){ totalBoard[y][x] = BLACK; }else if( board[y][x] == WHITE ){ totalBoard[y][x] = WHITE; }else{ if( blackBoard[y][x] > whiteBoard[y][x] ){ /* 黒地としてカウント */ totalBoard[y][x] = BLACK; value++; }else if( blackBoard[y][x] < whiteBoard[y][x] ){ /* 白地としてカウント */ totalBoard[y][x] = WHITE; value--; } } } } return( value ); } /*------------------------------------------------------------------*/ /* 地の設定 */ /*------------------------------------------------------------------*/ static void SetValue( int board[][BOARD_SIZE], int x, int y ) { if( x > 0 && x < (BOARD_SIZE-1) && y > 0 && y < (BOARD_SIZE-1) ){ board[y][x] += 1; } } /*------------------------------------------------------------------*/ /* 合法手かどうか調べる */ /* このサンプルプログラムでは劫のチェックは省略 */ /*------------------------------------------------------------------*/ static int CheckLegal( int board[][BOARD_SIZE], int color, int x, int y ) { /* 空点じゃないと置けません */ if( board[y][x] != SPACE ){ return( FALSE ); } /* 自殺手なら置けません */ if( CheckSuicide( board, color, x, y ) == TRUE ){ return( FALSE ); } /* 以上のチェックをすべてクリアできたので置けます */ return( TRUE ); } /*------------------------------------------------------------------*/ /* 自殺手かどうか調べる */ /*------------------------------------------------------------------*/ static int CheckSuicide( int board[][BOARD_SIZE], int color, int x, int y ) { int rtnVal; int opponent; /* 相手の色 */ /* 仮に石を置く */ board[y][x] = color; /* マークのクリア */ ClearCheckBoard(); /* その石は相手に囲まれているか調べる */ rtnVal = DoCheckRemoveStone( board, color, x, y ); /* 囲まれているならば自殺手の可能性あり */ if( rtnVal == TRUE ){ /* 相手の色を求める */ opponent = reverseColor( color ); /* その石を置いたことにより、隣の相手の石が取れるなら自殺手ではない */ if( x > 1 ){ /* 隣は相手? */ if( board[y][x-1] == opponent ){ /* マークのクリア */ ClearCheckBoard(); /* 相手の石は囲まれているか? */ rtnVal = DoCheckRemoveStone( board, opponent, x-1, y ); /* 相手の石を取れるので自殺手ではない */ if( rtnVal == TRUE ){ /* 盤を元に戻す */ board[y][x] = SPACE; return( FALSE ); } } } if( y > 1 ){ /* 隣は相手? */ if( board[y-1][x] == opponent ){ /* マークのクリア */ ClearCheckBoard(); /* 相手の石は囲まれているか? */ rtnVal = DoCheckRemoveStone( board, opponent, x, y-1 ); /* 相手の石を取れるので自殺手ではない */ if( rtnVal == TRUE ){ /* 盤を元に戻す */ board[y][x] = SPACE; return( FALSE ); } } } if( x < (BOARD_SIZE-2) ){ /* 隣は相手? */ if( board[y][x+1] == opponent ){ /* マークのクリア */ ClearCheckBoard(); /* 相手の石は囲まれているか? */ rtnVal = DoCheckRemoveStone( board, opponent, x+1, y ); /* 相手の石を取れるので自殺手ではない */ if( rtnVal == TRUE ){ /* 盤を元に戻す */ board[y][x] = SPACE; return( FALSE ); } } } if( y < (BOARD_SIZE-2) ){ /* 隣は相手? */ if( board[y+1][x] == opponent ){ /* マークのクリア */ ClearCheckBoard(); /* 相手の石は囲まれているか? */ rtnVal = DoCheckRemoveStone( board, opponent, x, y+1 ); /* 相手の石を取れるので自殺手ではない */ if( rtnVal == TRUE ){ /* 盤を元に戻す */ board[y][x] = SPACE; return( FALSE ); } } } /* 盤を元に戻す */ board[y][x] = SPACE; /* 相手の石を取れないなら自殺手 */ return( TRUE ); }else{ /* 盤を元に戻す */ board[y][x] = SPACE; /* 囲まれていないので自殺手ではない */ return( FALSE ); } } /*------------------------------------------------------------------*/ /* チェック用の碁盤をクリア */ /*------------------------------------------------------------------*/ static void ClearCheckBoard( void ) { int x, y; for( y=1; y < (BOARD_SIZE-1); y++ ){ for( x=1; x < (BOARD_SIZE-1); x++ ){ checkBoard[y][x] = SPACE; } } } /*------------------------------------------------------------------*/ /* 座標(x,y)にあるcolor石が相手に囲まれているか調べる */ /*------------------------------------------------------------------*/ static int /* 空点があればFALSEを返し、空点がなければTRUEを返す */ DoCheckRemoveStone( int board[][BOARD_SIZE], int color, int x, int y ) { int rtn; /* その場所は既に調べた点ならおしまい */ if( checkBoard[y][x] == TRUE ){ return( TRUE ); } /* 調べたことをマークする */ checkBoard[y][x] = TRUE; /* 何も置かれていないならばおしまい */ if( board[y][x] == SPACE ){ return( FALSE ); } /* 同じ色の石ならばその石の隣も調べる */ if( board[y][x] == color ){ /* その石の左(x-1,y)を調べる */ if( x > 1 ){ rtn = DoCheckRemoveStone( board, color, x-1, y ); if( rtn == FALSE ){ return( FALSE ); } } /* その石の上(x,y-1)を調べる */ if( y > 1 ){ rtn = DoCheckRemoveStone( board, color, x, y-1 ); if( rtn == FALSE ){ return( FALSE ); } } /* その石の右(x+1,y)を調べる */ if( x < (BOARD_SIZE-2) ){ rtn = DoCheckRemoveStone( board, color, x+1, y ); if( rtn == FALSE ){ return( FALSE ); } } /* その石の下(x,y+1)を調べる */ if( y < (BOARD_SIZE-2) ){ rtn = DoCheckRemoveStone( board, color, x, y+1 ); if( rtn == FALSE ){ return( FALSE ); } } } /* 相手の色の石があった */ return( TRUE ); } /*------------------------------------------------------------------*/ /* 碁盤を複写 */ /*------------------------------------------------------------------*/ static void CopyBoard( int source[][BOARD_SIZE], int copied[][BOARD_SIZE] ) { int x, y; for( y=0; y < BOARD_SIZE; y++ ){ for( x=0; x < BOARD_SIZE; x++ ){ copied[y][x] = source[y][x]; } } } /*------------------------------------------------------------------*/ /* 碁盤に石を置く */ /* このサンプルプログラムでは取った石数は使わない */ /*------------------------------------------------------------------*/ static void SetStone( int board[][BOARD_SIZE], int color, int x, int y ) { int prisonerN; /* 取り除かれた石の数(上) */ int prisonerE; /* 取り除かれた石の数(右) */ int prisonerS; /* 取り除かれた石の数(下) */ int prisonerW; /* 取り除かれた石の数(左) */ int prisonerAll; /* 取り除かれた石の総数 */ /* 座標(x,y)に石を置く */ board[y][x] = color; /* 取り除かれた石の数 */ prisonerN = prisonerE = prisonerS = prisonerW = 0; /* 置いた石の周囲の相手の石が死んでいれば碁盤から取り除く */ if( y > 1 ){ prisonerN = RemoveStone( board, color, x, y-1 ); } if( x > 1 ){ prisonerE = RemoveStone( board, color, x-1, y ); } if( y < (BOARD_SIZE-1) ){ prisonerS = RemoveStone( board, color, x, y+1 ); } if( x < (BOARD_SIZE-1) ){ prisonerW = RemoveStone( board, color, x+1, y ); } /* 取り除かれた石の総数 */ prisonerAll = prisonerN + prisonerE + prisonerS + prisonerW; } /*------------------------------------------------------------------*/ /* 座標(x,y)の石が死んでいれば碁盤から取り除く */ /*------------------------------------------------------------------*/ static int /* 碁盤から取り除かれた石数 */ RemoveStone( int board[][BOARD_SIZE], int color, int x, int y ) { int prisoner; /* 取り除かれた石数 */ /* 置いた石と同じ色なら取らない */ if( board[y][x] == color ){ return( 0 ); } /* 空点なら取らない */ if( board[y][x] == SPACE ){ return( 0 ); } /* マークのクリア */ ClearCheckBoard(); /* 囲まれているなら取る */ if( DoCheckRemoveStone( board, board[y][x], x, y ) == TRUE ){ prisoner = DoRemoveStone( board, board[y][x], x, y, 0 ); return( prisoner ); } return( 0 ); } /*------------------------------------------------------------------*/ /* 座標(x,y)のcolor石を碁盤から取り除き、取った石の数を返す */ /*------------------------------------------------------------------*/ static int /* アゲハマ */ DoRemoveStone( int board[][BOARD_SIZE], int color, int x, int y, int prisoner ) { /* 取り除かれる石と同じ色ならば石を取る */ if( board[y][x] == color ){ /* 取った石の数を1つ増やす */ prisoner++; /* その座標に空点を置く */ board[y][x] = SPACE; /* 左を調べる */ if( x > 1 ){ prisoner = DoRemoveStone( board, color, x-1, y, prisoner ); } /* 上を調べる */ if( y > 1 ){ prisoner = DoRemoveStone( board, color, x, y-1, prisoner ); } /* 右を調べる */ if( x < (BOARD_SIZE-2) ){ prisoner = DoRemoveStone( board, color, x+1, y, prisoner ); } /* 下を調べる */ if( y < (BOARD_SIZE-2) ){ prisoner = DoRemoveStone( board, color, x, y+1, prisoner ); } } /* 取った石の数を返す */ return( prisoner ); } /*---------------------- < end of program > ------------------------*/勝也のページへ戻る