public GameTree MiniMax(GameTree tree, eStoneType player, int depth) { if (depth == 0) { // 自分自身を返す return tree; } else if (tree.GetEnableMoveNodes().Count == 0) // ゲーム終了 { return tree; } // 本来置きたい手を置く場合は最も評価値の高い手を // 相手の手を置く場合は最も低い手を選ぶ bool to_max = (player == tree.StoneType); int top_value = 0; const int inf = (int)(1e9 + 7); if (to_max) top_value = -inf; else top_value = inf; Dictionary<int, List<GameTree>> dict = new Dictionary<int, List<GameTree>>(); foreach (var node in tree.GetEnableMoveNodes()) { int value = MiniMax(node, player, depth - 1).GetScoreDiff(); if (player == eStoneType.White) value *= -1; if (to_max) { if (value > top_value) { top_value = value; dict.Add(value, new List<GameTree>()); } if (value == top_value) { dict[value].Add(node); } } else { if (value < top_value) { top_value = value; dict.Add(value, new List<GameTree>()); } if (value == top_value) { dict[value].Add(node); } } } // 最も良いやつからランダム int n = dict[top_value].Count; return dict[top_value][Random.Range(0, n)]; }
// 盤面から移行できる次のノードを取得する public static List <GameTree> GetEnableMoveNodes(GameTree tree) { List <GameTree> res = new List <GameTree>(); var enable_pos = GetEnableHands(tree.Board, tree.StoneType); foreach (var pos in enable_pos) { var get_stones = GetObtainStones(tree.Board, pos, tree.StoneType); Board board = new Board(tree.Board); foreach (var p in get_stones) { board[p] = tree.StoneType; } res.Add(new GameTree(board, NextStone(tree.StoneType), pos, false)); } // 置くところがなかった if (res.Count == 0 && !tree.PrevPassed) { res.Add(new GameTree(new Board(tree.Board), NextStone(tree.StoneType), -1, true)); } return(res); }
// 未訪問の子ノード private void InitNotVisitChilds() { // 子ノードの順番をランダムに決める int n = NotVisitNum; List<int> order = new List<int>(n); for (int i = 0; i < n; ++i) order.Add(i); for (int i = 0; i < n; ++i) { int tail = n - i - 1; int j = Random.Range(0, tail + 1); int tmp = order[tail]; order[tail] = order[j]; order[j] = tmp; } // 順番通りにキューに入れる var trees = GameTree.GetEnableMoveNodes(); NotVisitChilds = new Queue<Node>(); //string s = ""; for (int i = 0; i < n; ++i) { //s += order[i].ToString(); NotVisitChilds.Enqueue(new Node(trees[order[i]], this)); } //Debug.Log(s); }
// ゲームを終了する private void FinalizeGame() { // ゲーム木を開放 game_tree_ = null; --trial_num_; }
public override GameTree Play(GameTree tree) { // 最大の取得数の中からランダムにする Dictionary <int, List <GameTree> > dict = new Dictionary <int, List <GameTree> >(); int max_value = -1; foreach (var node in tree.GetEnableMoveNodes()) { int value = ReversiUtils.GetObtainStones(tree.Board, node.PrevPos, tree.StoneType).Count; if (value > max_value) { dict.Add(value, new List <GameTree>()); max_value = value; } if (value == max_value) { dict[value].Add(node); } } int n = dict[max_value].Count; return(dict[max_value][Random.Range(0, n)]); }
public override GameTree Play(GameTree tree) { int top_value = -10000; Dictionary<int, List<GameTree>> dict = new Dictionary<int, List<GameTree>>(); // 勝った数で比較 foreach(var node in tree.GetEnableMoveNodes()) { int value = 0; for (int i = 0; i < trial_num_; ++i) value += SimulateRandomPlay(node, tree.StoneType); // 勝った数の最大値が複数個あった場合はその中からランダムにできるようにする if(value > top_value) { top_value = value; dict.Add(value, new List<GameTree>()); } if(value == top_value) { dict[value].Add(node); } } int n = dict[top_value].Count; return dict[top_value][Random.Range(0, n)]; }
// ゲーム木を構成する public GameTree(GameTree tree) { Board = new Board(tree.Board); StoneType = tree.StoneType; PrevPos = tree.PrevPos; PrevPassed = tree.PrevPassed; enable_move_nodes_ = tree.enable_move_nodes_; score = tree.score; }
// 盤面を更新する public void ProcBoard(GameTree tree) { black_cnt_ = 0; white_cnt_ = 0; // 石の色を変える for (int y = 0; y < 8; ++y) { for (int x = 0; x < 8; ++x) { int index = y * 8 + x; switch (tree.Board[x, y]) { case eStoneType.None: stones_[index].gameObject.SetActive(false); break; case eStoneType.Black: ++black_cnt_; stones_[index].gameObject.SetActive(true); stones_[index].color = front_color_; break; case eStoneType.White: ++white_cnt_; stones_[index].gameObject.SetActive(true); stones_[index].color = back_color_; break; default: break; } } } // 次に選択できるマスを半透明で載せる var enable_pos = ReversiUtils.GetEnableHands(tree.Board, tree.StoneType); foreach (var pos in enable_pos) { stones_[pos].gameObject.SetActive(true); Color color = front_color_; color.a = 0.4f; if (tree.StoneType == eStoneType.White) { color = back_color_; color.a = 0.55f; } stones_[pos].color = color; } // 石のテキストを更新 black_text_.text = black_cnt_.ToString(); white_text_.text = white_cnt_.ToString(); }
// ゲームを初期化する private void InitGame() { // 初期の盤面を作る Board board = MakeInitialBoard(); // 先行後攻どっちか int player = Random.Range(0, 2); eStoneType stone = (player == 0) ? eStoneType.Black : eStoneType.White; // ゲーム木を生成する game_tree_ = new GameTree(board, stone, -1, false); }
public Node(GameTree tree, Node parent) { GameTree = tree; Parent = parent; Childs = new List<Node>(); WinNum = 0; VisitNum = 0; c = Mathf.Sqrt(2); ChildNum = 0; NotVisitNum = tree.GetEnableMoveNodes().Count; child_inited_ = false; }
// 実際にシミュレーションする 勝ち 1 引き分け 0.5 負け 0 // 自分相手の手はランダム public float SimulateRandomPlay(eStoneType player) { GameTree node = GameTree; int n = node.GetEnableMoveNodes().Count; while (n != 0) { node = new GameTree(node.GetEnableMoveNodes()[Random.Range(0, n)]); n = node.GetEnableMoveNodes().Count; } int ret = ReversiUtils.JudgeResult(node.Board); if (player == eStoneType.White) ret *= -1; return ret / 2f + 0.5f; }
public override GameTree Play(GameTree tree) { // クリックされて有効な手ならそれを返す if (Input.GetMouseButtonDown(0)) { // くそ実装 GameTree ret = CalcNextNode(tree); return(ret); } else { return(null); } }
private int SimulateRandomPlay(GameTree tree, eStoneType player) { GameTree node = tree; int n = node.GetEnableMoveNodes().Count; // ゲームが終了するまでやる while(n != 0) { node = new GameTree(node.GetEnableMoveNodes()[Random.Range(0, n)]); n = node.GetEnableMoveNodes().Count; } int ret = ReversiUtils.JudgeResult(node.Board); if (player == eStoneType.White) ret *= -1; return ret; }
// 実際にシミュレーションする 勝ち 1 引き分け 0.5 負け 0 // 相手の手法を引数で渡す public float SimulateSelectedPlay(eStoneType player, BasePlayer opponent) { GameTree node = GameTree; int n = node.GetEnableMoveNodes().Count; while(n != 0) { // 自分の番ならランダムに 相手の番なら指定した手法を用いて考える if(node.StoneType == player) node = new GameTree(node.GetEnableMoveNodes()[Random.Range(0, n)]); else node = opponent.Play(node); n = node.GetEnableMoveNodes().Count; } int ret = ReversiUtils.JudgeResult(node.Board); if (player == eStoneType.White) ret *= -1; return ret / 2f + 0.5f; }
private IEnumerator GameFlow() { while (trial_num_ != 0) { // ゲームを初期化 InitGame(); renderer_.Reset(); renderer_.ProcBoard(game_tree_); renderer_.ShowPlayerNames(players_); while (true) { GameTree next_game_tree = null; // 入力を待つ while (next_game_tree == null) { next_game_tree = players_[(int)game_tree_.StoneType - 1].Play(game_tree_); yield return null; } // 現在のノードの更新 game_tree_ = next_game_tree; // 描画情報の更新 renderer_.ProcBoard(game_tree_); // 次の移行先がないなら終了 if (game_tree_.GetEnableMoveNodes().Count == 0) break; yield return new WaitForSeconds(ai_think_time_); } // 勝敗処理 renderer_.ProcResult(); // 終了処理 FinalizeGame(); yield return new WaitForSeconds(0.5f); } }
private GameTree CalcNextNode(GameTree tree) { // パスしかない場合 if (tree.GetEnableMoveNodes().Count == 1) { GameTree head_tree = tree.GetEnableMoveNodes()[0]; if (head_tree.PrevPos == -1) { return(head_tree); } } // マウスの座標はどこか Vector3 pos = Input.mousePosition; pos.z = 10f; pos = Camera.main.ScreenToWorldPoint(pos); // 1.05fずつ pos.x += 1.05f * 4.5f; pos.y += 1.05f * 4.5f; int x = (int)(pos.x / 1.05f + 0.01f); int y = (int)(pos.y / 1.05f + 0.01f); int p = y * 8 + x; foreach (var node in tree.GetEnableMoveNodes()) { if (p == node.PrevPos) { // 置ける return(node); } } return(null); }
// 次の手を打つ (返り値は次の盤面) public abstract GameTree Play(GameTree tree);
public override GameTree Play(GameTree tree) { int cnt = tree.GetEnableMoveNodes().Count; return(tree.GetEnableMoveNodes()[Random.Range(0, cnt)]); }
public override GameTree Play(GameTree tree) { // ノード生成 // open loop search的なことをして再利用する Node root; if (cur_node_ == null) root = new Node(tree, null); else { root = cur_node_; root.DeleteParent(); // 相手が選んだ手の方向に進める bool find = false; foreach(var child in root.Childs) { if(child.GameTree.PrevPos == tree.PrevPos) { find = true; cur_node_ = child; root = cur_node_; root.DeleteParent(); break; } } if (!find) { root = new Node(tree, null); cur_node_ = null; } } root.DeleteParent(); for(int i = 0; i < trial_num_; ++i) { var node = root; // 現在見てるノードの子ノードをすべて訪れてるなら,進めるところまで進む while(node.NotVisitNum == 0 && node.ChildNum != 0) { node = node.SelectChildNode(); } // まだ訪れていない子ノードがあるならそれに進む if(node.NotVisitNum != 0) { node = node.ExpandChildNode(); } float score = 0f; // もし仮想敵がいる場合はその敵を見立ててシミュレーションする if (virtual_opponent_ != null) score = node.SimulateSelectedPlay(tree.StoneType, virtual_opponent_); else score = node.SimulateRandomPlay(tree.StoneType); // 逆伝播させる node.BackPropagate(score); } // なるほどなぁ // 最も訪問数の多いノードへと進む AlphaGoがそう Dictionary<int, List<Node>> dict = new Dictionary<int, List<Node>>(); int top_value = -1000; foreach(var child in root.Childs) { int value = child.VisitNum; if(value > top_value) { top_value = value; dict.Add(value, new List<Node>()); } if(value == top_value) { dict[value].Add(child); } } int n = dict[top_value].Count; var ret = dict[top_value][Random.Range(0, n)]; cur_node_ = ret; //Debug.Log(ret.WinNum + " " + ret.VisitNum + " " + ret.WinNum / ret.VisitNum); return ret.GameTree; }
public override GameTree Play(GameTree tree) { return MiniMax(tree, tree.StoneType, depth_); }