/// <summary> /// Gets playground state identifying whether there's winning player. /// </summary> /// <returns> /// Playground state. /// </returns> public PlaygroundState GetState() { if (_emptyFields > MinimumFieldsToWin) { return(PlaygroundState.NotComplete); } bool FieldEqual(Field f1, Field f2) { return(!Player.IsNullOrBlank(f1.Player) && f1.Player.Equals(f2.Player)); } foreach (int[] wc in WinningCoords) { int a = wc[0]; int b = wc[1]; int c = wc[2]; if (FieldEqual(_fields[a], _fields[b]) && FieldEqual(_fields[b], _fields[c])) { return(PlaygroundState.Winner(_fields[a].Player)); } } return(_emptyFields == 0 ? PlaygroundState.Tie : PlaygroundState.NotComplete); }
/// <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()); }