import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.swing.JFrame; import javax.swing.JPanel; import org.joml.Vector2i; import javafx.util.Pair; public class LD41Map { static final int TUNNEL_THICKNESS = 4; public static class Vertex { public final Room r; public float value; public Vertex(Room r) { this.r = r; value = Float.POSITIVE_INFINITY; } } public static class Edge { public final Vertex r1, r2; public double dist; public Edge(Vertex r1, Vertex r2) { this.r1 = r1; this.r2 = r2; dist = r1.r.distance(r2.r); } } public static class Room { Rectangle bounds = new Rectangle(); Map tiles = new HashMap<>(); Set doorsUp = new HashSet<>(); Set doorsDown = new HashSet<>(); Set doorsLeft = new HashSet<>(); Set doorsRight = new HashSet<>(); public float distance(Room r) { return Math.abs(getCenter().x - r.getCenter().x) + Math.abs(getCenter().y - r.getCenter().y); } public Point getCenter() { return new Point((int) bounds.getCenterX(), (int) bounds.getCenterY()); } public Set allDoors() { Set ret = new HashSet<>(); ret.addAll(doorsUp); ret.addAll(doorsDown); ret.addAll(doorsLeft); ret.addAll(doorsRight); return ret; } } public static enum TileType { GROUND, WALL, DOOR; } public static void main(String[] args) { Set rooms = new HashSet<>(); rooms.addAll(generate().getKey()); { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(3); JPanel panel = new JPanel() { private static final long serialVersionUID = 5954622107064881267L; @Override public void paintComponent(Graphics h) { super.paintComponent(h); Pair, Pair>> graph = generate(); Graphics2D g = (Graphics2D) h; g.translate(getWidth() / 2, getHeight() / 2); g.scale(4, 4); g.setColor(new Color(1f - (float) Math.random() * 0.3f, 0f, 0f, 0.8f)); for (Room room : graph.getKey()) { room.tiles.forEach((v, type) -> { if (type == TileType.GROUND) g.fillRect(v.x, v.y, 1, 1); }); } g.setColor(new Color(0f, 0f, 1f, 0.8f)); for (Room room : graph.getKey()) { room.tiles.forEach((v, type) -> { if (type == TileType.WALL) g.fillRect(v.x, v.y, 1, 1); }); } g.setColor(new Color(0f, 1f, 0f, 0.8f)); for (Room room : graph.getKey()) { room.tiles.forEach((v, type) -> { if (type == TileType.DOOR) g.fillRect(v.x, v.y, 1, 1); }); } } }; frame.add(panel); frame.setPreferredSize(new Dimension(800, 500)); frame.pack(); frame.setVisible(true); frame.repaint(); frame.setExtendedState(6); frame.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { rooms.clear(); Pair, Pair>> graph = generate(); rooms.addAll(graph.getKey()); frame.repaint(); } @Override public void keyReleased(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { } }); } } public static Pair, Pair>> generate() { int minRoomSize = 50; Set rooms = new HashSet<>(); for (int i = 0; i < 7 + (int) (Math.random() * 4); i++) { Room room = new Room(); room.bounds.width = ((15 + (int) (Math.random() * 20)) / 2) * 2; room.bounds.height = ((15 + (int) (Math.random() * 20)) / 2) * 2; rooms.add(room); } outest: while (true) { for (Room r1 : rooms) { for (Room r2 : rooms) { if (r1 == r2) continue; Vector2i p1 = new Vector2i(r1.bounds.x + r1.bounds.width / 2, r1.bounds.y + r1.bounds.height / 2); Vector2i p2 = new Vector2i(r2.bounds.x + r2.bounds.width / 2, r2.bounds.y + r2.bounds.height / 2); if (p1.distanceSquared(p2) < 2 * minRoomSize * minRoomSize + 2) { r2.bounds.x += (int) ((Math.random() - 0.5) * 5); r2.bounds.y += (int) ((Math.random() - 0.5) * 2.5); continue outest; } } } break; } Set Q = new HashSet<>(); for (Room r : rooms) Q.add(new Vertex(r)); Vertex root = Q.stream().min(Comparator.comparing(v -> v.r.bounds.x)).get(); root.value = 0; Set E = new HashSet<>(); Set G = new HashSet<>(); Set F = new HashSet<>(); for (Vertex r1 : Q) { outer: for (Vertex r2 : Q) { if (r1 == r2) continue outer; for (Edge e : E) if (e.r2 == r1 && e.r1 == r2) continue outer; E.add(new Edge(r1, r2)); } } F.add(root); Q.remove(root); while (!Q.isEmpty()) { Edge start = E.stream() .filter(e -> (F.contains(e.r1) ^ F.contains(e.r2))) .min(Comparator.comparing(e -> e.dist)).get(); Q.remove(start.r2); Q.remove(start.r1); F.add(start.r2); F.add(start.r1); E.remove(start); G.add(start); if (start.r1.value < start.r2.value) { start.r2.value = (float) (start.r1.value + start.dist); } else { start.r1.value = (float) (start.r2.value + start.dist); } } // G list of edges // rooms list of rooms Set rooms2 = new HashSet<>(); for (Edge ed : G) { // horizontal float diff1 = ed.r1.r.bounds.y - ed.r2.r.bounds.y - ed.r2.r.bounds.height + TUNNEL_THICKNESS; float diff2 = ed.r2.r.bounds.y - ed.r1.r.bounds.y - ed.r1.r.bounds.height + TUNNEL_THICKNESS; // vertical float diff3 = ed.r1.r.bounds.x - ed.r2.r.bounds.x - ed.r2.r.bounds.width + TUNNEL_THICKNESS; float diff4 = ed.r2.r.bounds.x - ed.r1.r.bounds.x - ed.r1.r.bounds.width + TUNNEL_THICKNESS; if (diff1 < 0 && diff2 < 0) { addStraightHorizontal(rooms2, ed); } else if (diff3 < 0 && diff4 < 0) { addStraightVertical(rooms2, ed); } else addCurve(rooms2, ed); } Room path = new Room(); for (Room r : rooms2) { for (int x1 = r.bounds.x; x1 < r.bounds.x + r.bounds.width; x1++) for (int y1 = r.bounds.y; y1 < r.bounds.y + r.bounds.height; y1++) { path.tiles.put(new Vector2i(x1, y1), TileType.GROUND); for (int x2 = x1 - 1; x2 <= x1 + 1; x2++) for (int y2 = y1 - 1; y2 <= y1 + 1; y2++) { path.tiles.putIfAbsent(new Vector2i(x2, y2), TileType.WALL); } } for (Vector2i v : r.allDoors()) r.tiles.put(v, TileType.DOOR); } for (Room r : rooms) { for (int x1 = r.bounds.x; x1 < r.bounds.x + r.bounds.width; x1++) for (int y1 = r.bounds.y; y1 < r.bounds.y + r.bounds.height; y1++) { r.tiles.put(new Vector2i(x1, y1), TileType.WALL); } for (int x1 = r.bounds.x + 1; x1 < r.bounds.x + r.bounds.width - 1; x1++) for (int y1 = r.bounds.y + 1; y1 < r.bounds.y + r.bounds.height - 1; y1++) { r.tiles.put(new Vector2i(x1, y1), TileType.GROUND); } for (Vector2i v : r.allDoors()) r.tiles.put(v, TileType.DOOR); } rooms.add(path); return new Pair<>(rooms, new Pair<>(root, G)); } public static void addStraightHorizontal(Set rooms, Edge ed) { Room righter = ed.r1.r.getCenter().x > ed.r2.r.getCenter().x ? ed.r1.r : ed.r2.r; Room lefter = ed.r1.r.getCenter().x > ed.r2.r.getCenter().x ? ed.r2.r : ed.r1.r; Room tunnel = new Room(); int minX = Math.min(ed.r1.r.bounds.x + ed.r1.r.bounds.width, ed.r2.r.bounds.x + ed.r2.r.bounds.width); int minY = Math.max(ed.r1.r.bounds.y, ed.r2.r.bounds.y); int maxX = Math.max(ed.r1.r.bounds.x, ed.r2.r.bounds.x); int maxY = Math.min(ed.r1.r.bounds.y + ed.r1.r.bounds.height, ed.r2.r.bounds.y + ed.r2.r.bounds.height); tunnel.bounds.x = minX; tunnel.bounds.y = (minY + maxY) / 2 - TUNNEL_THICKNESS / 2; tunnel.bounds.width = (maxX - minX); tunnel.bounds.height = TUNNEL_THICKNESS; rooms.add(tunnel); for (int i = 0; i < TUNNEL_THICKNESS; i++) { lefter.doorsRight.add(new Vector2i(tunnel.bounds.x - 1, tunnel.bounds.y + i)); righter.doorsLeft.add(new Vector2i(tunnel.bounds.x + tunnel.bounds.width, tunnel.bounds.y + i)); } } public static void addStraightVertical(Set rooms, Edge ed) { Room higher = ed.r1.r.getCenter().y > ed.r2.r.getCenter().y ? ed.r1.r : ed.r2.r; Room lower = ed.r1.r.getCenter().y > ed.r2.r.getCenter().y ? ed.r2.r : ed.r1.r; Room tunnel = new Room(); int minX = Math.max(ed.r1.r.bounds.x, ed.r2.r.bounds.x); int minY = Math.min(ed.r1.r.bounds.y + ed.r1.r.bounds.height, ed.r2.r.bounds.y + ed.r2.r.bounds.height); int maxX = Math.min(ed.r1.r.bounds.x + ed.r1.r.bounds.width, ed.r2.r.bounds.x + ed.r2.r.bounds.width); int maxY = Math.max(ed.r1.r.bounds.y, ed.r2.r.bounds.y); tunnel.bounds.x = (minX + maxX) / 2 - TUNNEL_THICKNESS / 2; tunnel.bounds.y = minY; tunnel.bounds.width = TUNNEL_THICKNESS; tunnel.bounds.height = (maxY - minY); rooms.add(tunnel); for (int i = 0; i < TUNNEL_THICKNESS; i++) { lower.doorsUp.add(new Vector2i(tunnel.bounds.x + i, tunnel.bounds.y + tunnel.bounds.height)); higher.doorsDown.add(new Vector2i(tunnel.bounds.x + i, tunnel.bounds.y - 1)); } } public static void addCurve(Set rooms, Edge ed) { Room higher = ed.r1.r.getCenter().y > ed.r2.r.getCenter().y ? ed.r1.r : ed.r2.r; Room lower = ed.r1.r.getCenter().y > ed.r2.r.getCenter().y ? ed.r2.r : ed.r1.r; Room righter = ed.r1.r.getCenter().x > ed.r2.r.getCenter().x ? ed.r1.r : ed.r2.r; Room lefter = ed.r1.r.getCenter().x > ed.r2.r.getCenter().x ? ed.r2.r : ed.r1.r; Rectangle r = new Rectangle(lefter.getCenter().x, lower.getCenter().y, righter.getCenter().x - lefter.getCenter().x, higher.getCenter().y - lower.getCenter().y); Room verticalLefter = new Room(); verticalLefter.bounds.x = r.x - TUNNEL_THICKNESS / 2; verticalLefter.bounds.y = r.y - TUNNEL_THICKNESS / 2; verticalLefter.bounds.width = TUNNEL_THICKNESS; verticalLefter.bounds.height = r.height + TUNNEL_THICKNESS; Room horizontalLower = new Room(); horizontalLower.bounds.x = r.x - TUNNEL_THICKNESS / 2; horizontalLower.bounds.y = r.y - TUNNEL_THICKNESS / 2; horizontalLower.bounds.width = r.width + TUNNEL_THICKNESS; horizontalLower.bounds.height = TUNNEL_THICKNESS; Room verticalRighter = new Room(); verticalRighter.bounds.x = r.x + r.width - TUNNEL_THICKNESS / 2; verticalRighter.bounds.y = r.y - TUNNEL_THICKNESS / 2; verticalRighter.bounds.width = TUNNEL_THICKNESS; verticalRighter.bounds.height = r.height + TUNNEL_THICKNESS; Room horizontalHigher = new Room(); horizontalHigher.bounds.x = r.x - TUNNEL_THICKNESS / 2; horizontalHigher.bounds.y = r.y + r.height - TUNNEL_THICKNESS / 2; horizontalHigher.bounds.width = r.width + TUNNEL_THICKNESS; horizontalHigher.bounds.height = TUNNEL_THICKNESS; if (lower == lefter) { horizontalLower.bounds.x = r.x + lower.bounds.width / 2; horizontalLower.bounds.width = r.width - lower.bounds.width / 2 + TUNNEL_THICKNESS / 2; horizontalHigher.bounds.width = r.width - higher.bounds.width / 2 + TUNNEL_THICKNESS / 2; verticalLefter.bounds.y = r.y + lower.bounds.height / 2; verticalLefter.bounds.height = r.height - lower.bounds.height / 2 + TUNNEL_THICKNESS / 2; verticalRighter.bounds.height = r.height - higher.bounds.height / 2 + TUNNEL_THICKNESS / 2; } if (lower == righter) { horizontalHigher.bounds.x = r.x + higher.bounds.width / 2; horizontalHigher.bounds.width = r.width - higher.bounds.width / 2 + TUNNEL_THICKNESS / 2; horizontalLower.bounds.width = r.width - lower.bounds.width / 2 + TUNNEL_THICKNESS / 2; verticalRighter.bounds.y = r.y + lower.bounds.height / 2; verticalRighter.bounds.height = r.height - lower.bounds.height / 2 + TUNNEL_THICKNESS / 2; verticalLefter.bounds.height = r.height - higher.bounds.height / 2 + TUNNEL_THICKNESS / 2; } boolean flip = Math.random() > 0.5; boolean diffX = ed.r2.r.getCenter().x - ed.r1.r.getCenter().x > 0; boolean diffY = ed.r2.r.getCenter().y - ed.r1.r.getCenter().y > 0; boolean addHorizontal1 = false, addHorizontal2 = false, addVertical1 = false, addVertical2 = false; if (diffX && diffY) { if (flip) { addVertical1 = true; addHorizontal2 = true; } else { addVertical2 = true; addHorizontal1 = true; } } else if (diffX && !diffY) { if (flip) { addVertical2 = true; addHorizontal2 = true; } else { addVertical1 = true; addHorizontal1 = true; } } else if (!diffX && diffY) { if (flip) { addVertical1 = true; addHorizontal1 = true; } else { addVertical2 = true; addHorizontal2 = true; } } else if (!diffX && !diffY) { if (flip) { addVertical2 = true; addHorizontal1 = true; } else { addVertical1 = true; addHorizontal2 = true; } } if (addHorizontal1) { rooms.add(horizontalLower); if (lower == lefter) for (int i = 0; i < TUNNEL_THICKNESS; i++) { lower.doorsRight.add(new Vector2i(horizontalLower.bounds.x - 1, horizontalLower.bounds.y + i)); } else for (int i = 0; i < TUNNEL_THICKNESS; i++) { lower.doorsLeft.add(new Vector2i(horizontalLower.bounds.x + horizontalLower.bounds.width, horizontalLower.bounds.y + i)); } } if (addHorizontal2) { rooms.add(horizontalHigher); if (lower == righter) for (int i = 0; i < TUNNEL_THICKNESS; i++) { higher.doorsRight.add(new Vector2i(horizontalHigher.bounds.x - 1, horizontalHigher.bounds.y + i)); } else for (int i = 0; i < TUNNEL_THICKNESS; i++) { higher.doorsLeft.add(new Vector2i(horizontalHigher.bounds.x + horizontalHigher.bounds.width, horizontalHigher.bounds.y + i)); } } if (addVertical1) { rooms.add(verticalLefter); if (lower == lefter) for (int i = 0; i < TUNNEL_THICKNESS; i++) { lower.doorsDown.add(new Vector2i(verticalLefter.bounds.x + i, verticalLefter.bounds.y - 1)); } else for (int i = 0; i < TUNNEL_THICKNESS; i++) { lower.doorsUp.add(new Vector2i(verticalLefter.bounds.x + i, verticalLefter.bounds.y + verticalLefter.bounds.height)); } } if (addVertical2) { rooms.add(verticalRighter); if (lower == righter) for (int i = 0; i < TUNNEL_THICKNESS; i++) { higher.doorsDown.add(new Vector2i(verticalRighter.bounds.x + i, verticalRighter.bounds.y - 1)); } else for (int i = 0; i < TUNNEL_THICKNESS; i++) { higher.doorsUp.add(new Vector2i(verticalRighter.bounds.x + i, verticalRighter.bounds.y + verticalRighter.bounds.height)); } } } }