| 6.6 定石の探索 |
本節では、対局時に登録済みの定石を使用する方法について説明します。
この処理はComクラスで行ないます。
最初にComクラスの生成関数の定義を修正します。
ComクラスがOpeningクラスを使用できるようにします。
Com *Com_New(Evaluator *evaluator, Opening *opening)
Openingクラスへのポインタopeningを追加してあります。
次に定石に関係する関数を追加します。
void Com_SetOpening(Com *self, int in_use)
定石を使用するかどうかを設定します。
引数
self : Comクラスへのポインタ
in_use : 0なら定石使用、1なら定石を使用しない
戻り値 なし
Comクラスで定石を扱えるようにするために、Com構造体にメンバ変数を追加します。
追加する変数は2個です。
struct _Com
{
Board *Board;
Evaluator *Evaluator;
/* Openingクラスへのポインタを追加 */
Opening *Opening;
/* 定石を使用するかどうかのフラグを追加 */
int UseOpening;
int MidDepth;
int WLDDepth;
int ExactDepth;
int Node;
MoveList Moves[BOARD_SIZE * BOARD_SIZE];
};
Com構造体にメンバ変数を追加したので、生成関数も修正します。
生成時にそれぞれに変数を初期化するようにします。
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->UseOpening = 0;
self->MidDepth = 1;
self->WLDDepth = 1;
self->ExactDepth = 1;
self->Node = 0;
return 1;
}
Com *Com_New(Evaluator *evaluator, Opening *opening)
{
Com *self;
self = malloc(sizeof(Com));
if (self) {
if (!Com_Initialize(self, evaluator, opening)) {
Com_Delete(self);
self = NULL;
}
}
return self;
}
新たに関数を追加したのでその実装を行ないます。
Com_SetOpening()は以下のようになっています。
メンバ変数UseOpeningに指定された値を代入しているだけです。
void Com_SetOpening(Com *self, int in_use)
{
self->UseOpening = in_use;
}
コンピュータ思考時に、次の手を定石の中から選ばせるように修正します。
まずCom_NextMove()は以下のようになります。
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);
value = Com_OpeningSearch(self, in_color, Board_OpponentColor(in_color), &result);
if (result != NOMOVE) {
} else 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;
}
value = Com_MidSearch(self, self->MidDepth, -MAX_VALUE, MAX_VALUE, color, Board_OpponentColor(color), 0, &result);
}
if (out_value) {
*out_value = value;
}
return result;
}
最初にCom_OpeningSearch()を呼び出すようにしました。
Com_OpeningSearch()は定石データから手を選び出す関数です。
実装は以下のようになっています。
static int Com_OpeningSearch(Com *self, int in_color, int in_opponent, int *out_move)
{
MoveList *p;
PositionInfo info;
int value, max = -MAX_VALUE;
int count = 0;
*out_move = NOMOVE;
if (!self->UseOpening || !self->Opening) {
return max;
}
for (p = self->Moves->Next; p; p = p->Next) {
if (Board_Flip(self->Board, in_color, p->Pos)) {
if (Opening_Info(self->Opening, self->Board, in_opponent, &info)) {
value = -info.Value;
if (value > max) {
*out_move = p->Pos;
max = value;
count = 1;
} else if (value == max) {
count++;
if (get_rand(count) < 1) {
*out_move = p->Pos;
}
}
}
Board_Unflip(self->Board);
}
}
return max;
}
引数は以下の通りです。
self : Comクラスへのポインタ
in_color : 自分の手番
in_opponent : 相手の手番
out_move : 次の手を格納するためのint変数へのポインタ
定石データから手を選択できた場合にはout_moveに選択した手を格納してその手の評価値を返します。
手を選択できなかった場合にはout_moveにNOMOVEを格納します。
処理内容ですが、1手読みを行なって評価値が最大となる手を選択します。
ただし局面の評価値は定石データから取得しています。
また評価値が最大となる手が複数存在した場合にはランダムに手を選ぶようにしています。
乱数を生成するマクロget_rand()は以下の通りです。
0〜(in_max-1)までの整数をランダムに返します。
#define get_rand(in_max) ((int)((double)(in_max) * rand() / (RAND_MAX + 1.0)))