momodjib
momodjib

Reputation: 193

Game of Life not pausing properly

I'm trying to make my version of game of life but I'm stuck trying to make use of Threads to give the program the ability to pause and resume.

I use a Thread to execute the main Jpanel, where we can see the different generations, when I click on "pause" the screen successfully pauses but when it resumes 5 seconds later (because I use Thread.sleep(5000)), I realize that the screen froze but the game was actually still running, it just wasn't updating the screen.

Like it pauses at generation #5 and resumes at generation #11, and obviously I want the game to resume right where it paused but I tried many things and so far nothing works. Any help would be great.

GameOfLife Class:

public class GameOfLife extends JFrame implements ActionListener {

static JLabel aliveLabel = new JLabel("Alive:");
static JLabel GenerationLabel = new JLabel("Generation #");
static SimpleCellGrid body = new SimpleCellGrid();
static JPanel header = new JPanel();
static int genNumber = 1;
static JButton PlayToggleButton = new JButton("pause");
static JButton ResetButton = new JButton("b");
static Thread t1 = new Thread(body, String.valueOf(header));



public GameOfLife() throws IOException {
    super("Game of life");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(700, 660);
    setLocationRelativeTo(null);
    setLayout(new FlowLayout());

    GenerationLabel.setName("GenerationLabel");
    aliveLabel.setName("aliveLabel");
    PlayToggleButton.setName("PlayToggleButton");
    ResetButton.setName("ResetButton");

    PlayToggleButton.addActionListener(this);
    ResetButton.addActionListener(this);

    PlayToggleButton.setIcon(new ImageIcon(play));
    ResetButton.setIcon(new ImageIcon(reset));

    PlayToggleButton.setPreferredSize(new Dimension(40,30));
    ResetButton.setPreferredSize(new Dimension(40,30));

    header.setLayout(new FlowLayout());
    header.setPreferredSize(new Dimension(100, this.getHeight()));
    header.add(PlayToggleButton);
    header.add(ResetButton);
    header.add(GenerationLabel);
    header.add(aliveLabel);


    body.setLayout(new BorderLayout());
    body.setPreferredSize(new Dimension(500, this.getHeight()));

    add(header, BorderLayout.WEST);
    add(body, BorderLayout.CENTER);
    setVisible(true);

}

public static void updateLabels(){

    body.run();
    GenerationLabel.setText("Generation #"+ genNumber++);
    aliveLabel.setText("Alive: "+ body.totalAlive());

    try {
        Thread.sleep(100);
        updateLabels();
    } catch (InterruptedException ignore) { }
}

@Override
public void actionPerformed(ActionEvent e) {

    if(e.getActionCommand().equals("pause")){
        try {
            t1.sleep(5000);
            PlayToggleButton.setEnabled(true);

        } catch (InterruptedException ex) {
            t1.start();
        }

    }
}

public static void main(String[] args) throws IOException {
    new GameOfLife();
    updateLabels();
}
}

SimpleCellGrid class:

public class SimpleCellGrid extends JPanel implements Runnable{
private static final int ROWS = 50;
private static final int COLS = 50;
private static final int CELL_WIDTH = 10;
private static SimpleCell[][] cellGrid = new SimpleCell[ROWS][COLS];

public SimpleCellGrid() {
    for (int row = 0; row < cellGrid.length; row++) {
        for (int col = 0; col < cellGrid[row].length; col++) {
            int x = col * CELL_WIDTH;
            int y = row * CELL_WIDTH;
            cellGrid[row][col] = new SimpleCell(x, y, CELL_WIDTH);

            if (new Random().nextBoolean()) {
                cellGrid[row][col].setAlive(true);
            } else {
                cellGrid[row][col].setAlive(false);
            }
        }
    }
}
public int totalAlive(){
    int totalAlive = 0;
    for (SimpleCell[] simpleCells : cellGrid) {
        for (int j = 0; j < cellGrid.length; j++) {
            if (simpleCells[j].isAlive())
                totalAlive++;
        }
    }
    return totalAlive;
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    for (SimpleCell[] cellRow : cellGrid) {
        for (SimpleCell simpleCell : cellRow) {
            simpleCell.draw(g2);
        }
    }
}

@Override
public void run() {
    cellGrid = new GenerationMaker4().nextGeneration(cellGrid);
    repaint();
}
}

Upvotes: 0

Views: 66

Answers (2)

tanyehzheng
tanyehzheng

Reputation: 2221

You called updateLabels() using your main thread.

You called Thread.sleep(5000) using your t1 thread.

main thread will continue to run while t1 sleeps.

Refer to https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html on how to start a thread instead of calling run manually.

Try to use really simple small program to test out concepts. In your learning effort above, you mixed multithreading with UI programming and recursion which could make things hard for you

Upvotes: 0

Sleeping a thread is not what you'll need here.

To pause your game, re-factor the code so that you have a core game method that performs the game progression by a unit every time it is run. You can schedule this core game method to run at a regular rate with a java.util.TimerTask. When you want to pause your game simply stop the TimerTask from running the core game method, and when you un-pause, start running it again.

This way, your program will remain responsive 100% of the time.

Upvotes: 2

Related Questions