Improved Doorway Placement

Changed MazeDesigner to prune out some unnecessary doorways at random by
removing edges from the room graph. Previous mazes allowed all side
paths to exist, which resulted in a kind of circular layout. Although
that was disorienting to people at first, if you realized it was a
circle, it became simple to navigate through the maze. This update makes
branching paths more common.
This commit is contained in:
SenseiKiwi
2013-12-30 03:21:42 -04:00
parent 3574b0468b
commit 70ae2fd407
2 changed files with 44 additions and 5 deletions

View File

@@ -126,4 +126,9 @@ public class DisjointSet<T>
return (firstRoot == secondRoot); return (firstRoot == secondRoot);
} }
} }
public void clear()
{
mapping.clear();
}
} }

View File

@@ -39,10 +39,10 @@ public class MazeDesigner
// Cut out random subgraphs from the adjacency graph // Cut out random subgraphs from the adjacency graph
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores = createMazeSections(rooms, random); ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores = createMazeSections(rooms, random);
// Remove unnecessary passages through floors/ceilings // Remove unnecessary passages through floors/ceilings and some from the walls
for (IGraphNode<PartitionNode, DoorwayData> core : cores) for (IGraphNode<PartitionNode, DoorwayData> core : cores)
{ {
minimizeVerticalPassages(core, rooms, random); pruneDoorways(core, rooms, random);
} }
return new MazeDesign(root, rooms, cores); return new MazeDesign(root, rooms, cores);
@@ -459,7 +459,7 @@ public class MazeDesigner
return cores; return cores;
} }
private static void minimizeVerticalPassages(IGraphNode<PartitionNode, DoorwayData> core, private static void pruneDoorways(IGraphNode<PartitionNode, DoorwayData> core,
DirectedGraph<PartitionNode, DoorwayData> rooms, Random random) DirectedGraph<PartitionNode, DoorwayData> rooms, Random random)
{ {
// We receive a node for one of the rooms in a section of the maze // We receive a node for one of the rooms in a section of the maze
@@ -467,13 +467,15 @@ public class MazeDesigner
// still allowing any room to be reachable from any other room. // still allowing any room to be reachable from any other room.
// In technical terms, we receive a node from a connected subgraph // In technical terms, we receive a node from a connected subgraph
// and we need to remove as many Y_AXIS-type edges as possible while // and we need to remove as many Y_AXIS-type edges as possible while
// preserving connectedness. // preserving connectedness. We also want to randomly remove some of
// the other doorways without breaking connectedness.
// An efficient solution is to assign nodes to disjoint sets based // An efficient solution is to assign nodes to disjoint sets based
// on their components, ignoring all Y_AXIS edges, then iterate over // on their components, ignoring all Y_AXIS edges, then iterate over
// a list of those edges and remove them if they connect two nodes // a list of those edges and remove them if they connect two nodes
// in the same set. Otherwise, merge their sets and keep the edge. // in the same set. Otherwise, merge their sets and keep the edge.
// This is similar to algorithms for spanning trees. // This is similar to algorithms for spanning trees. The same
// idea applies for the other doorways with some chance added.
// First, list all nodes in the subgraph // First, list all nodes in the subgraph
IGraphNode<PartitionNode, DoorwayData> current; IGraphNode<PartitionNode, DoorwayData> current;
@@ -540,6 +542,38 @@ public class MazeDesigner
rooms.removeEdge(passage); rooms.removeEdge(passage);
} }
} }
// Repeat the pruning process with X_AXIS and Z_AXIS doorways
// In this case, unnecessary edges might be kept at random
components.clear();
targets.clear();
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
{
components.makeSet(room);
}
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
{
for (IEdge<PartitionNode, DoorwayData> passage : room.outbound())
{
if (passage.data().axis() == DoorwayData.Y_AXIS)
{
components.mergeSets(passage.head(), passage.tail());
}
else
{
targets.add(passage);
}
}
}
Collections.shuffle(targets, random);
for (IEdge<PartitionNode, DoorwayData> passage : targets)
{
if (!components.mergeSets(passage.head(), passage.tail()) && random.nextBoolean())
{
rooms.removeEdge(passage);
}
}
} }
} }