Simon Game

Simon is a game where a sequence of lights are played for you and you have to press the lights in the same order. If you get the sequence correct, another is added to the sequence. The object of the game to to try to remember the longest sequence possible.

Introduction/Explanation Video

Video Demo of Working version

There are three classes and you are to compete the Sequence Class. Once the game is working, you can adapt the SimonGame class to increase the number of buttons (lights) or display more statistics (Like the current record).

Exercises

  1. Complete the Sequnce constructor (see the comment for details)
  2. Complete the Sequnces method makeNext() (see the comment for details)
  3. Adapt the SimonGame class so that at a certain level the play timer is less than 1000 milliseconds (play = new Timer(500, this): for example)
  4. Adapt the SimonGame class so it keeps track and shows the highest level ever achieved. To add to what is shown, adapt the paint method.
  5. Adapt the SimonGame class so that there are 6 buttons.

SimonButton.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
/**
 * This is the light (and Button) that can be pressed 
 * and "illuminated"
 * 
 * @author Chris Thiel
 * @version 18 March 2016
 *
 */
public class SimonButton {

	private Color onColor, offColor;
	private boolean hilighted, isOn;
	private Rectangle bounds;
	public SimonButton(int x, int y, int size, Color color){

		this.onColor=color;
		this.offColor= new Color(color.getRed()/2, color.getGreen()/2, color.getBlue()/2);

		bounds=new Rectangle(x,y,size, size);
		hilighted=false;
		isOn=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(offColor);
		if (isOn)
			g.setColor(onColor);
		g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
	}
	public void setHighlight(boolean value){
		hilighted=value;
	}
	public void on(){
		isOn=true;
	}
	public void off(){
		isOn=false;
	}
	public boolean contains (int x, int y){
		return bounds.contains(x, y);
	}

}

Sequence.java

import java.util.ArrayList;

public class Sequence 
{
	private ArrayList<SimonButton> seq;
	private SimonButton[] btns;
	private int currentIndex;

	/**
	 * The constructor initializes all instance fields
	 * and makes the first entry of the sequence by 
	 * calling the makeNext() method
	 * 
	 * @param btns The array of SimonButton that the user presses
	 */
	public Sequence(SimonButton[] btns)
	{
		//Your code here  
	}
	/**
	 * getNext returns the SimonButon that is the current one 
	 * in the sequence
	 * @return
	 */
	public SimonButton getNext()
	{
		return seq.get(currentIndex);
	}

	/**
	 * get returns the ith SimonButton of the sequence
	 * @param i
	 * @return
	 */
	public SimonButton get(int i)
	{
		if ( i>=seq.size() || i<0)
			return null;
		return seq.get(i);
	}

	/**
	 * size
	 * @return returns the number of entries in the sequence
	 */
	public int size(){
		return seq.size();
	}
	/**
	 * makeNext adds a new random entry to the SimonButton sequence, and resets the current index.
	 * The index should be from 0 to the index of the last SimonButton in btns.
	 * Recall seq is made out of SimonButtons from btns.
	 * 
	 */
	public void makeNext()
	{
		//your code here
	}

	/**
	 * reset sets the current index to 0
	 */
	public void reset()
	{		
		currentIndex=0;
	}
	/**
	 * playNext will turn on the next btn 
	 * and increment the current index
	 *  
	 */
	public void playNext()
	{	
		seq.get(currentIndex).on();
		this.increment();
	}
	/**
	 * returns whether there is another in the sequence
	 * 
	 * @return true if the currentIndex() is less than
	 * the size of the sequence.
	 */
	public boolean hasAnotherLeft()
	{
		return currentIndex < seq.size();
	}
	public void increment()
	{
		currentIndex++;
	}
	public int toGo(){
		return seq.size()-currentIndex;
	}
	public int currentIndex(){
		return currentIndex;
	}

}

SimonGame.java

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
/**
 * Simon Game is a game where a sequence of "light" buttons are played for you 
 * and you have to "press" the buttons in the same order.  
 * If you get the sequence correct, another is added to the sequence.  
 * The object of the game to to try to remember the longest sequence possible.
 * 
 * @author Chris Thiel
 * @version 19 March 2016
 *
 */
public class SimonGame extends JPanel implements MouseListener, ActionListener
{
	public static int WIDTH=800,HEIGHT=600, TOP=100, LEFT=75, GAP=2, SIZE=200;
    private Font titleFont, regularFont, giantFont;
    private SimonButton[] btns;
    private String message, message2;
    private Sequence seq;
    private Color bgColor;    
    private Timer play, pause, countdown, delay; // timers to play the sequence or wait for user
	/**
	 * The class constructor will initialize the ArrayList<Balloon>
	 * and other object fields (variables), including a Timer
	 * object from the javax.swing.* library.  The timer will
	 * generate an Action Event, so that something happens
	 * in regular intervals without the user typing anything.
	 */
    public SimonGame()
    { 	
    	//initialize variables here...
    	titleFont = new Font("SansSerif", Font.BOLD, 24);
    giantFont = new Font("SansSerif", Font.BOLD, 72);
    	regularFont = new Font("Serif", Font.PLAIN, 12);
    	message="Click to start";
    	message2="";
    	btns= new SimonButton[4];
    	btns[0] = new SimonButton( LEFT, TOP, SIZE, new Color(255,0,0));
    	btns[1] = new SimonButton( LEFT+GAP+SIZE, TOP, SIZE, new Color(125,255,0));
    	btns[2] = new SimonButton( LEFT, TOP+GAP+SIZE, SIZE, new Color(0,125,255));
    	btns[3] = new SimonButton( LEFT+GAP+SIZE, TOP+GAP+SIZE, SIZE, new Color(255,255,0));
    	seq = new Sequence(btns);
    	play = new Timer(1000, this);
    	pause = new Timer(500, this);
    	countdown = new Timer(5000, this);
    	delay = new Timer(100, this);
    	bgColor = Color.WHITE;
    repaint();

    }
	/**
	 * the main method makes an instance of our application and puts it in a JFrame 
	 * that will end the application when it is closed.
	 * 
	 */
	public static void main(String[] args) {
		SimonGame app= new SimonGame();
		JFrame window = new JFrame("Simon Game");
		window.setSize(WIDTH, HEIGHT);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.getContentPane().add(app);
		window.addMouseListener(app);
		window.setVisible(true);

	}
	public void endGame(String m){
		message=m+" Your best was "+(seq.size()-1);
		message2="Click to start a new game";
		seq=new Sequence(btns);
		bgColor=new Color(255,200,200);
		repaint();
	}
	/**
	 *  This is the method to change what is drawn to the screen:
	 */
	public void paintComponent(Graphics g){
		//super.paintComponent(g);
		g.setColor(bgColor);
		g.fillRect(0, 0, getWidth(),getHeight());
		g.setColor(Color.BLUE);
		g.setFont(titleFont);
		g.drawString("Simon Game ver. 1.0", 20, 30);
		g.setColor(Color.RED);
		g.setFont(giantFont);
		g.drawString("LEVEL ", WIDTH-300, TOP+100);
		g.drawString(""+seq.size(), WIDTH-210, TOP+200);
		g.setColor(Color.BLACK);
		g.setFont(titleFont);
		g.drawString(message, 20, 60);
		g.drawString(message2, 20, 86);
		for (SimonButton b: btns)
			b.draw(g);

	}

	@Override
	/**
	 * actionPerformed is needed to implement the ActionListener that listens for the timer.
	 * If the play timer was active, we stop it and start the pause
	 * If pause was active, we stop it and and start the play if there are more left in the seq
	 * If wait was active, the player took too long, game over.
	 */
	public void actionPerformed(ActionEvent e) {

		Object source=e.getSource();

		if(source==play){
			play.stop();	
			for(SimonButton b:btns)
				b.off();
			pause.start();

		}
		if (source==pause) {
			pause.stop();
			if(seq.hasAnotherLeft()){	
				seq.playNext();
				play.start();
			}else{
				message="Now repeat after me";
				seq.reset();
			    countdown.start();
			}
		}
		if(source==countdown){
			countdown.stop();
			endGame("Sorry, you took too long--Game Over");
		}
		if(source==delay){
			delay.stop();
			for(SimonButton b:btns)
				b.off();
		}

		repaint();
	}
	/**
	 * These 5 methods need to be declared to implement the MouseListener Interface
	 */

	@Override
	public void mouseClicked(MouseEvent e) {}
	@Override
	public void mousePressed(MouseEvent e) {}
	@Override
	/**
	 * If all timers are off, we are beginning a new sequence.
	 * If wait timer is on, find out which button is pressed
	 * If it is correct, check if that is the last in the sequence 
	 * 	 (if last, we have success,  add to the seq)
	 *   (IF NOT the last, advance sequence)
	 * If it is not correct, report failure and make a new sequence
	 */
	public void mouseReleased(MouseEvent e) {
		if(!play.isRunning() && !pause.isRunning() && !countdown.isRunning()){
			//seq=new Sequence(btns);
			bgColor=Color.WHITE;
			message="Watch and memorize this sequence";
			seq.playNext();
			play.start();
			repaint();
			return;
		}
		if(!countdown.isRunning())
			return;
		SimonButton pressed=null;
		for(SimonButton b:btns){
			b.off();
			b.setHighlight(false);
			if (b.contains(e.getX(), e.getY()))
				pressed=b;
		}
		if(pressed==null)
			return;
		pressed.on();
		delay.start();// only have button on for a moment 
		if(countdown.isRunning())
			countdown.stop();

		repaint();
		if (pressed==seq.getNext()){// so far so good..
			seq.increment();
			if(seq.hasAnotherLeft()){
				message="Good work.. "+seq.toGo()+" to go!";
				countdown.start();
			} else {// good job, add to the seq
				seq.makeNext();
				message="Good work.. now there are "+seq.size();
				play.start();
			}

		}else{//wrong btn pressed
			endGame("Sorry-that wasn't the next button.");
		}
		repaint();
	}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}

}