#include "ThoughtGo.h" #include "MT.h" using namespace std; int m_playout_try = 5000; int m_ko_pos = 0; int m_playout_hands = 19 * 19 * 2; /* プレイアウト処理の最大手数 */ int m_dir4[4] = { 1,-1,999,-999 }; /* 右左下上の隣接位置の4つの加算値 */ /* (999 と -999 はサイズ決定後に書替え) */ int m_pure_board_size = PURE_BOARD_SIZE; // 盤の辺の大きさ int m_pos_array[(19 + 2)*(19 + 2)]; char m_check_base[(19 + 2)*(19 + 2)]; /* 各位置のチェック状態初期値 */ double m_komi = 6.5; node *m_que = NULL; node *m_root = NULL; /* プレイアウト数の設定 */ void SetPlayoutX(int in_playout) { m_playout_try = in_playout; } /* 使用する盤の路数の設定・盤面をクリアする */ void SetBoardSizeX(int size) { m_pure_board_size = size; /* プレイアウト処理の最大手数設定 */ m_playout_hands = size * size * 4; /* 隣接位置情報設定 */ m_dir4[2] = size + 2; m_dir4[3] = (-1) * size - 2; ClearBoardX(); } /* 盤面をクリアする */ void ClearBoardX() { int i; m_ko_pos = 0; node *work; /* 確保済メモリを解放 */ while (m_que != NULL) { work = m_que->next; free(m_que); m_que = work; } /* 各位置のチェック状態をチェック前として初期化 */ memset(m_check_base, '\0', (m_pure_board_size + 2) * (m_pure_board_size + 2)); memset(m_pos_array, '\0', (m_pure_board_size + 2) * (m_pure_board_size + 2)); /* 碁盤の外周(横方向の先頭と最終)をチェック済とする */ for (i = 0; i < (m_pure_board_size + 2); i++) { m_check_base[i] = 1; m_check_base[i + (m_pure_board_size + 2) * (m_pure_board_size + 1)] = 1; m_pos_array[i] = SENTINEL; m_pos_array[i + (m_pure_board_size + 2) * (m_pure_board_size + 1)] = SENTINEL; } /* 碁盤の外周(縦方向の先頭と最終)をチェック済とする */ for (i = 1; i < (m_pure_board_size + 1); i++) { m_check_base[(m_pure_board_size + 2) * i] = 1; m_check_base[m_pure_board_size + 1 + (m_pure_board_size + 2) * i] = 1; m_pos_array[(m_pure_board_size + 2) * i] = SENTINEL; m_pos_array[m_pure_board_size + 1 + (m_pure_board_size + 2) * i] = SENTINEL; } } /* コミを設定する */ void SetKomiX(double new_komi) { m_komi = new_komi; } /* 指定した色を指定した座標に着手する */ void PutStoneX(int pos, int color) { //printf("pos:%d color:%d\n",pos,color); verify_legal(pos, color, m_pos_array, &m_ko_pos, 2); //for(int i=0;i<(m_pure_board_size+2)*(m_pure_board_size+2);i++) //printf("%d,",m_pos_array[i]); //printf("\n"); } /* 指定した色の最良手を生成し着手する */ int UctSearchX(int color) { int i, j, rtn; int posl[(19 + 2)*(19 + 2)]; int playout_max, win_max; int best_pos; int rate_min, rate_max; node *work; /* 乱数シード値設定 */ init_genrand((unsigned)time(NULL)); /* ルートノード作成(ひとつ) */ m_que = NULL; m_root = make_uct(); if (m_root == NULL) return 0; /* メモリ不足 */ /* 子ノード作成(全着手可能な位置分) */ if (expand_node(m_root, m_pos_array, m_ko_pos) != 0) { free(m_root); return 0; /* メモリ不足 */ } /* プレイアウト数分UCT探索を繰り返す */ for (int i = 0; inext; free(m_que); if (work == NULL) break; m_que = work; } if (rtn == -1) return 0; /* 置ける場所がない */ else return -9; /* メモリ不足 */ } } /* 候補手を決定する */ playout_max = -999; win_max = 0; rate_min = 1; rate_max = 0; best_pos = 0; /* 1層目の子ノードを調べる */ for (i = 0, j = 0; j < m_root->child_num; i++) { if (m_root->child[i] == NULL) continue; j++; if (verify_legal(m_root->child[i]->pos, color, m_pos_array, &m_ko_pos, 0) == 0) /* 置けない石はないはずだが念のためチェックアウト */ continue; if (color == BLACK) { /* 現在の手番が黒石のときの処理 */ /* プレイアウト回数の多いものを候補手とする */ if (m_root->child[i]->playout_sum > playout_max) { best_pos = m_root->child[i]->pos; playout_max = m_root->child[i]->playout_sum; win_max = m_root->child[i]->win_black; } /* プレイアウト回数が同じなら勝利数の多いものを候補手とする */ else if (m_root->child[i]->playout_sum == playout_max) { if (m_root->child[i]->win_black > win_max) { best_pos = m_root->child[i]->pos; win_max = m_root->child[i]->win_black; } } /* 最小勝率 100% 未満はあるか? */ if (rate_min == 1) { if (m_root->child[i]->win_black < m_root->child[i]->playout_sum) rate_min = 0; } /* 最大勝率 0% 以外はあるか? */ if (rate_max == 0) { if (m_root->child[i]->win_black > 0) rate_max = 1; } } else { /* 現在の手番が白石のときの処理 */ /* プレイアウト回数の多いものを候補手とする */ if (m_root->child[i]->playout_sum > playout_max) { best_pos = m_root->child[i]->pos; playout_max = m_root->child[i]->playout_sum; win_max = m_root->child[i]->win_white; } /* プレイアウト回数が同じなら勝利数の多いものを候補手とする */ else if (m_root->child[i]->playout_sum == playout_max) { if (m_root->child[i]->win_white > win_max) { best_pos = m_root->child[i]->pos; win_max = m_root->child[i]->win_white; } } /* 最小勝率 100% 未満はあるか? */ if (rate_min == 1) { if (m_root->child[i]->win_white < m_root->child[i]->playout_sum) rate_min = 0; } /* 最大勝率 0% 以外はあるか? */ if (rate_max == 0) { if (m_root->child[i]->win_white > 0) rate_max = 1; } } } /* FILE *fp; fopen_s(&fp, "debug.txt", "a+"); // if (best_pos == 0 || rate_min == 1 || rate_max == 0) { for (i = 0, j = 0; j < m_root->child_num; i++) { if (m_root->child[i] == NULL) continue; j++; fprintf(fp, "%d %d %d %d\n", m_root->child[i]->pos, m_root->child[i]->win_black, m_root->child[i]->win_white, m_root->child[i]->playout_sum); } // } fclose(fp); */ /* 確保済メモリを解放 */ while (1) { work = m_que->next; free(m_que); if (work == NULL) break; m_que = work; } /* 候補手(盤上の碁石の位置)を返却 */ if (best_pos == 0) { /* 置ける場所がない */ return 0; } else if (rate_min == 1) { /* 勝利 */ return best_pos; // *x = 0; // if(color == BLACK) *y = 1; /* 黒の勝ち */ // else *y = 2; /* 白の勝ち */ } else if (rate_max == 0) { /* 敗北 */ return best_pos; // *x = 0; // if(color == BLACK) *y = 2; /* 白の勝ち */ // else *y = 1; /* 黒の勝ち */ // }else{ // *x = best_pos % (size+2); // *y = best_pos / (size+2); } return best_pos; } /* 終局した対局のスコアを計算する */ int UctAnalyzeX(int color) { return 0; } /* ノード(UCT)領域確保 */ /* 引数:メモリ解放用の先頭キューノード,碁盤サイズ(9,13,19) */ /* 戻値:着手位置管理ノード(UCT) */ node* make_uct() { node *uct, *work; /* メモリ確保 */ if ((uct = (node *) malloc(sizeof(node) + sizeof(uct->child)*(m_pure_board_size*m_pure_board_size - 1))) == NULL) return(NULL); /* メモリ不足 */ /* メモリクリア */ memset(uct, '\0', sizeof(node) + sizeof(uct->child)*(m_pure_board_size*m_pure_board_size - 1)); /* メモリ解放用にノードキュー先頭にエンキュー */ work = m_que; m_que = uct; uct->next = work; return uct; } /* 探索子ノード(UCT)作成 */ /* 引数:メモリ解放用の先頭キューノード,親ノード,碁盤サイズ(9,13,19),現碁盤配置,現劫位置 */ /* 戻値:0(正常) -9(メモリ不足) */ int expand_node(node* parent, int *posl, int ko) { for (int pos = 0; poschild[pos] = make_uct(); if (parent->child[pos] == NULL) return(-9); /* メモリ不足 */ /* 親ノードを更新 */ parent->child_num++; /* 子ノード数 */ /* 子ノードを更新 */ parent->child[pos]->pos = (pos + m_pure_board_size) / m_pure_board_size * (m_pure_board_size + 2) + 1 + pos % m_pure_board_size; /* 盤上の位置 */ parent->child[pos]->parent = parent; /* 親ノードのアドレス */ } } return(0); } /* 子ノード(UCT)探索 */ /* 引数:メモリ解放用の先頭キューノード,親ノード,現手番の色,碁盤サイズ(9,13,19),現碁盤配置 */ /* 戻値:0(正常) -1(置ける場所がない) -9(メモリ不足) */ int search_uct(node *my_node, int color, int size, int *posl) { int i, j, tmp_ko_pos, select_i; int score; double ucb, max_ucb; node *select_node; node *parent_node; tmp_ko_pos = 0; select_node = NULL; /* 子ノード検索 */ while (my_node->child_num > 0) { max_ucb = -999; select_i = 0; select_node = NULL; /* 子ノード選択 */ for (i = 0, j = 0; jchild_num; i++) { if (my_node->child[i] == NULL) continue; j++; /* UCB(Upper Confidence Bound)判定 */ if (my_node->child[i]->playout_sum == 0) { /* プレイアウト未のノードを優先するよう適当な値を設定 */ ucb = genrand_int32(); } else { /* プレイアウト済なら UCB を算出 */ if (color == BLACK) ucb = (double)(my_node->child[i]->win_black) / (double)(my_node->child[i]->playout_sum) + C * sqrt(2.0 * (double)log(my_node->child[i]->parent->playout_sum) / (double)my_node->child[i]->playout_sum); else ucb = (double)(my_node->child[i]->win_white) / (double)(my_node->child[i]->playout_sum) + C * sqrt(2.0 * (double)log(my_node->child[i]->parent->playout_sum) / (double)my_node->child[i]->playout_sum); } /* UCB の大きいノードを候補手とする */ if (ucb > max_ucb) { max_ucb = ucb; select_i = i; select_node = my_node->child[i]; } } /* 候補手がなければ検索は終了(置ける場所がない) */ if (select_node == NULL) return(-1); /* 碁石が打てれば子ノード検索は終了 */ if (verify_legal(select_node->pos, color, posl, &tmp_ko_pos, 1)) break; /* 碁石が打てない子ノードは選択候補からはずし再選択 */ my_node->child_num--; my_node->child[select_i] = NULL; } /* 選択ノードがなければ検索は終了(置ける場所がない) */ if (select_node == NULL) return(-1); /* 選択ノードのプレイアウト回数判定 */ if (select_node->playout_sum < THRESHOLD) { /* 選択ノードのプレイアウト回数が閾値未満はプレイアウト処理 */ score = playout((-1)*color, size, posl, tmp_ko_pos); /* 選択ノードのプレイアウト回数更新 */ select_node->playout_sum += 1; if (score > 0) { /* 黒石の勝ちなら選択ノードの黒石勝利数更新 */ select_node->win_black++; /* 親ノード更新(逆伝播) */ while (select_node->parent != NULL) { parent_node = select_node->parent; parent_node->win_black++; /* 黒石勝利数 */ parent_node->playout_sum += 1; /* プレイアウト回数 */ select_node = parent_node; } } else { /* 白石の勝ちなら選択ノードの白石勝利数更新 */ select_node->win_white++; /* 親ノード更新(逆伝播) */ while (select_node->parent != NULL) { parent_node = select_node->parent; parent_node->win_white++; /* 白石勝利数 */ parent_node->playout_sum += 1; /* プレイアウト回数 */ select_node = parent_node; } } return(0); } else { /* 選択ノードのプレイアウト回数が閾値になったら */ if (select_node->child_num == 0) { /* 子ノード作成 */ if (expand_node(select_node, posl, tmp_ko_pos) != 0) return(-9); /* メモリ不足 */ } /* 子ノード(UCT)探索(再帰処理) */ if (search_uct(select_node, (-1)*color, size, posl) == -9) return(-9); /* メモリ不足 */ } return(0); } /* プレイアウト処理 */ /* 引数:現手番の色,碁盤サイズ(9,13,19),現碁盤配置,現劫位置 */ /* 戻値:黒の地の数 - コミ */ int playout(int color, int size, int *posl, int ko_pos) { int i, j, jp, jr, selected, rand_pos; int win, color_tmp, pre_pos; int possibles[19 * 19 + 1]; int stone_num[3], neighbor_num[5]; int score; win = 1; color_tmp = color; pre_pos = -1; /* 黒白交互にランダム着手 */ for (i = 0; i 0 && neighbor_num[WHITE + 1] == 0) score++; /* 白石あり、黒石なしならば得点減算 */ if (neighbor_num[WHITE + 1] > 0 && neighbor_num[BLACK + 1] == 0) score--; } /* 得点に(黒 - 白 - コミ)を加算 */ score += (stone_num[BLACK + 1] - stone_num[WHITE + 1] - int(m_komi)); /* 黒の勝敗を返却 */ return(score); } /* 碁石の着手位置検証と更新 */ /* 引数:着手位置,現手番の色,現碁盤配置,現劫位置,更新要否 */ /* 戻値:黒石の地数 */ int verify_legal(int pos, int color, int *posl, int *ko_pos, int update) { int i, j, c_len, n_len, liberty; int exist_space, is_ko, eye; short n[19 * 19]; short captures[19 * 19]; char check[(19 + 2)*(19 + 2)]; /* 碁石の着手位置が空きなし、劫は着手不可で復帰 */ if (posl[pos] != SPACE || pos == *ko_pos) return(0); /* 劫位置と取り込む石(アゲハマ)を初期化 */ is_ko = 0; memset(captures, '\0', sizeof(captures)); c_len = 0; eye = 0; /* 着手位置のダメの数を確認 */ memcpy(check, m_check_base, sizeof(check)); liberty = verify_liberty(pos, color, check, posl); /* 隣接4位置をチェック */ for (i = 0; i<4; i++) { /* 相手の石のみチェックする */ if (posl[pos + m_dir4[i]] != (-1)*color) { if (posl[pos + m_dir4[i]] != SPACE) eye++; /* 自分の石か壁 */ continue; } memcpy(check, m_check_base, sizeof(check)); check[pos] = 1; exist_space = 0; /* 隣接する石の隣接状態をチェックする */ n_len = verify_neighbor(pos + m_dir4[i], (-1)*color, check, posl, n); /* 隣接する石が単独で囲まれていたら劫の可能性ありとする */ if (n_len == 1) is_ko = 1; /* 隣接する石が囲まれていたら */ /*if (n_len >= 1) eye--;*/ /* 隣接する石が囲まれていたら取り込む石(アゲハマ)に追加する */ for (j = 0; j 0) break; } if (i >= 4) return(0); } /* 着手位置のダメの数がひとつ以下なら着手不可とする */ if (liberty <= 1 && c_len == 0) return(0); if (update == 0) return(1); } /* 盤上の現在の碁石の配置状態を更新 */ posl[pos] = color; /* 隣接する取り込む石があれば */ if (c_len > 0) { /* 盤上の現在の碁石の配置状態を更新(取った位置を空きとする) */ for (i = 0; i 'I') x = alphabet - '@' - 1; else x = 0; y = m_pure_board_size - atoi(&cpos[1]) + 1; pos = x + y * (m_pure_board_size + 2); } return pos; }