Mazes #155

Merged
StevenRS11 merged 11 commits from mazes into mazes 2014-04-15 11:46:00 +00:00
5 changed files with 270 additions and 40 deletions
Showing only changes of commit 53b5591149 - Show all commits

View File

@@ -0,0 +1,87 @@
package StevenDimDoors.experimental;
public class LinkPlan
{
private RoomData source;
private RoomData destination;
private boolean entrance;
private LinkPlan(RoomData source, RoomData destination, boolean entrance)
{
this.source = source;
this.destination = destination;
this.entrance = entrance;
}
public static LinkPlan createInternalLink(RoomData source, RoomData destination)
{
if (source == null)
{
throw new IllegalArgumentException("source cannot be null.");
}
if (destination == null)
{
throw new IllegalArgumentException("destination cannot be null.");
}
LinkPlan plan = new LinkPlan(source, destination, false);
source.getOutboundLinks().add(plan);
destination.getInboundLinks().add(plan);
return plan;
}
public static LinkPlan createEntranceLink(RoomData source)
{
if (source == null)
{
throw new IllegalArgumentException("source cannot be null.");
}
LinkPlan plan = new LinkPlan(source, null, true);
source.getOutboundLinks().add(plan);
return plan;
}
public static LinkPlan createDungeonLink(RoomData source)
{
if (source == null)
{
throw new IllegalArgumentException("source cannot be null.");
}
LinkPlan plan = new LinkPlan(source, null, false);
source.getOutboundLinks().add(plan);
return plan;
}
public RoomData source()
{
return this.source;
}
public RoomData destination()
{
return this.destination;
}
public boolean isEntrance()
{
return entrance;
}
public boolean isInternal()
{
return (destination != null);
}
public void remove()
{
if (source != null)
{
source.getOutboundLinks().remove(this);
source = null;
}
if (destination != null)
{
destination.getInboundLinks().remove(this);
destination = null;
}
}
}

View File

@@ -14,6 +14,8 @@ public class MazeBuilder
public static void generate(World world, int x, int y, int z, Random random)
{
// ISSUE FOR LATER: The room needs to be shifted so as to be centered on its entrance
MazeDesign design = MazeDesigner.generate(random);
Point3D offset = new Point3D(x - design.width() / 2, y - design.height() - 1, z - design.length() / 2);
SphereDecayOperation decay = new SphereDecayOperation(random, 0, 0, Block.stoneBrick.blockID, 2);
@@ -21,7 +23,7 @@ public class MazeBuilder
buildRooms(design.getLayout(), world, offset);
carveDoorways(design.getLayout(), world, offset, decay, random);
//placeDoors(design, world, offset);
placeDoors(design.getLayout(), world, offset);
applyRandomDestruction(design, world, offset, decay, random);
}
@@ -41,6 +43,22 @@ public class MazeBuilder
}
}
private static void placeDoors(DirectedGraph<RoomData, DoorwayData> layout, World world, Point3D offset)
{
for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
{
RoomData room = node.data();
Point3D minCorner = room.getPartitionNode().minCorner();
if (!room.getOutboundLinks().isEmpty())
{
setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 1,
offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0);
setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 2,
offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0);
}
}
}
private static void carveDoorways(DirectedGraph<RoomData, DoorwayData> layout, World world,
Point3D offset, SphereDecayOperation decay, Random random)
{

View File

@@ -1,7 +1,9 @@
package StevenDimDoors.experimental;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
@@ -48,6 +50,9 @@ public class MazeDesigner
pruneDoorways(core.getLayoutNode(), layout, random);
}
// Set up the placement of dimensional doors within the maze
createMazeLinks(layout, cores, random);
return new MazeDesign(root, layout);
}
@@ -64,28 +69,6 @@ public class MazeDesigner
}
}
private static void removeRoom(RoomData room, DirectedGraph<RoomData, DoorwayData> layout)
{
// Remove the room from the partition tree and from the layout graph.
// Also remove any ancestors that become leaf nodes.
PartitionNode parent;
PartitionNode current;
current = room.getPartitionNode();
while (current != null && current.isLeaf())
{
parent = current.parent();
current.remove();
current = parent;
}
// Remove the room from the layout graph
layout.removeNode(room.getLayoutNode());
// Wipe the room's data, as a precaution.
room.clear();
}
private static PartitionNode partitionRooms(int width, int height, int length, int maxLevels, Random random)
{
PartitionNode root = new PartitionNode(width, height, length);
@@ -468,7 +451,7 @@ public class MazeDesigner
// Remove all the rooms that were listed for removal
for (RoomData target : removals)
{
removeRoom(target, layout);
target.remove();
}
return cores;
}
@@ -529,7 +512,6 @@ public class MazeDesigner
// at outbound edges since inbound edges mirror them. List any Y_AXIS
// doorways we come across to consider removing them later, depending
// on their impact on connectedness.
// doorways.
ArrayList<IEdge<RoomData, DoorwayData>> targets =
new ArrayList<IEdge<RoomData, DoorwayData>>();
@@ -557,6 +539,7 @@ public class MazeDesigner
if (!components.mergeSets(passage.head(), passage.tail()))
{
layout.removeEdge(passage);
}
}
@@ -593,4 +576,101 @@ public class MazeDesigner
}
}
private static void createMazeLinks(DirectedGraph<RoomData, DoorwayData> layout,
ArrayList<RoomData> cores, Random random)
{
// We have 4 objectives here...
// 1. Place the entrance to the maze
// 2. Place internal links connecting the different sections of the maze
// 3. Place links to other dungeons
// 4. Place more internal links to confuse people
// We need to start by counting the door capacity of each section and
// listing which rooms can have doors or destinations for each section.
int index;
int[] capacity = new int[cores.size()];
ArrayList<RoomData>[] sourceRooms = (ArrayList<RoomData>[]) Array.newInstance(cores.getClass(), cores.size());
ArrayList<RoomData>[] destinationRooms = (ArrayList<RoomData>[]) Array.newInstance(cores.getClass(), cores.size());
for (index = 0; index < sourceRooms.length; index++)
{
sourceRooms[index] = new ArrayList<RoomData>();
destinationRooms[index] = new ArrayList<RoomData>();
capacity[index] = listLinkRooms(cores.get(index).getLayoutNode(), sourceRooms[index], destinationRooms[index]);
}
// Now we select the room in which to place the entrance.
// We can safely assume all source room lists are non-empty because
// createMazeSections() guarantees that each section has at least
// the capacity for 2 doors.
index = random.nextInt(sourceRooms.length);
createEntranceLink(sourceRooms[index], random.nextInt(sourceRooms[index].size()));
// The next task is to place internal links. These links must connect
// the different maze sections to create a strongly connected graph.
}
private static int listLinkRooms(IGraphNode<RoomData, DoorwayData> core,
ArrayList<RoomData> sourceRooms, ArrayList<RoomData> destinationRooms)
{
int capacity = 0;
boolean hasHoles;
RoomData currentRoom;
IGraphNode<RoomData, DoorwayData> current;
IGraphNode<RoomData, DoorwayData> neighbor;
Stack<IGraphNode<RoomData, DoorwayData>> ordering = new Stack<IGraphNode<RoomData, DoorwayData>>();
HashSet<IGraphNode<RoomData, DoorwayData>> visited = new HashSet<IGraphNode<RoomData, DoorwayData>>();
visited.add(core);
ordering.add(core);
while (!ordering.isEmpty())
{
current = ordering.pop();
hasHoles = false;
for (IEdge<RoomData, DoorwayData> edge : current.outbound())
{
neighbor = edge.tail();
if (visited.add(neighbor))
{
ordering.add(neighbor);
}
}
for (IEdge<RoomData, DoorwayData> edge : current.inbound())
{
neighbor = edge.head();
if (visited.add(neighbor))
{
ordering.add(neighbor);
}
if (edge.data().axis() == DoorwayData.Y_AXIS)
{
hasHoles = true;
}
}
if (!hasHoles)
{
currentRoom = current.data();
destinationRooms.add(currentRoom);
if (currentRoom.estimateDoorCapacity() > 0)
{
capacity += currentRoom.estimateDoorCapacity();
sourceRooms.add(currentRoom);
}
}
}
return capacity;
}
private static void createEntranceLink(ArrayList<RoomData> sources, int index)
{
RoomData entranceRoom = sources.get(index);
LinkPlan.createEntranceLink(entranceRoom);
if (entranceRoom.getRemainingDoorCapacity() == 0)
{
sources.remove(index);
}
}
}

View File

@@ -1,6 +0,0 @@
package StevenDimDoors.experimental;
public class MazeLinkData
{
}

View File

@@ -4,20 +4,32 @@ import java.util.ArrayList;
public class RoomData
{
/* Implementation Note:
* Plans for links between rooms are stored in lists rather than a graph,
* even though they duplicate some graph functionality, because there are
* relatively few of them compared to the number of rooms. Moreover, some
* links don't even have destinations because they're intended to lead to
* other dungeons.
*/
private int capacity;
private int distance;
private boolean decayed;
private PartitionNode partitionNode;
private ArrayList<MazeLinkData> inboundLinks;
private ArrayList<MazeLinkData> outboundLinks;
private ArrayList<LinkPlan> inboundLinks;
private ArrayList<LinkPlan> outboundLinks;
private DirectedGraph<RoomData, DoorwayData> layout;
private IGraphNode<RoomData, DoorwayData> layoutNode;
public RoomData(PartitionNode partitionNode)
{
this.partitionNode = partitionNode;
this.inboundLinks = new ArrayList<MazeLinkData>();
this.outboundLinks = new ArrayList<MazeLinkData>();
this.inboundLinks = new ArrayList<LinkPlan>();
this.outboundLinks = new ArrayList<LinkPlan>();
this.layoutNode = null;
this.layout = null;
this.distance = -1;
this.capacity = -1;
this.decayed = false;
partitionNode.setData(this);
}
@@ -34,6 +46,7 @@ public class RoomData
public void addToLayout(DirectedGraph<RoomData, DoorwayData> layout)
{
this.layout = layout;
this.layoutNode = layout.addNode(this);
}
@@ -47,12 +60,12 @@ public class RoomData
this.decayed = value;
}
public ArrayList<MazeLinkData> getInboundLinks()
public ArrayList<LinkPlan> getInboundLinks()
{
return this.inboundLinks;
}
public ArrayList<MazeLinkData> getOutboundLinks()
public ArrayList<LinkPlan> getOutboundLinks()
{
return this.outboundLinks;
}
@@ -67,16 +80,54 @@ public class RoomData
distance = value;
}
public void clear()
public void remove()
{
// Remove the room from the partition tree and from the layout graph.
// Also remove any ancestors that become leaf nodes.
PartitionNode parent;
PartitionNode current = partitionNode;
while (current != null && current.isLeaf())
{
parent = current.parent();
current.remove();
current = parent;
}
// Remove the room from the layout graph
layout.removeNode(layoutNode);
// Remove any links
while (!inboundLinks.isEmpty())
inboundLinks.get(inboundLinks.size() - 1).remove();
while (!outboundLinks.isEmpty())
outboundLinks.get(outboundLinks.size() - 1).remove();
// Wipe the room's data, as a precaution
layout = null;
partitionNode = null;
layoutNode = null;
inboundLinks = null;
outboundLinks = null;
}
public int estimateDoorCapacity()
{
if (capacity >= 0)
return capacity;
int cellsX = (partitionNode.width() - 3) / 2;
int cellsZ = (partitionNode.length() - 3) / 2;
return Math.min(cellsX * cellsZ, 3);
capacity = Math.min(cellsX * cellsZ, 3);
return capacity;
}
public int getRemainingDoorCapacity()
{
return (estimateDoorCapacity() - outboundLinks.size());
}
public boolean isProtected()
{
return !inboundLinks.isEmpty() || !outboundLinks.isEmpty();
}
}