예제 #1
0
        public override FutureMove Think(Board board, CancellationToken ct)
        {
            Winner winner;
            PColor colorOfOurAI = board.Turn;

            possibleMoves.Clear();
            nonLosingMoves.Clear();

            for (int col = 0; col < Cols; col++)
            {
                if (board.IsColumnFull(col))
                {
                    continue;
                }

                for (int shp = 0; shp < 2; shp++)
                {
                    PShape shape = (PShape)shp;

                    if (board.PieceCount(colorOfOurAI, shape) == 0)
                    {
                        continue;
                    }

                    possibleMoves.Add(new FutureMove(col, shape));

                    board.DoMove(shape, col);

                    winner = board.CheckWinner();

                    // immediately
                    board.UndoMove();

                    if (winner.ToPColor() == colorOfOurAI)
                    {
                        return(new FutureMove(col, shape));
                    }
                    else if (winner.ToPColor() != colorOfOurAI.Other())
                    {
                        nonLosingMoves.Add(new FutureMove(col, shape));
                    }
                }
            }

            if (nonLosingMoves.Count > 0)
            {
                return(nonLosingMoves[random.Next(nonLosingMoves.Count)]);
            }

            return(possibleMoves[random.Next(possibleMoves.Count)]);
        }
예제 #2
0
        // Raise and invalid play event and return the match winner (which is
        // the opponent of the thinker that made an invalid play)
        private Winner OnInvalidPlay(
            PColor color, IThinker thinker, string reason)
        {
            // Set the other thinker as the winner of the match
            Winner winner = color.Other().ToWinner();

            // Set solution to null
            solution = null;

            // Notify listeners that thinker made an invalid play
            InvalidPlay?.Invoke(color, thinker.ToString(), reason);

            // Return the winner
            return(winner);
        }
예제 #3
0
        // Negamax with alpha beta pruning.
        private (FutureMove move, float score) Negamax(
            Board board, CancellationToken ct, PColor turn, int depth,
            float alpha, float beta)
        {
            (FutureMove move, float score)currentMove;

            Winner winner;

            // In case of cancellation request, skips rest of algorithm.
            if (ct.IsCancellationRequested)
            {
                // Makes no move.
                currentMove = (FutureMove.NoMove, float.NaN);
            }

            // If game has ended, returns final score.
            else if ((winner = board.CheckWinner()) != Winner.None)
            {
                // Oizys wins, returns maximum score.
                if (winner.ToPColor() == turn)
                {
                    currentMove = (FutureMove.NoMove, float.PositiveInfinity);
                }

                // Opponent wins, returns minimum score.
                else if (winner.ToPColor() == turn.Other())
                {
                    currentMove = (FutureMove.NoMove, float.NegativeInfinity);
                }

                // A draw board, return 0.
                else
                {
                    currentMove = (FutureMove.NoMove, 0f);
                }
            }

            // If maximum depth has been reached, get heuristic value.
            else if (depth == maxDepth)
            {
                currentMove = (FutureMove.NoMove, Heuristic(board, turn));
            }

            // Board isn't final and maximum depth hasn't been reached.
            else
            {
                // Set up currentMove for future maximizing.
                currentMove = (FutureMove.NoMove, float.NegativeInfinity);

                // Iterate each column.
                for (int c = 0; c < Cols; c++)
                {
                    // If column is full, skip to next column.
                    if (board.IsColumnFull(c))
                    {
                        continue;
                    }

                    // Try both shapes.
                    for (int s = 0; s < 2; s++)
                    {
                        // Store current shape.
                        PShape shape = (PShape)s;

                        // If player doesn't have this piece, skip.
                        if (board.PieceCount(turn, shape) == 0)
                        {
                            continue;
                        }

                        // Test move.
                        board.DoMove(shape, c);

                        // Call Negamax and register board's score.
                        float score = -Negamax(
                            board, ct, turn.Other(), depth + 1, -beta,
                            -alpha).score;

                        // Undo move.
                        board.UndoMove();

                        // If this move has the best score yet, keep it.
                        if (score > currentMove.score)
                        {
                            currentMove = (new FutureMove(c, shape), score);
                        }

                        // Update alpha value.
                        if (score > alpha)
                        {
                            alpha = score;
                        }

                        // Beta pruning.
                        if (alpha >= beta)
                        {
                            return(currentMove);
                        }
                    }
                }
            }

            // Returns move and its value.
            return(currentMove);
        }
예제 #4
0
        // Our minimax implementation
        private (FutureMove move, float score) Minimax(
            Board board, CancellationToken ct, PColor player, PColor turn, int depth)
        {
            // Move to return and its heuristic value
            (FutureMove move, float score)selectedMove;

            // Current board state
            Winner winner;

            // If a cancellation request was made...
            if (ct.IsCancellationRequested)
            {
                // ...set a "no move" and skip the remaining part of the algorithm
                selectedMove = (FutureMove.NoMove, float.NaN);
            }
            // Otherwise, if it's a final board, return the appropriate evaluation
            else if ((winner = board.CheckWinner()) != Winner.None)
            {
                if (winner.ToPColor() == player)
                {
                    // AI player wins, return highest possible score
                    selectedMove = (FutureMove.NoMove, float.PositiveInfinity);
                }
                else if (winner.ToPColor() == player.Other())
                {
                    // Opponent wins, return lowest possible score
                    selectedMove = (FutureMove.NoMove, float.NegativeInfinity);
                }
                else
                {
                    // A draw, return zero
                    selectedMove = (FutureMove.NoMove, 0f);
                }
            }
            // If we're at maximum depth and don't have a final board, use
            // the heuristic
            else if (depth == maxDepth)
            {
                // Where did this Heuristic() function come from?
                // We'll return to it in a moment
                selectedMove = (FutureMove.NoMove, Heuristic(board, player));
            }
            else // Board not final and depth not at max...
            {
                //...so let's test all possible moves and recursively call Minimax()
                // for each one of them, maximizing or minimizing depending on who's
                // turn it is

                // Initialize the selected move...
                selectedMove = turn == player
                               // ...with negative infinity if it's the AI's turn and we're
                               // maximizing (so anything except defeat will be better than this)
                    ? (FutureMove.NoMove, float.NegativeInfinity)
                               // ...or with positive infinity if it's the opponent's turn and we're
                               // minimizing (so anything except victory will be worse than this)
                    : (FutureMove.NoMove, float.PositiveInfinity);

                // Test each column
                for (int i = 0; i < Cols; i++)
                {
                    // Skip full columns
                    if (board.IsColumnFull(i))
                    {
                        continue;
                    }

                    // Test shapes
                    for (int j = 0; j < 2; j++)
                    {
                        // Get current shape
                        PShape shape = (PShape)j;

                        // Use this variable to keep the current board's score
                        float eval;

                        // Skip unavailable shapes
                        if (board.PieceCount(turn, shape) == 0)
                        {
                            continue;
                        }

                        // Test move, call minimax and undo move
                        board.DoMove(shape, i);
                        eval = Minimax(board, ct, player, turn.Other(), depth + 1).score;
                        board.UndoMove();

                        // If we're maximizing, is this the best move so far?
                        if (turn == player && eval > selectedMove.score)
                        {
                            // If so, keep it
                            selectedMove = (new FutureMove(i, shape), eval);
                        }
                        // Otherwise, if we're minimizing, is this the worst move so far?
                        else if (turn == player.Other() && eval < selectedMove.score)
                        {
                            // If so, keep it
                            selectedMove = (new FutureMove(i, shape), eval);
                        }
                    }
                }
            }

            // Return selected move and its heuristic value
            return(selectedMove);
        }