public CreatureAI(Creature creature, string name, EnemySensor sensor, PlanService planService) : base(name, creature.Physics) { Movement = new CreatureMovement(); GatherManager = new GatherManager(this); Blackboard = new Blackboard(); Creature = creature; CurrentPath = null; DrawPath = false; PlannerTimer = new Timer(0.1f, false); LocalControlTimeout = new Timer(5, false); WanderTimer = new Timer(1, false); Creature.Faction.Minions.Add(this); DrawAIPlan = false; WaitingOnResponse = false; PlanSubscriber = new PlanSubscriber(planService); ServiceTimeout = new Timer(2, false); Sensor = sensor; Sensor.OnEnemySensed += Sensor_OnEnemySensed; Sensor.Creature = this; CurrentTask = null; Tasks = new List<Task>(); Thoughts = new List<Thought>(); IdleTimer = new Timer(15.0f, true); SpeakTimer = new Timer(5.0f, true); XPEvents = new List<int>(); }
public CreatureAI(Creature creature, string name, EnemySensor sensor, PlanService planService) : base(name, creature.Physics) { Movement = new CreatureMovement(); GatherManager = new GatherManager(this); Blackboard = new Blackboard(); Creature = creature; CurrentPath = null; DrawPath = false; PlannerTimer = new Timer(0.1f, false); LocalControlTimeout = new Timer(5, false); WanderTimer = new Timer(1, false); Creature.Faction.Minions.Add(this); DrawAIPlan = false; WaitingOnResponse = false; PlanSubscriber = new PlanSubscriber(planService); ServiceTimeout = new Timer(2, false); Sensor = sensor; Sensor.OnEnemySensed += Sensor_OnEnemySensed; Sensor.Creature = this; CurrentTask = null; Tasks = new List <Task>(); Thoughts = new List <Thought>(); IdleTimer = new Timer(15.0f, true); SpeakTimer = new Timer(5.0f, true); XPEvents = new List <int>(); }
/// <summary> /// Finds the path from the start to the goal region of move actions that can be performed by the creature. /// </summary> /// <param name="mover">The creature following the path.</param> /// <param name="start">The voxel the path starts with.</param> /// <param name="goal">Goal conditions that must be satisfied by the path.</param> /// <param name="chunks">The chunks the creature is moving through.</param> /// <param name="maxExpansions">Maximum number of voxels to explore before giving up.</param> /// <param name="weight"> /// The heuristic weight of the planner. If 1.0, the path returned is optimal. /// Higher values result in suboptimal paths, but the search may be faster. /// </param> /// <param name="numPlans"></param> /// <param name="continueFunc"></param> /// <returns>The path of movements the creature must take to reach the goal. Returns null if no such path exists.</returns> public static List <MoveAction> FindPath(CreatureMovement mover, VoxelHandle start, GoalRegion goal, ChunkManager chunks, int maxExpansions, float weight, int numPlans, Func <bool> continueFunc, out PlanResultCode resultCode) { var p = new List <MoveAction>(); bool use_inverse = goal.IsReversible() && OpennessHeuristic(goal.GetVoxel()) < OpennessHeuristic(start); //bool use_inverse = false; var result = use_inverse ? InversePath(mover, start, goal, chunks, maxExpansions, ref p, weight, continueFunc) : Path(mover, start, goal, chunks, maxExpansions, ref p, weight, continueFunc); resultCode = result.Result; var length = (start.WorldPosition - goal.GetVoxel().WorldPosition).Length(); if (result.Result == PlanResultCode.Success) { return(p); } if (result.Result == PlanResultCode.Invalid || result.Result == PlanResultCode.NoSolution || result.Result == PlanResultCode.Cancelled) { return(null); } if (!goal.IsReversible()) { return(null); } result = use_inverse ? Path(mover, start, goal, chunks, maxExpansions, ref p, weight, continueFunc) : InversePath(mover, start, goal, chunks, maxExpansions, ref p, weight, continueFunc); resultCode = result.Result; return(result.Result == PlanResultCode.Success ? p : null); }
/// <summary> /// Finds the path from the start to the goal region of move actions that can be performed by the creature. /// </summary> /// <param name="mover">The creature following the path.</param> /// <param name="start">The voxel the path starts with.</param> /// <param name="goal">Goal conditions that must be satisfied by the path.</param> /// <param name="chunks">The chunks the creature is moving through.</param> /// <param name="maxExpansions">Maximum number of voxels to explore before giving up.</param> /// <param name="weight"> /// The heuristic weight of the planner. If 1.0, the path returned is optimal. /// Higher values result in suboptimal paths, but the search may be faster. /// </param> /// <returns>The path of movements the creature must take to reach the goal. Returns null if no such path exists.</returns> public static List <Creature.MoveAction> FindPath(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions, float weight) { var p = new List <Creature.MoveAction>(); bool success = Path(mover, start, goal, chunks, maxExpansions, ref p, weight); if (success) { return(p); } return(null); }
public CreatureAI( ComponentManager Manager, string name, EnemySensor sensor) : base(name, Manager) { Movement = new CreatureMovement(this); PlanSubscriber = new PlanSubscriber(Manager.World.PlanService); Sensor = sensor; Sensor.OnEnemySensed += Sensor_OnEnemySensed; Sensor.Creature = this; }
/// <summary> /// Given two voxels, and an action taken between the voxels, returns the cost of moving /// between the two voxels given that action. /// </summary> /// <param name="a">The source voxel of the action.</param> /// <param name="b">The destination voxel of the action.</param> /// <param name="action">The action taken to get between voxels.</param> /// <param name="movement">The creature making the movement.</param> /// <returns>The cost of going from a to b using the given action.</returns> public static float GetDistance(Voxel a, Voxel b, Creature.MoveType action, CreatureMovement movement) { // If trying to move through a non-empty voxel, the cost is just a big number. if (!b.IsEmpty) { return(100000); } // Otherwise, the cost is the distance between the voxels multiplied by the intrinsic cost // of an action. float score = (a.Position - b.Position).LengthSquared() * ActionCost(movement, action); return(score); }
public static List<Creature.MoveAction> FindPath(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions) { List<Creature.MoveAction> p = new List<Creature.MoveAction>(); bool success = Path(mover, start, goal, chunks, maxExpansions, ref p, false); if(success) { return p; } else { return null; } }
public CreatureAI() { Movement = new CreatureMovement(); }
/// <summary> /// Returns the intrinsic cost of an action. /// </summary> private static float ActionCost(CreatureMovement movement, MoveType action) { return(movement.Cost(action)); }
/// <summary> /// Given two voxels, and an action taken between the voxels, returns the cost of moving /// between the two voxels given that action. /// </summary> /// <param name="a">The source voxel of the action.</param> /// <param name="b">The destination voxel of the action.</param> /// <param name="action">The action taken to get between voxels.</param> /// <param name="movement">The creature making the movement.</param> /// <returns>The cost of going from a to b using the given action.</returns> public static float GetDistance(VoxelHandle a, VoxelHandle b, MoveType action, CreatureMovement movement) { // Otherwise, the cost is the distance between the voxels multiplied by the intrinsic cost // of an action. var actionCost = ActionCost(movement, action); float score = (a.WorldPosition - b.WorldPosition).LengthSquared() * actionCost + actionCost; return(score); }
public CreatureAI() { Movement = new CreatureMovement(this.Creature); }
// Find a path from the start to the goal by computing an inverse path from goal to the start. Should only be used // if the forward path fails. private static PlanResult InversePath(CreatureMovement mover, VoxelHandle startVoxel, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List <MoveAction> toReturn, float weight, Func <bool> continueFunc) { // Create a local clone of the octree, using only the objects belonging to the player. var octree = new OctTreeNode(mover.Creature.World.ChunkManager.Bounds.Min, mover.Creature.World.ChunkManager.Bounds.Max); List <Body> playerObjects = new List <Body>(mover.Creature.World.PlayerFaction.OwnedObjects); foreach (var obj in playerObjects) { octree.Add(obj, obj.GetBoundingBox()); } MoveState start = new MoveState() { Voxel = startVoxel }; var startTime = DwarfTime.Tick(); if (mover.IsSessile) { return(new PlanResult() { Result = PlanResultCode.Invalid, Expansions = 0, TimeSeconds = 0 }); } if (!start.IsValid) { return(new PlanResult() { Result = PlanResultCode.Invalid, Expansions = 0, TimeSeconds = 0 }); } // Sometimes a goal may not even be achievable a.priori. If this is true, we know there can't be a path // which satisifies that goal. // It only makes sense to do inverse plans for goals that have an associated voxel. if (!goal.IsPossible() || !goal.GetVoxel().IsValid) { toReturn = null; return(new PlanResult() { Result = PlanResultCode.Invalid, Expansions = 0, TimeSeconds = 0 }); } if (goal.IsInGoalRegion(start.Voxel)) { toReturn = new List <MoveAction>(); return(new PlanResult() { Result = PlanResultCode.Success, Expansions = 0, TimeSeconds = 0 }); } // Voxels that have already been explored. var closedSet = new HashSet <MoveState>(); // Voxels which should be explored. var openSet = new HashSet <MoveState>(); // Dictionary of voxels to the optimal action that got the mover to that voxel. var cameFrom = new Dictionary <MoveState, MoveAction>(); // Optimal score of a voxel based on the path it took to get there. var gScore = new Dictionary <MoveState, float>(); // Expansion priority of voxels. var fScore = new PriorityQueue <MoveState>(); var goalVoxels = new List <VoxelHandle>(); goalVoxels.Add(goal.GetVoxel()); // Starting conditions of the search. foreach (var goalVoxel in VoxelHelpers.EnumerateCube(goal.GetVoxel().Coordinate) .Select(c => new VoxelHandle(start.Voxel.Chunk.Manager.ChunkData, c))) { if (!goalVoxel.IsValid) { continue; } var goalState = new MoveState() { Voxel = goalVoxel }; if (goal.IsInGoalRegion(goalVoxel)) { goalVoxels.Add(goalVoxel); openSet.Add(goalState); gScore[goalState] = 0.0f; fScore.Enqueue(goalState, gScore[goalState] + weight * (goalVoxel.WorldPosition - start.Voxel.WorldPosition).LengthSquared()); } } // Keep count of the number of expansions we've taken to get to the goal. int numExpansions = 0; // Loop until we've either checked every possible voxel, or we've exceeded the maximum number of // expansions. while (openSet.Count > 0 && numExpansions < maxExpansions) { if (numExpansions % 10 == 0 && !continueFunc()) { return new PlanResult { Result = PlanResultCode.Cancelled, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) } } ; // Check the next voxel to explore. var current = GetStateWithMinimumFScore(fScore); if (!current.IsValid) { continue; } //Drawer3D.DrawBox(current.GetBoundingBox(), Color.Blue, 0.1f); numExpansions++; // If we've reached the goal already, reconstruct the path from the start to the // goal. if (current.Equals(start)) { toReturn = ReconstructInversePath(goal, cameFrom, cameFrom[start]); return(new PlanResult() { Result = PlanResultCode.Success, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) }); } // We've already considered the voxel, so add it to the closed set. openSet.Remove(current); closedSet.Add(current); IEnumerable <MoveAction> neighbors = null; // Get the voxels that can be moved to from the current voxel. neighbors = mover.GetInverseMoveActions(current, octree); // Otherwise, consider all of the neighbors of the current voxel that can be moved to, // and determine how to add them to the list of expansions. foreach (MoveAction n in neighbors) { //Drawer3D.DrawBox(n.SourceVoxel.GetBoundingBox(), Color.Red, 0.1f); // If we've already explored that voxel, don't explore it again. if (closedSet.Contains(n.SourceState)) { continue; } // Otherwise, consider the case of moving to that neighbor. float tenativeGScore = gScore[current] + GetDistance(current.Voxel, n.SourceState.Voxel, n.MoveType, mover); // IF the neighbor can already be reached more efficiently, ignore it. if (openSet.Contains(n.SourceState) && gScore.ContainsKey(n.SourceState) && !(tenativeGScore < gScore[n.SourceState])) { continue; } // Otherwise, add it to the list of voxels for consideration. openSet.Add(n.SourceState); // Add an edge to the voxel from the current voxel. var cameAction = n; cameFrom[n.SourceState] = cameAction; // Update the expansion scores for the next voxel. gScore[n.SourceState] = tenativeGScore; fScore.Enqueue(n.SourceState, gScore[n.SourceState] + weight * ((n.SourceState.Voxel.WorldPosition - start.Voxel.WorldPosition).LengthSquared() + (!n.SourceState.VehicleState.IsRidingVehicle ? 100.0f : 0.0f))); } // If we've expanded too many voxels, just give up. if (numExpansions >= maxExpansions) { return(new PlanResult() { Result = PlanResultCode.MaxExpansionsReached, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) }); } } // Somehow we've reached this code without having found a path. Return false. toReturn = null; return(new PlanResult() { Result = PlanResultCode.NoSolution, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) }); }
/// <summary> /// Creates a path from the start voxel to some goal region, returning a list of movements that can /// take a mover from the start voxel to the goal region. /// </summary> /// <param name="mover">The agent that we want to find a path for.</param> /// <param name="startVoxel"></param> /// <param name="goal">Goal conditions that the agent is trying to satisfy.</param> /// <param name="chunks">The voxels that the agent is moving through</param> /// <param name="maxExpansions">Maximum number of voxels to consider before giving up.</param> /// <param name="toReturn">The path of movements that the mover should take to reach the goal.</param> /// <param name="weight"> /// A heuristic weight to apply to the planner. If 1.0, the path is garunteed to be optimal. Higher values /// usually result in faster plans that are suboptimal. /// </param> /// <param name="continueFunc"></param> /// <returns>True if a path could be found, or false otherwise.</returns> private static PlanResult Path(CreatureMovement mover, VoxelHandle startVoxel, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List <MoveAction> toReturn, float weight, Func <bool> continueFunc) { // Create a local clone of the octree, using only the objects belonging to the player. var octree = new OctTreeNode(mover.Creature.World.ChunkManager.Bounds.Min, mover.Creature.World.ChunkManager.Bounds.Max); List <Body> playerObjects = new List <Body>(mover.Creature.World.PlayerFaction.OwnedObjects); foreach (var obj in playerObjects) { octree.Add(obj, obj.GetBoundingBox()); } var start = new MoveState() { Voxel = startVoxel }; var startTime = DwarfTime.Tick(); if (mover.IsSessile) { return(new PlanResult() { Expansions = 0, Result = PlanResultCode.Invalid, TimeSeconds = DwarfTime.Tock(startTime) }); } // Sometimes a goal may not even be achievable a.priori. If this is true, we know there can't be a path // which satisifies that goal. if (!goal.IsPossible()) { toReturn = null; return(new PlanResult() { Expansions = 0, Result = PlanResultCode.Invalid, TimeSeconds = DwarfTime.Tock(startTime) }); } // Voxels that have already been explored. var closedSet = new HashSet <MoveState>(); // Voxels which should be explored. var openSet = new HashSet <MoveState> { start }; // Dictionary of voxels to the optimal action that got the mover to that voxel. var cameFrom = new Dictionary <MoveState, MoveAction>(); // Optimal score of a voxel based on the path it took to get there. var gScore = new Dictionary <MoveState, float>(); // Expansion priority of voxels. var fScore = new PriorityQueue <MoveState>(); // Starting conditions of the search. gScore[start] = 0.0f; fScore.Enqueue(start, gScore[start] + weight * goal.Heuristic(start.Voxel)); // Keep count of the number of expansions we've taken to get to the goal. int numExpansions = 0; // Check the voxels adjacent to the current voxel as a quick test of adjacency to the goal. //var manhattanNeighbors = new List<VoxelHandle>(6); //for (int i = 0; i < 6; i++) //{ // manhattanNeighbors.Add(new VoxelHandle()); //} // Loop until we've either checked every possible voxel, or we've exceeded the maximum number of // expansions. while (openSet.Count > 0 && numExpansions < maxExpansions) { if (numExpansions % 10 == 0 && !continueFunc()) { return new PlanResult { Result = PlanResultCode.Cancelled, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) } } ; // Check the next voxel to explore. var current = GetStateWithMinimumFScore(fScore); if (!current.IsValid) { // If there wasn't a voxel to explore, just try to expand from // the start again. numExpansions++; continue; } numExpansions++; // If we've reached the goal already, reconstruct the path from the start to the // goal. if (goal.IsInGoalRegion(current.Voxel) && cameFrom.ContainsKey(current)) { toReturn = ReconstructPath(cameFrom, cameFrom[current], start); return(new PlanResult() { Result = PlanResultCode.Success, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) }); } //Drawer3D.DrawLine(start.Voxel.GetBoundingBox().Center(), goal.GetVoxel().GetBoundingBox().Center(), Color.Red, 0.1f); //Drawer3D.DrawBox(current.Voxel.GetBoundingBox(), Color.Red, 0.1f, true); // We've already considered the voxel, so add it to the closed set. openSet.Remove(current); closedSet.Add(current); IEnumerable <MoveAction> neighbors = null; // Get the voxels that can be moved to from the current voxel. neighbors = mover.GetMoveActions(current, octree).ToList(); //currentChunk.GetNeighborsManhattan(current, manhattanNeighbors); var foundGoalAdjacent = neighbors.FirstOrDefault(n => !n.DestinationState.VehicleState.IsRidingVehicle && goal.IsInGoalRegion(n.DestinationState.Voxel)); // A quick test to see if we're already adjacent to the goal. If we are, assume // that we can just walk to it. if (foundGoalAdjacent.DestinationState.IsValid && foundGoalAdjacent.SourceState.IsValid) { if (cameFrom.ContainsKey(current)) { List <MoveAction> subPath = ReconstructPath(cameFrom, foundGoalAdjacent, start); toReturn = subPath; return(new PlanResult() { Result = PlanResultCode.Success, Expansions = numExpansions, TimeSeconds = DwarfTime.Tock(startTime) }); } } // Otherwise, consider all of the neighbors of the current voxel that can be moved to, // and determine how to add them to the list of expansions. foreach (MoveAction n in neighbors) { // If we've already explored that voxel, don't explore it again. if (closedSet.Contains(n.DestinationState)) { continue; } // Otherwise, consider the case of moving to that neighbor. float tenativeGScore = gScore[current] + GetDistance(current.Voxel, n.DestinationState.Voxel, n.MoveType, mover); // IF the neighbor can already be reached more efficiently, ignore it. if (openSet.Contains(n.DestinationState) && !(tenativeGScore < gScore[n.DestinationState])) { continue; } // Otherwise, add it to the list of voxels for consideration. openSet.Add(n.DestinationState); // Add an edge to the voxel from the current voxel. var cameAction = n; cameFrom[n.DestinationState] = cameAction; // Update the expansion scores for the next voxel. gScore[n.DestinationState] = tenativeGScore; fScore.Enqueue(n.DestinationState, gScore[n.DestinationState] + weight * (goal.Heuristic(n.DestinationState.Voxel) + (!n.DestinationState.VehicleState.IsRidingVehicle ? 100.0f : 0.0f))); } // If we've expanded too many voxels, just give up. if (numExpansions >= maxExpansions) { return(new PlanResult() { Expansions = numExpansions, Result = PlanResultCode.MaxExpansionsReached, TimeSeconds = DwarfTime.Tock(startTime) }); } } // Somehow we've reached this code without having found a path. Return false. toReturn = null; return(new PlanResult() { Expansions = numExpansions, Result = PlanResultCode.NoSolution, TimeSeconds = DwarfTime.Tock(startTime) }); }
/// <summary> /// Creates a path from the start voxel to some goal region, returning a list of movements that can /// take a mover from the start voxel to the goal region. /// </summary> /// <param name="mover">The agent that we want to find a path for.</param> /// <param name="start">The voxel that the agent starts in.</param> /// <param name="goal">Goal conditions that the agent is trying to satisfy.</param> /// <param name="chunks">The voxels that the agent is moving through</param> /// <param name="maxExpansions">Maximum number of voxels to consider before giving up.</param> /// <param name="toReturn">The path of movements that the mover should take to reach the goal.</param> /// <param name="weight"> /// A heuristic weight to apply to the planner. If 1.0, the path is garunteed to be optimal. Higher values /// usually result in faster plans that are suboptimal. /// </param> /// <returns>True if a path could be found, or false otherwise.</returns> private static bool Path(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List <Creature.MoveAction> toReturn, float weight) { // Sometimes a goal may not even be achievable a.priori. If this is true, we know there can't be a path // which satisifies that goal. if (!goal.IsPossible()) { toReturn = null; return(false); } // Voxels that have already been explored. var closedSet = new HashSet <Voxel>(); // Voxels which should be explored. var openSet = new HashSet <Voxel> { start }; // Dictionary of voxels to the optimal action that got the mover to that voxel. var cameFrom = new Dictionary <Voxel, Creature.MoveAction>(); // Optimal score of a voxel based on the path it took to get there. var gScore = new Dictionary <Voxel, float>(); // Expansion priority of voxels. var fScore = new PriorityQueue <Voxel>(); // Starting conditions of the search. gScore[start] = 0.0f; fScore.Enqueue(start, gScore[start] + weight * goal.Heuristic(start)); // Keep count of the number of expansions we've taken to get to the goal. int numExpansions = 0; // Check the voxels adjacent to the current voxel as a quick test of adjacency to the goal. var manhattanNeighbors = new List <Voxel>(6); for (int i = 0; i < 6; i++) { manhattanNeighbors.Add(new Voxel()); } // Loop until we've either checked every possible voxel, or we've exceeded the maximum number of // expansions. while (openSet.Count > 0 && numExpansions < maxExpansions) { // Check the next voxel to explore. Voxel current = GetVoxelWithMinimumFScore(fScore); if (current == null) { // If there wasn't a voxel to explore, just try to expand from // the start again. current = start; numExpansions++; } numExpansions++; // If we've reached the goal already, reconstruct the path from the start to the // goal. if (goal.IsInGoalRegion(current)) { // Assume that the last action in the path involves walking to the goal. var first = new Creature.MoveAction { Voxel = current, MoveType = Creature.MoveType.Walk }; toReturn = ReconstructPath(cameFrom, first); return(true); } // We've already considered the voxel, so add it to the closed set. openSet.Remove(current); closedSet.Add(current); VoxelChunk currentChunk = chunks.ChunkData.ChunkMap[current.ChunkID]; List <Creature.MoveAction> neighbors = null; // Get the voxels that can be moved to from the current voxel. neighbors = mover.GetMoveActions(current); currentChunk.GetNeighborsManhattan(current, manhattanNeighbors); // A quick test to see if we're already adjacent to the goal. If we are, assume // that we can just walk to it. if (manhattanNeighbors.Contains(goal.GetVoxel())) { var first = new Creature.MoveAction { Voxel = current, MoveType = Creature.MoveType.Walk }; var last = new Creature.MoveAction { Voxel = goal.GetVoxel(), MoveType = Creature.MoveType.Walk }; List <Creature.MoveAction> subPath = ReconstructPath(cameFrom, first); subPath.Add(last); toReturn = subPath; return(true); } // Otherwise, consider all of the neighbors of the current voxel that can be moved to, // and determine how to add them to the list of expansions. foreach (Creature.MoveAction n in neighbors) { // If we've already explored that voxel, don't explore it again. if (closedSet.Contains(n.Voxel)) { continue; } // Otherwise, consider the case of moving to that neighbor. float tenativeGScore = gScore[current] + GetDistance(current, n.Voxel, n.MoveType, mover); // IF the neighbor can already be reached more efficiently, ignore it. if (openSet.Contains(n.Voxel) && !(tenativeGScore < gScore[n.Voxel])) { continue; } // Otherwise, add it to the list of voxels for consideration. openSet.Add(n.Voxel); // Add an edge to the voxel from the current voxel. Creature.MoveAction cameAction = n; cameAction.Voxel = current; cameFrom[n.Voxel] = cameAction; // Update the expansion scores for the next voxel. gScore[n.Voxel] = tenativeGScore; fScore.Enqueue(n.Voxel, gScore[n.Voxel] + weight * goal.Heuristic(n.Voxel)); } // If we've expanded too many voxels, just give up. if (numExpansions >= maxExpansions) { return(false); } } // Somehow we've reached this code without having found a path. Return false. toReturn = null; return(false); }
private static bool Path(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List<Creature.MoveAction> toReturn, bool reverse) { VoxelChunk startChunk = chunks.ChunkData.ChunkMap[start.ChunkID]; VoxelChunk endChunk = chunks.ChunkData.ChunkMap[goal.GetVoxel().ChunkID]; if(startChunk.IsCompletelySurrounded(start) || endChunk.IsCompletelySurrounded(goal.GetVoxel())) { toReturn = null; return false; } HashSet<Voxel> closedSet = new HashSet<Voxel>(); HashSet<Voxel> openSet = new HashSet<Voxel> { start }; Dictionary<Voxel, Creature.MoveAction> cameFrom = new Dictionary<Voxel, Creature.MoveAction>(); Dictionary<Voxel, float> gScore = new Dictionary<Voxel, float>(); PriorityQueue<Voxel> fScore = new PriorityQueue<Voxel>(); gScore[start] = 0.0f; fScore.Enqueue(start, gScore[start] + Heuristic(start, goal.GetVoxel())); int numExpansions = 0; List<Voxel> manhattanNeighbors = new List<Voxel>(6); for (int i = 0; i < 6; i++) { manhattanNeighbors.Add(new Voxel()); } while(openSet.Count > 0 && numExpansions < maxExpansions) { Voxel current = GetVoxelWithMinimumFScore(fScore, openSet); if (current == null) { current = start; numExpansions++; } numExpansions++; if (goal.IsInGoalRegion(current)) { Creature.MoveAction first = new Creature.MoveAction() { Voxel = current, MoveType = Creature.MoveType.Walk }; toReturn = ReconstructPath(cameFrom, first); return true; } openSet.Remove(current); closedSet.Add(current); VoxelChunk currentChunk = chunks.ChunkData.ChunkMap[current.ChunkID]; List<Creature.MoveAction> neighbors = null; neighbors = mover.GetMoveActions(current); currentChunk.GetNeighborsManhattan(current, manhattanNeighbors); if(manhattanNeighbors.Contains(goal.GetVoxel())) { Creature.MoveAction first = new Creature.MoveAction() { Voxel = current, MoveType = Creature.MoveType.Walk }; Creature.MoveAction last = new Creature.MoveAction() { Voxel = goal.GetVoxel(), MoveType = Creature.MoveType.Walk }; List<Creature.MoveAction> subPath = ReconstructPath(cameFrom, first); subPath.Add(last); toReturn = subPath; return true; } foreach(Creature.MoveAction n in neighbors) { if(closedSet.Contains(n.Voxel)) { continue; } float tenativeGScore = gScore[current] + GetDistance(current, n.Voxel, n.MoveType, chunks); if(openSet.Contains(n.Voxel) && !(tenativeGScore < gScore[n.Voxel])) { continue; } openSet.Add(n.Voxel); Creature.MoveAction cameAction = n; cameAction.Voxel = current; cameFrom[n.Voxel] = cameAction; gScore[n.Voxel] = tenativeGScore; fScore.Enqueue(n.Voxel, gScore[n.Voxel] + Heuristic(n.Voxel, goal.GetVoxel())); } if(numExpansions >= maxExpansions) { return false; } } toReturn = null; return false; }
private static bool Path(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List <Creature.MoveAction> toReturn, bool reverse) { VoxelChunk startChunk = chunks.ChunkData.ChunkMap[start.ChunkID]; VoxelChunk endChunk = chunks.ChunkData.ChunkMap[goal.GetVoxel().ChunkID]; if (startChunk.IsCompletelySurrounded(start) || endChunk.IsCompletelySurrounded(goal.GetVoxel())) { toReturn = null; return(false); } HashSet <Voxel> closedSet = new HashSet <Voxel>(); HashSet <Voxel> openSet = new HashSet <Voxel> { start }; Dictionary <Voxel, Creature.MoveAction> cameFrom = new Dictionary <Voxel, Creature.MoveAction>(); Dictionary <Voxel, float> gScore = new Dictionary <Voxel, float>(); PriorityQueue <Voxel> fScore = new PriorityQueue <Voxel>(); gScore[start] = 0.0f; fScore.Enqueue(start, gScore[start] + Heuristic(start, goal.GetVoxel())); int numExpansions = 0; List <Voxel> manhattanNeighbors = new List <Voxel>(6); for (int i = 0; i < 6; i++) { manhattanNeighbors.Add(new Voxel()); } while (openSet.Count > 0 && numExpansions < maxExpansions) { Voxel current = GetVoxelWithMinimumFScore(fScore, openSet); if (current == null) { current = start; numExpansions++; } numExpansions++; if (goal.IsInGoalRegion(current)) { Creature.MoveAction first = new Creature.MoveAction() { Voxel = current, MoveType = Creature.MoveType.Walk }; toReturn = ReconstructPath(cameFrom, first); return(true); } openSet.Remove(current); closedSet.Add(current); VoxelChunk currentChunk = chunks.ChunkData.ChunkMap[current.ChunkID]; List <Creature.MoveAction> neighbors = null; neighbors = mover.GetMoveActions(current); currentChunk.GetNeighborsManhattan(current, manhattanNeighbors); if (manhattanNeighbors.Contains(goal.GetVoxel())) { Creature.MoveAction first = new Creature.MoveAction() { Voxel = current, MoveType = Creature.MoveType.Walk }; Creature.MoveAction last = new Creature.MoveAction() { Voxel = goal.GetVoxel(), MoveType = Creature.MoveType.Walk }; List <Creature.MoveAction> subPath = ReconstructPath(cameFrom, first); subPath.Add(last); toReturn = subPath; return(true); } foreach (Creature.MoveAction n in neighbors) { if (closedSet.Contains(n.Voxel)) { continue; } float tenativeGScore = gScore[current] + GetDistance(current, n.Voxel, n.MoveType, chunks); if (openSet.Contains(n.Voxel) && !(tenativeGScore < gScore[n.Voxel])) { continue; } openSet.Add(n.Voxel); Creature.MoveAction cameAction = n; cameAction.Voxel = current; cameFrom[n.Voxel] = cameAction; gScore[n.Voxel] = tenativeGScore; fScore.Enqueue(n.Voxel, gScore[n.Voxel] + Heuristic(n.Voxel, goal.GetVoxel())); } if (numExpansions >= maxExpansions) { return(false); } } toReturn = null; return(false); }