Lights Out
<< Fancy Joust | LabTrailIndex | Card Shark >>
For a working demo of the game, go here
Light.java
import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; public class Light { private int x, y, size; private boolean hilighted, on; private Rectangle bounds; public Light(int x, int y, int size){ this.x=x; this.y=y; this.size=size; bounds=new Rectangle(x,y,size, size); hilighted=false; on=false; } public void draw(Graphics g){ if (hilighted){ g.setColor(Color.YELLOW); g.fillRect(bounds.x-2, bounds.y-2, bounds.width+4, bounds.height+4); } g.setColor(Color.BLUE); if (on) g.setColor(Color.CYAN); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); } public void setHighlight(boolean value){ hilighted=value; } public void click(){ on=!on; } public boolean contains (int x, int y){ return bounds.contains(x, y); } }
Puzzle.java
import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; public class Puzzle { private int size; private int n, top, left; public static final int GAP=7; private Rectangle bounds; private Light[][] lights; public Puzzle(int dimension, int size, int topLeftX, int topLeftY){ n=dimension; this.size=size; this.top=topLeftY; this.left=topLeftX; bounds = new Rectangle(left, top, n*size+(n+1)*GAP, n*size+(n+1)*GAP); lights = new Light[n][n]; for(int i=0; i<n; i++) for(int j=0; j<n; j++) lights[i][j]=new Light(bounds.x+GAP+i*(size+GAP), bounds.y+GAP+j*(size+GAP), size); } public void draw(Graphics g){ g.setColor(Color.BLACK); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); for(int i=0; i<n; i++) for(int j=0; j<n; j++) lights[i][j].draw(g); } public boolean contains (int x, int y){ return bounds.contains(x, y); } public void hoverOver(int x, int y) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) lights[i][j].setHighlight(lights[i][j].contains(x, y)); } public void clickOn(int x, int y) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) if (lights[i][j].contains(x, y)){ lights[i][j].click(); //your code here } } }
LightsOutApplet,java
import java.applet.Applet; import java.awt.Button; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; public class LightsOutApplet extends Applet implements MouseListener, MouseMotionListener, ActionListener { //instance variables: private Image virtualMem; private BufferedImage img; private Graphics gBuffer; private String message; private Button resetButton; private int moves; private Puzzle puzzle; public static final int SIZE=50; public static final int DIMENSION=5; public void init(){ //Setup Mouse listening: addMouseListener(this); addMouseMotionListener(this); //Setup graphics buffer to get rid of flickering in Windoze virtualMem = createImage(getWidth(),getHeight()); gBuffer = virtualMem.getGraphics(); //Setup Reset Button: resetButton = new Button(" Start "); resetButton.addActionListener(this); add(resetButton); //Set up variables message="Press buttons until all are off"; moves=0; puzzle = new Puzzle(DIMENSION, SIZE, 50, 70); } public void paint(Graphics g) { // simple text displayed on applet gBuffer.setColor(Color.white); gBuffer.fillRect(0, 0, getWidth(), getHeight()); gBuffer.setColor(Color.black); gBuffer.drawString("Lights Out", 20, 20); gBuffer.setColor(Color.blue); gBuffer.drawString(message, 20, 50); puzzle.draw(gBuffer); g.drawImage(virtualMem,0,0,this); } public void update(Graphics g){ paint(g); } //Mouse interface implementations: @Override public void mouseDragged(MouseEvent arg0) {} @Override public void mouseMoved(MouseEvent e) { int x=e.getX(); int y=e.getY(); if (puzzle.contains(x, y)){ puzzle.hoverOver(x,y); repaint(); } } @Override public void mouseClicked(MouseEvent arg0) {} @Override public void mouseEntered(MouseEvent arg0) {} @Override public void mouseExited(MouseEvent arg0) {} @Override public void mousePressed(MouseEvent arg0) {} @Override public void mouseReleased(MouseEvent e) { int x=e.getX(); int y=e.getY(); if (puzzle.contains(x, y)){ puzzle.clickOn(x,y); repaint(); } } @Override public void actionPerformed(ActionEvent arg0) { // resetButton pressed message=" 0 moves"; moves=0; resetButton.setLabel("Start Over"); puzzle = new Puzzle(DIMENSION, SIZE, 50, 70); repaint(); } }
What to do
Right now the code works so that a single clicked square is turned on or off as you click it. In the actual game, when you click on a light, it changes not only the light you clicked on, but also the one above, below and the one on the right and the left. You need to modify the Puzzle
method clickOn
so it will work properly. To do this without run time errors, you need to check if the neighboring light exists (there is nothing below the bottom row or the right of the last column, for instance).
What else?
- Once you have that working, change the constructor of the
Puzzle
so it randomly chooses to turn on several lights. - There is a version where there are no borders, so when you click on one of the lights on the edge, it changes the lights on the other side (Hint: use mod % )
- change the dimensions.
- count the moves
- not all random situations form a solvable puzzle. Make you code teak the random starting position so that it is solvable. To do this make sure there are an even number of lights turned on in the three "quiet positions" described here
- keep score based on the minimum necessary moves