// Recursive part of iterative deepening private Tuple<Move, bool> RecursiveIterativeDeepening(State state, int depth, double alpha, double beta, int timeLimit, Stopwatch timer, Deck deck) { Move bestMove; if (depth == 0 || state.IsGameOver()) { if (state.Player == GameEngine.PLAYER) { bestMove = new PlayerMove(); // default constructor creates dummy action bestMove.Score = AI.Evaluate(state); return new Tuple<Move, Boolean>(bestMove, true); } else if (state.Player == GameEngine.COMPUTER) { bestMove = new ComputerMove(); // default constructor creates dummy action bestMove.Score = AI.Evaluate(state); return new Tuple<Move, Boolean>(bestMove, true); } else { throw new Exception(); } } if (state.Player == GameEngine.PLAYER){ bestMove = new PlayerMove(); double highestScore = Double.MinValue, currentScore = Double.MinValue; List<Move> moves = state.GetAllMoves(); foreach (Move move in moves) { State resultingState = state.ApplyMove(move); currentScore = RecursiveIterativeDeepening(resultingState, depth - 1, alpha, beta, timeLimit, timer, deck).Item1.Score; if (currentScore > highestScore) { highestScore = currentScore; bestMove = (PlayerMove)move; } alpha = Math.Max(alpha, highestScore); if (beta <= alpha) break; if (timer.ElapsedMilliseconds > timeLimit) { bestMove.Score = highestScore; return new Tuple<Move, Boolean>(bestMove, false); // recursion not completed, return false } } bestMove.Score = highestScore; return new Tuple<Move, Boolean>(bestMove, true); } else { bestMove = new ComputerMove(); double lowestScore = Double.MaxValue, currentScore = Double.MaxValue; List<Move> moves = null; if (depth == definedDepth - 1) { int nextCard = game.nextCard; moves = state.GetAllComputerMoves(nextCard); } else { if (deck.IsEmpty()) deck = new Deck(); moves = state.GetAllComputerMoves(deck); } foreach (Move move in moves) { deck.Remove(((ComputerMove)move).Card); State resultingState = state.ApplyMove(move); currentScore = RecursiveIterativeDeepening(resultingState, depth - 1, alpha, beta, timeLimit, timer, deck).Item1.Score; if (currentScore < lowestScore) { lowestScore = currentScore; bestMove = (ComputerMove)move; } beta = Math.Min(beta, lowestScore); if (beta <= alpha) break; deck.Add(((ComputerMove)move).Card); if (timer.ElapsedMilliseconds > timeLimit) { bestMove.Score = lowestScore; return new Tuple<Move, Boolean>(bestMove, false); // recursion not completed, return false } } bestMove.Score = lowestScore; return new Tuple<Move, Boolean>(bestMove, true); } }
// Evaluation function used by Minimax and Expectimax public static double Evaluate(State state) { if (state.IsGameOver()) return -1000; double smoothness = Smoothness(state); double monotonicity = Monotonicity(state); double emptycells = EmptyCells(state); double highestvalue = HighestCard(state); double trappedpenalty = TrappedPenalty(state); return smoothness_weight * smoothness + monotonicity_weight * monotonicity + emptycells_weight * emptycells + highestvalue_weight * highestvalue - trappedpenalty_weight * trappedpenalty; }
// Classic Minimax using alpha-beta pruning private Move MinimaxAlgorithm(State state, int depth, double alpha, double beta, Deck deck) { Move bestMove; if (depth == 0 || state.IsGameOver()) { if (state.Player == GameEngine.PLAYER) { bestMove = new PlayerMove(); // default constructor creates dummy action bestMove.Score = AI.Evaluate(state); return bestMove; } else if (state.Player == GameEngine.COMPUTER) { bestMove = new ComputerMove(); // default constructor creates dummy action bestMove.Score = AI.Evaluate(state); return bestMove; } else { throw new Exception(); } } if (state.Player == GameEngine.PLAYER) return Max(state, depth, alpha, beta); else return Min(state, depth, alpha, beta); }
// Starts the time limited Monte Carlo Tree Search and returns the best child node // resulting from the search public Node TimeLimitedMCTS(State rootState, int timeLimit, Deck deck) { Stopwatch timer = new Stopwatch(); Node bestNode = null; while (bestNode == null && !rootState.IsGameOver()) { timer.Start(); Node rootNode = TimeLimited(rootState, timeLimit, timer, deck); bestNode = FindBestChild(rootNode.Children); timeLimit += 10; timer.Reset(); } return bestNode; }