// Return a numeric array for evaluation // Self stone => +1.0, Opponent stone => -1.0, Blank => 0.0 int[,] GetBoardForEval(int color) { int[,] arr = new int[8, 8]; Array.Copy(board, arr, board.Length); for (int i = 0; i < arr.GetLength(0); i++) { for (int j = 0; j < arr.GetLength(1); j++) { if (arr[i, j] == color) { arr[i, j] = 1; } else if (arr[i, j] == StoneColor.OppColor(color)) { arr[i, j] = -1; } else { arr[i, j] = 0; } } } return(arr); }
// Return the 2 dimentional array of the position where the stone is reversible internal List <List <Pos> > GetReversibles(Pos pos, int color) { int[,] directionXY = new int[8, 2] { { -1, -1 }, { 0, -1 }, { 1, -1 }, { -1, 0 }, { 1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 } }; List <List <Pos> > reversibleStones = new List <List <Pos> >(); for (int i = 0; i < directionXY.GetLength(0); i++) { reversibleStones.Add(new List <Pos>()); } for (int i = 0; i < directionXY.GetLength(0); i++) { int dx = directionXY[i, 0]; int dy = directionXY[i, 1]; int x = pos.x; int y = pos.y; for (int j = 0; j < boardSize; j++) { x += dx; y += dy; if (!(0 <= x && x < boardSize && 0 <= y && y < boardSize)) { reversibleStones[i] = new List <Pos>(); break; } if (board[y, x] == StoneColor.OppColor(color)) { reversibleStones[i].Add(new Pos() { x = x, y = y }); } else if (board[y, x] == color) { break; } else { reversibleStones[i] = new List <Pos>(); break; } } } return(reversibleStones); }
// Calculate evaluation value internal double Evaluate(int color) { System.Random r = new System.Random(); double fs = (Confirms(color) - Confirms(StoneColor.OppColor(color)) + r.NextDouble() * 3) * 11; double cn = (Availables(color).Count + r.NextDouble() * 2) * 10; double bp = 0.0; int[,] tmpBoard = GetBoardForEval(color); for (int i = 0; i < boardSize; i++) { for (int j = 0; j < boardSize; j++) { bp += evaluateValue[i, j] * tmpBoard[i, j] * r.NextDouble() * 3; } } return(w1 * bp + w2 * fs + w3 * cn); }
// 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); } }
void Reverse(Pos pos) { board[pos.y, pos.x] = StoneColor.OppColor(board[pos.y, pos.x]); }