public override void ChooseMovesAsPBar(MoveResult moveResult) { Move move = moveResult.Move; LogDebugMessage("*** ANTAGONIST (PBar = {0}) ***", move.pBar); LogDebugMessage("Slack: {0}", moveResult.Slack); double valueOfMove = ScenarioValueFunctions.ClearRunAtBaseScenarioValueFunction.Evaluate(moveResult.Slack); LogDebugMessage("Value of move: {0}", valueOfMove); // Defend against an enemy attack on your base: for (int tankNumber = 0; tankNumber < Constants.TANKS_PER_PLAYER; tankNumber++) { LogDebugMessage("Tank number: {0}", tankNumber); TankActionRecommendation recommendation = moveResult.GetRecommendedTankActionsByPlayerAndTankNumber(move.pBar, tankNumber); if (recommendation.IsAMoveRecommended) { TankSituation tankSituation = GameSituation.GetTankSituationByPlayerAndTankNumber(move.pBar, tankNumber); TankAction recommendedTankAction = recommendation.RecommendedTankAction; LogDebugMessage("Recommended tank action: {0}", recommendedTankAction); tankSituation.AdjustTankActionValue(recommendedTankAction, valueOfMove); } else { LogDebugMessage("No moves recommended"); } } }
public override void ChooseMovesAsP(MoveResult moveResult) { Move move = moveResult.Move; LogDebugMessage("*** PROTAGONIST (P = {0}) ***", move.p); LogDebugMessage("Slack: {0}", moveResult.Slack); double valueOfMove = ScenarioValueFunctions.ClearRunAtBaseScenarioValueFunction.Evaluate(moveResult.Slack); LogDebugMessage("Value of move: {0}", valueOfMove); // Attack the enemy base: LogDebugMessage("Tank number: {0}", move.i); TankActionRecommendation recommendation = moveResult.GetRecommendedTankActionsByPlayerAndTankNumber(move.p, move.i); if (recommendation.IsAMoveRecommended) { TankSituation tankSituation = GameSituation.GetTankSituationByPlayerAndTankNumber(move.p, move.i); TankAction recommendedTankAction = recommendation.RecommendedTankAction; LogDebugMessage("Recommended tank action: {0}", recommendedTankAction); tankSituation.AdjustTankActionValue(recommendedTankAction, valueOfMove); } else { LogDebugMessage("No moves recommended"); } }
public override MoveResult EvaluateLeafNodeMove(Move move) { TankAction[] actions_p_i; TankAction[] actions_p_iBar = new TankAction[0]; TankAction[] actions_pBar_j; TankAction[] actions_pBar_jBar; MobileState tankState_j = GetTankState(move.pBar, move.j); MobileState tankState_jBar = GetTankState(move.pBar, move.jBar); MoveResult moveResult = new MoveResult(move); if (!IsValid(move)) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } // Get the attack distance of player p's tank i to the enemy base: int A_p_i = GetAttackDistanceOfTankToEnemyBaseFromDirection(move.p, move.i, move.dir1); if (A_p_i >= Constants.UNREACHABLE_DISTANCE) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } // In this scenario the other tank (iBar) is either dead or too far from the action to be of any use: int A_p_iBar = GetAttackDistanceOfTankToEnemyBaseFromDirection(move.p, move.iBar, move.dir1); if (A_p_iBar < A_p_i) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } // Set the recommended action for the attacking tank: actions_p_i = GetActionsToAttackEnemyBaseFromDirection(move.p, move.i, move.dir1); TankActionRecommendation tankActionRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = actions_p_i[0] }; moveResult.SetTankActionRecommendation(move.p, move.i, tankActionRec); // *** The role for tank p_i is to attack the enemy base along direction dir1. // Get the minimum attack distance of player pBar's tanks: int A_pBar_j = GetAttackDistanceOfTankToEnemyBase(move.pBar, move.j); int A_pBar_jBar = GetAttackDistanceOfTankToEnemyBase(move.pBar, move.jBar); int A_pBar_MIN = Math.Min(A_pBar_j, A_pBar_jBar); // iBar is not part of this scenario. Ensure they can't interfere with the defence: int D_p_iBar = GetCentralLineOfFireDefenceDistanceToHomeBase(move.p, move.iBar); if (D_p_iBar <= A_pBar_MIN) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } // Calculate slack A as p's attack distance less pBar's attack distance int slackA = A_p_i - A_pBar_MIN; // *** slackA is the attack slack (p's attack distance less pBar's best attack distance) // Get the minimum defence distances of player pBar's tank j to the base: int D_pBar_j = GetLineOfFireDefenceDistanceToHomeBaseByIncomingAttackDirection(move.pBar, move.j, move.dir1); int D_pBar_jBar = GetLineOfFireDefenceDistanceToHomeBaseByIncomingAttackDirection(move.pBar, move.jBar, move.dir1); int D_pBar_MIN = Math.Min(D_pBar_j, D_pBar_jBar); // Calculate slack D as p's attack distance less pBar's defence distance // (to the same base and on the same direction of attack): int slackD = A_p_i - D_pBar_MIN; // *** slackD is the defence slack (defender distance to defence less attacker distance to attack with direction dir1 // Get the overall slack (distance to activating this scenario): int slack = Math.Max(slackA, slackD); moveResult.Slack = slack; if (slack < 0) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Current; } else if (slack <= EVALUATION_OUTCOME_CLOSE_THRESHOLD) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Close; } else { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Possible; } // Calculate best defensive actions for the defender: bool pBar_j_defends; bool pBar_jBar_defends; if (slackD < slackA) { // Defending is the smallest slack, so put best defensive effort here: if (D_pBar_j < D_pBar_jBar) { pBar_j_defends = true; pBar_jBar_defends = false; // *** Tank goal: pBar_j defends base from incoming attack with direction dir1 // *** Tank goal: pBar_jBar does nothing } else { pBar_j_defends = false; pBar_jBar_defends = true; // *** Tank goal: pBar_j does nothing // *** Tank goal: pBar_jBar defends base from incoming attack with direction dir1 } } else { // Attacking is the smallest slack, so put most effort here: if (A_pBar_j < A_pBar_jBar) { pBar_j_defends = false; pBar_jBar_defends = true; // *** Tank goal: pBar_j does nothing // *** Tank goal: pBar_jBar defends base from incoming attack with direction dir1 } else { pBar_j_defends = true; pBar_jBar_defends = false; // *** Tank goal: pBar_j defends base from incoming attack with direction dir1 // *** Tank goal: pBar_jBar does nothing } } // Determine best action for pBar.j: // TODO: Check if is alive, is locked down, is locked in a quadrant, already has a move assigned, etc. if (pBar_j_defends) { actions_pBar_j = base.GetActionsToReachLineOfFireDefencePointByIncomingAttackDirection(move.pBar, move.j, move.dir1); } else { actions_pBar_j = GetActionsToAttackEnemyBase(move.pBar, move.j); } if (actions_pBar_j != null && actions_pBar_j.Length > 0) { tankActionRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = actions_pBar_j[0] }; moveResult.SetTankActionRecommendation(move.pBar, move.j, tankActionRec); } // Determine best action for pBar.jBar: // TODO: Check if is alive, is locked down, is locked in a quadrant, already has a move assigned, etc. if (pBar_jBar_defends) { actions_pBar_jBar = base.GetActionsToReachLineOfFireDefencePointByIncomingAttackDirection(move.pBar, move.jBar, move.dir1); } else { actions_pBar_jBar = GetActionsToAttackEnemyBase(move.pBar, move.jBar); } if (actions_pBar_jBar != null && actions_pBar_jBar.Length > 0) { tankActionRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = actions_pBar_jBar[0] }; moveResult.SetTankActionRecommendation(move.pBar, move.jBar, tankActionRec); } return(moveResult); }
// Tank t_p_i is the tank that locks down the enemy tank // Tank t_pBar_j is the enemy tank that gets locked down // Tank t_p_j is the one that finishes off the locked down tank // Tank t_pBar_jBar is the other enemy tank. It must not be able to attack t_p_i sooner than t_p_iBar can attack t_pBar_j // Note: Rely on scenario 1 to prevent a situation where the locked down enemy tank is destroyed, but the other enemy tank reaches the base first // dir1 is the direction that t_p_i attacks t_pBar_j from (and their attack direction is the opposite) // dir2 is the direction that t_p_iBar attack t_pBar_j from - and must be different from dir1 // dir3 is the direction that t_pBar_jBar attacks t_p_i from - and must be different from dir1.GetOpposite() public override MoveResult EvaluateLeafNodeMove(Move move) { MoveResult moveResult = new MoveResult(move); if (!IsValid(move)) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } MobileState tankState_i = GetTankState_i(move); MobileState tankState_j = GetTankState_j(move); // Note that the following could be Direction.None if in line... Direction horizDir = tankState_i.Pos.GetHorizontalDirectionToPoint(tankState_j.Pos); Direction vertDir = tankState_i.Pos.GetVerticalDirectionToPoint(tankState_j.Pos); // Don't try and attack from a different direction than the two obvious ones: if ((move.dir1 == horizDir.GetOpposite()) || (move.dir1 == vertDir.GetOpposite())) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } int tank_i_moveDistance_to_j = base.GetDistanceFromTankToTargetTank(move.p, move.i, move.pBar, move.j); if (tank_i_moveDistance_to_j > THRESHOLD_DISTANCE_CONSIDERED_CLOSE_TO_LOCKDOWN) { moveResult = EvaluateLeafNodeMoveWhenTanksAreNotCloseToLockDown( move, tankState_i, tankState_j, horizDir, vertDir); return(moveResult); } // *************************************************** // We are in close-range lock-down mode. Direction movementDir; if (horizDir != move.dir1) { movementDir = horizDir; } else { movementDir = vertDir; } int tank_j_attackDistance_to_i = base.GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.pBar, move.j, tankState_i.Pos, move.dir1.GetOpposite(), TankHelper.EdgeOffsets); int tank_i_attackDistance_to_j = base.GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.p, move.i, tankState_i.Pos, move.dir1.GetOpposite(), TankHelper.EdgeOffsets); Point newPos = tankState_i.Pos; if (movementDir == Direction.NONE) { // In lock-down position. Hope we don't get shot! movementDir = move.dir1; bool moveCloser = tank_j_attackDistance_to_i > 3; TankAction[] tankActions = GetTankActionsFromTankToAttackTankAtPointAlongDirectionOfMovement( move.p, move.i, tankState_j.Pos, move.dir1, new EdgeOffset[] { EdgeOffset.Centre }, keepMovingCloserOnFiringLastBullet: moveCloser); if (tankActions.Length > 0) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions[0] }; moveResult.SetTankActionRecommendation(move.p, move.i, tankActRec); } } else { // Move towards the enemy tank, but be careful not to become a target: // TODO: Check not walking into a bullet // Time has run out for the competition. Just move, don't calculate if it's a good idea... Point movementOffset = movementDir.GetOffset(); newPos = tankState_i.Pos + movementOffset; TankAction[] tankActions = GetTankActionsToMoveToPoint(move.p, move.i, movementDir, newPos); if (tankActions.Length > 0) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions[0] }; moveResult.SetTankActionRecommendation(move.p, move.i, tankActRec); } // TODO: For future improvement... // We want to move close to the enemy, but not so close that he can attack us. // - unless we are on the same line as him, as then the lock-down can commence. // So his attack distance to us must be kept above a certain number, based on how far from the centre line we are: // tank_j_attackDistance_to_i = base.GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( // move.pBar, move.j, newPos, move.dir1.GetOpposite(), TankHelper.EdgeOffsets); } int A_p_iBar = GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.p, move.iBar, tankState_j.Pos, move.dir2, TankHelper.EdgeOffsets); int A_pBar_jBar = GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.pBar, move.jBar, newPos, move.dir3, TankHelper.EdgeOffsets); int slack = A_p_iBar - A_pBar_jBar; moveResult.Slack = slack; // p_iBar can now commence the attack (actually it's premature, but I'm running out of time!): TankAction[] tankActions_p_iBar = GetTankActionsFromTankToAttackTankAtPointAlongDirectionOfMovement( move.p, move.iBar, tankState_j.Pos, move.dir2, TankHelper.EdgeOffsets, keepMovingCloserOnFiringLastBullet: false); if (tankActions_p_iBar.Length > 0) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions_p_iBar[0], }; moveResult.SetTankActionRecommendation(move.p, move.iBar, tankActRec); } // Tank pBar_j can commence attack on target p_i: TankAction[] tankActions_pBar_j = GetTankActionsFromTankToAttackTankAtPointAlongDirectionOfMovement( move.pBar, move.j, newPos, move.dir3, TankHelper.EdgeOffsets, keepMovingCloserOnFiringLastBullet: false); if (tankActions_pBar_j.Length > 0) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions_pBar_j[0], }; moveResult.SetTankActionRecommendation(move.pBar, move.j, tankActRec); } if (slack < 0) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Current; } else { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Close; } return(moveResult); }
public MoveResult EvaluateLeafNodeMoveWhenTanksAreNotCloseToLockDown(Move move, MobileState tankState_i, MobileState tankState_j, Direction horizDir, Direction vertDir) { MoveResult moveResult = new MoveResult(move); // Find the Voronoi point on the straight line between the two tanks as an estimate of where the lock-down could take place: Point[] zigZagPoints = tankState_i.Pos.GetPointsOnZigZagLineToTargetPoint(tankState_j.Pos); int minDiff = Constants.UNREACHABLE_DISTANCE; int distToMinDiffPoint = Constants.UNREACHABLE_DISTANCE; Point minDiffPoint = new Point(); foreach (Point pointOnLine in zigZagPoints) { int dist1 = GetDistanceFromTankToPoint(move.p, move.i, move.dir1, pointOnLine); int dist2 = GetDistanceFromTankToPoint(move.pBar, move.j, move.dir1.GetOpposite(), pointOnLine); int distDiff = Math.Abs(dist1 - dist2); if (distDiff < minDiff) { minDiff = distDiff; minDiffPoint = pointOnLine; distToMinDiffPoint = dist1; } } if (minDiff == Constants.UNREACHABLE_DISTANCE) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Invalid; return(moveResult); } Rectangle boardBoundary = new Rectangle(0, 0, (short)(GameState.Walls.Width - 1), (short)(GameState.Walls.Height - 1)); Point estimated_pos_pBar_j = minDiffPoint + move.dir1.GetOffset(4); // 4 points away on one side estimated_pos_pBar_j = estimated_pos_pBar_j.BringIntoBounds(boardBoundary); Point estimated_pos_p_i = minDiffPoint + move.dir1.GetOpposite().GetOffset(4); // 4 points away on the other side estimated_pos_p_i = estimated_pos_p_i.BringIntoBounds(boardBoundary); int slack_lock_down = distToMinDiffPoint; int A_p_iBar = GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.p, move.iBar, estimated_pos_pBar_j, move.dir2, TankHelper.EdgeOffsets); int A_pBar_jBar = GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.pBar, move.jBar, estimated_pos_p_i, move.dir3, TankHelper.EdgeOffsets); int slack_Bar = A_p_iBar - A_pBar_jBar; int slack = Math.Max(slack_Bar, slack_lock_down); // slack_Bar is symmetrical. If it's above zero, then we are in trouble. // If below, then he's in trouble. So choose actions based on this slack: if (slack_Bar < 0) { // Move tank p_i closer to the estimated interception point with pBar_j: TankAction[] tankActions_move_p_i = GetTankActionsToMoveToPoint(move.p, move.i, move.dir1, estimated_pos_pBar_j); if (tankActions_move_p_i.Length > 0) { moveResult.SetTankActionRecommendation(move.p, move.i, new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions_move_p_i[0] }); } // Move tank pBar_j further away from attackers p_i and p_iBar: // (Also ideally towards the edge of the board, if that's not where the attack is coming from, to act as a decoy) int maxDistanceFromAttackers = int.MinValue; TankAction escapeAction = TankAction.NONE; foreach (Direction escapeDir in BoardHelper.AllRealDirections) { Point adjacentPoint = tankState_j.Pos + escapeDir.GetOffset(); TurnCalculationCache turnCalcCache = Game.Current.Turns[GameState.Tick].CalculationCache; TankLocation tankLoc = turnCalcCache.TankLocationMatrix[adjacentPoint]; if (tankLoc.IsValid) { int adj_moveDistance_pBar_j = GetDistanceFromTankToPoint(move.pBar, move.j, escapeDir, adjacentPoint); if (adj_moveDistance_pBar_j < Constants.UNREACHABLE_DISTANCE) { int adj_attackDistance_p_i = GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.p, move.i, adjacentPoint, move.dir1, TankHelper.EdgeOffsets); int adj_attackDistance_p_iBar = GetAttackDistanceFromTankToTankAtPointAlongDirectionOfMovement( move.p, move.iBar, adjacentPoint, move.dir2, TankHelper.EdgeOffsets); int adj_attackDistance_SUM = adj_attackDistance_p_i + adj_attackDistance_p_iBar - adj_moveDistance_pBar_j; if (adj_attackDistance_SUM < maxDistanceFromAttackers) { TankAction[] escapeActions = GetTankActionsToMoveToPoint(move.pBar, move.j, escapeDir, adjacentPoint); if (escapeActions.Length > 0) { maxDistanceFromAttackers = adj_attackDistance_SUM; escapeAction = escapeActions[0]; } } } } } if (escapeAction != TankAction.NONE) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = escapeAction }; moveResult.SetTankActionRecommendation(move.pBar, move.j, tankActRec); } } // Move tank p_iBar closer to lock down target pBar_j (don't attack yet, because lock-down hasn't occurred): TankAction[] tankActions_p_iBar = GetTankActionsToMoveToPoint(move.p, move.iBar, move.dir2, estimated_pos_pBar_j); if (tankActions_p_iBar.Length > 0) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions_p_iBar[0], }; moveResult.SetTankActionRecommendation(move.p, move.iBar, tankActRec); } // Move tank pBar_j closer to lock down target p_i: TankAction[] tankActions_pBar_j = GetTankActionsToMoveToPoint(move.pBar, move.j, move.dir3, estimated_pos_p_i); if (tankActions_pBar_j.Length > 0) { TankActionRecommendation tankActRec = new TankActionRecommendation { IsAMoveRecommended = true, RecommendedTankAction = tankActions_pBar_j[0], }; moveResult.SetTankActionRecommendation(move.pBar, move.j, tankActRec); } if (slack < 0) { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Close; } else { moveResult.EvaluationOutcome = ScenarioEvaluationOutcome.Possible; } return(moveResult); }