// Constructor public CommandInfo(Haxxit.Maps.Point newSource, Haxxit.Maps.Point newTarget, Haxxit.Maps.ProgramHeadNode newTargetProgram, Stack<Haxxit.Maps.Point> newPath) { source = newSource; target = newTarget; targetProgram = newTargetProgram; path = newPath; }
// Constructor public AINodeData(int column, int row) { coordinate = new Haxxit.Maps.Point(column, row); isAvailable = false; hasSilicoin = false; hasData = false; isSpawn = false; occupiedBy = null; aStarTrackStatus = AStarStatus.Unlisted; parent = null; f = int.MaxValue; g = int.MaxValue; h = int.MaxValue; }
// This is a recursive helper function for finding all of the valid nodes which are within a specified range // of the target node. Since programs can traverse their own nodes a program object is passed for this // validity check. private List<Haxxit.Maps.Point> FindNodesInRange(Haxxit.Maps.ProgramHeadNode program, Haxxit.Maps.Point target, int range) { List<Haxxit.Maps.Point> nodesInRange; if (range == 0) // Base case instantiates the actual list object { nodesInRange = new List<Haxxit.Maps.Point>(); return nodesInRange; } else // Find all of the valid nodes in the current range ring (each recursive call handles a smaller ring) { nodesInRange = FindNodesInRange(program, target, range - 1); for (int negativeX = range * -1; negativeX < 0; negativeX++) // Find options in lower left quadrant from target { Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(target.X + negativeX, target.Y + range + negativeX); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { nodesInRange.Add(checkPoint); } } } for (int positiveY = range; positiveY > 0; positiveY--) // Find options in lower right quadrant from target { Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(target.X + range - positiveY, target.Y + positiveY); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { nodesInRange.Add(checkPoint); } } } for (int positiveX = range; positiveX > 0; positiveX--) // Find options in upper right quadrant from target { Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(target.X + positiveX, target.Y - range + positiveX); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { nodesInRange.Add(checkPoint); } } } for (int negativeY = range * -1; negativeY < 0; negativeY++) // Find options in upper left quadrant from target { Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(target.X - range - negativeY, target.Y + negativeY); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { nodesInRange.Add(checkPoint); } } } } return nodesInRange; }
// Finds the closest enemy node from the specified program's head node. This is used as // part a fallback for pathfinding when no paths to valid attack positions can be found // for a program on this turn. private Haxxit.Maps.Point FindClosestEnemy(Haxxit.Maps.ProgramHeadNode program) { Haxxit.Maps.Point closestEnemyPoint = new Haxxit.Maps.Point(-1, -1); int closestEnemyDistance = int.MaxValue; // For every enemy program... if (enemyPrograms.Any()) { foreach (Haxxit.Maps.ProgramHeadNode enemyProgram in enemyPrograms) { // For every node in that enemy program... foreach (Haxxit.Maps.ProgramNode node in enemyProgram.GetAllNodes()) { // Determine the distance to that enemy node int xDistanceToEnemy = Math.Abs(node.coordinate.X - program.coordinate.X); int yDistanceToEnemy = Math.Abs(node.coordinate.Y - program.coordinate.Y); int distanceToEnemy = xDistanceToEnemy + yDistanceToEnemy; // Determine if the distance is shorter than any others yet calculated if (distanceToEnemy < closestEnemyDistance) { closestEnemyPoint = node.coordinate; closestEnemyDistance = distanceToEnemy; } } } } return closestEnemyPoint; }
// Moves the program directly left if possible. This was implemented as a proof of concept. // May be useful for future testing. private void BehaviorMoveLeft(Haxxit.Maps.ProgramHeadNode program) { Haxxit.Maps.Point head = program.coordinate; Haxxit.Maps.Point moveLeft = new Haxxit.Maps.Point(-1, 0); for (int moves = 0; program.Program.Moves.MovesLeft > moves; moves++) { Haxxit.Maps.Point movedHead = new Haxxit.Maps.Point(head.X - moves, head.Y); if (movedHead.X - 1 < 0) // Can't move off edge of Map { break; } if (mapData[movedHead.X - 1, head.Y].canHoldCurrentProgram(program)) { Haxxit.Maps.MoveEventArgs moveHeadLeft = new Haxxit.Maps.MoveEventArgs(movedHead, moveLeft); turnActions.Enqueue(new NotifyArgs("haxxit.map.move", this, moveHeadLeft, NotifyArgs.ArgType.Move)); } } }
// Locates the nearest move/attack combination and attempts to use it private void BehaviorAttackNearest(Haxxit.Maps.ProgramHeadNode program) { List<Commands.Command> commands = GetActualCommands(program); List<PrioritizedCommand> prioritizedCommands = new List<PrioritizedCommand>(); // For each of the current program's available commands... foreach(Commands.Command command in commands) { // Record all potential options for using that command PrioritizedCommand newPrioritizedCommand = FindCommandOptions(program, command); prioritizedCommands.Add(newPrioritizedCommand); } // Sort the different commands (and their lists of usage options) by usefulness priority (IE: Damage & range). // The PrioritizedCommand class has an internal comparable interface for performing this sort. prioritizedCommands.Sort(); Stack<Haxxit.Maps.Point> chosenPath = null; Haxxit.Maps.Point chosenSource = new Haxxit.Maps.Point(-1, -1); Haxxit.Maps.Point chosenTarget = new Haxxit.Maps.Point(-1, -1); Commands.Command chosenCommand = null; bool optionChosen = false; // For each of the current program's prioritized commands... foreach(PrioritizedCommand prioritizedCommand in prioritizedCommands) { if (optionChosen) { break; } if (prioritizedCommand.TargetOptions.Count != 0) // If the command has any potential usages... { CommandInfo closestOption = prioritizedCommand.TargetOptions.First(); if (closestOption.Path.Count <= program.Program.Moves.MovesLeft) { // We can use the command this turn, so we queue up the moves! chosenPath = closestOption.Path; chosenSource = closestOption.Source; chosenTarget = closestOption.Target; chosenCommand = prioritizedCommand.Command; optionChosen = true; } } } // If the program isn't close enough to use any commands this turn after moving... if (!optionChosen) { // For each of the current program's prioritized commands... foreach (PrioritizedCommand prioritizedCommand in prioritizedCommands) { if (optionChosen) { break; } if (prioritizedCommand.TargetOptions.Count != 0) // If the command has any potential usages... { // We begin moving towards usage points for next turn. CommandInfo closestOption = prioritizedCommand.TargetOptions.First(); chosenPath = closestOption.Path; optionChosen = true; } } } // If there are no accessible positions from which to use a command anywhere on the Map... if (!optionChosen) { // Find the first reachable point in expanding circles from the nearest enemy and move towards it instead Haxxit.Maps.Point closestEnemy = FindClosestEnemy(program); int checkRange = 1; // First circle to try will have a range of 1 from target while (chosenPath == null) { for (int negativeX = checkRange * -1; negativeX < 0; negativeX++) // Find options in lower left quadrant from target { if (optionChosen) { break; } Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(closestEnemy.X + negativeX, closestEnemy.Y + checkRange + negativeX); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { chosenPath = AStar(program, checkPoint); if (chosenPath != null) { optionChosen = true; } } } } for (int positiveY = checkRange; positiveY > 0; positiveY--) // Find options in lower right quadrant from target { if (optionChosen) { break; } Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(closestEnemy.X + checkRange - positiveY, closestEnemy.Y + positiveY); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { chosenPath = AStar(program, checkPoint); if (chosenPath != null) { optionChosen = true; } } } } for (int positiveX = checkRange; positiveX > 0; positiveX--) // Find options in upper right quadrant from target { if (optionChosen) { break; } Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(closestEnemy.X + positiveX, closestEnemy.Y - checkRange + positiveX); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { chosenPath = AStar(program, checkPoint); if (chosenPath != null) { optionChosen = true; } } } } for (int negativeY = checkRange * -1; negativeY < 0; negativeY++) // Find options in upper left quadrant from target { if (optionChosen) { break; } Haxxit.Maps.Point checkPoint = new Haxxit.Maps.Point(closestEnemy.X - checkRange - negativeY, closestEnemy.Y + negativeY); if (IsInBounds(checkPoint)) { if (mapData[checkPoint.X, checkPoint.Y].canHoldCurrentProgram(program)) { chosenPath = AStar(program, checkPoint); if (chosenPath != null) { optionChosen = true; } } } } checkRange++; // Try next circle from target } } Queue<Haxxit.Maps.Point> finalPath = new Queue<Haxxit.Maps.Point>(); if (chosenPath.Count < program.Program.Moves.MovesLeft) { for (int i = chosenPath.Count; i > 0; i--) { finalPath.Enqueue(chosenPath.Pop()); } } else { for (int i = program.Program.Moves.MovesLeft; i > 0; i--) { finalPath.Enqueue(chosenPath.Pop()); } } MoveCode moveCode = MoveCode.Success; if (finalPath != null && finalPath.Any()) { moveCode = PerformMoves(program, finalPath); } if (moveCode != MoveCode.Success) // For debugging AI movement { #if DEBUG throw new Exception(); #endif } CommandCode commandCode = CommandCode.Success; if(chosenCommand != null) { commandCode = PerformCommand(chosenTarget, chosenSource, chosenCommand); } if (commandCode != CommandCode.Success) // For debugging AI commands { #if DEBUG throw new Exception(); #endif } }