8.7 その他の処理と効果の検証 |
本節では、前節までに説明しなかった処理を実装します。
最初に"com.h"に以下のプロトタイプを追加します。
Com_CountHashGet()は直前の探索でHash_Get()を呼び出した回数を返します。
Com_CountHashHit()は直前の探索でHash_Hit()が1を返した回数を返します。
Com_CountHashGet()の戻り値をCom_CountHashHit()の戻り値で割ることで、どの程度重複局面が発生しているかを知ることができます。
int Com_CountHashGet(const Com *self); int Com_CountHashHit(const Com *self);
次に"com.c"に関数の内部実装を追加します。
処理は単純で、Comクラス内部の置換表に対して問い合わせを行うだけです。
int Com_CountHashGet(const Com *self) { return Hash_CountGet(self->Hash); } int Com_CountHashHit(const Com *self) { return Hash_CountHit(self->Hash); }
対局時に置換表のヒット率(置換表から局面を取得しようとして、実際に取得できた割り合い)を表示します。
"main.c"に以下の処理を追加します。
Com_CountHashHit()の戻り値をCom_CountHashGet()の戻り値で割ってパーセンテージに直しますが、Com_CountHashGet()が0の場合にはヒット率0%とします。
static void play(Board *board, Com *com) { (中略) printf("コンピュータ思考中...\n"); if (Board_CanPlay(board, color)) { clock_start = clock(); move = Com_NextMove(com, board, color, &score); clock_end = clock(); printf("%c%cに置きます\n", "ABCDEFGH"[Board_X(move)], "12345678"[Board_Y(move)]); printf("評価値: %.2f\n", (double)score / DISK_VALUE); printf("思考時間: %.2f 秒 ノード数: %d NPS: %.2f knps\n", (double)(clock_end - clock_start) / CLOCKS_PER_SEC, Com_CountNodes(com), (double)Com_CountNodes(com) / (clock_end - clock_start + 1) * CLOCKS_PER_SEC / 1000); if (Com_CountHashGet(com) > 0) { printf("ヒット率 %.2f %%\n", (double)Com_CountHashHit(com) / Com_CountHashGet(com) * 100); } else { printf("ヒット率 %.2f %%\n", 0.0); } Board_Flip(board, color, move); } else { printf("パスします\n"); move = PASS; } (中略) }以上で置換表に関する実装は終了です。
置換表導入前と導入後とで対局を行ってみました。
結果は以下の通りです。
中盤探索の比較(終盤は18手完全読み固定)
中盤手数 | 勝率 ( % ) | ノード数比 | 思考時間比 |
---|---|---|---|
8 | 51 | 0.967 | 0.987 |
10 | 50 | 0.921 | 0.938 |
12 | 56 | 0.885 | 0.903 |
14 | 52 | 0.845 | 0.868 |
16 | 54 | 0.808 | 0.831 |
終盤探索(1局)の比較(中盤は8読み手固定)
中盤手数 | 勝率 ( % ) | ノード数比 | 思考時間比 |
---|---|---|---|
14 | 51 | 0.597 | 0.607 |
16 | 50 | 0.542 | 0.549 |
18 | 56 | 0.484 | 0.491 |
20 | 52 | 0.441 | 0.442 |
22 | 54 | 0.463 | 0.434 |
終盤探索(完全読み開始手数のみ)の比較(中盤は8読み手固定)
中盤手数 | 勝率 ( % ) | ノード数比 | 思考時間比 |
---|---|---|---|
8 | 51 | 0.914 | 0.911 |
10 | 50 | 0.832 | 0.837 |
12 | 56 | 0.737 | 0.744 |
14 | 52 | 0.684 | 0.682 |
16 | 54 | 0.695 | 0.622 |
表の項目はMPCの場合と同じです。
1局中の中盤探索だけを比較した場合、1局中の終盤探索だけを比較した場合、終盤探索の完全読み開始手数のみを比較した場合の3通りがあります。
中盤では最大17%、終盤では最大57%(1局中)思考時間を減らすことができました。
いずれも手数が長くなる程ノード数比、思考時間比がともに減少する傾向があります。
また1局中の終盤探索ではノード数比が顕著に減っていますが、これは空きマスが小さい局面での探索ノード数が大幅に減るためです。
例えば20個空き局面で探索を行った後に18個空き局面で探索を行うと、20個空き局面での探索で発生した局面を探索に使用できます。
このため空きマスが小さい局面ではほとんど探索せずに次の手を選ぶことができます。