new path finding algorithm for the block breaking robots

This commit is contained in:
Hea3veN 2015-02-02 07:48:02 -03:00
parent bb16f3f529
commit c873857807
12 changed files with 256 additions and 117 deletions

View file

@ -0,0 +1,17 @@
/**
* Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
*
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.core.utils.concurrency;
public interface IIterableAlgorithm {
abstract void iterate();
abstract boolean isDone();
}

View file

@ -6,33 +6,32 @@
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.core.utils;
package buildcraft.core.utils.concurrency;
import java.util.Date;
public class PathFindingJob extends Thread {
public class IterableAlgorithmRunner extends Thread {
private PathFinding pathFinding;
private IIterableAlgorithm pathFinding;
private boolean stop = false;
private int maxIterations;
private boolean done = false;
public PathFindingJob(PathFinding iPathFinding, int iMaxIterations) {
public IterableAlgorithmRunner(IIterableAlgorithm iPathFinding, int iMaxIterations) {
super("Path Finding");
pathFinding = iPathFinding;
maxIterations = iMaxIterations;
}
public PathFindingJob(PathFinding iPathFinding) {
public IterableAlgorithmRunner(IIterableAlgorithm iPathFinding) {
this(iPathFinding, 1000);
}
@Override
public void run() {
try {
pathFinding.preRun();
for (int i = 0; i < maxIterations; ++i) {
if (isTerminated() || pathFinding.isDone()) {
break;

View file

@ -6,7 +6,7 @@
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.core.utils;
package buildcraft.core.utils.concurrency;
import java.util.ArrayList;
import java.util.Collection;
@ -17,26 +17,23 @@ import net.minecraft.world.World;
import buildcraft.api.core.BlockIndex;
import buildcraft.api.core.BuildCraftAPI;
import buildcraft.api.core.IZone;
import buildcraft.core.utils.IBlockFilter;
/**
* This class implements a 3D path finding based on the A* algorithm, following
* guidelines documented on http://www.policyalmanac.org/games/aStarTutorial.htm
* .
*/
public class PathFinding {
public class PathFinding implements IIterableAlgorithm {
public static int PATH_ITERATIONS = 1000;
private World world;
private BlockIndex start;
private BlockIndex end;
private BlockIndex boxEnd;
private IBlockFilter pathFound;
private float maxDistance = -1;
private float sqrMaxDistance = -1;
private IZone zone;
private double maxDistanceToEnd = 0;
private boolean targetNotFound;
private HashMap<BlockIndex, Node> openList = new HashMap<BlockIndex, PathFinding.Node>();
private HashMap<BlockIndex, Node> closedList = new HashMap<BlockIndex, PathFinding.Node>();
@ -68,58 +65,7 @@ public class PathFinding {
maxDistanceToEnd = iMaxDistanceToEnd;
}
// TODO: It's probably more efficient to start a search first, and then to
// compute the path, instead of computing all possible path from the get
// go.
public PathFinding(World iWorld, BlockIndex iStart, IBlockFilter iPathFound, float iMaxDistance, IZone iZone) {
world = iWorld;
start = iStart;
pathFound = iPathFound;
Node startNode = new Node();
startNode.parent = null;
startNode.movementCost = 0;
startNode.destinationCost = 0;
startNode.totalWeight = startNode.movementCost + startNode.destinationCost;
startNode.index = iStart;
openList.put(start, startNode);
nextIteration = startNode;
maxDistance = iMaxDistance;
sqrMaxDistance = maxDistance * maxDistance;
maxDistance = maxDistance * 1.25f;
zone = iZone;
targetNotFound = false;
}
public void preRun() {
if (end == null) {
targetNotFound = searchForTarget(64);
}
}
private boolean searchForTarget(int range) {
for (int dx = -range; dx <= range; dx++) {
for (int dz = -range; dz <= range; dz++) {
int x = start.x + dx;
int z = start.z + dz;
if (world.getChunkProvider().chunkExists (x >> 4, z >> 4)) {
int height = world.getChunkFromChunkCoords(x >> 4, z >> 4).getHeightValue(x & 0xF, z & 0xF);
for (int dy = -range; dy <= height; dy++) {
int y = Math.max(0, start.y + dy);
if (zone != null && !zone.contains(x, y, z)) {
continue;
}
if (pathFound.matches(world, x, y, z)) {
return false;
}
}
}
}
}
return true;
}
@Override
public void iterate() {
iterate(PATH_ITERATIONS);
}
@ -145,8 +91,9 @@ public class PathFinding {
}
}
@Override
public boolean isDone() {
return nextIteration == null || (end == null && targetNotFound);
return nextIteration == null;
}
public LinkedList<BlockIndex> getResult() {
@ -400,33 +347,7 @@ public class PathFinding {
resultMoves[2][2][2] = 0;
}
if (maxDistance != -1) {
for (int dx = -1; dx <= +1; ++dx) {
for (int dy = -1; dy <= +1; ++dy) {
for (int dz = -1; dz <= +1; ++dz) {
int x = from.index.x + dx;
int y = from.index.y + dy;
int z = from.index.z + dz;
float distX = x - start.x;
float distY = y - start.y;
float distZ = z - start.z;
float sqrDist = distX * distX + distY * distY + distZ * distZ;
if (sqrDist > sqrMaxDistance) {
resultMoves[dx + 1][dy + 1][dz + 1] = 0;
}
}
}
}
}
return resultMoves;
}
private boolean totalDistanceExceeded(Node nextNode) {
return maxDistance != -1 && nextNode.totalWeight > maxDistance;
}
}

View file

@ -0,0 +1,188 @@
/**
* Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
*
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.core.utils.concurrency;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.world.World;
import buildcraft.api.core.BlockIndex;
import buildcraft.api.core.BuildCraftAPI;
import buildcraft.api.core.IZone;
import buildcraft.core.utils.IBlockFilter;
public class PathFindingSearch implements IIterableAlgorithm {
public static int PATH_ITERATIONS = 1000;
private static HashMap<Integer, HashSet<BlockIndex>> reservations = new HashMap<Integer, HashSet<BlockIndex>>();
private World world;
private BlockIndex start;
private List<PathFinding> pathFinders;
private IBlockFilter pathFound;
private IZone zone;
private int searchRadius;
private int searchX;
private int searchY;
private int searchZ;
private int searchHeight;
public PathFindingSearch(World iWorld, BlockIndex iStart, IBlockFilter iPathFound, float iMaxDistance, IZone iZone) {
world = iWorld;
start = iStart;
pathFound = iPathFound;
pathFinders = new LinkedList<PathFinding>();
searchRadius = 1;
searchX = -1;
searchY = -1;
searchZ = -1;
getSearchHeight(start.x + searchX, start.z + searchZ);
}
@Override
public void iterate() {
if (pathFinders.size() < 5 && searchRadius < 64) {
iterateSearch(PATH_ITERATIONS * 50);
}
iteratePathFind(PATH_ITERATIONS);
}
private void iterateSearch(int itNumber) {
for (int i = 0; i < itNumber; ++i) {
int currX = start.x + searchX;
int currY = start.y + searchY;
int currZ = start.z + searchZ;
if (0 <= currY && currY <= searchHeight) {
if (isTarget(currX, currY, currZ)) {
pathFinders.add(new PathFinding(world, start, new BlockIndex(currX, currY, currZ)));
}
}
searchY += 1;
if (searchY > searchRadius) {
searchY = -searchRadius;
searchZ += 1;
if (searchZ > searchRadius) {
searchZ = -searchRadius;
searchX += 1;
if (searchX > searchRadius) {
searchRadius += 1;
searchX = -searchRadius;
searchY = -searchRadius;
searchZ = -searchRadius;
}
}
searchHeight = getSearchHeight(start.x + searchX, start.z + searchZ);
}
if (pathFinders.size() >= 5) {
return;
}
}
}
private boolean isTarget(int x, int y, int z) {
if (zone != null && !zone.contains(x, y, z)) {
return false;
}
if (!pathFound.matches(world, x, y, z)) {
return false;
}
synchronized (reservations) {
if (reservations.containsKey(world.provider.dimensionId)) {
HashSet<BlockIndex> dimReservations = reservations
.get(world.provider.dimensionId);
if (dimReservations.contains(new BlockIndex(x, y, z))) {
return false;
}
}
}
if (!BuildCraftAPI.isSoftBlock(world, x - 1, y, z)
&& !BuildCraftAPI.isSoftBlock(world, x + 1, y, z)
&& !BuildCraftAPI.isSoftBlock(world, x, y, z - 1)
&& !BuildCraftAPI.isSoftBlock(world, x, y, z + 1)
&& !BuildCraftAPI.isSoftBlock(world, x, y - 1, z)
&& !BuildCraftAPI.isSoftBlock(world, x, y + 1, z)) {
return false;
}
return true;
}
private int getSearchHeight(int x, int z) {
if (world.getChunkProvider().chunkExists(x >> 4, z >> 4)) {
return 256;
} else {
return -1;
}
}
public void iteratePathFind(int itNumber) {
for (PathFinding pathFinding : new ArrayList<PathFinding>(pathFinders)) {
pathFinding.iterate(itNumber / pathFinders.size());
if (pathFinding.isDone()) {
LinkedList<BlockIndex> path = pathFinding.getResult();
if (path != null && path.size() > 0) {
if (reserve(path.getLast())) {
return;
}
}
pathFinders.remove(pathFinding);
}
}
}
@Override
public boolean isDone() {
for (PathFinding pathFinding : pathFinders) {
if (pathFinding.isDone()) {
return true;
}
}
return searchRadius >= 64;
}
public LinkedList<BlockIndex> getResult() {
for (PathFinding pathFinding : pathFinders) {
if (pathFinding.isDone()) {
return pathFinding.getResult();
}
}
return new LinkedList<BlockIndex>();
}
private boolean reserve(BlockIndex block) {
synchronized (reservations) {
if (!reservations.containsKey(world.provider.dimensionId)) {
reservations.put(world.provider.dimensionId,
new HashSet<BlockIndex>());
}
HashSet<BlockIndex> dimReservations = reservations
.get(world.provider.dimensionId);
if (dimReservations.contains(block)) {
return false;
}
dimReservations.add(block);
return true;
}
}
public void unreserve(BlockIndex block) {
synchronized (reservations) {
if (reservations.containsKey(world.provider.dimensionId)) {
reservations.get(world.provider.dimensionId).remove(block);
}
}
}
}

View file

@ -247,6 +247,7 @@ public class EntityRobot extends EntityRobotBase implements
@Override
public void onEntityUpdate() {
this.worldObj.theProfiler.startSection("bcEntityRobot");
if (!firstUpdateDone) {
firstUpdate();
firstUpdateDone = true;
@ -305,7 +306,9 @@ public class EntityRobot extends EntityRobotBase implements
}
if (linkedDockingStation != null) {
this.worldObj.theProfiler.startSection("bcRobotAIMainCycle");
mainAI.cycle();
this.worldObj.theProfiler.endSection();
if (energySpendPerCycle != mainAI.getActiveAI().getEnergyCost()) {
energySpendPerCycle = mainAI.getActiveAI().getEnergyCost();
@ -319,6 +322,7 @@ public class EntityRobot extends EntityRobotBase implements
}
super.onEntityUpdate();
this.worldObj.theProfiler.endSection();
}
@SideOnly(Side.CLIENT)
private void spawnEnergyFX() {

View file

@ -17,15 +17,15 @@ import net.minecraftforge.common.util.Constants;
import buildcraft.api.core.BlockIndex;
import buildcraft.api.robots.EntityRobotBase;
import buildcraft.core.utils.PathFinding;
import buildcraft.core.utils.PathFindingJob;
import buildcraft.core.utils.concurrency.IterableAlgorithmRunner;
import buildcraft.core.utils.concurrency.PathFinding;
public class AIRobotGotoBlock extends AIRobotGoto {
public boolean unreachable = false;
private PathFinding pathSearch;
private PathFindingJob pathSearchJob;
private IterableAlgorithmRunner pathSearchJob;
private LinkedList<BlockIndex> path;
private double prevDistance = Double.MAX_VALUE;
private float finalX, finalY, finalZ;
@ -70,7 +70,7 @@ public class AIRobotGotoBlock extends AIRobotGoto {
(int) Math.floor(robot.posY), (int) Math.floor(robot.posZ)), new BlockIndex(
(int) Math.floor(finalX), (int) Math.floor(finalY), (int) Math.floor(finalZ)), maxDistance);
pathSearchJob = new PathFindingJob(pathSearch, 100);
pathSearchJob = new IterableAlgorithmRunner(pathSearch, 100);
pathSearchJob.start();
} else if (path != null) {
double distance = robot.getDistance(nextX, nextY, nextZ);

View file

@ -15,8 +15,8 @@ import buildcraft.api.core.IZone;
import buildcraft.api.robots.AIRobot;
import buildcraft.api.robots.EntityRobotBase;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.core.utils.PathFinding;
import buildcraft.core.utils.PathFindingJob;
import buildcraft.core.utils.concurrency.IterableAlgorithmRunner;
import buildcraft.core.utils.concurrency.PathFinding;
public class AIRobotGotoRandomGroundBlock extends AIRobot {
@ -24,7 +24,7 @@ public class AIRobotGotoRandomGroundBlock extends AIRobot {
private int range;
private PathFinding pathFinding;
private PathFindingJob pathFindingJob;
private IterableAlgorithmRunner pathFindingJob;
private IBlockFilter filter;
private IZone zone;
@ -68,7 +68,7 @@ public class AIRobotGotoRandomGroundBlock extends AIRobot {
blockFound = aiFind.blockFound;
pathFinding = new PathFinding(robot.worldObj, new BlockIndex(robot), blockFound);
pathFindingJob = new PathFindingJob(pathFinding);
pathFindingJob = new IterableAlgorithmRunner(pathFinding);
pathFindingJob.start();
} else if (ai instanceof AIRobotGotoBlock) {
terminate();

View file

@ -16,17 +16,16 @@ import buildcraft.api.core.BlockIndex;
import buildcraft.api.robots.AIRobot;
import buildcraft.api.robots.EntityRobotBase;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.core.utils.PathFinding;
import buildcraft.core.utils.PathFindingJob;
import buildcraft.core.utils.concurrency.IterableAlgorithmRunner;
import buildcraft.core.utils.concurrency.PathFindingSearch;
public class AIRobotSearchBlock extends AIRobot {
public BlockIndex blockFound;
public LinkedList<BlockIndex> path;
private PathFinding blockScanner = null;
private PathFindingJob blockScannerJob;
private PathFindingSearch blockScanner = null;
private IterableAlgorithmRunner blockScannerJob;
private IBlockFilter pathFound;
private int stopBefore = 0;
public AIRobotSearchBlock(EntityRobotBase iRobot) {
super(iRobot);
@ -36,13 +35,12 @@ public class AIRobotSearchBlock extends AIRobot {
super(iRobot);
pathFound = iPathFound;
stopBefore = 0;
}
@Override
public void start() {
blockScanner = new PathFinding(robot.worldObj, new BlockIndex(robot), pathFound, 64, robot.getZoneToWork());
blockScannerJob = new PathFindingJob(blockScanner);
blockScanner = new PathFindingSearch(robot.worldObj, new BlockIndex(robot), pathFound, 64, robot.getZoneToWork());
blockScannerJob = new IterableAlgorithmRunner(blockScanner, 40000);
blockScannerJob.start();
}
@ -94,4 +92,8 @@ public class AIRobotSearchBlock extends AIRobot {
blockFound = new BlockIndex(nbt.getCompoundTag("blockFound"));
}
}
public void unreserve() {
blockScanner.unreserve(blockFound);
}
}

View file

@ -20,13 +20,13 @@ import buildcraft.api.core.BuildCraftAPI;
import buildcraft.api.robots.AIRobot;
import buildcraft.api.robots.EntityRobotBase;
import buildcraft.core.inventory.filters.IStackFilter;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.ai.AIRobotFetchAndEquipItemStack;
import buildcraft.robots.ai.AIRobotGotoBlock;
import buildcraft.robots.ai.AIRobotGotoSleep;
import buildcraft.robots.ai.AIRobotSearchBlock;
import buildcraft.robots.ai.AIRobotUseToolOnBlock;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.RobotRegistry;
public class BoardRobotFarmer extends RedstoneBoardRobot {
@ -71,6 +71,7 @@ public class BoardRobotFarmer extends RedstoneBoardRobot {
if (searchAI.blockFound != null
&& RobotRegistry.getRegistry(robot.worldObj).take(
new ResourceIdBlock(searchAI.blockFound), robot)) {
((AIRobotSearchBlock) ai).unreserve();
if (blockFound != null) {
robot.getRegistry().release(new ResourceIdBlock(blockFound));
@ -79,11 +80,12 @@ public class BoardRobotFarmer extends RedstoneBoardRobot {
blockFound = searchAI.blockFound;
startDelegateAI(new AIRobotGotoBlock(robot, searchAI.path));
} else {
if (searchAI.blockFound != null) {
((AIRobotSearchBlock) ai).unreserve();
}
startDelegateAI(new AIRobotGotoSleep(robot));
}
} else if (ai instanceof AIRobotGotoBlock) {
AIRobotGotoBlock gotoBlock = (AIRobotGotoBlock) ai;
startDelegateAI(new AIRobotUseToolOnBlock(robot, blockFound));
} else if (ai instanceof AIRobotFetchAndEquipItemStack) {
if (robot.getHeldItem() == null) {

View file

@ -23,13 +23,13 @@ import buildcraft.api.robots.EntityRobotBase;
import buildcraft.api.statements.IStatementParameter;
import buildcraft.api.statements.StatementParameterItemStack;
import buildcraft.core.inventory.filters.IStackFilter;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.ai.AIRobotBreak;
import buildcraft.robots.ai.AIRobotFetchAndEquipItemStack;
import buildcraft.robots.ai.AIRobotGotoBlock;
import buildcraft.robots.ai.AIRobotGotoSleep;
import buildcraft.robots.ai.AIRobotSearchBlock;
import buildcraft.robots.DockingStation;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.statements.ActionRobotFilter;
import buildcraft.transport.gates.ActionIterator;
@ -104,6 +104,7 @@ public abstract class BoardRobotGenericBreakBlock extends RedstoneBoardRobot {
if (robot.getRegistry().take(new ResourceIdBlock(indexStored), robot)) {
startDelegateAI(new AIRobotGotoBlock(robot, ((AIRobotSearchBlock) ai).path));
}
((AIRobotSearchBlock) ai).unreserve();
}
} else if (ai instanceof AIRobotGotoBlock) {
startDelegateAI(new AIRobotBreak(robot, indexStored));

View file

@ -31,15 +31,14 @@ import buildcraft.core.inventory.filters.ArrayStackFilter;
import buildcraft.core.inventory.filters.ArrayStackOrListFilter;
import buildcraft.core.inventory.filters.CompositeFilter;
import buildcraft.core.inventory.filters.IStackFilter;
import buildcraft.core.inventory.filters.OreStackFilter;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.ai.AIRobotFetchAndEquipItemStack;
import buildcraft.robots.ai.AIRobotGotoBlock;
import buildcraft.robots.ai.AIRobotGotoRandomGroundBlock;
import buildcraft.robots.ai.AIRobotGotoSleep;
import buildcraft.robots.ai.AIRobotSearchBlock;
import buildcraft.robots.ai.AIRobotUseToolOnBlock;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.statements.ActionRobotFilter;
public class BoardRobotPlanter extends RedstoneBoardRobot {
@ -140,10 +139,15 @@ public class BoardRobotPlanter extends RedstoneBoardRobot {
robot.getRegistry().release(new ResourceIdBlock(blockFound));
}
((AIRobotSearchBlock) ai).unreserve();
blockFound = gotoBlock.blockFound;
gotoBlock.path.removeLast();
startDelegateAI(new AIRobotGotoBlock(robot, gotoBlock.path));
} else {
if (gotoBlock.blockFound != null) {
gotoBlock.unreserve();
}
startDelegateAI(new AIRobotGotoSleep(robot));
}
} else if (ai instanceof AIRobotGotoBlock) {

View file

@ -28,14 +28,14 @@ import buildcraft.api.robots.AIRobot;
import buildcraft.api.robots.EntityRobotBase;
import buildcraft.api.statements.IStatementParameter;
import buildcraft.api.statements.StatementParameterItemStack;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.DockingStation;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.ai.AIRobotGotoBlock;
import buildcraft.robots.ai.AIRobotGotoSleep;
import buildcraft.robots.ai.AIRobotGotoStationAndUnloadFluids;
import buildcraft.robots.ai.AIRobotPumpBlock;
import buildcraft.robots.ai.AIRobotSearchBlock;
import buildcraft.robots.DockingStation;
import buildcraft.core.utils.IBlockFilter;
import buildcraft.robots.ResourceIdBlock;
import buildcraft.robots.statements.ActionRobotFilter;
import buildcraft.transport.gates.ActionIterator;
import buildcraft.transport.gates.StatementSlot;
@ -92,6 +92,7 @@ public class BoardRobotPump extends RedstoneBoardRobot {
} else {
startDelegateAI(new AIRobotGotoBlock(robot, ((AIRobotSearchBlock) ai).path));
}
((AIRobotSearchBlock) ai).unreserve();
}
} else if (ai instanceof AIRobotGotoBlock) {
if (!ai.success()) {