diff --git a/src/main/java/StevenDimDoors/experimental/MazeGenerator.java b/src/main/java/StevenDimDoors/experimental/MazeGenerator.java index 5eb9e63..aa7e99c 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeGenerator.java +++ b/src/main/java/StevenDimDoors/experimental/MazeGenerator.java @@ -1,5 +1,7 @@ package StevenDimDoors.experimental; +import java.util.ArrayList; +import java.util.Collections; import java.util.Random; import net.minecraft.block.Block; @@ -13,18 +15,60 @@ public class MazeGenerator { public static final int ROOT_WIDTH = 40; public static final int ROOT_LENGTH = 40; - public static final int ROOT_HEIGHT = 19; + public static final int ROOT_HEIGHT = 20; private static final int MIN_HEIGHT = 4; private static final int MIN_SIDE = 3; - private static final int SPLIT_COUNT = 8; + private static final int SPLIT_COUNT = 9; private MazeGenerator() { } public static void generate(World world, int x, int y, int z, Random random) { SpatialNode root = partitionRooms(ROOT_WIDTH, ROOT_HEIGHT, ROOT_LENGTH, SPLIT_COUNT, random); + // Collect all the leaf nodes by performing a tree traversal + ArrayList rooms = new ArrayList(1 << SPLIT_COUNT); + listRooms(root, rooms); + removeRandomRooms(rooms, random); buildRooms(root, world, new Point3D(x - ROOT_WIDTH / 2, y - ROOT_HEIGHT - 1, z - ROOT_WIDTH / 2)); + } + + private static void listRooms(SpatialNode node, ArrayList rooms) + { + if (node.isLeaf()) + { + rooms.add(node); + } + else + { + listRooms(node.leftChild(), rooms); + listRooms(node.rightChild(), rooms); + } + } + private static void removeRandomRooms(ArrayList rooms, Random random) + { + // Randomly remove a fraction of the rooms + Collections.shuffle(rooms, random); + int remaining = rooms.size() / 2; + for (int k = rooms.size() - 1; k >= remaining; k--) + { + removeRoom(rooms.remove(k)); + } + } + + private static void removeRoom(SpatialNode node) + { + // Remove a node and any of its ancestors that become leaf nodes + SpatialNode parent; + SpatialNode current; + + current = node; + while (current != null && current.isLeaf()) + { + parent = current.parent(); + current.remove(); + current = parent; + } } private static SpatialNode partitionRooms(int width, int height, int length, int maxLevels, Random random) @@ -99,8 +143,10 @@ public class MazeGenerator } else { - buildRooms(node.leftChild(), world, offset); - buildRooms(node.rightChild(), world, offset); + if (node.leftChild() != null) + buildRooms(node.leftChild(), world, offset); + if (node.rightChild() != null) + buildRooms(node.rightChild(), world, offset); } } diff --git a/src/main/java/StevenDimDoors/experimental/SpatialNode.java b/src/main/java/StevenDimDoors/experimental/SpatialNode.java index 0aef08f..1b53bbe 100644 --- a/src/main/java/StevenDimDoors/experimental/SpatialNode.java +++ b/src/main/java/StevenDimDoors/experimental/SpatialNode.java @@ -6,17 +6,20 @@ public class SpatialNode { private Point3D minCorner; private Point3D maxCorner; + private SpatialNode parent; private SpatialNode leftChild = null; private SpatialNode rightChild = null; public SpatialNode(int width, int height, int length) { + parent = null; minCorner = new Point3D(0, 0, 0); maxCorner = new Point3D(width - 1, height - 1, length - 1); } - private SpatialNode(Point3D minCorner, Point3D maxCorner) + private SpatialNode(SpatialNode parent, Point3D minCorner, Point3D maxCorner) { + this.parent = parent; this.minCorner = minCorner; this.maxCorner = maxCorner; } @@ -38,7 +41,7 @@ public class SpatialNode public boolean isLeaf() { - return (leftChild == null); + return (leftChild == null && rightChild == null); } public SpatialNode leftChild() @@ -61,9 +64,14 @@ public class SpatialNode return maxCorner; } + public SpatialNode parent() + { + return parent; + } + public void splitByX(int rightStart) { - if (leftChild != null) + if (!this.isLeaf()) { throw new IllegalStateException("This node has already been split."); } @@ -71,13 +79,13 @@ public class SpatialNode { throw new IllegalArgumentException("The specified cutting plane is invalid."); } - leftChild = new SpatialNode(minCorner, new Point3D(rightStart - 1, maxCorner.getY(), maxCorner.getZ())); - rightChild = new SpatialNode(new Point3D(rightStart, minCorner.getY(), minCorner.getZ()), maxCorner); + leftChild = new SpatialNode(this, minCorner, new Point3D(rightStart - 1, maxCorner.getY(), maxCorner.getZ())); + rightChild = new SpatialNode(this, new Point3D(rightStart, minCorner.getY(), minCorner.getZ()), maxCorner); } public void splitByY(int rightStart) { - if (leftChild != null) + if (!this.isLeaf()) { throw new IllegalStateException("This node has already been split."); } @@ -85,13 +93,13 @@ public class SpatialNode { throw new IllegalArgumentException("The specified cutting plane is invalid."); } - leftChild = new SpatialNode(minCorner, new Point3D(maxCorner.getX(), rightStart - 1, maxCorner.getZ())); - rightChild = new SpatialNode(new Point3D(minCorner.getX(), rightStart, minCorner.getZ()), maxCorner); + leftChild = new SpatialNode(this, minCorner, new Point3D(maxCorner.getX(), rightStart - 1, maxCorner.getZ())); + rightChild = new SpatialNode(this, new Point3D(minCorner.getX(), rightStart, minCorner.getZ()), maxCorner); } public void splitByZ(int rightStart) { - if (leftChild != null) + if (!this.isLeaf()) { throw new IllegalStateException("This node has already been split."); } @@ -99,7 +107,19 @@ public class SpatialNode { throw new IllegalArgumentException("The specified cutting plane is invalid."); } - leftChild = new SpatialNode(minCorner, new Point3D(maxCorner.getX(), maxCorner.getY(), rightStart - 1)); - rightChild = new SpatialNode(new Point3D(minCorner.getX(), minCorner.getY(), rightStart), maxCorner); + leftChild = new SpatialNode(this, minCorner, new Point3D(maxCorner.getX(), maxCorner.getY(), rightStart - 1)); + rightChild = new SpatialNode(this, new Point3D(minCorner.getX(), minCorner.getY(), rightStart), maxCorner); + } + + public void remove() + { + if (parent != null) + { + if (parent.leftChild == this) + parent.leftChild = null; + else + parent.rightChild = null; + parent = null; + } } }