private Turn GetTurnNormal(Game originalGame) { try { GameClientStatsCollector?.StartGetTurn(originalGame); int maxMoves = GetMaxMoves(originalGame); // MaxMoves AlphaBetaSearch abs = new AlphaBetaSearch(originalGame, maxMoves, Evaluator, false, DoPrune, CollectStats, DoLog); var gameResults = abs.GetGameResult(); if (gameResults.Item1 == AlphaBetaSearch.GameResultWinning) { Turn winningTurn = originalGame.GetDirectlyWinningTurn(); if (winningTurn != null) { return(winningTurn); } } GameResult = gameResults.Item1; EvaluationScore = abs.EvaluationScore; NodeInfos = abs.NodeInfos; return(gameResults.Item2.OrderByDescending(kvp => kvp.Value).First().Key); } finally { GameClientStatsCollector?.EndGetTurn(); } }
public Turn GetTurn(Game game) { var tup = new MiniMax(game, 2, doChecks: false).GetGameResult(); if (tup.Item1 == MiniMax.GameResultLosing) { // we're f****d } else if (tup.Item1 == MiniMax.GameResultWinning) { // we're winning. // let's not extend the misery of the opponent and take a directly winning turn when we get one. Turn winningTurn = game.GetDirectlyWinningTurn(); if (winningTurn != null) { return(winningTurn); } var turn = tup.Item2.First(kvp => kvp.Value == MiniMax.GameResultWinning).Key; return(turn); } //Turn capturingTurn = MiniMax.GetCapturingTurn(game); //if (capturingTurn != null) return capturingTurn; // make the move that brings the pawns closest to the target Vector target = GetTarget(game); var pawns = game.GameState.InTurnPlayerPieces.Where(p => p is Pawn).ToList(); List <int> bestDistances = pawns.Select(p => Board.GetDistance(target, p.Position)).OrderBy(_ => _).ToList(); Turn bestTurn = null; foreach (Turn turn in game.GetValidTurns()) { Vector position = turn.OriginalPosition; Piece piece = game.GameState.Board[position.X, position.Y]; piece.Position = turn.OriginalPosition.Add(turn.Move); List <int> trialDistances = pawns.Select(p => Board.GetDistance(target, p.Position)).OrderBy(_ => _).ToList(); piece.Position = position; if (Compare(trialDistances, bestDistances) < 0) { if (tup.Item2.ContainsKey(turn) && tup.Item2[turn] == MiniMax.GameResultLosing) { // skip, don't play a losing move (if possible) } else { bestDistances = trialDistances; bestTurn = turn; } } } if (bestTurn != null) { return(bestTurn); } if (tup.Item1 != MiniMax.GameResultLosing) { // make any non-losing move return(tup.Item2.First(kvp => kvp.Value != MiniMax.GameResultLosing).Key); } else { // we're f****d anyway, make any move return(game.GetAnyTurn()); } }
/// <summary> /// Returns all neighbouring states and whether or not the move to the neighbouring state is winning or not and the neighbouring gameStateId. /// Changes Game.GameState during loop. /// Return neighbours in order of score of evaluation function if DoPrune is true. /// </summary> private IEnumerable <Tuple <Turn, bool, long?> > GetNeighbours(bool isMaxNode, int movesDone) { GetNeighboursCount++; if (movesDone == MaxMoves - 1 || !DoPrune) { // we don't want to calculate the evaluation score of all leave nodes (to sort on them) // because that's slower than traversing them unsorted and then (due to ABS pruning) NOT calculating the evaluation score of some of the leaves at all. // so, getting neighbours for the almost-leave-nodes is different than for the other nodes. foreach (Turn turn in Game.GetValidTurns()) { TurnResult turnResult = null; try { turnResult = Game.GameState.PlayTurn(turn, DoChecks); if (turnResult.GameIsFinished) { yield return(Tuple.Create(turn, true, (long?)null)); } else { yield return(Tuple.Create(turn, false, (long?)null)); } } finally { // roll back Game.GameState.UndoTurn(turn, turnResult, DoChecks); } } } else { List <Tuple <Turn, double, long?> > turnsWithScore = new List <Tuple <Turn, double, long?> >(); foreach (Turn turn in Game.GetValidTurns()) { TurnResult turnResult = null; try { turnResult = Game.GameState.PlayTurn(turn, DoChecks); long? gameStateId = GetUniqueIdentifier(Game.GameState); // don't calculate gameStateId again in the ABS min/max loop, so pass it on. double score = GetEvaluationScore(gameStateId.Value); turnsWithScore.Add(Tuple.Create(turn, score, gameStateId)); } finally { // roll back Game.GameState.UndoTurn(turn, turnResult, DoChecks); } } if (isMaxNode) { turnsWithScore.Sort((tup1, tup2) => - 1 * tup1.Item2.CompareTo(tup2.Item2)); // descending by score for max nodes. } else { turnsWithScore.Sort((tup1, tup2) => 1 * tup1.Item2.CompareTo(tup2.Item2)); // ascending by score for min nodes. } foreach (var tup in turnsWithScore) { Turn turn = tup.Item1; TurnResult turnResult = null; long? gameStateId = tup.Item3; try { turnResult = Game.GameState.PlayTurn(turn, DoChecks); if (turnResult.GameIsFinished) { yield return(Tuple.Create(turn, true, gameStateId)); } else { yield return(Tuple.Create(turn, false, gameStateId)); } } finally { // roll back Game.GameState.UndoTurn(turn, turnResult, DoChecks); } } } }
private bool IsForwardTurn(Turn turn) { return(turn.Move.Y > 0 && turn.Player.PlayerIndex == 0 || turn.Move.Y < 0 && turn.Player.PlayerIndex == 1); }
public Board(GameState gameState) { GameState = gameState; LastTurn = null; }
public TurnEventArgs(Game game, Turn turn, TurnResult turnResult) { Game = game; Turn = turn; TurnResult = turnResult; }
/// <summary> /// Returns whether or not the game is finished after this move. /// </summary> public TurnResult PlayTurn(Turn turn, bool doChecks = true) { MiniMax.PlayTurnCount++; AlphaBetaSearch.PlayTurnCount++; Piece piece = Board[turn.OriginalPosition.X, turn.OriginalPosition.Y]; if (doChecks) // set doChecks to false for performance if necessary { if (piece == null) { throw new ArgumentException("Invalid Turn, bad OriginalPosition"); } if (piece.PlayerIndex != InTurnPlayerIndex) { throw new ArgumentException("Invalid Turn, wrong piece"); } if (piece.IsCaptured) { throw new ArgumentException("Invalid Turn, piece was already captured"); } if (!InTurnPlayerCards.Contains(turn.Card)) { throw new ArgumentException("Invalid Turn, wrong card"); } if (!turn.Card.GetMoves(InTurnPlayerIndex).Contains(turn.Move)) { throw new ArgumentException("Invalid Turn, bad move for card"); } if (!IsValidMove(piece, turn.Move)) { throw new ArgumentException("Invalid Turn, bad move"); } } // move pieces Vector newPosition = turn.OriginalPosition.Add(turn.Move); Piece captured = Board[newPosition.X, newPosition.Y]; Board[turn.OriginalPosition.X, turn.OriginalPosition.Y] = null; piece.Position = newPosition; Board[newPosition.X, newPosition.Y] = piece; if (captured != null) { captured.IsCaptured = true; CapturedPieces.Push(captured); } // swap played card with middle card Card tmp = GameCards[MiddleCardIndex]; GameCards[MiddleCardIndex] = GameCards[PlayerCardIndices[InTurnPlayerIndex][turn.CardIndex]]; GameCards[PlayerCardIndices[InTurnPlayerIndex][turn.CardIndex]] = tmp; bool gameIsFinshed = false; if (captured != null && captured is King) { gameIsFinshed = true; } else if (piece is King && newPosition.Equals(Domain.Board.PlayerBases[1 - InTurnPlayerIndex])) { gameIsFinshed = true; } if (gameIsFinshed) { WinningPlayerIndex = InTurnPlayerIndex; } InTurnPlayerIndex = 1 - InTurnPlayerIndex; // let's toggle this for consistency, even when the game has just been finished. return(new TurnResult(captured, gameIsFinshed)); }