public static void Print(this Playground playground) { ConsoleColor foreground = Console.ForegroundColor; for (var r = 0; r < 3; r++) { Console.WriteLine("+---+---+---+"); for (var c = 0; c < 3; c++) { Field field = playground.Fields[r * 3 + c]; Console.Write("| "); Console.ForegroundColor = field.IsEmpty ? ConsoleColor.Gray : ConsoleColor.Green; Console.Write(GetFieldValue(field)); Console.ForegroundColor = foreground; Console.Write(" "); } Console.WriteLine("|"); } Console.WriteLine("+---+---+---+"); }
public static IEnumerable <Field> EmptyFields(this Playground playground) { if (playground == null) { throw new ArgumentNullException(nameof(playground)); } return(playground.Fields.Where(f => f.IsEmpty)); }
/// <summary> /// Calculates the best move for given playground state. /// </summary> /// <param name="playground">Game playground.</param> /// <returns> /// Returns index of field of the best move for player. /// </returns> public (bool CanTurn, int Index) CalulateBestMove(Playground playground) { if (playground == null) { throw new ArgumentNullException(nameof(playground)); } FieldScore result = MiniMax(playground, true); return(result.Index > 0 ? (true, result.Index) : (false, 0)); }
/// <summary> /// Evaluate every possible move using minimax algorithm. /// </summary> /// <param name="playground">Game playground.</param> /// <param name="isMaximizing"><c>true</c> if player's move, <c>false</c> if opponent's move.</param> /// <returns> /// Returns field-score. /// </returns> private FieldScore MiniMax(Playground playground, bool isMaximizing) { PlaygroundState state = playground.GetState(); // board is in final state, return score immediately // (since we are not aware of previous move (chosen field) // we return only score part) if (state.State != GameState.NotComplete) { return(state.State == GameState.Tie ? new FieldScore { Score = 0 } : _player.Equals(state.Player) ? new FieldScore { Score = 1 } : new FieldScore { Score = -1 }); } Player currentPlayer = isMaximizing ? _player : _opponent; // calculate scores for each possible move // (NB! recursion is about to happen) IEnumerable <FieldScore> moves = playground.EmptyFields() .Select( f => new FieldScore { Index = f.Index, Score = MiniMax(playground.Turn(f.Index, currentPlayer), !isMaximizing).Score }); // captain obvious to the service: // player - get the highest score (ORDER BY score DESC) // opponent - get the lowest score (ORDER BY score ASC) moves = isMaximizing ? moves.OrderByDescending(m => m.Score) : moves.OrderBy(m => m.Score); return(moves.First()); }
static void Main(string[] args) { var p1 = new Player('X'); var p2 = new Player('O'); var s1 = new Solver(p1, p2); var s2 = new Solver(p2, p1); var playground = new Playground(); playground = playground.Turn(s1.CalulateBestMove(playground).Index, p1); playground.Print(); playground = playground.Turn(s2.CalulateBestMove(playground).Index, p2); playground.Print(); playground = playground.Turn(s1.CalulateBestMove(playground).Index, p1); playground.Print(); playground = playground.Turn(s2.CalulateBestMove(playground).Index, p2); playground.Print(); playground = playground.Turn(s1.CalulateBestMove(playground).Index, p1); playground.Print(); playground = playground.Turn(s2.CalulateBestMove(playground).Index, p2); playground.Print(); playground = playground.Turn(s1.CalulateBestMove(playground).Index, p1); playground.Print(); playground = playground.Turn(s2.CalulateBestMove(playground).Index, p2); playground.Print(); playground = playground.Turn(s2.CalulateBestMove(playground).Index, p1); playground.Print(); Debugger.Break(); }