3.7 中盤の探索 |
前節までで評価関数について説明しました。
本節では評価関数を使用した中盤探索について説明します。
前章の中盤探索を修正しています。
修正箇所にはコメントをつけました。
最初に行うことは、ComクラスでEvaluatorクラスを使用できるようにすることです。
まず構造体メンバにEvaluatorクラスを追加します。
struct _Com { Board *Board; /* Evaluatorを追加 */ Evaluator *Evaluator; int MidDepth; int WLDDepth; int ExactDepth; int Node; };
次にComクラスの生成関数の引数にEvaluatorクラスへのポインタを追加します。
/* 初期化関数にEvaluatorを追加 */ static int Com_Initialize(Com *self, Evaluator *evaluator) { memset(self, 0, sizeof(Com)); self->Board = Board_New(); if (!self->Board) { return 0; } /* メンバ変数にEvaluatorクラスへのポインタを代入する */ self->Evaluator = evaluator; if (!self->Evaluator) { return 0; } self->MidDepth = 1; self->WLDDepth = 1; self->ExactDepth = 1; self->Node = 0; return 1; } /* 生成関数にEvaluatorを追加 */ Com *Com_New(Evaluator *evaluator) { Com *self; self = malloc(sizeof(Com)); if (self) { /* 初期化関数にEvaluatorを追加 */ if (!Com_Initialize(self, evaluator)) { Com_Delete(self); self = NULL; } } return self; }
次に思考関数を修正します。
修正点は以下の通りです。
int Com_NextMove(Com *self, const Board *in_board, int in_color, int *out_value) { int result; int left; int value; int color; Board_Copy(in_board, self->Board); self->Node = 0; left = Board_CountDisks(self->Board, EMPTY); if (left <= self->ExactDepth) { value = Com_EndSearch(self, left, -BOARD_SIZE * BOARD_SIZE, BOARD_SIZE * BOARD_SIZE, in_color, Board_OpponentColor(in_color), 0, &result); /* 評価値の正規化 */ value *= DISK_VALUE; } else if (left <= self->WLDDepth) { value = Com_EndSearch(self, left, -BOARD_SIZE * BOARD_SIZE, 1, in_color, Board_OpponentColor(in_color), 0, &result); /* 評価値の正規化 */ value *= DISK_VALUE; } else { if ((in_color == WHITE && self->MidDepth % 2 == 0) || (in_color == BLACK && self->MidDepth % 2 == 1)) { Board_Reverse(self->Board); color = Board_OpponentColor(in_color); } else { color = in_color; } /* 探索範囲を-MAX_VALUEからMAX_VALUEまでにする */ value = Com_MidSearch(self, self->MidDepth, -MAX_VALUE, MAX_VALUE, color, Board_OpponentColor(color), 0, &result); } if (out_value) { *out_value = value; } return result; }
最後に中盤探索を修正します。
修正箇所は2点です。
static int Com_MidSearch(Com *self, int in_depth, int in_alpha, int in_beta, int in_color, int in_opponent, int in_pass, int *out_move) { int x, y; int value, max = in_alpha; int can_move = 0; int move; if (in_depth == 0) { self->Node++; /* 評価関数呼び出し */ return Evaluator_Value(self->Evaluator, self->Board); } *out_move = NOMOVE; for (x = 0; x < BOARD_SIZE; x++) { for (y = 0; y < BOARD_SIZE; y++) { if (Board_Flip(self->Board, in_color, Board_Pos(x, y))) { if (!can_move) { *out_move = Board_Pos(x, y); can_move = 1; } value = -Com_MidSearch(self, in_depth - 1, -in_beta, -max, in_opponent, in_color, 0, &move); Board_Unflip(self->Board); if (value > max) { max = value; *out_move = Board_Pos(x, y); if (max >= in_beta) { return in_beta; } } } } } if (!can_move) { if (in_pass) { *out_move = NOMOVE; self->Node++; /* 終局していたら石差のDISK_VALUE倍を評価値とする */ max = DISK_VALUE * (Board_CountDisks(self->Board, in_color) - Board_CountDisks(self->Board, in_opponent)); } else { *out_move = PASS; max = -Com_MidSearch(self, in_depth - 1, -in_beta, -max, in_opponent, in_color, 1, &move); } } return max; }
これでコンピュータとの対局が可能になりました。
ただし、まだ評価パラメータの設定を行っていないので、コンピュータは適切な手を選択することができません。
次節で評価パラメータの設定を行います。