Java
I never complained about GolfScript or CJam, but here's a Java answer for you anyway. This was a really enjoyable challenge. ;)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.geom.Ellipse2D;
import java.util.*;
import javax.swing.*;
public class MazeMaker {
int row, col, exitRow, exitCol, numCells, score, array[][];
final int SQWIDTH = 20;
boolean gameOver = true;
Ellipse2D.Double ellipse;
JFrame frame;
JPanel mazePanel;
JLabel scoreLabel;
public MazeMaker() {
frame = new JFrame("Maze");
frame.setLayout(new BorderLayout());
JPanel topPanel = createTopPanel();
frame.add(topPanel,BorderLayout.NORTH);
createMazePanel();
frame.add(new JScrollPane(mazePanel),BorderLayout.CENTER);
setKeyActions();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private void constructArray(int seed, int rows, int cols) {
array = new int[rows*2-1][cols*2-1];
for (int[] a : array)
Arrays.fill(a,-1);
numCells = (array.length / 2 + 1) * (array[0].length / 2 + 1);
Random rand = new Random(seed);
int row = rand.nextInt(array.length / 2 + 1) * 2;
int col = rand.nextInt(array[0].length / 2 + 1) * 2;
array[row][col] = 0;
boolean first = true, a = false, exitFound = false;
while (true) {
if (first) {
int direction = rand.nextInt(4);
if (direction == 0 && row != 0) {
array[row-1][col] = 0;
array[row-2][col] = 0;
row -= 2;
first = false;
}
else if (direction == 1 && col != array[0].length - 1) {
array[row][col+1] = 0;
array[row][col+2] = 0;
col += 2;
first = false;
}
else if (direction == 2 && row != array.length - 1) {
array[row+1][col] = 0;
array[row+2][col] = 0;
row += 2;
first = false;
}
else if (direction == 3 && col != 0) {
array[row][col-1] = 0;
array[row][col-2] = 0;
col -= 2;
first = false;
}
}
else {
int availableCells = 0;
boolean up = false, down = false, left = false, right = false;
if (row != 0 && array[row-2][col] == -1) {
availableCells++;
up = true;
}
if (col != array[0].length-1 && array[row][col+2] == -1) {
availableCells++;
right = true;
}
if (row != array.length-1 && array[row+2][col] == -1) {
availableCells++;
down = true;
}
if (col != 0 && array[row][col-2] == -1) {
availableCells++;
left = true;
}
if (availableCells != 0) {
a = true;
while (true) {
boolean[] b = {up,right,down,left};
int i = rand.nextInt(4);
if (b[i]) {
if (i == 0) {
array[row-1][col] = 0;
array[row-2][col] = 0;
row -= 2;
}
else if (i == 1) {
array[row][col+1] = 0;
array[row][col+2] = 0;
col += 2;
}
else if (i == 2) {
array[row+1][col] = 0;
array[row+2][col] = 0;
row += 2;
}
else if (i == 3) {
array[row][col-1] = 0;
array[row][col-2] = 0;
col -= 2;
}
break;
}
}
}
else {
array[row][col] = 1;
if (!exitFound && a) {
if (new Random().nextInt(5) == 0) {
exitFound = true;
exitRow = row;
exitCol = col;
}
}
a = false;
if (row != 0 && array[row-1][col] == 0 && (array[row-2][col] == 0 || array[row-2][col] == 1)) {
array[row-1][col] = 1;
array[row-2][col] = 1;
row -= 2;
}
else if (col != array[0].length-1 && array[row][col+1] == 0 && (array[row][col+2] == 0 || array[row][col+2] == 1)) {
array[row][col+1] = 1;
array[row][col+2] = 1;
col += 2;
}
else if (row != array.length-1 && array[row+1][col] == 0 && (array[row+2][col] == 0 || array[row+2][col] == 1)) {
array[row+1][col] = 1;
array[row+2][col] = 1;
row += 2;
}
else if (col != 0 && array[row][col-1] == 0 && (array[row][col-2] == 0 || array[row][col-2] == 1)) {
array[row][col-1] = 1;
array[row][col-2] = 1;
col -= 2;
}
}
if (checkDone()) {
if (!exitFound) {
exitRow = row;
exitCol = col;
}
break;
}
}
}
}
private boolean checkDone() {
int count = 0;
for (int k = 0; k < array.length; k+=2) {
for (int l = 0; l < array[0].length; l+=2) {
if (array[k][l] == 0 || array[k][l] == 1)
count++;
}
}
return count == numCells;
}
private JPanel createTopPanel() {
GridBagLayout l = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
JPanel panel = new JPanel(l);
JLabel inputLabel = new JLabel("ID:");
c.gridwidth = 1;
l.setConstraints(inputLabel,c);
panel.add(inputLabel);
JTextField inputField = new JTextField(5);
l.setConstraints(inputField,c);
panel.add(inputField);
JLabel rowLabel = new JLabel("Rows:");
l.setConstraints(rowLabel,c);
panel.add(rowLabel);
JTextField rowField = new JTextField(3);
l.setConstraints(rowField,c);
panel.add(rowField);
JLabel colLabel = new JLabel("Columns:");
l.setConstraints(colLabel,c);
panel.add(colLabel);
JTextField colField = new JTextField(3);
l.setConstraints(colField,c);
panel.add(colField);
JButton applyButton = new JButton("Apply");
applyButton.addActionListener(ev -> {
try {
int seed = Integer.parseInt(inputField.getText()),
rows = Integer.parseInt(rowField.getText()),
cols = Integer.parseInt(colField.getText());
if (seed >= 0 && rows >= 3 && cols >= 3) {
gameOver = false;
scoreLabel.setText("Score: " + (score = 0));
constructArray(seed,rows,cols);
row = (int) (Math.random() * (array.length / 2 + 1)) * 2;
col = (int) (Math.random() * (array[0].length / 2 + 1)) * 2;
frame.setSize((1+SQWIDTH * array[0].length)/2 > 750 ? (1+SQWIDTH * array[0].length)/2 : 750,
75+(1+SQWIDTH * array.length)/2);
mazePanel.setPreferredSize(new Dimension(
(1+SQWIDTH * array[0].length)/2 > 750 ? (1+SQWIDTH * array[0].length)/2 - 15 : 750,
15+(1+SQWIDTH * array.length)/2));
ellipse = new Ellipse2D.Double(col*SQWIDTH/2+3,row*SQWIDTH/2+3,10,10);
setItems();
mazePanel.repaint();
}
} catch (NumberFormatException ignore) {}
});
l.setConstraints(applyButton,c);
panel.add(applyButton);
scoreLabel = new JLabel("Score: ");
c.gridwidth = GridBagConstraints.REMAINDER;
l.setConstraints(scoreLabel,c);
panel.add(scoreLabel);
return panel;
}
private void createMazePanel() {
mazePanel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = 0, y = 0;
if (!gameOver) {
for (int k = 0; k < array.length; k+=2) {
for (int l = 0; l < array[0].length; l+=2) {
int n = array[k][l];
if (n == 0 || n == 1 || n == 4 || n == 5 || n == 6)
g.setColor(Color.white);
else if (n == 2)
g.setColor(Color.green);
else if (n == 3)
g.setColor(Color.red);
g.fillRect(x, y, SQWIDTH, SQWIDTH);
if (n == 4) {
g.setColor(new Color(245,209,34));
g.fillOval(x+3, y+3, 10, 10);
}
else if (n == 5) {
g.setColor(new Color(255,223,55));
g.fillPolygon(new int[]{x,x+3,x+8,x+13,x+16,x+14,x+2},new int[]{y+2,y+6,y+2,y+6,y+2,y+16,y+16},7);
g.setColor(new Color(12,234,44));
g.fillOval(x+7,y+6,4,7);
}
else if (n == 6) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setStroke(new BasicStroke(2,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
g2.setColor(new Color(108,225,119));
g2.drawOval(x+5, y+1, 8, 8);
g2.drawLine(x+5, y+3, x+5, y+11);
g2.drawArc(x+5, y+8, 7, 7, 180, 180);
}
g.setColor(Color.black);
if (k != array.length-1 && array[k+1][l] == -1)
g.fillRect(x-3, y+SQWIDTH-3, SQWIDTH+3, 3);
if (l != array[0].length-1 && array[k][l+1] == -1)
g.fillRect(x+SQWIDTH-3,y,3,SQWIDTH);
x += SQWIDTH;
}
x = 0;
y += SQWIDTH;
}
g.setColor(Color.red);
((Graphics2D) g).fill(ellipse);
}
}
};
}
private void setKeyActions() {
InputMap im = mazePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = mazePanel.getActionMap();
im.put(KeyStroke.getKeyStroke("pressed UP"), "up");
am.put("up", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ev) {
if (row != 0 && array[row-1][col] != -1 && array[row-2][col] != 3 && !gameOver) {
int n = array[row][col];
array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
row -= 2;
n = array[row][col];
if (n == 4)
scoreLabel.setText("Score: " + (score += 20));
else if (n == 5)
scoreLabel.setText("Score: " + (score += 50));
else if (n == 2)
scoreLabel.setText("Score: " + (score -= 1));
ellipse.y = row * SQWIDTH/2 + 3;
mazePanel.repaint();
}
if (!gameOver && array[row][col] == 6) {
JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
else if (!gameOver && checkGameOver()) {
JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
}
});
im.put(KeyStroke.getKeyStroke("pressed RIGHT"), "right");
am.put("right",new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ev) {
if (col != array[0].length-1 && array[row][col+1] != -1 && array[row][col+2] != 3 && !gameOver) {
int n = array[row][col];
array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
col += 2;
n = array[row][col];
if (n == 4)
scoreLabel.setText("Score: " + (score += 20));
else if (n == 5)
scoreLabel.setText("Score: " + (score += 50));
else if (n == 2)
scoreLabel.setText("Score: " + (score -= 1));
ellipse.x = col * SQWIDTH/2 + 3;
mazePanel.repaint();
}
if (!gameOver && array[row][col] == 6) {
JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
else if (!gameOver && checkGameOver()) {
JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
}
});
im.put(KeyStroke.getKeyStroke("pressed DOWN"), "down");
am.put("down", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ev) {
if (row != array.length-1 && array[row+1][col] != -1 && array[row+2][col] != 3 && !gameOver) {
int n = array[row][col];
array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
row += 2;
n = array[row][col];
if (n == 4)
scoreLabel.setText("Score: " + (score += 20));
else if (n == 5)
scoreLabel.setText("Score: " + (score += 50));
else if (n == 2)
scoreLabel.setText("Score: " + (score -= 1));
ellipse.y = row * SQWIDTH/2 + 3;
mazePanel.repaint();
}
if (!gameOver && array[row][col] == 6) {
JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
else if (!gameOver && checkGameOver()) {
JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
}
});
im.put(KeyStroke.getKeyStroke("pressed LEFT"), "left");
am.put("left",new AbstractAction() {
@Override
public void actionPerformed(ActionEvent ev) {
if (col != 0 && array[row][col-1] != -1 && array[row][col-2] != -1 && !gameOver) {
int n = array[row][col];
array[row][col] = n == 0 || n == 1 || n == 4 || n == 5 ? 2 : 3;
col -= 2;
n = array[row][col];
if (n == 4)
scoreLabel.setText("Score: " + (score += 20));
else if (n == 5)
scoreLabel.setText("Score: " + (score += 50));
else if (n == 2)
scoreLabel.setText("Score: " + (score -= 1));
ellipse.x = col * SQWIDTH/2 + 3;
mazePanel.repaint();
}
if (!gameOver && array[row][col] == 6) {
JOptionPane.showMessageDialog(frame, "Huzzah! You found the exit! ", "Finish", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
else if (!gameOver && checkGameOver()) {
JOptionPane.showMessageDialog(frame, "You got trapped! Try again!", "Game over", JOptionPane.PLAIN_MESSAGE);
gameOver = true;
}
}
});
}
private void setItems() {
array[exitRow][exitCol] = 6;
Random r = new Random();
for (int k = 1; k < (array.length * array[0].length) / 20; k++) {
int row = r.nextInt(array.length / 2 + 1) * 2,
col = r.nextInt(array[0].length / 2 + 1) * 2;
if ((row == this.row && col == this.col) || array[row][col] == 4 || array[row][col] == 5 || array[row][col] == 6)
k--;
else
array[row][col] = r.nextInt(2) + 4;
}
}
private boolean checkGameOver() {
if (row == 0 && col == 0)
return (array[row+2][col] == 3 && array[row][col+2] == 3) ||
(array[row+1][col] == -1 && array[row][col+2] == 3) ||
(array[row+2][col] == 3 && array[row][col+1] == -1);
else if (row == 0 && col == array[0].length-1)
return (array[row+2][col] == 3 && array[row][col-2] == 3) ||
(array[row+1][col] == -1 && array[row][col-2] == 3) ||
(array[row+2][col] == 3 && array[row][col-1] == -1);
else if (row == array.length-1 && col == 0)
return (array[row-2][col] == 3 && array[row][col+2] == 3) ||
(array[row-1][col] == -1 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row][col+1] == -1);
else if (row == array.length-1 && col == array[0].length-1)
return (array[row-2][col] == 3 && array[row][col-2] == 3) ||
(array[row-1][col] == -1 && array[row][col-2] == 3) ||
(array[row-2][col] == 3 && array[row][col-1] == -1);
else if (row == 0)
return (array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+1] == -1) ||
(array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row+1][col] == -1 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
(array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3);
else if (col == 0)
return (array[row][col+2] == 3 && array[row-1][col] == -1 && array[row+1][col] == -1) ||
(array[row][col+1] == -1 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
(array[row][col+1] == -1 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
(array[row][col+1] == -1 && array[row-2][col] == 3 && array[row+2][col] == 3) ||
(array[row][col+2] == 3 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
(array[row][col+2] == 3 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
(array[row][col+2] == 3 && array[row-2][col] == 3 && array[row+2][col] == 3);
else if (row == array.length-1)
return (array[row-2][col] == 3 && array[row][col-1] == -1 && array[row][col+1] == -1) ||
(array[row-1][col] == -1 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row-1][col] == -1 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row-1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row-2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3);
else if (col == array[0].length-1)
return (array[row][col-2] == 3 && array[row-1][col] == -1 && array[row+1][col] == -1) ||
(array[row][col-1] == -1 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
(array[row][col-1] == -1 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
(array[row][col-1] == -1 && array[row-2][col] == 3 && array[row+2][col] == 3) ||
(array[row][col-2] == 3 && array[row-2][col] == 3 && array[row+1][col] == -1) ||
(array[row][col-2] == 3 && array[row-1][col] == -1 && array[row+2][col] == 3) ||
(array[row][col-2] == 3 && array[row-2][col] == 3 && array[row+2][col] == 3);
else
return (array[row-2][col] == 3 && array[row][col+1] == -1 && array[row+1][col] == -1 && array[row][col-1] == -1) ||
(array[row-1][col] == -1 && array[row][col+2] == 3 && array[row+1][col] == -1 && array[row][col-1] == -1) ||
(array[row-1][col] == -1 && array[row][col+1] == -1 && array[row+2][col] == 3 && array[row][col-1] == -1) ||
(array[row-1][col] == -1 && array[row][col+1] == -1 && array[row+1][col] == -1 && array[row][col-2] == 3) ||
(array[row-1][col] == -1 && array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+1] == -1) ||
(array[row-2][col] == 3 && array[row+1][col] == -1 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row-1][col] == -1 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row-1][col] == -1 && array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row-1][col] == -1 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row+1][col] == -1 && array[row][col-2] == 3 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-1] == -1 && array[row][col+2] == 3) ||
(array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+1] == -1) ||
(array[row-2][col] == 3 && array[row+2][col] == 3 && array[row][col-2] == 3 && array[row][col+2] == 3);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(MazeMaker::new);
}
}
The process of creating the actual maze uses the depth-first search algorithm, but with an iterative approach. Here's how it works.
We start with a 2D array of int
values, each element being a -1. A random element with even indices is chosen, and its value becomes 0:
-1 -1 -1 -1 -1 -1 0 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1
Then the program enters a loop, checking for available cells until it reaches a state where there are no available cells. This can happen several times, and is when it begins to backtrack. At this time all 0s that it comes across become 1s. Also during this time is when it determines whether an exit should be placed at that location. So at the end of it all, the array could look like this:
0 0 0 0 0 0 1 1 1
0 -1 -1 -1 -1 -1 0 -1 1
0 0 0 0 0 -1 0 -1 1
-1 -1 -1 -1 -1 -1 0 -1 1
0 0 0 0 0 0 0 -1 1
0 -1 -1 -1 -1 -1 -1 -1 1
0 -1 1 1 1 1 1 -1 1
0 -1 1 -1 1 -1 -1 -1 1
0 -1 1 -1 1 1 1 1 1
When there are a predetermined number of 1s or 0s at certain spots in the array, the loop exits. Then, using the array, the resulting maze is drawn:
It's easy to see that the -1s in the array represent walls, and the 0s and 1s are corridors. Items are randomly distributed throughout the maze. The red ellipse is the "player" that you control.
The maze is wrapped in a scroll pane for convenience so that if the size exceeds the frame's maximum size you can scroll to see the rest of the maze.
I'd say the only problem with this is how the end of game check is carried out. I thought about several ways to go about doing it, but I ended up resorting to hard-coding it all. I'm open to suggestions on how it can be done.