/// <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); }
/// 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); }