8.6 置換表を使用した探索 |
本節では置換表を利用した探索を行います。
修正を行うファイルは"com.c"です。
Comクラスに置換表を追加します。
最初にメンバ変数の追加します。
struct _Com { Board *Board; Evaluator *Evaluator; Opening *Opening; Hash *Hash; int UseOpening; int MidDepth; int WLDDepth; int ExactDepth; int Node; MoveList Moves[BOARD_SIZE * BOARD_SIZE]; int MPCInfoNum; MPCInfo *MPCInfo; };
次にComクラスを生成するときにHashクラスを生成するようにします。
static int Com_Initialize(Com *self, Evaluator *evaluator, Opening *opening) { memset(self, 0, sizeof(Com)); self->Board = Board_New(); if (!self->Board) { return 0; } self->Evaluator = evaluator; if (!self->Evaluator) { return 0; } self->Opening = opening; if (!self->Opening) { return 0; } self->Hash = Hash_New(HASH_SIZE); if (!self->Hash) { return 0; } Hash_Clear(self->Hash); self->UseOpening = 0; self->MidDepth = 1; self->WLDDepth = 1; self->ExactDepth = 1; self->Node = 0; self->MPCInfoNum = 0; self->MPCInfo = NULL; return 1; }
HADH_SIZEは以下のように定義しました。
局面表の大きさは20bit(1048576局)になります。
#define HASH_SIZE 20
探索時に置換表の初期化を行っいます。
ただし置換表そのものを初期化するのではなく、Hash_ClearInfo()を呼ぶだけにします。
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); Com_MakeList(self); Board_InitializePattern(self->Board); Hash_ClearInfo(self->Hash); value = Com_OpeningSearch(self, in_color, Board_OpponentColor(in_color), &result); (中略) }
中盤探索関数Com_MidSearch()で置換表を参照するようにします。
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) { MoveList *p; int value, max = in_alpha; int can_move = 0; int move; MoveInfo info[BOARD_SIZE * BOARD_SIZE / 2]; int i, info_num; MPCInfo *mpc_info; HashValue hash_value; HashInfo hash_info; if (in_depth == 0) { self->Node++; return Evaluator_Value(self->Evaluator, self->Board); } if (in_depth > 2) { Board_HashValue(self->Board, in_color, &hash_value); if (Hash_Get(self->Hash, &hash_value, &hash_info)) { if (hash_info.Depth >= in_depth) { if (hash_info.Upper <= in_alpha) { *out_move = hash_info.Move; return in_alpha; } else if (hash_info.Lower >= in_beta) { *out_move = hash_info.Move; return in_beta; } else if (hash_info.Lower >= hash_info.Upper) { *out_move = hash_info.Move; return hash_info.Lower; } if (hash_info.Upper < in_beta) { in_beta = hash_info.Upper; } if (hash_info.Lower > in_alpha) { in_alpha = hash_info.Lower; } } else { hash_info.Depth = in_depth; hash_info.Lower = -MAX_VALUE; hash_info.Upper = MAX_VALUE; } } else { hash_info.Depth = in_depth; hash_info.Lower = -MAX_VALUE; hash_info.Upper = MAX_VALUE; } } if (in_depth >= MPC_DEPTH_MIN && in_depth < MPC_DEPTH_MIN + self->MPCInfoNum) { mpc_info = &self->MPCInfo[in_depth - MPC_DEPTH_MIN]; value = in_alpha - mpc_info->Deviation + mpc_info->Offset; if (Com_MidSearch(self, mpc_info->Depth, value - 1, value, in_color, in_opponent, in_pass, out_move) < value) { return in_alpha; } value = in_beta + mpc_info->Deviation + mpc_info->Offset; if (Com_MidSearch(self, mpc_info->Depth, value, value + 1, in_color, in_opponent, in_pass, out_move) > value) { return in_beta; } } *out_move = NOMOVE; if (in_depth > 2) { info_num = Com_Sort(self, in_color, info); if (info_num > 0) { *out_move = info[0].Move->Pos; can_move = 1; } for (i = 0; i < info_num; i++) { Board_FlipPattern(self->Board, in_color, info[i].Move->Pos); RemoveList(info[i].Move); value = -Com_MidSearch(self, in_depth - 1, -in_beta, -max, in_opponent, in_color, 0, &move); Board_UnflipPattern(self->Board); RecoverList(info[i].Move); if (value > max) { max = value; *out_move = info[i].Move->Pos; if (max >= in_beta) { hash_info.Lower = max; hash_info.Move = *out_move; Hash_Set(self->Hash, &hash_value, &hash_info); return in_beta; } } } if (*out_move != PASS) { hash_info.Upper = max; hash_info.Move = *out_move; Hash_Set(self->Hash, &hash_value, &hash_info); } } else { (中略) }
探索深さが2より大きい場合にだけ置換表を利用するようにしました。
最初に局面情報の取得を行います。
局面情報の取得に成功し、かつ登録情報の探索手数が現在の探索手数以上のときに、以下の処理を行います。
また関数終了時には、局面情報を設定して置換表に登録を行います。
終盤探索関数Com_EndSearch()でも置換表を参照するようにします。
内容はCom_MidSearch()と同じなので、説明は割愛します。
static int Com_EndSearch(Com *self, int in_depth, int in_alpha, int in_beta, int in_color, int in_opponent, int in_pass, int *out_move) { MoveList *p; int value, max = in_alpha; int can_move = 0; int move; MoveInfo info[BOARD_SIZE * BOARD_SIZE / 2]; int i, info_num; HashValue hash_value; HashInfo hash_info; if (in_depth == 1) { self->Node++; p = self->Moves->Next; value = Board_CountFlips(self->Board, in_color, p->Pos); max = Board_CountDisks(self->Board, in_color) - Board_CountDisks(self->Board, in_opponent); if (value > 0) { *out_move = p->Pos; return max + value + value + 1; } value = Board_CountFlips(self->Board, in_opponent, self->Moves->Next->Pos); if (value > 0) { *out_move = PASS; return max - value - value - 1; } *out_move = NOMOVE; return max; } *out_move = NOMOVE; if (in_depth > 8) { Board_HashValue(self->Board, in_color, &hash_value); if (Hash_Get(self->Hash, &hash_value, &hash_info)) { if (hash_info.Depth >= in_depth) { if (hash_info.Upper <= in_alpha) { *out_move = hash_info.Move; return in_alpha; } else if (hash_info.Lower >= in_beta) { *out_move = hash_info.Move; return in_beta; } else if (hash_info.Lower >= hash_info.Upper) { *out_move = hash_info.Move; return hash_info.Lower; } if (hash_info.Upper < in_beta) { in_beta = hash_info.Upper; } if (hash_info.Lower > in_alpha) { in_alpha = hash_info.Lower; } } else { hash_info.Depth = in_depth; hash_info.Lower = -MAX_VALUE; hash_info.Upper = MAX_VALUE; } } else { hash_info.Depth = in_depth; hash_info.Lower = -MAX_VALUE; hash_info.Upper = MAX_VALUE; } info_num = Com_Sort(self, in_color, info); if (info_num > 0) { *out_move = info[0].Move->Pos; can_move = 1; } for (i = 0; i < info_num; i++) { Board_FlipPattern(self->Board, in_color, info[i].Move->Pos); RemoveList(info[i].Move); value = -Com_EndSearch(self, in_depth - 1, -in_beta, -max, in_opponent, in_color, 0, &move); Board_UnflipPattern(self->Board); RecoverList(info[i].Move); if (value > max) { max = value; *out_move = info[i].Move->Pos; if (max >= in_beta) { hash_info.Lower = max; hash_info.Move = *out_move; Hash_Set(self->Hash, &hash_value, &hash_info); return in_beta; } } } if (*out_move != PASS && *out_move != NOMOVE) { hash_info.Upper = max; hash_info.Move = *out_move; Hash_Set(self->Hash, &hash_value, &hash_info); } } else { (中略) }
次節では、残りの関数の実装と置換表の効果の検証を行います。