package com.yatchboy.practice;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import acm.graphics.GLine;
import acm.graphics.GOval;
import acm.program.GraphicsProgram;
import acm.util.RandomGenerator;
//Program class
public class RacerProgram extends GraphicsProgram {
public static final int NUMRACERS = 8;
public static final int NUMSTEPS = 20;
public static final int START_X = 100;
public static final int START_Y = 100;
public static final int STEPSPAN = 50;
private static final double RACERSIZE = 30;
private static final int PANELMARGIN = 40;
private static final int LANEWIDTH = 60;
public static final boolean[] racerStatus = new boolean[NUMRACERS];
private static Racer[] racers = new Racer[NUMRACERS];
private static final RandomGenerator rgen = new RandomGenerator();
//executor service introduced from Java5, should use this.
private static ExecutorService exec = Executors.newFixedThreadPool(NUMRACERS);
//this is not necessary if using Stanford menu to run in Eclipse.
//adding this will make the program command line "parameter free"
public static void main(String[] args){
new RacerProgram().start(args);
}
//initialize the race
public void init(){
//draw start line using blue color
GLine startLine = new GLine(START_X, START_Y, START_X, START_Y + NUMRACERS * LANEWIDTH );
startLine.setColor(Color.BLUE);
add(startLine);
//draw finish line using red color
GLine finishLine = new GLine(START_X + STEPSPAN * NUMSTEPS, START_Y, START_X + STEPSPAN * NUMSTEPS, START_Y + NUMRACERS * LANEWIDTH);
finishLine.setColor(Color.RED);
add(finishLine);
//create the button and add action listener(instead of mouse listener)
add(new JButton("Let's Rock!"), SOUTH);
add(new JButton("Again!"), SOUTH);
addActionListeners();
//initialize the racers with random colors
for(int i = 0; i < NUMRACERS; i++){
racers[i] = new Racer(i, RACERSIZE, racerStatus);
racers[i].setFilled(true);
racers[i].setColor(rgen.nextColor());
add(racers[i], START_X - RACERSIZE, START_Y + i * LANEWIDTH);
}
//set the initial size of the window
setSize(START_X * 2 + STEPSPAN * NUMSTEPS, START_Y * 2 + NUMRACERS * LANEWIDTH + PANELMARGIN * 2);
}
@Override
public void actionPerformed(ActionEvent e){
String actionCommand = e.getActionCommand();
if(actionCommand.equals("Let's Rock!")){
for(int i = 0; i < NUMRACERS; i++){
exec.submit(racers[i]);
}
}
else if (actionCommand.equals("Again!")){
for(int i = 0; i < NUMRACERS; i++){
racers[i].setLocation(START_X - RACERSIZE, racers[i].getY());
racers[i].setFilled(true);
racerStatus[i] = false;
}
}
}
}
//Racer class
class Racer extends GOval implements Runnable{
private static final RandomGenerator rgen = new RandomGenerator();
private int myIndex;
public Racer(int index, double size, boolean[] racerStatus){
super(size, size);
myIndex = index;
}
@Override
public void run() {
runTillFinished();
markFinished(this);
}
// all thread running sharing the same racer instance access this method one by one
// instance lock
synchronized void runTillFinished(){
while(getX() < RacerProgram.NUMSTEPS * RacerProgram.STEPSPAN + RacerProgram.START_X){
pause(rgen.nextDouble(50, 100));
move(RacerProgram.STEPSPAN, 0);
}
}
// all racers access this method one by one, since this is class level lock
// class lock
static synchronized void markFinished(Racer racer) {
boolean result = false;
for(int i = 0; i<RacerProgram.racerStatus.length; i++){
if(RacerProgram.racerStatus[i] == true)
result = true;
}
if(result == false){
racer.setFilled(false);
//racer.pause(500);
RacerProgram.racerStatus[racer.myIndex] = true;
}
}
}