Our final project for school is to create a class that implements a horse racing animation with betting capabilities. We have to make sure it's multi-threaded, and we have to have buttons the user can click to begin the race. The five horses need to all start the race at the same time and make their way to the finish line. We've finished most of the project requirements, but we're having trouble getting all of the horses to move.
Sometimes on pushing the start button, only two horses will move. Sometimes three of the horses will move. Sometimes all the horses will move. Sometimes none of the horses will move. The method we're currently using uses a while loop to change the layoutX of the horse to whatever the current layoutX is plus 10. I added a print out statement to let me know whenever a horse crosses the finish line, and according to that all five horses are always crossing the finish line.
BUT, visually this isn't happening. The images will randomly just not go anywhere, even though the horse the image is representing DOES. I'm sure it has something to do with how we're moving the images, or how we're inserting them into the program, but just in case I'll also show the run method for the threads and how we're calling that too.
This is the method we used to add the images of the five horses.
public ArrayList<ImageView> callAllHorses() {
//In this method, we have created an array of type image and an array list of type ImageView,
//Using a for loop, make a new Image, add to the image array,
//and add that array to the ArrayList. At the same, we set the spacing for the horses evenly
//using a mathematical expression. The first horse, in index 0, doesn't
//need any more space vertically beyond the first 50 pixels we leave open for our buttons.
Image[] allHorses;
ArrayList<ImageView> allHorseView = new ArrayList<ImageView>();
allHorses = new Image[5];
for (int i = 0; i < allHorses.length; i++) {
allHorses[i] = new Image("file:C:\\User\\Downloads\\finalhorse" + (i + 1) + ".png");
allHorseView.add(new ImageView(allHorses[i]));
allHorseView.get(i).setY(50 + (150 * i));
}
return allHorseView;
}
This arraylist gets added to a pane, which then gets added to a group.
Here's the inner class and run method for the horses and what we're asking them to do:
public class Horse implements Runnable {
//A horse inner class was needed in order to consolidate thread creation.
//AND in order to be able to store the horse's number into a rankings list.
//This will allow us to record where the horses placed in the race,
//and then compare the rankings to the user's bet.
Node horse;
int horseNumber;
public Horse(int i) {
horseNumber = i;
createHorse(horseNumber);
}
public void createHorse(int i) {
horse = horses.getChildren().get(i);
}
public int getHorseNumber() {
return horseNumber;
}
@Override
public void run() {
//In this run method, the horses will move 10 pixels to the right and then sleep for a random number
//between 1-500 milliseconds, then resume their progress. Once their tails have reached the 650 pixel
//mark they stop. This puts them right at the edge of the window. Change 650 to change when the horse stops.
//Once the horse has reached the point it needs to, it finishes running by adding the number it has to the rankings arraylist.
Random horseSlow = new Random();
// TODO Auto-generated method stub
while (horse.getLayoutX() < 650) {
horse.setLayoutX(horse.getLayoutX() + 10);
try {
Thread.sleep(horseSlow.nextInt(500));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rankings.add(getHorseNumber() + 1);
}
}
// TODO Auto-generated method stub
}
This is our begin race method, where we create the horses using the inner class, and assign them to their corresponding images. All the horses are individual threads, which are all ran through an executor.
public void beginRace(){
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new Horse(0));
executor.execute(new Horse(1));
executor.execute(new Horse(2));
executor.execute(new Horse(3));
executor.execute(new Horse(4));
}
And this is the action event that occurs whenever the start button is pressed.
startBtn.setOnAction(e -> {
beginRace();
System.out.println("This button is active.");
resetBtn.setDisable(false);
startBtn.setDisable(true);
});
I've been trying to solve this problem for three days now, and for the life of me I can't figure it out.