// 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); } }
// MIN part of Minimax Move Min(State state, int depth, double alpha, double beta) { ComputerMove 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 = MinimaxAlgorithm(resultingState, depth - 1, alpha, beta, deck).Score; if (currentScore < lowestScore) { lowestScore = currentScore; bestMove = (ComputerMove)move; } beta = Math.Min(beta, lowestScore); if (beta <= alpha) break; deck.Add(((ComputerMove)move).Card); } bestMove.Score = lowestScore; return bestMove; }
// 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); }
// Generates a new card on the board private void GenerateNewCard() { int numTiles = 0; int card = 0; if (nextIsBonus) { List<int> possibleBonusCards = currentState.GeneratePossibleBonusCards(); card = possibleBonusCards[random.Next(0, possibleBonusCards.Count)]; numTiles = possibleBonusCards.Count * currentState.columnsOrRowsWithMovedTiles.Count; } else { card = deck.DealCard(); numTiles = currentState.columnsOrRowsWithMovedTiles.Count * 3; } // decide on position to put new tile int row = 0, column = 0; int index = random.Next(0, currentState.columnsOrRowsWithMovedTiles.Count); // random index of list of possible rows/columns if (((PlayerMove)currentState.GeneratingMove).Direction == DIRECTION.LEFT) { row = currentState.columnsOrRowsWithMovedTiles[index]; column = 3; // right-most column } else if (((PlayerMove)currentState.GeneratingMove).Direction == DIRECTION.RIGHT) { row = currentState.columnsOrRowsWithMovedTiles[index]; column = 0; // left-most column } else if (((PlayerMove)currentState.GeneratingMove).Direction == DIRECTION.UP) { column = currentState.columnsOrRowsWithMovedTiles[index]; row = 0; // down-most row } else if (((PlayerMove)currentState.GeneratingMove).Direction == DIRECTION.DOWN) { column = currentState.columnsOrRowsWithMovedTiles[index]; row = 3; // up-most row } currentState.Grid[column][row] = card; ComputerMove move = new ComputerMove(card, new Tuple<int, int>(column, row)); currentState.GeneratingMove = move; }