// Quick sort using transposition table in ascending order List <int[, ]> SortByScore(List <int[, ]> list, int color) { if (list.Count <= 1) { return(list); } List <int[, ]> left = new List <int[, ]>(); List <int[, ]> center = new List <int[, ]>(); List <int[, ]> right = new List <int[, ]>(); int[,] refBoard = list[0]; string refBoardHash = BoardToHash(refBoard); double refScore; if (transpositionTable.ContainsKey(refBoardHash)) { refScore = transpositionTable[refBoardHash].Score; } else { OthelloEvaluator tmpEvaluator = new OthelloEvaluator(); tmpEvaluator.SetBoard(refBoard); refScore = tmpEvaluator.Evaluate(selfColor); transpositionTable[refBoardHash] = new TranspositionTableEntry(refBoard, currentDepthMax, refScore, "TEMP"); } center.Add(refBoard); foreach (int[,] board in list) { sortAllCount += 1; string boardHash = BoardToHash(board); double boardScore; if (transpositionTable.ContainsKey(boardHash)) { boardScore = transpositionTable[boardHash].Score; } else { sortEvalCount += 1; OthelloEvaluator tmpEvaluator = new OthelloEvaluator(); tmpEvaluator.SetBoard(board); boardScore = tmpEvaluator.Evaluate(selfColor); transpositionTable[boardHash] = new TranspositionTableEntry(board, currentDepthMax, boardScore, "TEMP"); } if (boardScore < refScore) { left.Add(board); } else if (boardScore > refScore) { right.Add(board); } else { center.Add(board); } } left = SortByScore(left, color); right = SortByScore(right, color); return(left.Concat(center).Concat(right).ToList()); }
// 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); } }