6.4 局面の比較 |
1つの定石データが局面状態と局面情報から構成されることは既に述べました。
ある局面が定石として登録されているか調べたり、定石として登録するためには、
既に登録されている定石データの局面と比較する処理が必要です。
本節では、局面を比較する処理について説明します。
局面状態が以下の構造体であることは既に述べました。
typedef struct _PositionKey PositionKey; struct _PositionKey { unsigned long int bl; unsigned long int bh; unsigned long int wl; unsigned long int wh; };
黒番として考えてみましょう。
例えばA1が黒であればblの1ビット目を1に、B1が白であればwlの2ビット目を2に、、、
という処理を全てのマスに対して行ないます。
ただし注意しなければ点があります。
1つの局面には左右反転等の対称な局面が7つあります。
これら8つの局面を同一の局面として扱わなければなりません。
それにはまず1つの局面から対称な8つの局面を生成します。
そして8つの局面を比較して、一番「小さい」局面を局面状態として採用します。
どちらの局面が「小さい」かを比較する方法は以下の通りです。
では実際に局面を生成する関数Board_Key()を実装してみましょう。
引数は以下の通りです。
in_board : Boardクラスへのポインタ
in_color : 手番
out_key : 生成した局面状態が格納されます
static void Board_Key(const Board *in_board, int in_color, PositionKey *out_key) { PositionKey key; unsigned int flag; int i, x, y, c; int op_color; op_color = Board_OpponentColor(in_color); for (i = 0; i < 8; i++) { memset(&key, 0, sizeof(PositionKey)); flag = 1; for (y = 0; y < BOARD_SIZE / 2; y++) { for (x = 0; x < BOARD_SIZE; x++) { c = Board_Disk(in_board, Board_RotatePos(x, y, i)); if (c == in_color) { key.bl |= flag; } else if (c == op_color) { key.wl |= flag; } c = Board_Disk(in_board, Board_RotatePos(x, y + BOARD_SIZE / 2, i)); if (c == in_color) { key.bh |= flag; } else if (c == op_color) { key.wh |= flag; } flag <<= 1; } } if (i == 0 || PositionKey_Comp(&key, out_key) < 0) { *out_key = key; } } }
各マスを調べて、石があれば該当するビットを1にしています。
ただし、8つの対称な局面を調べるために、Board_RotatePos()という関数を呼び出しています。
また局面を比較するためにPositionKey_Comp()を呼び出しています。
Board_RotatePos()は以下のようになっています。
static int Board_RotatePos(int in_x, int in_y, int in_type) { switch (in_type) { case 0: return Board_Pos(in_x, in_y); case 1: return Board_Pos(BOARD_SIZE - in_x - 1, in_y); case 2: return Board_Pos(in_x, BOARD_SIZE - in_y - 1); case 3: return Board_Pos(BOARD_SIZE - in_x - 1, BOARD_SIZE - in_y - 1); case 4: return Board_Pos(in_y, in_x); case 5: return Board_Pos(BOARD_SIZE - in_y - 1, in_x); case 6: return Board_Pos(in_y, BOARD_SIZE - in_x - 1); case 7: return Board_Pos(BOARD_SIZE - in_y - 1, BOARD_SIZE - in_x - 1); default: break; } return 0; }
マスのX座標(in_x)とY座標(in_y)、それと対称の種類(in_type)を指定すると、反転または回転した局面でのマスの位置を返します。
in_typeと反転または回転の方法の対応は以下の通りです。
PositionKey_Comp()は以下のようになっています。
static int PositionKey_Comp(const PositionKey *in_key1, const PositionKey *in_key2) { if (in_key1->wh > in_key2->wh) { return 1; } else if (in_key1->wh < in_key2->wh) { return -1; } else if (in_key1->wl > in_key2->wl) { return 1; } else if (in_key1->wl < in_key2->wl) { return -1; } else if (in_key1->bh > in_key2->bh) { return 1; } else if (in_key1->bh < in_key2->bh) { return -1; } else if (in_key1->bl > in_key2->bl) { return 1; } else if (in_key1->bl < in_key2->bl) { return -1; } return 0; }
2つの局面状態(in_key1, in_key2)を渡します。
in_key1が大きければ1、in_key2が大きければ-1、同じであれば0を返します。
局面状態の比較ができるようになったので、次は局面情報を検索できるようにします。
局面情報の検索はOpening_Find()という関数で行なっています。
引数は以下の通りです。
self : Openingクラスへのポインタ
in_board : 検索対象の局面
in_color : 検索対象の手番
該当する局面情報が見つかったら局面情報へのポインタを返します。
見つからなかった場合にはNULLを返します。
static PositionInfo * Opening_Find(const Opening *self, const Board *in_board, int in_color) { int i; PositionKey key; Board_Key(in_board, in_color, &key); for (i = 0; i < self->Num; i++) { if (!PositionKey_Comp(&key, &self->Data[i].key)) { return &self->Data[i].info; } } return NULL; }
最初に、渡された局面から局面状態を生成します。
次に登録済みの定石データの局面状態と生成した局面状態を比較します。
目的の定石データが見つかったらそのデータが持つ局面情報へのポインタを返し、見つからなかったらNULLを返します。