I'm sketching the grid on the user interface using the width and height that I specified when I created the GridPanel, accordingly. For representational purposes, I am laying the grid starting at 30, 30, and ending at x - 60, y - 60. Depending on the grid dimensions, dynamic shrink and grow are implemented using scaleX and scaleY.
The image below displays the internals of the cell object.
GenerateMaze is a recursive backtracking algorithm used to generate perfect mazes. (Surely not complete, but I still should be able to see the result.)
public class GridPanel extends JPanel {
private class Cell {
private int y, x;
private int[] south;
private int[] north;
private int[] west;
private int[] east;
private Cell(int y, int x) {
this.y = y;
this.x = x;
this.south = new int[4];
this.north = new int[4];
this.west = new int[4];
this.east = new int[4];
}
}
private int w, h;
private final int scaleX, scaleY;
private final int Vx, Vy;
private final Cell[][] grid;
private boolean[][] discovered;
public GridPanel(int w, int h) {
setLayout(new GridLayout(1, 1));
this.w = w;
this.h = h;
scaleX = (int) (w / Math.sqrt(w));
scaleY = (int) (h / Math.sqrt(h));
Vx = w / scaleX;
Vy = w / scaleY;
discovered = new boolean[Vy][Vx];
grid = new Cell[Vy][Vx];
init(grid);
}
private void init(Cell[][] grid) {
for (int j = 0; j < grid.length; j++) {
for (int i = 0; i < grid[0].length; i++) {
grid[j][i] = new Cell(j, i);
}
}
}
@Override
protected void paintComponent(Graphics g) {
setDoubleBuffered(true);
g.setColor(Color.CYAN);
for (int y = 30, j = 0; y < h - 60; y += scaleY, j++) {
for (int x = 30, i = 0; x < w - 60; x += scaleX, i++) {
Cell cell = grid[j][i];
cell.east = new int[]{x, y, x, y + scaleY};
cell.west = new int[]{x + scaleX, y, x + scaleX, y + scaleY};
cell.north = new int[]{x, y, x + scaleX, y};
cell.south = new int[]{x, y + scaleY, x + scaleX, y + scaleY};
g.drawLine(cell.east[0], cell.east[1], cell.east[2], cell.east[3]);
g.drawLine(cell.west[0], cell.west[1], cell.west[2], cell.west[3]);
g.drawLine(cell.south[0], cell.south[1], cell.south[2], cell.south[3]);
g.drawLine(cell.north[0], cell.north[1], cell.north[2], cell.north[3]);
}
}
generateMaze(g, 0, 0);
}
private void generateMaze(Graphics g, int y, int x) {
discovered[y][x] = true;
while (true) {
Cell current = grid[y][x];
if (validUp(y) && !discovered[y - 1][x]) {
removeWall(g, current.north);
generateMaze(g, y - 1, x);
}
if (validDown(y) && !discovered[y + 1][x]) {
removeWall(g, current.south);
generateMaze(g, y + 1, x);
}
if (validRight(x) && !discovered[y][x + 1]) {
removeWall(g, current.east);
generateMaze(g, y, x + 1);
}
if (validLeft(x) && !discovered[y][x - 1]) {
removeWall(g, current.west);
generateMaze(g, y, x - 1);
}
// All neighbors have been visited, break the loop
break;
}
}
private boolean validLeft(int x) {
if (x - 1 < 0) return false;
return true;
}
private boolean validRight(int x) {
if (x + 1 < grid[0].length) return true;
return false;
}
private void removeWall(Graphics g, int[] coordinate) {
SwingUtilities.invokeLater(() -> {
g.setColor(Color.BLACK);
g.drawLine(coordinate[0], coordinate[1], coordinate[2], coordinate[3]);
});
}
private boolean validDown(int y) {
if (y + 1 < grid.length) return true;
return false;
}
private boolean validUp(int y) {
if (y - 1 < 0) return false;
return true;
}
}
° Application Frame:
public class Maze extends JFrame {
private int w, h;
public Maze(int w, int h) throws HeadlessException {
super("Perfect Maze");
setResizable(false);
setBackground(Color.BLACK);
this.w = w;
this.h = h;
add(new GridPanel(w, h));
setSize(w, h);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
}
° Runner:
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Maze(800, 800);
});
}
}
Given that the user interface appears and the application closes successfully, I believe the removeWall function is the cause of the issue.
It did not work when I attempted to add method: repaint() at the end of the method.
BufferedImage
. You can callgetGraphics
then modify the image with that graphics. Then you don't even need a paintComponent, just use a JLable with an ImageIcon.setDoubleBuffered(true);
is pointless,JPanel
is already double buffered, you should also be callingsuper.paintComponent
first before doing any custom paintingBufferedImage
, but you might have issues if you want it to resize dynamically