I made a Conway's Game of Life. The first round works fine. A grid of white and black panels represent life and dead cells. The view is refreshed to display the change in every generation. However when you click "New Game" I dispose of the old frame and call the games constructor - creating a new one. The problem is: The frame stays blank until the game finishes (loop through a certain number of generations) and then displays the last state of the cells. I used print statements and can see that the program runs correctly. The frame just doesn't display anything until the end. My guess is that I create an instance of the game in the main method and terefore the first game runs in the main thread. The new game doesn't run in the main thread and therefore the GUI is somehow blocked. Can someone point me in the right direction here?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class GameOfLife extends WindowAdapter {
boolean [][] feld;
static JFrame view;
JPanel main, settings;
JMenuBar menubar;
JMenu menu;
JMenuItem newgame, help;
JPanel[][] panels;
static int field = 100;
static int generations = 20;
@Override
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
public GameOfLife(int s, int g){
// Create a random starting field with r rows and c columns
// field is always rectangular, g is number of generations
int r, c;
r = s;
c = s;
view = new JFrame();
main = new JPanel(new GridLayout(r,c));
// Panels are visual representation
// Feld is the model (ie boolean array, true = alive, false = dead)
panels = new JPanel[r][c];
feld = new boolean [r][c];
for(int i = 0; i < r; i++) {
for (int j = 0; j < c; j ++) {
if(i == 0 || j == 0 || i == r-1 || j == c-1) {
feld[i][j] = false;
panels[i][j] = new JPanel();
panels[i][j].setBackground(Color.BLACK);
main.add(panels[i][j]);
} else {
panels[i][j] = new JPanel();
int random = (int)(Math.random () * 2) + 1;
if (random == 1) {
feld[i][j] = true;
panels[i][j].setBackground(Color.WHITE);
} else {
feld[i][j] = false;
panels[i][j].setBackground(Color.BLACK);
}
main.add(panels[i][j]);
}
}
}
view.addWindowListener(this);
view.add(main);
view.setSize(new Dimension(600, 600));
view.setLocationRelativeTo(null);
// Creating the small menu
settings = new JPanel();
settings.setLayout(new FlowLayout());
JTextField size = new JTextField(3);
settings.add(size);
menubar = new JMenuBar();
menu = new JMenu("Menu");
newgame = new JMenuItem("New Game");
help = new JMenuItem("help");
newgame.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
view.dispose();
showOptionsDialog();
newGame(field, generations);
}
});
help.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
showHelpDialog();
}
});
menu.add(newgame);
menu.add(help);
menubar.add(menu);
view.setJMenuBar(menubar);
view.setVisible(true);
// run for number of generations
for (int i = 0; i < g; i++) {
print();
nextGeneration();
System.out.println();
}
} // End Constructor
public static void main(String[] args) {
newGame(field, generations);
}
public static void newGame(int s, int g) {
GameOfLife game = new GameOfLife(s, g);
}
void print() {
for (int i = 0; i < feld.length; i++) {
for (int j = 0; j < feld[i].length; j++) {
if (feld[i][j]) {
panels[i][j].setBackground(Color.WHITE);
System.out.print("o ");
} else {
panels[i][j].setBackground(Color.BLACK);
System.out.print(". ");
}
}
System.out.println();
}
main.revalidate();
try {
Thread.sleep(800);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void nextGeneration() {
/*Zelle ij lebt weiter, wenn 2 oder 3 Nachbarn leben
* Zelle ij stirbt, wenn weniger als 2 oder mehr als 3 Nachbarn leben
* Eine tote Zelle ij wird lebendig, wenn 3 Nachbarn leben
* Die Randzellen ohne genau 8 Nachbarn sind immer tot.
* Aufgabe: Neues Feld für die nächste Generation gemäß der 4 Regeln erzeugen*/
boolean zwischenFeld [][] = new boolean [feld.length][feld[0].length];
// Den Rand ignorierend das innere Feld durchlaufen
for (int i = 1; i < feld.length - 1; i++) {
for (int j = 1; j < feld[i].length - 1; j++) {
// Wieviele Nachbarn leben?
int lebendeNachbarn = 0;
if (feld[i-1][j-1]) {lebendeNachbarn++;}
if (feld[i-1][j]) {lebendeNachbarn++;}
if (feld[i-1][j+1]) {lebendeNachbarn++;}
if (feld[i][j-1]) {lebendeNachbarn++;}
if (feld[i][j+1]) {lebendeNachbarn++;}
if (feld[i+1][j-1]) {lebendeNachbarn++;}
if (feld[i+1][j]) {lebendeNachbarn++;}
if (feld[i+1][j+1]) {lebendeNachbarn++;}
// Ist die Zelle ij lebendig?
if (feld[i][j] == true) {
// Dann wenn 2 oder 3 Nachbarn auchleben lebe weiter
if (lebendeNachbarn == 2 || lebendeNachbarn == 2) {
zwischenFeld[i][j] = true;
// Wenn mehr als 3 Nachbarn oder weniger als 2 dann sterbe
} else if (lebendeNachbarn > 3 || lebendeNachbarn < 2){
zwischenFeld[i][j] = false;
}
// Die Zelle ij ist tot:
} else {
// Erwache zum leben wenn 3 Nachbarn leben
if (lebendeNachbarn == 3) {
zwischenFeld[i][j] = true;
} else {
zwischenFeld[i][j] = false;
}
}
}
}
// Jetzt das Zwischenfeld in das originale Feld kopieren
// Den Rand nach wie vor ignorieren, der bleibt immer tot
for (int i = 1; i < feld.length - 1; i++) {
for (int j = 1; j < feld[i].length - 1; j++) {
feld[i][j] = zwischenFeld[i][j];
}
}
}
public static void showOptionsDialog() {
JDialog showOptionsDialog = new JDialog(view, true);
showOptionsDialog.setLocationRelativeTo(view);
Panel dataPanel = new Panel(new GridLayout(0,1));
TextField sizeField = new TextField("Fieldsize (Def. 100)", 3);
TextField genField = new TextField("Generations (Def. 20)", 2);
dataPanel.add(sizeField);
dataPanel.add(genField);
showOptionsDialog.add(dataPanel, BorderLayout.NORTH);
JButton confirmButton = new JButton("Confirm");
// Make a lokal ActionListener, its used for button
class ConfirmListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// Accept user input for field-size and number of generations
if (sizeField.getText().length() < 5 && genField.getText().length() < 3) {
String strInput = sizeField.getText();
field = Integer.parseInt(strInput);
if(field < 0) {
field *= -1;
}
String strInput2 = genField.getText();
generations = Integer.parseInt(strInput2);
if(generations<0) {
generations*= -1;
}
showOptionsDialog.dispose();
} else {
sizeField.setText("too large or NaN");
}
}
}
ConfirmListener cl = new ConfirmListener();
confirmButton.addActionListener(cl);
sizeField.addActionListener(cl);
Panel buttonPanel = new Panel(new FlowLayout());
buttonPanel.add(confirmButton);
showOptionsDialog.add(buttonPanel, BorderLayout.SOUTH);
showOptionsDialog.setPreferredSize(new Dimension(200,200));
showOptionsDialog.pack();
showOptionsDialog.setVisible(true);
}
public static void showHelpDialog() {
JDialog helpDialog = new JDialog(view, true);
helpDialog.setLocationRelativeTo(view);
Panel dataPanel = new Panel(new FlowLayout());
JTextArea helptext = new JTextArea("The Game of Life.");
helptext.setText("My interpretation of Conway's Game of Life."
+ "Read the rules on Wikipedia:"
+ "https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life");
helptext.setEditable(false);
dataPanel.add(helptext);
helpDialog.setPreferredSize(new Dimension(200,200));
helpDialog.pack();
helpDialog.setVisible(true);
}
}