/// <summary> /// Make all possible moves and chooses the once which guarantees the max profit /// </summary> private Tuple <double, MoveInformation> MakeAnalyzingMoves(MoveInformation moveInformation, int depth, PlayerType playerType, List <Cell> currentPlayerArmyCells, List <Cell> otherPlayerArmyCells) { var memorizedFrom = new ItemAndPosition(GetItemByPosition(moveInformation.From), moveInformation.From); // memorizing position var memorizedTo = new ItemAndPosition(GetItemByPosition(moveInformation.To), moveInformation.To); MakeMove(moveInformation.From, moveInformation.To); ChangeArmyListAfterMoving(currentPlayerArmyCells, playerType, moveInformation); // changing list of armies after move ChangeArmyListAfterMoving(otherPlayerArmyCells, GetOppositePlayerType(playerType), moveInformation); Tuple <double, MoveInformation> result; if (PlayerArmiesWereKilled(currentPlayerArmyCells) || PlayerArmiesWereKilled(otherPlayerArmyCells)) // If game ends because of armies of one player were killed { result = OnOnceArmiesKilled(currentPlayerArmyCells, otherPlayerArmyCells, playerType); } else { result = AnalyzeStrategy(GetOppositePlayerType(playerType), false, // Just analyzing the move profit depth - 1, otherPlayerArmyCells, currentPlayerArmyCells); } CancelMove(memorizedFrom, memorizedTo); ChangeArmyListAfterCancellingMove(currentPlayerArmyCells, otherPlayerArmyCells, // Changing list of armies after cancelling move playerType, moveInformation, memorizedFrom, memorizedTo); return(result); }
/// <summary> /// Among possible moves method chooses the one, which guarantees max position profit /// </summary> private Tuple <double, MoveInformation> AnalyzeMoves(PlayerType playerType, List <MoveInformation> possibleMoves, int depth, List <Cell> currentPlayerArmyCells, List <Cell> otherPlayerArmyCells) { var resultBenefit = InfinityByPlayerType(playerType); // The best guaranteed profit MoveInformation bestMoveInformation = null; // The best move, null means that it is better not to move foreach (var moveInformation in possibleMoves) { // Analyzing single move var intermediateResult = MakeAnalyzingMoves(moveInformation, depth, playerType, currentPlayerArmyCells, otherPlayerArmyCells); var distanceEnemyCastleTo = boardStorage.GetDistanceToEnemyCastle(moveInformation.To, playerType); var distanceEnemyCastleFrom = boardStorage.GetDistanceToEnemyCastle(moveInformation.From, playerType); if (distanceEnemyCastleTo == 0) // If castle was reached { resultBenefit = playerType == personPlayerType ? double.PositiveInfinity : double.NegativeInfinity; bestMoveInformation = moveInformation; break; } // Comparing best and current moves if (IsMoveBetter(resultBenefit, intermediateResult.Item1, bestMoveInformation, playerType, distanceEnemyCastleTo)) { resultBenefit = intermediateResult.Item1; bestMoveInformation = moveInformation; if (IsMovePerfect(resultBenefit, playerType, distanceEnemyCastleTo, distanceEnemyCastleFrom)) { break; } } } return(new Tuple <double, MoveInformation>(resultBenefit, bestMoveInformation)); }
/// <summary> /// Checks that analyzing move is better than current the best /// </summary> private Boolean IsMoveBetter(double currentBenefit, double moveBenefit, MoveInformation currentMove, PlayerType playerType, double distanceEnemyCastleTo) { if (playerType == aiPlayerType) { return(moveBenefit < currentBenefit || Math.Abs(moveBenefit - currentBenefit) < EPSILON && (currentMove == null || boardStorage.GetDistanceToEnemyCastle(currentMove.To, playerType) > distanceEnemyCastleTo)); } return(moveBenefit > currentBenefit || Math.Abs(moveBenefit - currentBenefit) < EPSILON && (currentMove == null || boardStorage.GetDistanceToEnemyCastle(currentMove.To, playerType) > distanceEnemyCastleTo)); }
/// <summary> /// After army was moved its position was changed and maybe position of some other armies was changed too /// (if it was located on target cell), so list of armies should be updated /// </summary> private void ChangeArmyListAfterMoving(List <Cell> armyCells, PlayerType playerType, MoveInformation move) { armyCells.Remove(move.From); armyCells.Remove(move.To); var resultItem = boardStorage.GetItem(move.To) as ArmyStorageItem; if (resultItem?.Army != null && resultItem.Army is UserArmy userArmy) { if (playerType == userArmy.PlayerType) { armyCells.Add(move.To); } } }
/// <summary> /// After cancelling move in game simulation all armies should return to their cells and list of /// armies should be updated /// </summary> private void ChangeArmyListAfterCancellingMove(List <Cell> currentPlayerArmyCells, List <Cell> otherPlayerArmyCells, PlayerType playerType, MoveInformation move, ItemAndPosition memorizedFrom, ItemAndPosition memorizedTo) { currentPlayerArmyCells.Remove(move.To); otherPlayerArmyCells.Remove(move.To); if (memorizedFrom.Item?.Army != null) { AddCellToArmyCellsList(currentPlayerArmyCells, otherPlayerArmyCells, playerType, memorizedFrom.Item.Army.PlayerType, move.From); } if (memorizedTo.Item?.Army != null) { AddCellToArmyCellsList(currentPlayerArmyCells, otherPlayerArmyCells, playerType, memorizedTo.Item.Army.PlayerType, move.To); } }