001package squidpony.squidgrid.mapping; 002 003import squidpony.ArrayTools; 004import squidpony.squidmath.GWTRNG; 005import squidpony.squidmath.IRNG; 006 007/** 008 * Meant to produce the sort of narrow, looping, not-quite-maze-like passages found in a certain famous early arcade game. 009 * Created by Tommy Ettinger on 3/30/2016. 010 */ 011public class PacMazeGenerator { 012 public IRNG rng; 013 public int width, height; 014 private boolean[][] map; 015 private int[][] env; 016 private char[][] maze; 017 018 public PacMazeGenerator() { 019 this(250, 250); 020 } 021 022 public PacMazeGenerator(int width, int height) { 023 this.height = height; 024 this.width = width; 025 rng = new GWTRNG(); 026 } 027 028 public PacMazeGenerator(int width, int height, IRNG rng) { 029 this.height = height; 030 this.width = width; 031 this.rng = rng; 032 } 033 034 private static final byte[] //unbiased_connections = new byte[]{3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15}, 035 connections = new byte[]{ 036 3, 5, 6, 9, 10, 12,/* 037 3, 5, 6, 9, 10, 12, 038 3, 5, 6, 9, 10, 12, 039 3, 5, 6, 9, 10, 12, 040 7, 11, 13, 14, 041 7, 11, 13, 14, 042 15*/ 043 }; 044 private static final int connections_length = connections.length; 045 046 private boolean write(boolean[][] m, int x, int y, int xOffset, int yOffset, boolean value) { 047 int nx = x * 3 + xOffset + 1, ny = y * 3 + yOffset + 1; 048 if (nx >= 0 && nx < m.length && ny >= 0 && ny < m[nx].length) { 049 m[nx][ny] = value; 050 return true; 051 } 052 return false; 053 } 054 055 public boolean[][] create() { 056 map = new boolean[width][height]; 057 byte[][] conns = new byte[(width + 2) / 3][(height + 2) / 3]; 058 int xOff = (width % 3 == 1) ? -1 : 0, yOff = (height % 3 == 1) ? -1 : 0; 059 for (int x = 0; x < (width + 2) / 3; x++) { 060 for (int y = 0; y < (height + 2) / 3; y++) { 061 conns[x][y] = connections[rng.nextInt(connections_length)]; 062 } 063 } 064 for (int x = 0; x < (width + 2) / 3; x++) { 065 for (int y = 0; y < (height + 2) / 3; y++) { 066 write(map, x, y, xOff, yOff, true); 067 if (x > 0 && ((conns[x - 1][y] & 1) != 0 || (conns[x][y] & 2) != 0)) { 068 conns[x - 1][y] |= 1; 069 conns[x][y] |= 2; 070 } 071 if (x < conns.length - 1 && ((conns[x + 1][y] & 2) != 0 || (conns[x][y] & 1) != 0)) { 072 conns[x + 1][y] |= 2; 073 conns[x][y] |= 1; 074 } 075 if (y > 0 && ((conns[x][y - 1] & 4) != 0 || (conns[x][y] & 8) != 0)) { 076 conns[x][y - 1] |= 4; 077 conns[x][y] |= 8; 078 } 079 if (y < conns[0].length - 1 && ((conns[x][y + 1] & 8) != 0 || (conns[x][y] & 4) != 0)) { 080 conns[x][y + 1] |= 8; 081 conns[x][y] |= 4; 082 } 083 } 084 } 085 086 for (int x = 1; x < (width - 1) / 3; x++) { 087 for (int y = 1; y < (height - 1) / 3; y++) { 088 if (Integer.bitCount(conns[x][y]) >= 4) { 089 //byte temp = connections[rng.nextInt(connections_length)]; 090 int temp = 1 << rng.nextInt(4); 091 conns[x][y] ^= temp; 092 if ((temp & 2) != 0) conns[x - 1][y] ^= 1; 093 else if ((temp & 1) != 0) conns[x + 1][y] ^= 2; 094 else if ((temp & 8) != 0) conns[x][y - 1] ^= 4; 095 else if ((temp & 4) != 0) conns[x][y + 1] ^= 8; 096 } 097 } 098 } 099 for (int x = 0; x < (width + 2) / 3; x++) { 100 for (int y = 0; y < (height + 2) / 3; y++) { 101 write(map, x, y, xOff, yOff, true); 102 if (x > 0 && (conns[x][y] & 2) != 0) 103 write(map, x, y, xOff - 1, yOff, true); 104 if (x < conns.length - 1 && (conns[x][y] & 1) != 0) 105 write(map, x, y, xOff + 1, yOff, true); 106 if (y > 0 && (conns[x][y] & 8) != 0) 107 write(map, x, y, xOff, yOff - 1, true); 108 if (y < conns[0].length - 1 && (conns[x][y] & 4) != 0) 109 write(map, x, y, xOff, yOff + 1, true); 110 } 111 } 112 int upperY = height - 1; 113 int upperX = width - 1; 114 for (int i = 0; i < width; i++) { 115 map[i][0] = false; 116 map[i][upperY] = false; 117 } 118 for (int i = 0; i < height; i++) { 119 map[0][i] = false; 120 map[upperX][i] = false; 121 } 122 return map; 123 } 124 125 public char[][] generate() { 126 create(); 127 maze = new char[width][height]; 128 env = new int[width][height]; 129 for (int x = 0; x < width; x++) { 130 for (int y = 0; y < height; y++) { 131 maze[x][y] = map[x][y] ? '.' : '#'; 132 env[x][y] = map[x][y] ? DungeonUtility.CORRIDOR_FLOOR : DungeonUtility.CORRIDOR_WALL; 133 } 134 } 135 136 return maze; 137 } 138 139 public int[][] getEnvironment() { 140 if (env == null) 141 return ArrayTools.fill(DungeonUtility.CORRIDOR_WALL, width, height); 142 return env; 143 } 144 145 /** 146 * Gets the maze as a 2D array of true for passable or false for blocked. 147 * 148 * @return a 2D boolean array; true is passable and false is not. 149 */ 150 public boolean[][] getMap() { 151 if (map == null) 152 return new boolean[width][height]; 153 return map; 154 } 155 156 /** 157 * Gets the maze as a 2D array of ',' for passable or '#' for blocked. 158 * 159 * @return a 2D char array; '.' is passable and '#' is not. 160 */ 161 public char[][] getMaze() { 162 if (maze == null) 163 return ArrayTools.fill('#', width, height); 164 return maze; 165 } 166 167 /** 168 * Gets the maze as a 2D array of ',' for passable or '#' for blocked. 169 * 170 * @return a 2D char array; '.' is passable and '#' is not. 171 */ 172 public char[][] getDungeon() { 173 return getMaze(); 174 } 175}