Exemplo n.º 1
0
        public override GameState <S, M> ReadJson(JsonReader reader, Type objectType, GameState <S, M> existingValue, bool hasExistingValue,
                                                  JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
            {
                return(null);
            }

            JObject obj = JObject.Load(reader);

            serializer.Converters.Add(new GameStageConverter());
            serializer.Converters.Add(new FutureMoveConverter <M>());
            GameStage stage = obj["_gameStateStage"]
                              .ToObject <GameStage>(serializer);

            S state = obj["_gameStateState"].ToObject <S>(serializer);


            FutureMove <M> futureMove = obj["_gameStateFutureMove"].Type == JTokenType.Null
                ? null
                : obj["_gameStateFutureMove"].ToObject <FutureMove <M> >(serializer);

            return(new GameState <S, M> {
                State = state, FutureMove = futureMove, Stage = stage
            });
        }
Exemplo n.º 2
0
        // Make an actual move
        private void MakeAMove(FutureMove move)
        {
            // Who is making this move?
            PColor whoPlayed = board.Turn;

            // Perform move in game board, get column where move was performed
            int row = board.DoMove(move.shape, move.column);

            // If the column had space for the move...
            if (row >= 0)
            {
                // Get possible winner and solution
                Winner winner = board.CheckWinner(solution);

                // Update UI board with performed move, and possibly the
                // winner/solution
                view.UpdateBoard(
                    new Move(
                        row, move.column, new Piece(whoPlayed, move.shape)),
                    winner,
                    solution);

                // If the game is over...
                if (winner != Winner.None)
                {
                    // Invoke MatchOver event
                    OnMatchOver(winner);
                }
            }
            else // If we get here, column didn't have space for the move
            {
                throw new InvalidOperationException(
                          $"Column {move.column + 1} is full.");
            }
        }
Exemplo n.º 3
0
        public override void WriteJson(JsonWriter writer, FutureMove <T> value, JsonSerializer serializer)
        {
            JObject o = (JObject)JToken.FromObject(new { });

            o.Add("_futureMoveMove", JToken.FromObject(value.Move, serializer));
            o.Add("_futureMoveTime", JToken.FromObject(value.DateTime.ToUniversalTime(), DateTimeJsonSerializer));
            o.WriteTo(writer);
        }
Exemplo n.º 4
0
 // Displays the move performed by the specified thinker
 private void MovePerformed(
     PColor thinkerColor, string thinkerName,
     FutureMove move, int thinkingTime)
 {
     Console.WriteLine(thinkerColor.FormatName(thinkerName)
                       + $" placed a {move.shape} piece at column {move.column}"
                       + $" after {thinkingTime}ms");
 }
Exemplo n.º 5
0
        private static void Main(string[] args)
        {
            // ////////////////////////////////////////////////////////////// //
            // Create an instance of our Ultron thinker via ThinkerPrototype. //
            // If we created directly with new, it would not be properly      //
            // configured.                                                    //
            // ////////////////////////////////////////////////////////////// //

            // Create a configuration for a default ColorShapeLinks match
            MatchConfig mc = new MatchConfig();

            // Get the fully qualified name of our basic Ultron thinker
            string ultronFullName = typeof(UltronThinker).FullName;

            // Create a prototype for our thinker
            ThinkerPrototype tp = new ThinkerPrototype(ultronFullName, "", mc);

            // Create an instance of our basic Ultron thinker
            IThinker ultronThinker = tp.Create();

            // //////////////////////////////////////////////////////// //
            // Create a board so we can test how our thinker will play. //
            // //////////////////////////////////////////////////////// //

            // A cancellation token, will be ignored
            CancellationToken ct = new CancellationToken();

            // Create a ColorShapeLinks board with default size
            Board board = new Board();

            // Show initial board
            Console.WriteLine("\n=== Initial board ===\n");
            ShowBoard(board);

            // Make some moves manually
            board.DoMove(PShape.Round, 0);  // White plays round piece in col 0
            board.DoMove(PShape.Square, 4); // Red plays square piece in col 4
            board.DoMove(PShape.Square, 5); // White plays round piece in col 5

            // Show board after our three manual moves
            Console.WriteLine("\n=== Board after three manual moves ===\n");
            ShowBoard(board);

            // What move would Ultron make at this moment?
            FutureMove ultronMove = ultronThinker.Think(board, ct);

            // Show move
            Console.WriteLine($"-> Ultron will play {ultronMove}");

            // Make the move selected by Ultron
            board.DoMove(ultronMove.shape, ultronMove.column);

            // Show board after Ultron made its move
            Console.WriteLine("\n=== Board after Ultron made move ===\n");
            ShowBoard(board);
        }
Exemplo n.º 6
0
        // Callback method to perform a human move
        private void HumanMove(FutureMove move)
        {
            // Show message indicating what move the human chose
            view.SubmitMessage(string.Format(
                                   "{0} placed a {1} piece at column {2}",
                                   CurrPlrNameColor,
                                   move.shape.ToString().ToLower(),
                                   move.column));

            // Make the move
            MakeAMove(move);

            // In the next human turn, show the human a message for him to play
            showHumanTurnMessage = true;
        }
Exemplo n.º 7
0
    /// <summary>
    /// Check enemy colums for a winning move according to shapes.
    /// </summary>
    /// <param name="board"></param>
    /// <param name="col"></param>
    /// <returns>A move that blocks enemy win.</returns>
    private FutureMove?CheckEnemyColsShape(Board board, int col)
    {
        FutureMove? move;
        List <bool> threeInLine = new List <bool>();
        Piece       piece;
        PShape      enemyShape =
            color == PColor.White ? PShape.Square : PShape.Round;

        for (int i = 0; i < board.rows; i++)
        {
            if (board[i, col] == null)
            {
                return(null);
            }
            piece = (Piece)board[i, col];
            if (piece.shape == enemyShape)
            {
                threeInLine.Add(true);
            }
            else
            {
                threeInLine.RemoveRange(0, threeInLine.Count);
            }
            if (threeInLine.Count == 3)
            {
                if (board[i + 1, col].HasValue || i == board.rows)
                {
                    piece = (Piece)board[i + 1, col];
                    if (piece.shape == shape)
                    {
                        threeInLine.RemoveRange(0, threeInLine.Count);
                    }
                }
                else
                {
                    if (!board.IsColumnFull(col))
                    {
                        return(move = new FutureMove(col, shape));
                    }
                }
            }
        }

        return(move = null);
    }
Exemplo n.º 8
0
        public override FutureMove <T> ReadJson(JsonReader reader, Type objectType, FutureMove <T> existingValue, bool hasExistingValue,
                                                JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
            {
                return(null);
            }

            JObject  obj  = JObject.Load(reader);
            T        move = obj["_futureMoveMove"].ToObject <T>(serializer);
            DateTime dt   = obj["_futureMoveTime"].ToObject <DateTime>(DateTimeJsonSerializer).ToLocalTime();

            return(new FutureMove <T>
            {
                Move = move,
                DateTime = dt
            });
        }
    /// @copydoc IThinker.Think
    /// <seealso cref="IThinker.Think"/>
    public FutureMove Think(Board board, CancellationToken ct)
    {
        // The move to perform
        FutureMove move;

        // Find next free column where to play
        do
        {
            // Get next column
            lastCol++;
            if (lastCol >= board.cols)
            {
                lastCol = 0;
            }
            // Is this task to be cancelled?
            if (ct.IsCancellationRequested)
            {
                return(FutureMove.NoMove);
            }
        }while (board.IsColumnFull(lastCol));

        // Try to use a round piece first
        if (board.PieceCount(board.Turn, PShape.Round) > 0)
        {
            move = new FutureMove(lastCol, PShape.Round);
        }
        // If there's no round pieces left, let's try a square pieces
        else if (board.PieceCount(board.Turn, PShape.Square) > 0)
        {
            move = new FutureMove(lastCol, PShape.Square);
        }
        // Otherwise return a "no move" (this should never happen)
        else
        {
            move = FutureMove.NoMove;
        }

        // Return move
        return(move);
    }
    private List <FutureMove> getFutureMoves(Node node, String path, String pathIds)
    {
        List <FutureMove> rows           = new List <FutureMove>();
        StringBuilder     currentPath    = new StringBuilder();
        StringBuilder     currentPathIds = new StringBuilder();

        if (path != null)
        {
            currentPath.AppendFormat("{0}", path);
        }
        else
        {
            currentPath.AppendFormat("{0}", Common.ConvertToBoardPosition(node.Square));
        }
        if (pathIds != null)
        {
            currentPathIds.AppendFormat("{0}", pathIds);
        }
        else
        {
            currentPathIds.AppendFormat("{0}", node.Square);
        }
        foreach (var n in node.ReachableSquares)
        {
            string temp        = String.Format("{0}-{1}", currentPath, Common.ConvertToBoardPosition(n.Square));
            string tempPathIds = String.Format("{0}-{1}", currentPathIds, n.Square);
            if (n.ReachableSquares.Any())
            {
                rows.AddRange(getFutureMoves(n, temp, tempPathIds));
            }
            FutureMove fm = new FutureMove();
            fm.Depth   = n.RecurseDepth;
            fm.Path    = temp.ToString();
            fm.PathIds = tempPathIds;
            fm.Content = n.Content;
            rows.Add(fm);
        }
        return(rows);
    }
Exemplo n.º 11
0
    public FutureMove Think(Board board, CancellationToken ct)
    {
        int        bestScore         = int.MinValue;
        FutureMove finalMoveToReturn = FutureMove.NoMove;

        for (int i = 0; i < board.cols; i++)
        {
            //check if the column is full
            if (board.IsColumnFull(i))
            {
                /*
                 * if (i == board.cols)
                 * {
                 *  i = 0;
                 *  //return FutureMove.NoMove;
                 * }
                 */
                continue;
            }

            //score for this iteration
            int iterationScore;

            int roundPieceMoveScore  = 0;
            int squarePieceMoveScore = 0;

            //best shape to be returned
            PShape bestShape;

            if (board.PieceCount(board.Turn, PShape.Round) != 0)
            {
                //try a move with a round piece and get score
                board.DoMove(PShape.Round, i);
                roundPieceMoveScore += -NegaMax(board, ct, _actualIteration + 1, board.Turn);
                board.UndoMove();
            }

            if (board.PieceCount(board.Turn, PShape.Round) != 0)
            {
                //try a move with a square piece and get score
                board.DoMove(PShape.Square, i);
                squarePieceMoveScore = -NegaMax(board, ct, _actualIteration + 1, board.Turn);
                board.UndoMove();
            }

            if (board.PieceCount(board.Turn, PShape.Round) == 0 && board.PieceCount(board.Turn, PShape.Square) == 0)
            {
                _cancelationRequested = true;
            }


            //check what piece is better
            if (squarePieceMoveScore > roundPieceMoveScore)
            {
                iterationScore = squarePieceMoveScore;
                if (board.PieceCount(board.Turn, PShape.Square) != 0)
                {
                    bestShape = PShape.Square;
                }
                else
                {
                    bestShape = PShape.Round;
                }
            }
            else
            {
                iterationScore = roundPieceMoveScore;
                if (board.PieceCount(board.Turn, PShape.Round) != 0)
                {
                    bestShape = PShape.Round;
                }
                else
                {
                    bestShape = PShape.Square;
                }
            }

            //Get the move with the best score
            if (iterationScore > bestScore)
            {
                bestScore         = iterationScore;
                finalMoveToReturn = new FutureMove(i, bestShape);
            }
        }

        //check for think cancelation request

        if (_cancelationRequested == true)
        {
            finalMoveToReturn = FutureMove.NoMove;
        }


        //return the move
        return(finalMoveToReturn);
    }
Exemplo n.º 12
0
    private int NegaMax(Board board, CancellationToken ct, int iteration, PColor turn)
    {
        //score variable
        int bestScore = int.MinValue;

        //future move to be returned
        FutureMove finalMoveToReturn = FutureMove.NoMove;

        //check for cancelation request
        if (ct.IsCancellationRequested == true)
        {
            _cancelationRequested = true;
        }
        else
        {
            //check if we cant search more
            if (iteration == _iterationsLimit || board.CheckWinner() != Winner.None)
            {
                return(bestScore);
            }
            //if we can

            for (int i = 0; i < board.cols; i++)
            {
                //check if column is full
                if (board.IsColumnFull(i))
                {
                    /*
                     * if (i == board.cols)
                     * {
                     *  i = 0;
                     *  //return 0;
                     * }
                     */
                    // i = 0;
                    continue;
                }


                int iterationScore;

                //score for each move
                int roundPieceMoveScore  = 0;
                int squarePieceMoveScore = 0;

                //best shape to be returned
                PShape bestShape;

                //check if AI still have pieces
                if (board.PieceCount(board.Turn, PShape.Round) != 0)
                {
                    //try a move
                    board.DoMove(PShape.Round, i);
                    roundPieceMoveScore = -NegaMax(board, ct, iteration + 1, turn) + GetBoardScore(board);
                    board.UndoMove();
                }

                //check if AI still have pieces
                if (board.PieceCount(board.Turn, PShape.Square) != 0)
                {
                    //try a move
                    board.DoMove(PShape.Square, i);
                    squarePieceMoveScore = -NegaMax(board, ct, iteration + 1, turn) + GetBoardScore(board);
                    board.UndoMove();
                }

                if (board.PieceCount(board.Turn, PShape.Round) == 0 && board.PieceCount(board.Turn, PShape.Square) == 0)
                {
                    _cancelationRequested = true;
                }

                //check witch move is better
                if (squarePieceMoveScore > roundPieceMoveScore)
                {
                    iterationScore = squarePieceMoveScore;
                    bestShape      = PShape.Square;
                }
                else
                {
                    iterationScore = roundPieceMoveScore;
                    bestShape      = PShape.Round;
                }

                //Set maxScore to largest value
                if (iterationScore > bestScore)
                {
                    bestScore         = iterationScore;
                    finalMoveToReturn = new FutureMove(i, bestShape);
                }
            }
        }
        //return best value
        return(bestScore);
    }
Exemplo n.º 13
0
        // Update is called once per frame
        private void Update()
        {
            // Don't run update if the game's over
            if (matchOver)
            {
                return;
            }

            // Is the current player human? If so, let's see if we're supposed
            // to show him a message or if we've done so already
            if (matchData.CurrentThinker is HumanThinker)
            {
                if (showHumanTurnMessage)
                {
                    view.SubmitMessage(
                        $"Attention {CurrPlrNameColor}, it's your turn");
                    showHumanTurnMessage = false;
                }
                ;
            }

            // If the current player is an AI, let's see if we have to start
            // the AI thinking task, if the task is running, if it's completed,
            // etc.
            else
            {
                // If the AI task is null, we need to start an AI thinking task
                if (aiTask == null)
                {
                    // Submit a message informing the user the AI is thinking
                    view.SubmitMessage(
                        $"{CurrPlrNameColor} is thinking, please wait...");

                    // Keep note of task start time (both system time and game
                    // time)
                    stopwatch.Restart();
                    taskStartGameTime = Time.time;

                    // Create a new task cancellation token, so task can be
                    // interrupted
                    ts = new CancellationTokenSource();

                    // Get this AI's thinker
                    thinker = matchData.CurrentThinker;

                    // Start task in a separate thread using a copy of the
                    // board
                    aiTask = Task.Run(
                        () => thinker.Think(board.Copy(), ts.Token));
                }
                else // This else will run if the task is not null
                {
                    // Did the task throw an exception?
                    if (aiTask.IsFaulted)
                    {
                        // If so, notify user
                        view.SubmitMessage(string.Format(
                                               "{0} exception: {1}",
                                               CurrPlrNameColor,
                                               aiTask.Exception.InnerException.Message));

                        // Send a cancellation token to the task in the hope it
                        // might actually terminate
                        ts.Cancel();

                        // Set task to null
                        aiTask = null;

                        // The AI player that throwed the exception will lose
                        // the game, sorry
                        OnMatchOver(board.Turn.Other().ToWinner());
                    }
                    // Is the AI thinking task completed in time?
                    else if (aiTask.IsCompleted &&
                             cancellationStopwatch == null)
                    {
                        // Register task duration, if we haven't done so yet
                        if (float.IsNaN(lastTaskDuration))
                        {
                            lastTaskDuration = (float)(
                                stopwatch.ElapsedTicks
                                /
                                (double)(TimeSpan.TicksPerSecond));
                        }

                        // Did we pass the minimum time between AI moves?
                        if (Time.time >
                            taskStartGameTime + matchConfig.MinMoveTimeSeconds)
                        {
                            // Get the move chosen by the thinker
                            FutureMove move = aiTask.Result;

                            // Was the thinker able to chose a move?
                            if (move.IsNoMove)
                            {
                                // Thinker was not able to chose a move,
                                // submit a message informing user of this
                                view.SubmitMessage(string.Format(
                                                       "{0} unable to perform move",
                                                       CurrPlrNameColor));

                                // The AI player unable to move will lose
                                // the game, sorry
                                OnMatchOver(board.Turn.Other().ToWinner());
                            }
                            else
                            {
                                try
                                {
                                    // If so, submit a message informing of
                                    // the move chosen and the system time it
                                    // took the AI to think
                                    view.SubmitMessage(string.Format(
                                                           "{0} placed a {1} piece at column {2} after {3}",
                                                           CurrPlrNameColor,
                                                           aiTask.Result.shape.ToString().ToLower(),
                                                           aiTask.Result.column,
                                                           $"thinking for {lastTaskDuration:f4}s"));

                                    // Player was able to make a move decision,
                                    // let's perform the actual move
                                    MakeAMove(aiTask.Result);
                                }
                                catch (Exception e)
                                {
                                    // The act of making an actual move caused
                                    // an exception, which means the thinker
                                    // chose an invalid move, as such,
                                    // notify user of this
                                    view.SubmitMessage(string.Format(
                                                           "{0} exception: {1}",
                                                           CurrPlrNameColor, e.Message));

                                    // The AI player that caused the exception
                                    // will lose the game, sorry
                                    OnMatchOver(board.Turn.Other().ToWinner());
                                }
                            }

                            // Set the task to null, so it can be
                            // started again
                            aiTask = null;

                            // Reset the last task duration
                            lastTaskDuration = float.NaN;
                        }
                    }
                    // Is the task overdue?
                    else if (stopwatch.Elapsed > aiTimeLimit)
                    {
                        // If so, check the status of the thinking cancellation
                        // process
                        if (cancellationStopwatch is null)
                        {
                            // The thinking cancellation process has not yet
                            // been started, so let's start it

                            // Inform user that the time limit for
                            // the current thinker has been exceeded
                            view.SubmitMessage(
                                $"Time limit exceeded for {CurrPlrNameColor}!");

                            // Notify task it should cancel its thinking
                            ts.Cancel();

                            // Start cancellation stopwatch
                            cancellationStopwatch = Stopwatch.StartNew();
                        }
                        else if (aiTask.IsCompleted)
                        {
                            // The thinking task is completed after the
                            // cancelation request, terminate match normally

                            // Set task to null
                            aiTask = null;

                            // Set cancellation stopwatch to null
                            cancellationStopwatch = null;

                            // The AI player that was overdue loses the game
                            OnMatchOver(board.Turn.Other().ToWinner());
                        }
                        else if (cancellationStopwatch.ElapsedMilliseconds >
                                 UncooperativeThinkerException.HardThinkingLimitMs)
                        {
                            UnityEngine.Debug.LogWarning($"{cancellationStopwatch.ElapsedMilliseconds}ms have passed :(");

                            // If the hard thinking process time limit has been
                            // reached, throw an exception to terminate the app
                            throw new UncooperativeThinkerException(thinker);
                        }
                    }
                }
            }
        }
Exemplo n.º 14
0
        private static void Main(string[] args)
        {
            // ////////////////////////////////////////////////////////////// //
            // Create an instance of our Oizys thinker via ThinkerPrototype. //
            // If we created directly with new, it would not be properly      //
            // configured.                                                    //
            // ////////////////////////////////////////////////////////////// //

            // Create a configuration for a default ColorShapeLinks match
            MatchConfig mc = new MatchConfig();

            // Get the fully qualified name of our basic Oizys thinker
            string oizysFullName = typeof(OizysThinker).FullName;

            // Create a prototype for our thinker
            ThinkerPrototype tp = new ThinkerPrototype(oizysFullName, "", mc);

            // Create an instance of our basic Oizys thinker
            IThinker oizysThinker = tp.Create();

            // //////////////////////////////////////////////////////// //
            // Create a board so we can test how our thinker will play. //
            // //////////////////////////////////////////////////////// //

            // A cancellation token, will be ignored
            CancellationToken ct = new CancellationToken();

            // Create a ColorShapeLinks board with default size
            Board board = new Board();

            // Show initial board
            Console.WriteLine("\n=== Initial board ===\n");
            ShowBoard(board);

            // Make some moves manually
            board.DoMove(PShape.Round, 3);  // White plays (Round,col 3)
            board.DoMove(PShape.Square, 3); // Red   plays (Square,col 3)
            board.DoMove(PShape.Round, 2);  // White plays (Round,col 2)
            // board.DoMove(PShape.Square, 3); // Red   plays (Square,col 3)
            // board.DoMove(PShape.Round, 1);  // White plays (Round,col 1)
            // board.DoMove(PShape.Square, 3); // Red   plays (Square,col 3)
            // board.DoMove(PShape.Round, 3); // White plays (Round,col 3)

            // Show board after our three manual moves
            Console.WriteLine("\n=== Board after three manual moves ===\n");
            ShowBoard(board);

            // Starts timer
            DateTime startTime = DateTime.Now;

            // What move would Oizys make at this moment?
            FutureMove oizysMove = oizysThinker.Think(board, ct);

            // Show move and time
            Console.WriteLine(string.Format(
                                  "-> Oizys will play {0} after {1} ms.",
                                  oizysMove, (DateTime.Now - startTime).TotalMilliseconds));

            // Make the move selected by Oizys
            board.DoMove(oizysMove.shape, oizysMove.column);

            // Show board after Oizys made its move
            Console.WriteLine("\n=== Board after Oizys made move ===\n");
            ShowBoard(board);
        }
Exemplo n.º 15
0
        /// @copydoc ColorShapeLinks.Common.AI.IThinker.Think
        /// <remarks>
        /// This method asks the human to play.
        /// </remarks>
        /// <seealso cref="ColorShapeLinks.Common.AI.IThinker.Think"/>
        public override FutureMove Think(Board board, CancellationToken ct)
        {
            // By default, no move is performed in case of timeout
            FutureMove move = FutureMove.NoMove;

            // No thinking notification has taken place
            DateTime lastNotificationTime = DateTime.MinValue;

            // Set thinking notification interval between frames to 20ms
            TimeSpan notificationInterval = TimeSpan.FromMilliseconds(20);

            // Calculate the time limit for the human to play
            DateTime timeLimit =
                DateTime.Now + TimeSpan.FromMilliseconds(TimeLimitMillis);

            // Show some info on what keys are used for input
            OnThinkingInfo("T to toggle piece, < > to change selected column");

            // If a certain type of piece is not available, make the other
            // type the selected one
            if (board.PieceCount(board.Turn, PShape.Round) == 0)
            {
                selectedShape = PShape.Square;
            }
            if (board.PieceCount(board.Turn, PShape.Square) == 0)
            {
                selectedShape = PShape.Round;
            }

            // If the current column is full, iterate column selection until
            // one is not
            while (board.IsColumnFull(selectedCol))
            {
                selectedCol++;
                if (selectedCol >= Cols)
                {
                    selectedCol = 0;
                }
            }

            // Wait for human input at most until the cancellation token is
            // activated
            while (true)
            {
                // Was a key pressed?
                if (Console.KeyAvailable)
                {
                    // Retrieve key
                    ConsoleKey key = Console.ReadKey().Key;

                    // Check if it was a column increment key
                    if (key == ConsoleKey.RightArrow ||
                        key == ConsoleKey.D ||
                        key == ConsoleKey.NumPad6 ||
                        key == ConsoleKey.D6)
                    {
                        // Increment column...
                        do
                        {
                            // ...making sure a full column is not selectable,
                            // and wraping around
                            selectedCol++;
                            if (selectedCol >= Cols)
                            {
                                selectedCol = 0;
                            }
                        } while (board.IsColumnFull(selectedCol));
                    }
                    // Check if it was a column decrement key
                    else if (key == ConsoleKey.LeftArrow ||
                             key == ConsoleKey.A ||
                             key == ConsoleKey.NumPad4 ||
                             key == ConsoleKey.D4)
                    {
                        // Decrement column...
                        do
                        {
                            // ...making sure a full column is not selectable,
                            // and wraping around
                            selectedCol--;
                            if (selectedCol < 0)
                            {
                                selectedCol = Cols - 1;
                            }
                        } while (board.IsColumnFull(selectedCol));
                    }
                    // Check if it was a piece toggle key
                    else if (key == ConsoleKey.T)
                    {
                        // Toggle piece if the other type of piece is available
                        if (selectedShape == PShape.Round &&
                            board.PieceCount(board.Turn, PShape.Square) > 0)
                        {
                            selectedShape = PShape.Square;
                        }
                        else if (selectedShape == PShape.Square &&
                                 board.PieceCount(board.Turn, PShape.Round) > 0)
                        {
                            selectedShape = PShape.Round;
                        }
                    }
                    // Check if it was a piece drop key
                    else if (key == ConsoleKey.Enter)
                    {
                        // Drop piece and get out of the input loop
                        move = new FutureMove(selectedCol, selectedShape);
                        break;
                    }
                }

                // If cancellation token is activated, terminate input loop
                if (ct.IsCancellationRequested)
                {
                    break;
                }

                // Is it time for another thinking notification?
                if (DateTime.Now > lastNotificationTime + notificationInterval)
                {
                    // Show dialog
                    Console.CursorLeft = 0;
                    Console.Write(String.Format(
                                      "Col [{0,4}] | Shape [{1,7}] | Time [{2,14}]",
                                      selectedCol,
                                      selectedShape,
                                      timeLimit - DateTime.Now));
                    Console.CursorLeft = 0;

                    // Update last notification time
                    lastNotificationTime = DateTime.Now;
                }
            }

            // Return chosen move
            return(move);
        }
Exemplo n.º 16
0
        /// Current thinker makes its move
        private Winner Play()
        {
            // Get a reference to the current thinker
            IThinker thinker = matchData.CurrentThinker;

            // Determine the color of the current thinker
            PColor color = board.Turn;

            // Match result so far
            Winner winner = Winner.None;

            // Thinking start time
            DateTime startTime = DateTime.Now;

            // Real think time in milliseconds
            int thinkTimeMillis;

            // Apparent thinking time left
            int timeLeftMillis;

            // Task to execute the thinker in a separate thread
            Task <FutureMove> thinkTask;

            // Notify listeners that next turn is about to start
            NextTurn?.Invoke(color, thinker.ToString());

            // Ask thinker to think about its next move
            thinkTask = Task.Run(
                () => thinker.Think(board.Copy(), ts.Token));

            // The thinking process might throw an exception, so we wrap
            // task waiting in a try/catch block
            try
            {
                // Wait for thinker to think... until the allowed time limit
                if (thinkTask.Wait(timeLimitMillis))
                {
                    // Thinker successfully made a move within the time limit

                    // Get the move selected by the thinker
                    FutureMove move = thinkTask.Result;

                    // Was the thinker able to chose a move?
                    if (move.IsNoMove)
                    {
                        // Thinker was not able to chose a move

                        // Raise an invalid play event and set the other
                        // thinker as the winner of the match
                        winner = OnInvalidPlay(
                            color, thinker,
                            "Thinker unable to perform move");
                    }
                    else
                    {
                        // Thinker was able to chose a move

                        // Perform move in game board, get column where move
                        // was performed
                        int row = board.DoMove(move.shape, move.column);

                        // If the column had space for the move...
                        if (row >= 0)
                        {
                            // Obtain thinking end time
                            thinkTimeMillis = (int)(DateTime.Now - startTime)
                                              .TotalMilliseconds;

                            // How much time left for the minimum apparent move
                            // time?
                            timeLeftMillis =
                                minMoveTimeMillis - thinkTimeMillis;

                            // Was the minimum apparent move time reached
                            if (timeLeftMillis > 0)
                            {
                                // If not, wait until it is reached
                                Thread.Sleep(timeLeftMillis);
                            }

                            // Notify listeners of the move performed
                            MovePerformed?.Invoke(
                                color, thinker.ToString(),
                                move, thinkTimeMillis);

                            // Get possible winner and solution
                            winner = board.CheckWinner(solution);
                        }
                        else
                        {
                            // If we get here, column didn't have space for the
                            // move, which means that thinker made an invalid
                            // move and should lose the game

                            // Raise an invalid play event and set the other
                            // thinker as the winner of the match
                            winner = OnInvalidPlay(
                                color, thinker,
                                "Tried to place piece in column "
                                + $"{move.column}, which is full");
                        }
                    }
                }
                else // Did the time limit expired?
                {
                    // Notify thinker to voluntarily stop thinking
                    ts.Cancel();

                    // Raise an invalid play event and set the other thinker
                    // as the winner of the match
                    winner = OnInvalidPlay(
                        color, thinker, "Time limit expired");
                }
            }
            catch (Exception e)
            {
                // Is this an inner exception?
                if (e.InnerException != null)
                {
                    // If so, use it for error message purposes
                    e = e.InnerException;
                }

                // Raise an invalid play event and set the other thinker as
                // the winner of the match
                winner = OnInvalidPlay(
                    color, thinker,
                    $"Thinker exception: '{e.Message}'");
            }

            // Notify listeners that the board was updated
            BoardUpdate?.Invoke(board);

            // Return winner
            return(winner);
        }
Exemplo n.º 17
0
    public FutureMove Negamax(Board board, PColor turn, int depth, CancellationToken ct)
    {
        FutureMove bestMove = default;
        PColor     proxTurn =
            turn == PColor.White ? PColor.Red : PColor.White;

        if (ct.IsCancellationRequested)
        {
            return(FutureMove.NoMove);
        }
        else
        {
            if (myDepth == depth)
            {
                return(bestMove);
            }

            myDepth++;

            for (int i = 0; i < board.cols; i++)
            {
                for (int j = 0; j < board.rows; j++)
                {
                    Vector2Int pos = new Vector2Int(i, j);

                    if (board[i, j] == null)
                    {
                        int roundPieces = board.PieceCount(board.Turn, PShape.Round);

                        int squarePieces = board.PieceCount(board.Turn, PShape.Square);

                        if (shape == PShape.Round)
                        {
                            if (roundPieces == 0)
                            {
                                shape = PShape.Square;
                            }
                            else
                            if (squarePieces == 0)
                            {
                                shape = PShape.Round;
                            }
                        }

                        FutureMove move = default;

                        board.DoMove(shape, i);

                        if (board.CheckWinner() == Winner.None)
                        {
                            move = Negamax(board, proxTurn, depth, ct);
                        }

                        board.UndoMove();
                    }
                }
            }

            /*if (board.Turn == PColor.Red)
             *  bestMove = new FutureMove(board.cols - 1, PShape.Round);
             * else
             *  bestMove = new FutureMove(0, PShape.Round);*/
        }
        return(bestMove);
    }