5

I have a loop that is running until the user clicks a ready button, and then it starts the the loop inside the if-statement, but it only works it there is a print statement before it. Does it have to do with how the Rule Set is static? The Button works no matter what, but it only enters the loop if the print statement is there.

package gameoflife;

public class GameOfLife {

    public static final int HEIGHT = 16;
    public static final int LENGTH = 16;
    public static Grid current;

    public static void main(String[] args) {
        Ui gui = new Ui();
        int time = 0;
        while (true) {
            RuleSet.checkReady();
            //System.out.println(RuleSet.checkReady());
            if (RuleSet.checkReady() == true) {
                //System.out.println("ready!");
                if(time == 0){
                    current = gui.getUserSeed();
                }
                while (time < 100) {
                    current.print();
                    Grid next = new Grid(HEIGHT, LENGTH);
                    for (int i = 0; i < HEIGHT; i++) {
                        for (int j = 0; j < LENGTH; j++) {
                            next.changeState(i, j, RuleSet.determineState(current, i, j));
                        }
                    }
                    current = next;
                    time++;
                }
                break;
            }
        }
    }
}

Rule Class:

public class RuleSet {

public Grid grid;
public static boolean readyToStart = false;

/*checkReady()
 * input:   
 * purpose: checks ready flag 
 * output:  none
 */
public static boolean checkReady() {
    return readyToStart;
}

/*isReady()
 * input:   none
 * purpose: sets ready flag to ready
 * output:  none
 */
public static void isReady() {
    readyToStart = true;
}

/*determineState()
 * input:   Grid grid, int y, int x
 * purpose: determines the state of a cell for the next 
 *          generationusing the four rules below
 * output:  true/false
 */
public static boolean determineState(Grid grid, int y, int x) {
    grid = grid;
    int neighbors = grid.getNeighbors(y, x);
    if (grid.getState(y, x)) {
        return (ruleOne(neighbors) && ruleTwo(neighbors)
                && ruleThree(neighbors));
    } else {
        return (ruleFour(neighbors));
    }
}

/*
 * Rule 1:
 * Any live cell with fewer than two live neighbours dies, 
 * as if caused by under-population.
 */
private static boolean ruleOne(int neighbors) {
    return (neighbors >= 2);
}

/*
 * Rule 2:
 * Any live cell with two or three live neighbours 
 * lives on to the next generation.
 */
private static boolean ruleTwo(int neighbors) {
    return (neighbors == 2 || neighbors == 3);
}

/*
 * Rule 3: 
 * Any live cell with more than three live neighbours dies, 
 * as if by overcrowding
 */
private static boolean ruleThree(int neighbors) {
    return (neighbors < 4);
}

/*
 * Rule 4:
 * Any dead cell with exactly three live neighbours becomes a live cell, 
 * as if by reproduction.
 */
private static boolean ruleFour(int neighbors) {
    return (neighbors == 3);
} }

Gui

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 *
 * @author peter
 */
public class Ui extends JFrame {

public static JPanel panelGrid;
public static JPanel panelControl;
public static JPanel panelManager;
public static JButton[][] buttons;
public static JButton isReady;
public static JButton nextGen;
public static final int HEIGHT = 16;
public static final int LENGTH = 16;
public Grid temp;

public Ui() {
    setTitle("The Game Of Life");
    setSize(800, 600);
    setLocationRelativeTo(null);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    temp = new Grid(HEIGHT, LENGTH);
    //Creates and sets up the contentPane Container
    Container contentPane = getContentPane();
    contentPane.setLayout(new BorderLayout());
    contentPane.add(initButtonGrid(), BorderLayout.CENTER);
    contentPane.add(initContButton(), BorderLayout.PAGE_END);
    //add(contentPane);
    setVisible(true);
}

/*initButtonGrid()
 * input:   none
 * purpose: to return initialize the button array, and return the 
 *          corresponding JPanel
 * output:  JPanel panelGrid
 */
private JPanel initButtonGrid() {
    buttons = new JButton[HEIGHT][LENGTH];
    panelGrid = new JPanel(new GridLayout(HEIGHT, LENGTH));

    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < LENGTH; j++) {
            buttons[i][j] = new JButton();
            buttons[i][j].setSize(80, 80);
            buttons[i][j].setBackground(Color.white);
            //Creates an action listener that allows user
            //to setup seed
            buttons[i][j].addActionListener(new ActionListener() {                    
                //Loops through and checks the state of the button/array
                //and then changes state if needed
                public void actionPerformed(ActionEvent e) {
                    for (int i = 0; i < HEIGHT; i++) {
                        for (int j = 0; j < LENGTH; j++) {
                            if (buttons[i][j] == e.getSource()
                                    && !RuleSet.checkReady()) {
                                temp.changeState(i, j, !temp.getState(i, j));
                                if (temp.getState(i, j)) {
                                    buttons[i][j].setBackground(Color.red);
                                } else {
                                    buttons[i][j].setBackground(Color.white);
                                }
                                temp.print();
                            }
                        }
                    }
                }
            });
            panelGrid.add(buttons[i][j]);
        }
    }
    return panelGrid;
}

/*getUserSeed()
 * input:   none
 * purpose: to return the seed the user made with the buttons. I was having
 *          trouble passing the current grid from main() to here in a static
 *          way. Will attempt to update at later point
 * output:  Grid temp
 */
public Grid getUserSeed() {
    return temp;
}

/*initContButton()
 * input:   none
 * purpose: to return initialize the buttons for commands, and return the 
 *          corresponding JPanel
 * output:  JPanel panelControl
 */
private JPanel initContButton() {
    panelControl = new JPanel(new GridLayout(1, 2));
    JButton ready = new JButton("Start Simulation");
    ready.setSize(80, 190);
    ready.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent e) {
            RuleSet.isReady();
            System.out.println("Ready Pressed");
        }
    });
    panelControl = new JPanel(new GridLayout(1, 2));
    JButton nextGen = new JButton("Generation");
    nextGen.setSize(80, 190);
    nextGen.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
        }
    });
    panelControl.add(ready);
    panelControl.add(nextGen);
    return panelControl;
} 
}
ajb
  • 31,309
  • 3
  • 58
  • 84
Peter Chappy
  • 1,165
  • 4
  • 22
  • 41
  • You never set the flag to `true`. – Sotirios Delimanolis Oct 15 '13 at 16:14
  • I'll add the gui class, but there is a button that has an action event sets it to true; – Peter Chappy Oct 15 '13 at 16:15
  • Add `volatile` to your `readyToStart` field. – Sotirios Delimanolis Oct 15 '13 at 16:22
  • your print statement `//System.out.println(RuleSet.checkReady());` is actually calling `checkReady()` once again. It may be a clue why your pgm works when its uncommented? – mazaneicha Oct 15 '13 at 16:23
  • I assume that the ready flag is set in a different thread. The `while (true)` loop is not giving any cycles to the other thread, nor is it giving the CPU a reason to refresh its cache. The print statement interrupts the control flow, likely results in an I/O wait, and causes the cache to refresh. This allows the changed state of the flag to be seen. – Hot Licks Oct 15 '13 at 16:30

2 Answers2

3

I have a loop that is running until the user clicks a ready button, and then it starts the the loop inside the if-statement, but it only works it there is a print statement before it.

The reason behind your problem is, "when the user clicks the ready button the event handling code is being run in different thread than the thread in which your loop is running". So, the behavior can be unexpected: because you are assuming that readyToStart is being set before you get in to the if condition.

Adding System.out.println makes the thread running the loop to wait for the io and in the meanwhile readyToStart is being set by the other thread.

You can make readyToStart volatile by adding volatile in it's declaration, so that both the threads can have consistant view of it.

public class RuleSet {

           public Grid grid;
           public static volatile boolean readyToStart = false;

           /*Rest of code goes here*/
}
Vaibhav Raj
  • 2,214
  • 4
  • 23
  • 39
1

First: Please do not use an is...() method to change state. This should be an accessor, not an mutator by naming convention. (You may want to use userIsReady() or setReady() instead).

I assume that you experience a multi-threading issue, since you have the AWT thread and your user thread working with the same value, but have not ensured that there is some kind of synchronization.

You can check if changeing the declaration of the ready flag helps:

public static boolean readyToStart = false; // change this to
public volatile static boolean readyToStart = false;

You can read more about multithreading and concurrency issues here:

http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

http://docs.oracle.com/javase/tutorial/uiswing/concurrency/

Thomas
  • 11,272
  • 2
  • 24
  • 40