package squidpony.squidgrid.mapping; import squidpony.squidgrid.Direction; import squidpony.squidmath.RNG; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; /** * Recursively divided maze. Creates only walls and passages. * * This dungeon generator is based on a port of the rot.js version. * * @author Eben Howard - http://squidpony.com - howard@squidpony.com */ public class DividedMazeGenerator { private class DividedMazeRoom { private int left, top, right, bottom; public DividedMazeRoom(int left, int top, int right, int bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } } private int width, height; private boolean[][] map; private RNG rng; /** * Sets up the generator to make mazes the given width and height. The mazes * have a solid wall border. * * @param width * @param height */ public DividedMazeGenerator(int width, int height) { this.width = width; this.height = height; rng = new RNG(); } /** * Sets up the generator to make mazes the given width and height. The mazes * have a solid wall border. * * @param width in cells * @param height in cells * @param rng the random number generator to use */ public DividedMazeGenerator(int width, int height, RNG rng) { this.width = width; this.height = height; this.rng = rng; } /** * Builds a maze. True values represent walls. * * @return */ public boolean[][] create() { map = new boolean[width][height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { map[x][y] = x == 0 || y == 0 || x + 1 == width || y + 1 == height; } } process(); return map; } private void process() { LinkedList<DividedMazeRoom> stack = new LinkedList<>(); stack.offer(new DividedMazeRoom(1, 1, width - 2, height - 2)); while (!stack.isEmpty()) { DividedMazeRoom room = stack.removeFirst(); ArrayList<Integer> availX = new ArrayList<>(), availY = new ArrayList<>(); for (int x = room.left + 1; x < room.right; x++) { boolean top = map[x][room.top - 1]; boolean bottom = map[x][room.bottom + 1]; if (top && bottom && x % 2 == 0) { availX.add(x); } } for (int y = room.top + 1; y < room.bottom; y++) { boolean left = map[room.left - 1][y]; boolean right = map[room.right + 1][y]; if (left && right && y % 2 == 0) { availY.add(y); } } if (availX.isEmpty() || availY.isEmpty()) { continue; } int x2 = rng.getRandomElement(availX); int y2 = rng.getRandomElement(availY); map[x2][y2] = true; for (Direction dir : Direction.CARDINALS) { switch (dir) { case LEFT: for (int x = room.left; x < x2; x++) { map[x][y2] = true; } break; case RIGHT: for (int x = x2 + 1; x <= room.right; x++) { map[x][y2] = true; } break; case UP: for (int y = room.top; y < y2; y++) { map[x2][y] = true; } break; case DOWN: for (int y = y2 + 1; y <= room.bottom; y++) { map[x2][y] = true; } break; case NONE: break; case DOWN_LEFT: case DOWN_RIGHT: case UP_LEFT: case UP_RIGHT: throw new IllegalStateException("There should only be cardinal directions here"); } } List<Direction> dirs = Arrays.asList(Direction.CARDINALS); dirs.remove(rng.getRandomElement(dirs)); for (Direction dir : dirs) { switch (dir) { case LEFT: map[rng.between(room.left, x2)][y2] = false; break; case RIGHT: map[rng.between(x2 + 1, room.right + 1)][y2] = false; break; case UP: map[x2][rng.between(room.top, y2)] = false; break; case DOWN: map[x2][rng.between(y2 + 1, room.bottom + 1)] = false; break; case NONE: break; case DOWN_LEFT: case DOWN_RIGHT: case UP_LEFT: case UP_RIGHT: throw new IllegalStateException("There should only be cardinal directions here"); } } stack.offer(new DividedMazeRoom(room.left, room.top, x2 - 1, y2 - 1)); stack.offer(new DividedMazeRoom(x2 + 1, room.top, room.right, y2 - 1)); stack.offer(new DividedMazeRoom(room.left, y2 + 1, x2 - 1, room.bottom)); stack.offer(new DividedMazeRoom(x2 + 1, y2 + 1, room.right, room.bottom)); } } }