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?

  1. Once you have that working, change the constructor of the Puzzle so it randomly chooses to turn on several lights.
  2. 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 % )
  3. change the dimensions.
  4. count the moves
  5. 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
  6. keep score based on the minimum necessary moves