Example #1
0
        /// <summary>
        /// Runs the match.
        /// </summary>
        /// <returns>The match result.</returns>
        public Winner Run()
        {
            // Initially there's no winner
            Winner winner = Winner.None;

            // Notify listeners match is about to start
            MatchStart?.Invoke(
                matchConfig,
                new string[] {
                matchData.GetThinker(PColor.White).ToString(),
                matchData.GetThinker(PColor.Red).ToString()
            });

            // Notify listeners that we have a new empty board
            BoardUpdate?.Invoke(board);

            // Game loop
            while (true)
            {
                // Next player plays
                winner = Play();
                // Break loop if a winner is found
                if (winner != Winner.None)
                {
                    break;
                }
            }

            // Notify listeners that match is over
            MatchOver?.Invoke(
                winner,
                solution,
                new string[] {
                matchData.GetThinker(PColor.White).ToString(),
                matchData.GetThinker(PColor.Red).ToString()
            });

            // Return match result
            return(winner);
        }
Example #2
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);
        }