CS 1: Lecture 37 – Lights Out
Dear students,
It’s the last week! Given that we’re all stressed and ready to be done, let’s play a game today. Just kidding, let’s make one instead. We’re going to implement Lights Out, which started off as a handheld game from Tiger Electronics:
The game is played on a 5×5 grid of lights, some of which are on and some of which are off. Your task is turn all of them off. But here’s the thing: when you press a button, you toggle it and its 4-neighbors. We’ll write a graphical application that listens to mouse clicks, detects a win state, and saves and loads board configurations. Here’s our checklist:
- Create a window.
- Plot a 5×5 board of lights.
- Add a gap between lights.
- Toggle individual lights.
- Toggle 4-neighbors as well.
- Detect win condition.
- Add a menu.
- Save a board configuration to a file.
- Load a previously saved level.
- Export application to a runnable JAR file.
Here’s your TODO to complete before we meet again:
- CS 145, tomorrow is our last lab. In case it hasn’t been made clear, tomorrow is your only opportunity to complete the lab 12 checkpoints. I will not have much time to help you during lab, as I will be running your peers’ robots.
See you next class!
Sincerely,
P.S. It’s time for a haiku!
I used to play games
Making them’s even harder
It’s that final boss
P.P.S. Here’s the code we wrote together…
LightsOutFrame.java
package lecture1211;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class LightsOutFrame extends JFrame implements MouseListener {
private static final int PIXELS = 100;
private static final int GAP = 2;
private LightsOutPanel panel;
private boolean[][] board;
public LightsOutFrame() {
board = new boolean[5][5];
// board[2][3] = true;
panel = new LightsOutPanel();
panel.setPreferredSize(new Dimension(PIXELS * 5, PIXELS * 5));
panel.addMouseListener(this);
add(panel);
JMenuItem saveLevel = new JMenuItem("Save level");
JMenu menu = new JMenu("Level");
JMenuBar menuBar = new JMenuBar();
menu.add(saveLevel);
menuBar.add(menu);
setJMenuBar(menuBar);
saveLevel.addActionListener(e -> {
saveLevel();
});
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private void saveLevel() {
try {
PrintWriter out = new PrintWriter(new File("/Users/johnch/Desktop/level"));
for (int r = 0; r < 5; ++r) {
for (int c = 0; c < 5; ++c) {
out.print(board[r][c] + " ");
}
out.println();
}
out.close();
} catch (IOException e) {
System.out.println(e);
}
}
class LightsOutPanel extends JPanel {
public void paintComponent(Graphics g) {
for (int r = 0; r < 5; ++r) {
for (int c = 0; c < 5; ++c) {
// (c, r)
if (board[r][c]) {
g.setColor(Color.YELLOW);
} else {
g.setColor(Color.BLACK);
}
g.fillRect(c * PIXELS + GAP, r * PIXELS + GAP, PIXELS - 2 * GAP, PIXELS - 2 * GAP);
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
// System.out.println(e);
int r = e.getY() / PIXELS;
int c = e.getX() / PIXELS;
flop(c, r);
flop(c + 1, r);
flop(c - 1, r);
flop(c, r - 1);
flop(c, r + 1);
repaint();
if (isWin()) {
JOptionPane.showMessageDialog(this, "A winner is you!");
}
}
private void flop(int c,
int r) {
if (r >= 0 && r < 5 && c >= 0 && c < 5) {
board[r][c] = !board[r][c];
}
}
private boolean isWin() {
for (int r = 0; r < 5; ++r) {
for (int c = 0; c < 5; ++c) {
if (board[r][c]) {
return false;
}
}
}
return true;
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
LightsOut.java
package lecture1211;
public class LightsOut {
public static void main(String[] args) {
new LightsOutFrame();
}
}
LightsOutFrame.java
package lecture1211;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class LightsOutFrame extends JFrame implements MouseListener {
private static final int PIXELS = 50;
private static final int BORDER = 3;
private boolean[][] board;
public LightsOutFrame() {
board = new boolean[5][5];
// board[2][4] = true;
LightsOutPanel panel = new LightsOutPanel();
add(panel);
panel.addMouseListener(this);
panel.setPreferredSize(new Dimension(5 * PIXELS, 5 * PIXELS));
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class LightsOutPanel extends JPanel {
public void paintComponent(Graphics g) {
for (int r = 0; r < 5; ++r) {
for (int c = 0; c < 5; ++c) {
if (board[r][c]) {
g.setColor(Color.GREEN);
} else {
g.setColor(Color.RED);
}
g.fillRect(c * PIXELS + BORDER, r * PIXELS + BORDER, PIXELS - BORDER * 2, PIXELS - BORDER * 2);
}
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
int r = e.getY() / PIXELS;
int c = e.getX() / PIXELS;
flip(c, r);
flip(c + 1, r);
flip(c - 1, r);
flip(c, r - 1);
flip(c, r + 1);
repaint();
if (isWin()) {
JOptionPane.showMessageDialog(this, "You winned!");
}
}
private boolean isWin() {
for (int r = 0; r < 5; ++r) {
for (int c = 0; c < 5; ++c) {
if (board[r][c]) {
return false;
}
}
}
return true;
}
private void flip(int c,
int r) {
if (c >= 0 && c < 5 && r >= 0 && r < 5) {
board[r][c] = !board[r][c];
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
LightsOut.java
package lecture1211;
public class LightsOut {
public static void main(String[] args) {
new LightsOutFrame();
}
}