// Alpha beta searching with transposition table private double AlphaBeta(int[,] board, int color, int depth = 0, double alpha = double.NegativeInfinity, double beta = double.PositiveInfinity) { nodeCount += 1; evaluator.SetBoard(board); // Return evaluation value if reaching depth = depthMax or terminal node if (depth >= currentDepthMax) { return(evaluator.Evaluate(selfColor)); } // Return evaluation when game ends List <Pos> newOptions = evaluator.Availables(color); List <Pos> oppNewOptions = evaluator.Availables(StoneColor.OppColor(color)); if (newOptions.Count == 0 && oppNewOptions.Count == 0) { int selfStones = evaluator.CountStones(selfColor); int oppStones = evaluator.CountStones(StoneColor.OppColor(selfColor)); if (selfStones > oppStones) { return(double.PositiveInfinity); } else if (selfStones < oppStones) { return(double.NegativeInfinity); } else { return(evaluator.Evaluate(selfColor)); } } // When only the opponent can put stone, go to next depth if (newOptions.Count == 0) { depth += 1; color = StoneColor.OppColor(color); newOptions = oppNewOptions; } // Expand board and store the all child boards in children list // Associate the child and the action of that time List <int[, ]> children = new List <int[, ]>(); Dictionary <int[, ], Pos> actionChildTable = new Dictionary <int[, ], Pos>(); foreach (Pos action in newOptions) { Board childBoard = new Board(); childBoard.SetBoard(board); childBoard.UpdateBoard(action, color); children.Add(childBoard.GetBoard()); actionChildTable.Add(childBoard.GetBoard(), action); } // Sort children in order of the score // In descending order when self turn and in ascending order when opponent turn st.Start(); if (depth <= 3) { children = OrderBoards(children, color); } st.Stop(); // Alpha beta searching if (color == selfColor) { // In self turn, search max value of children double score = double.NegativeInfinity; foreach (int[,] child in children) { // Check if the child is stored in transposition table and the node type is EXACT // If it does, set the value for the score // If not, start alpha-beta-searching in next depth and store the score string childHash = BoardToHash(child); if (transpositionTable.ContainsKey(childHash) && transpositionTable[childHash].Depth >= currentDepthMax && transpositionTable[childHash].NodeType == "EXACT") { transpositionCutCount += 1; score = transpositionTable[childHash].Score; } else { score = AlphaBeta(child, StoneColor.OppColor(color), depth + 1, alpha, beta); transpositionTable[childHash] = new TranspositionTableEntry(child, currentDepthMax, score); } if (score > alpha) { alpha = score; // Get best action if (depth == 0) { foreach (KeyValuePair <int[, ], Pos> kvp in actionChildTable) { if (kvp.Key.Cast <int>().SequenceEqual(child.Cast <int>())) { bestAction = kvp.Value; } } } } // Beta cut if (alpha >= beta) { betaCutCount += 1; break; } } return(alpha); } else { // If the opponent turn, search minimum value of children double score = double.PositiveInfinity; foreach (int[,] child in children) { string childHash = BoardToHash(child); if (transpositionTable.ContainsKey(childHash) && transpositionTable[childHash].Depth >= currentDepthMax && transpositionTable[childHash].NodeType == "EXACT") { transpositionCutCount += 1; score = transpositionTable[childHash].Score; } else { score = AlphaBeta(child, StoneColor.OppColor(color), depth + 1, alpha, beta); transpositionTable[childHash] = new TranspositionTableEntry(child, currentDepthMax, score); } beta = Math.Min(beta, score); // Alpha cut if (beta <= alpha) { alphaCutCount += 1; break; } } return(beta); } }
// Whether the stone at the position is imreversible bool IsConfirmed(Pos pos, int color) { // Return false if there is not self stone at the position if (board[pos.y, pos.x] != color) { return(false); } // Check if the position is connected to the corners only with own stones // Vertical line // (0, pos.x) ~ (pos.y-1, pos.x) are self stones or (pos.y+1, pos.x) ~ (boarSize-1, pos.x) are self stones if (pos.x == 0 || pos.x == boardSize - 1) { bool upperFlag = true; bool lowerFlag = true; for (int j = 0; j < pos.y; j++) { if (board[j, pos.x] != color) { upperFlag = false; break; } } for (int j = pos.y + 1; j < boardSize; j++) { if (board[j, pos.x] != color) { lowerFlag = false; break; } } return(upperFlag || lowerFlag); } // Horizontal line // (pos.y, 0) ~ (pos.y, pos.x-1) are self stones or (pos.y, pos.x+1) ~ (pos.y, boardSize-1) are self stones if (pos.y == 0 || pos.y == boardSize - 1) { bool leftFlag = true; bool rightFlag = true; for (int i = 0; i < pos.x; i++) { if (board[pos.y, i] != color) { leftFlag = false; break; } } for (int i = pos.x + 1; i < boardSize; i++) { if (board[pos.y, i] != color) { rightFlag = false; break; } } return(leftFlag || rightFlag); } return(false); }