Beispiel #1
0
 public Minimax(GameEngine game, int depth)
 {
     this.game = game;
     this.definedDepth = depth;
     this.current = new State(game.currentState.Grid, GameEngine.PLAYER);
     this.deck = new Deck();
 }
Beispiel #2
0
 public MCTS(GameEngine gameEngine)
 {
     this.gameEngine = gameEngine;
     this.deck = new Deck();
     this.currentState = new State(BoardHelper.CloneGrid(this.gameEngine.currentState.Grid), GameEngine.PLAYER);
     this.random = new Random();
 }
Beispiel #3
0
 public GameEngine()
 {
     deck = new Deck();
     int[][] grid = initializeGrid();
     currentState = new State(grid, COMPUTER);
     UpdatePeekCard();
 }
Beispiel #4
0
 public Node(Move move, Node parent, State state, Deck deck)
 {
     this.state = state;
     this.generatingMove = move;
     this.parent = parent;
     this.results = 0;
     this.visits = 0;
     this.children = new List<Node>();
     this.untriedMoves = state.GetMoves(deck);
 }
Beispiel #5
0
 // Returns all available moves in the state
 internal List<Move> GetMoves(Deck deck)
 {
     if (player == GameEngine.PLAYER)
     {
         return GetAllPlayerMoves();
     }
     else
     {
         return GetAllComputerMoves(deck);
     }
 }
Beispiel #6
0
        // Returns a list of all available computer moves given a deck
        public List<Move> GetAllComputerMoves(Deck deck)
        {
            IEnumerable<int> cardsLeft = deck.GetAllCards().Distinct(); // get all distinct cards left
            List<Move> moves = new List<Move>();

            foreach (int card in cardsLeft)
            {
                switch (((PlayerMove)generatingMove).Direction)
                {

                    case DIRECTION.LEFT:
                        return AddComputerMovesAfterLeft(moves, card);

                    case DIRECTION.RIGHT:
                        return AddComputerMovesAfterRight(moves, card);

                    case DIRECTION.UP:
                        return AddComputerMovesAfterUp(moves, card);

                    case DIRECTION.DOWN:
                        return AddComputerMovesAfterDown(moves, card);

                    default:
                        throw new Exception();
                }
            }
            return moves;
        }
Beispiel #7
0
 // To take advantage of the fact that we can count cards
 private void UpdateDeckMemory(int card, bool firstMove)
 {
     if (firstMove)
     {
         for (int i = 0; i < GameEngine.COLUMNS; i++)
         {
             for (int j = 0; j < GameEngine.ROWS; j++)
             {
                 if (current.Grid[i][j] != 0)
                 {
                     deck.Remove(current.Grid[i][j]);
                 }
             }
         }
     }
     else
     {
         deck.Remove(card); // remove the observed card from our deck memory
         if (deck.IsEmpty())
         {
             deck = new Deck();
         }
     }
 }
Beispiel #8
0
        // 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);
            }
        }
Beispiel #9
0
        // Runs an entire game using a parallelized version of iterative deepening minimax
        private Move ParallelIterativeDeepening(State state, int timeLimit, Deck deck)
        {
            Move bestMove = new PlayerMove();

            List<Move> moves = state.GetMoves(deck);
            ConcurrentBag<Tuple<double, Move>> scores = new ConcurrentBag<Tuple<double, Move>>();

            if (moves.Count == 0)
            {
                // game over
                return bestMove;
            }

            // create the resulting states before starting the threads
            List<State> resultingStates = new List<State>();
            foreach (Move move in moves)
            {
                State resultingState = state.ApplyMove(move);
                resultingStates.Add(resultingState);
            }

            Parallel.ForEach(resultingStates, resultingState =>
            {
                double score = IterativeDeepening(resultingState, timeLimit, deck.Clone()).Score;
                scores.Add(new Tuple<double, Move>(score, resultingState.GeneratingMove));
            });
            // find the best score
            double highestScore = Double.MinValue;
            foreach (Tuple<double, Move> score in scores)
            {
                PlayerMove move = (PlayerMove)score.Item2;
                if (score.Item1 > highestScore)
                {
                    highestScore = score.Item1;
                    bestMove = score.Item2;
                }
            }
            return bestMove;
        }
Beispiel #10
0
 // 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;
 }
Beispiel #11
0
        // 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;
        }
Beispiel #12
0
 // Iterative deepening minimax
 private Move IterativeDeepening(State state, int timeLimit, Deck deck)
 {
     int depth = 1;
     Stopwatch timer = new Stopwatch();
     Move bestMove = null;
     // start the search
     timer.Start();
     while (timer.ElapsedMilliseconds < timeLimit)
     {
         Tuple<Move, Boolean> result = RecursiveIterativeDeepening(state, depth, Double.MinValue, Double.MaxValue, timeLimit, timer, deck);
         if (result.Item2) bestMove = result.Item1; // only update bestMove if full recursion
         depth++;
     }
     return bestMove;
 }
Beispiel #13
0
 // adds a child node to the list of children
 // after exploring a move - removes the move from untried
 public Node AddChild(Move move, State state, Deck deck)
 {
     Node child = new Node(move, this, state, deck);
     this.untriedMoves.Remove(move);
     this.children.Add(child);
     return child;
 }
Beispiel #14
0
        // Time limited MCTS
        private Node TimeLimited(State rootState, int timeLimit, Stopwatch timer, Deck deck)
        {
            Node rootNode = new Node(null, null, rootState, deck);

            while (true)
            {
                if (timer.ElapsedMilliseconds > timeLimit)
                {
                    if (FindBestChild(rootNode.Children) == null && !rootNode.state.IsGameOver())
                    {
                        timeLimit += 10;
                        timer.Restart();
                    }
                    else
                    {
                        return rootNode;
                    }

                }
                Node node = rootNode;
                State state = rootState.Clone();
                Deck clonedDeck = deck.Clone();

                // 1: Select
                while (node.UntriedMoves.Count == 0 && node.Children.Count != 0)
                {
                    node = node.SelectChild();
                    state = state.ApplyMove(node.GeneratingMove);
                    if (node.GeneratingMove is ComputerMove)
                    {
                        clonedDeck.Remove(((ComputerMove)node.GeneratingMove).Card);
                        if (clonedDeck.IsEmpty()) clonedDeck = new Deck();
                    }
                }

                // 2: Expand
                if (node.UntriedMoves.Count != 0)
                {
                    Move randomMove = node.UntriedMoves[random.Next(0, node.UntriedMoves.Count)];
                    if (randomMove is ComputerMove)
                    {
                        if (clonedDeck.IsEmpty()) clonedDeck = new Deck();
                        clonedDeck.Remove(((ComputerMove)randomMove).Card);
                        state = state.ApplyMove(randomMove);
                        node = node.AddChild(randomMove, state, clonedDeck);

                    }
                    else
                    {
                        state = state.ApplyMove(randomMove);
                        node = node.AddChild(randomMove, state, clonedDeck);
                    }
                }

                // 3: Simulation
                while (state.GetMoves(clonedDeck).Count != 0)
                {
                    Move move = state.GetRandomMove(clonedDeck);
                    if (move is ComputerMove)
                    {
                        if (clonedDeck.IsEmpty()) clonedDeck = new Deck();
                        clonedDeck.Remove(((ComputerMove)move).Card);
                        state = state.ApplyMove(move);
                    }
                    else
                    {
                        state = state.ApplyMove(move);
                    }
                }

                // 4: Backpropagation
                while (node != null)
                {
                    node.Update(state.GetResult());
                    node = node.Parent;
                }
            }
        }
Beispiel #15
0
        // Parallel time limited MCTS
        private DIRECTION ParallelTimeLimitedMCTS(State currentState, int timeLimit, Deck deck)
        {
            ConcurrentBag<Node> allChildren = new ConcurrentBag<Node>();
            int numOfChildren = currentState.GetMoves(deck).Count;

            Stopwatch timer = new Stopwatch();
            timer.Start();

            Parallel.For(0, NUM_THREADS, i =>
            {
                Node resultRoot = TimeLimited(currentState, timeLimit, timer, deck);
                foreach (Node child in resultRoot.Children)
                {
                    allChildren.Add(child);
                }
            });
            timer.Stop();

            List<int> totalVisits = new List<int>(4) { 0, 0, 0, 0 };
            List<double> totalResults = new List<double>(4) { 0, 0, 0, 0 };

            foreach (Node child in allChildren)
            {
                int direction = (int)((PlayerMove)child.GeneratingMove).Direction;
                totalVisits[direction] += child.Visits;
                totalResults[direction] += child.Results;
            }

            double best = Double.MinValue;
            int bestDirection = -1;
            for (int k = 0; k < 4; k++)
            {
                double avg = totalResults[k] / totalVisits[k];
                if (avg > best)
                {
                    best = avg;
                    bestDirection = k;
                }
            }
            if (bestDirection == -1) return (DIRECTION)(-1);
            return (DIRECTION)bestDirection;
        }
Beispiel #16
0
 // Returns a random move
 internal Move GetRandomMove(Deck deck)
 {
     List<Move> moves = GetMoves(deck);
     int index = random.Next(0, moves.Count);
     return moves[index];
 }
Beispiel #17
0
 public Deck(Deck deck)
 {
     this.cards = new List<int>(deck.cards);
 }
Beispiel #18
0
 // 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);
 }