尝试复制康威的生命游戏无法扩展或无法正常工作

Attempt at Replicating Conway's Game of Life does not expand or work properly

如果您不知道 Conway 的人生游戏是什么,请查看此游戏,这是一款零玩家游戏,并且是蜂窝自动化游戏。 https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

我需要帮助调试我的代码中的以下问题。如有必要,我可以编辑此问题以包括我的程序的旧版本,这些版本不打算扩展。

我也会接受改进我的程序的提示,包括(但不限于)时间效率,(但是离题,因为这不是主要问题)和不正确使用现有方法。 (我的 paint() 方法)

Problems:

Examine Glider A in image 1. (Expected behavior) The whole point of the program was to make the cells shrink in the JFrame and add another row or column of dead cells to the ArrayList<ArrayList<Boolean>> grid. This glider, unfortunately does not cause this to happen. Instead, the result (Unexpected behavior) was that it simply "flattened into a square" as shown in image 2 (the square marked with an A).

Examine Glider B in image 1. (Expected behavior) Seeing the results Glider A in image 1 made me think that glider B would end the same. However, this is not remotely true. Look at glider B in image 2. It, for some reason, does not even reach the border. (Unexpected)

这个程序的用途和目的:Conway 的人生游戏是在无限的二维平面上进行的,所以我想复制它。我的其他程序使用固定的数组大小,但是这个使用嵌套的 ArrayLists 来扩展,并且真正是无限的(直到内存限制到来)

不幸的是,这个扩展程序根本无法扩展边界。 (边框实际上比看起来要小。基本上,(假装我的 ArrayList> 网格是一个数组)grid[0][0 to grid.length-1] 包含,grid[grid.length-1][0 to grid.length-1] 包含,grid[1 to grid.length-1][0] 包含,grid[1 to grid.length-1][grid.length-1]包括在内。)图像 1 中的滑翔机 A 没有执行指定的步骤来腾出空间,而是靠着边界展平。 (边框在单元格下方,灰色线条不存在)

图片 1 Image 2

这些图像相互对应;图片 1 中的滑翔机 A 标题生成图片 2 中的正方形,也 标记为 A。如果您在代码的注释中看到任何描述 "line 74," 指向 "line 74" 的词或类似 "UP" 的词(没有任何其他含义),请告诉我,以便我删除它们.那是一个单独的、已经修复的错误。

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Expansion {
    static int delay = 50;
    static SquarePaint sp = new SquarePaint();
    static int K = 75; // An integer used below to create the starting setup for the grid.
    static{
        // 001 101 011
        boolean[][] array = new boolean [300][200];
        //      array[30][30]
        array[K][K+2] = true;
        array[K+1][K] = true;
        array[K+1][K+2] = true;
        array[K+2][K+1] = true;
        array[K+2][K+2] = true;
        sp.define(array); // Uses array to make first setup
    }
    public static void main (String[] args){
        // It is possible to put this information in a constructor instead
        JFrame j = new JFrame();
        j.add(sp); // Must add a custom paint component overriding the paint component, 
        //a class could and will do this
        j.setVisible(true);
        j.setSize(2000, 1000);
        j.setResizable(false);
        j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public static class SquarePaint extends Component{
        static boolean VALUE = false;
        public static final int screenX = 1000, screenY = 500;
        static int cellSize = 5;
        int cellsX = screenX / cellSize, cellsY = screenY / cellSize, frames = 0;

        ArrayList<ArrayList<Boolean>> grid = new ArrayList<ArrayList<Boolean>>(); ////
        Timer timer = new Timer(delay, new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent A) {
                //UPDATE FUNCTION
                frames++;
                boolean [][] oldGrid = new boolean[cellsX][cellsY];
                for(int i=0; i<cellsX && i<grid.get(1).size(); i++){
                    for(int j=0; j<cellsY && j<grid.get(1).size(); j++){
                        oldGrid[i][j] = grid.get(i).get(j); // <<Line 74>><<Line 74>><<Line 74>>

                    }
                }
                /* These are notes
                 * How would I:
                 * Expand LinkedLists, for example
                         (Grid in a picture, 0 = variable, + = new cell)
                            0 0 0 +
                            0 0 0 +
                            0 0 0 +
                            0 0 0 +
                 * Adjust existing variables (and poorly coded methods if they exist) to suit this new change
                 */
                for(int i=1; i<cellsX-2; i++) {
                    for(int j=1; j<cellsY-2; j++) { // TODO
//                      System.out.println(i + " " + j);
                        int nearbyCells = nearbyCells(oldGrid, i, j);
                        /////////////////////////////////////////////
                        if(oldGrid[i][j]){ 
                            // If the cell is alive 
                            // and is beside the no interact border, expand it by 1
                            // (Basically, to prevent ArrayOutOfBoundsException s, 
                            // (the ArrayList equivalent) only cells from 1 to cellsX-1(-1)/cellsY-1(-1) 
                            // are counted,)
                            // meaning that a whole row/column of cells is ignored and is not interacted with,
                            // until the grid expands.
                            ArrayList <Boolean> l = new ArrayList<Boolean>();
                            for(int k=0; k<cellsX; k++){
                                l.add(false);
                            }
                            if(i <= 1){ // You may notice that the above comments are wrong.
                                // This is because I thought this might be a small chance for it to succeed
                                // while I was frustrated this was not working.
                                grid.add(0, l);
                                cellSize = screenX / (cellSize + 1);
                                cellsX++;
                                VALUE = true;
                                repaint();
                                System.out.println("i<=5");
                            }
                            if(i >= cellsX-1){
                                grid.add(l);
                                cellSize = screenX / (cellSize + 1);
                                cellsX++;
                                VALUE = true;
                                repaint();
                                System.out.println("i>=cellsX");
                            }
                            if(j <= 1) {
                                for(int k=0; k<cellsY; k++){
                                    grid.get(k).add(false);
                                }
                                cellSize = screenY / (cellSize + 1);
                                cellsY++;
                                VALUE = true;
                                System.out.println("j<=5");
                                repaint();
                            }
                            if(j >= cellsY-1) {
                                for(int k=0; k<cellsY; k++){
                                    grid.get(k);
                                }
                                cellSize = screenY / (cellSize + 1);
                                cellsY++;
                                VALUE = true;
                                System.out.println("j>cellsY");
                                repaint();
                            }
                            //corner cases aren't a problem, it'll add to both sides
                            //  && (i == 1 || i == cellsX-1 || j == 1 || j == cellsY-1)
                            /* i.e.
                             * 0 = dead cells
                             * 1 = annoying cells in the corner
                             * + = new cells
                             * First, it'll do this
                             * + + +
                             * 0 1 1
                             * 0 1 1 // Note that the square is a "still life form" meaning that it will
                             * 0 0 0 // simply sit there and do nothing for infinite generations if 
                             *      // untouched
                             * But the if statements above will return true more than once, so
                             * 0 0 0 +
                             * 0 1 1 +
                             * 0 1 1 +
                             * 0 0 0 +
                             */
                        }
                        /////////////////////////////////////////////
                        if (oldGrid[i][j] && !(nearbyCells == 2 || nearbyCells == 3)){ 
                            // if it is alive, sustain rules
                            grid.get(i).set(j, false);
                        }
                        else if (!oldGrid[i][j] && nearbyCells == 3){ // if it is dead, birth rules
                            grid.get(i).set(j, true);
                        }
                    }
                }
                repaint(); // never erase, note that in bigger applications I assume that they 
                // draw to pictures that get slapped onto the screen so the paint function doesn't 
                // get called 9999 times and slow the fps down

            }
        });
        int nearbyCells(boolean[][] oldGrid, int i, int j) { // A method that calculates how many cells are
            //alive near it, i.e.
            /* 0 = Dead
             * 1 = Alive
             * "+" = center
             * 1 0 0
             * 0 + 1
             * 0 0 1
             * If you called nearbyCells on the center in the example, then it would return 3.
             */
            int nearbyCells = 0;

            if(oldGrid[i+1][j+1]) nearbyCells++;
            if(oldGrid[i+1][j]) nearbyCells++;
            if(oldGrid[i+1][j-1]) nearbyCells++;

            if(oldGrid[i][j+1]) nearbyCells++;
            if(oldGrid[i][j-1]) nearbyCells++; // i, j is where the to-be-changed cell is, skip it

            if(oldGrid[i-1][j+1]) nearbyCells++;
            if(oldGrid[i-1][j]) nearbyCells++;
            if(oldGrid[i-1][j-1]) nearbyCells++;
            return nearbyCells;
        }
        public SquarePaint(boolean[][] grid){
            define(grid);
            timer.start();
        }
        public SquarePaint(){
            timer.start();
        }
        public void define (boolean[][] grid){ // define cannot be called twice, not intended
            for(int i=0; i<cellsX; i++){
                ArrayList<Boolean> l = new ArrayList<Boolean>();
                for(int j=0; j<cellsY; j++){
                    l.add(grid[i][j]);
                }
                this.grid.add(l);
            }
        }
        @Override
        public void paint(Graphics g) {
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, screenX, screenY); // Warning: Color must be set 
            // each time if the paint command is ever to be called again


            for(int i=0; i<cellsX-1; i++){
                for(int j=0; j<cellsY-1; j++){
                    try{
                        if(grid.get(i).get(j)){
                            g.setColor(Color.WHITE);
                            g.fillRect(i * cellSize, j * cellSize, cellSize, cellSize); 
                        } // Basically, 2 for loops
                    }// Nested for loops draw each cell on the potentially expanding ArrayList "grid" of cells

                    catch(java.lang.IndexOutOfBoundsException e){
                        System.out.println(i + " " + j);
                        e.getStackTrace();
                    }
                    g.setColor(Color.GRAY);
                    g.drawRect(i * cellSize, j * cellSize, cellSize, cellSize);
                }
            }


        }

    }

}

我不知道您的应用程序中间的逻辑应该做什么。不应该那么复杂。这在另一个线程中运行模拟,因此可能有助于解决性能问题。

public class GameOfLife extends JFrame {
    private static final long serialVersionUID = 1L;
    private Thread thread;
    private SimulationThread simulationThread;

    public GameOfLife() {
        super("GameOfLife");
    }


    class MyPanel extends JPanel {
        private static final long serialVersionUID = 1L;

        public MyPanel() {
            setBorder(BorderFactory.createLineBorder(Color.black));
        }

        public Dimension getPreferredSize() {
            return new Dimension(1000, 1000);
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);       
            Graphics2D g2d = (Graphics2D)  g;
            simulationThread.drawScreenItems(g2d);
        }
    }
    /**
     * Create the GUI and show it. For thread safety, this method should be invoked
     * from the event-dispatching thread.
     */
    public void createAndShowGUI() {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        MyPanel myPanel = new MyPanel();
        add(myPanel);

        // Display the window.
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
        // Create and set up the window.

        // create thread with genetic reproduction callback code.
        simulationThread = new SimulationThread(myPanel);
        thread = new Thread(simulationThread);
        thread.start();
    }

    public static void main(String[] args) {
        final GameOfLife swingGa = new GameOfLife();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                swingGa.createAndShowGUI();
            }
        });
    }
}

public class SimulationThread implements Runnable {
    private MyPanel myPanel;
    public boolean run = true;
    static final int GAME_SIZE = 200;

    boolean[][] cells;
    boolean[][] newCells;

    public SimulationThread(MyPanel myPanel) {
        this.myPanel = myPanel;
        cells = new boolean[GAME_SIZE][GAME_SIZE];
        for (int i = 1; i < cells.length - 1; i++) {
            for (int j = 1; j < cells[0].length - 1; j++) {
                cells[i][j] = Math.random() > 0.5;
            }
        }
        newCells = new boolean[GAME_SIZE][GAME_SIZE];
    }

    @Override
    public void run() {
        while (run) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // Iterate through the array, follow game of life rules
            for (int i = 1; i < cells.length - 1; i++) {
                for (int j = 1; j < cells[0].length - 1; j++) {
                    int surrounding = 0;
                    if (cells[i - 1][j - 1]) surrounding++;
                    if (cells[i - 1][j]) surrounding++;
                    if (cells[i - 1][j + 1]) surrounding++;
                    if (cells[i][j - 1]) surrounding++;
                    if (cells[i][j + 1]) surrounding++;
                    if (cells[i + 1][j - 1]) surrounding++;
                    if (cells[i + 1][j]) surrounding++;
                    if (cells[i + 1][j + 1]) surrounding++;
                    newCells[i][j] = false;
                    if (cells[i][j]) {
                        // Cell is alive, Can the cell live? (2-3)
                        if ((surrounding == 2) || (surrounding == 3)) {
                            newCells[i][j] = true;
                        }
                    } else {
                        // Cell is dead, will the cell be given birth? (3)
                        if (surrounding == 3) {
                            newCells[i][j] = true;
                        }
                    }
                }
            }
            synchronized(cells) {
                for (int i = 1; i < cells.length - 1; i++) {
                    for (int j = 1; j < cells[0].length - 1; j++) {
                        cells[i][j] = newCells[i][j];
                    }
                }
            }
            myPanel.repaint();
        }

    }

    public void drawScreenItems(Graphics2D g2d) {
        synchronized(cells) {
            for (int i = 1; i < cells.length - 1; i++) {
                for (int j = 1; j < cells[0].length - 1; j++) {
                    if (cells[i][j])
                        g2d.fillRect(i * (1000 / GAME_SIZE), j * (1000 / GAME_SIZE), 1000 / GAME_SIZE, 1000 / GAME_SIZE);
                }
            }
        }
    }
}