0

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);
        }
        
        
}
Ben_R
  • 73
  • 4
  • This is quite the wall of code. At first glance, I see that your GUI is not running in the Event Dispatch Thread (EDT). Did you check out the [Concurrency in Swing](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) tutorial? – maloomeister Nov 05 '20 at 13:38
  • Generally, when you create a Swing GUI, you create the GUI components one time. If you want to reuse the GUI, you update the text or images in the GUI components. Also, it's a really good idea to create separate application model, view3, and controller classes. That way, you can focus on one part of the application at a time. – Gilbert Le Blanc Nov 05 '20 at 15:06
  • *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.* - GUI code should run on the `Event Dispatch Thread (EDT)`. This is the Thread that responds to events and repaints the GUI. All Swing components should be updated on the EDT. When you start the game your code runs on the AWT Thread, which is not correct. When you click a button the code in the ActionListener does execute on the EDT, which is good. – camickr Nov 05 '20 at 15:07
  • However, your Thread.sleep() is now preventing the GUI from repainting itself until the looping code is finished executing. The solution is to NOT use Thread.sleep() and looping code. See: https://stackoverflow.com/questions/64196198/bubble-sort-animation for two approaches to restructure looping code. – camickr Nov 05 '20 at 15:07

0 Answers0