繁体   English   中英

Java游戏人生

[英]Java Game of life wrap around

我有一个运行中的生活游戏,但是我不知道的一件事是如何环绕网格或木板,我想这与邻居数和网格有关,我需要一种方法来指示数组包装。

规则 :

生命游戏的宇宙是由正方形单元组成的无限二维正交网格,

每种状态都处于两种可能的状态之一,即生存或死亡。

每个单元格都与其八个邻居互动,这八个邻居是直接水平的单元格,

垂直或对角线相邻。 在每个时间步上,都会发生以下转换:

1,任何少于两个活邻居的活细胞都会死亡,好像是由于人口不足造成的。

2.具有三个以上活邻居的任何活细胞都会死亡,好像人满为患。

3.任何有两个或三个活邻居的活细胞都可以存活到下一代。

4,任何有三个活邻居的死细胞都变成活细胞。

初始模式构成了系统的种子。 第一代是通过将上述规则同时应用于种子中的每个细胞而创建的-生与死同时发生。

这是一些与网格/板有关的代码; 称为CellsGrid单元;

  GameOfLife2(int nbRow, int nbCol) {

            super(" New GameOfLife");

            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);



            // create the labels (2 more on each size) these wont be shown

            // but will be used in calculating the cells alive around

            Cells = new CellsGrid[nbRow+2][nbCol+2];

            for(int r = 0; r < nbRow+2; r++) {

                for(int c = 0; c < nbCol+2; c++) {

                    Cells[r][c] = new CellsGrid();

                }

            }

for(int r = 1; r < nbRow+1; r++) {

            for(int c = 1; c < nbCol+1; c++) {

                panel.add(Cells[r][c]);

                Cells[r][c].addNeighbour(Cells[r-1][c]);    // North

                Cells[r][c].addNeighbour(Cells[r+1][c]);    // South

                Cells[r][c].addNeighbour(Cells[r][c-1]);    // West

                Cells[r][c].addNeighbour(Cells[r][c+1]);    // East

                Cells[r][c].addNeighbour(Cells[r-1][c-1]);  // North West

                Cells[r][c].addNeighbour(Cells[r-1][c+1]);  // North East

                Cells[r][c].addNeighbour(Cells[r+1][c-1]);  // South West

                Cells[r][c].addNeighbour(Cells[r+1][c+1]);  // South East

            }


        }

 if(!gameRunning)

            return;

        ++generation;

        CellsIteration.setText("Generation: " + generation);

        for(int r = 0; r < Cells.length; r++) {

            for(int c = 0; c < Cells[r].length; c++) {

                Cells[r][c].checkState();

            }

        }

        for(int r = 0; r < Cells.length; r++) {

            for(int c = 0; c < Cells[r].length; c++) {

                Cells[r][c].updateState();

            }

        }

    }



void checkState() {



  // number alive around

    int NumNeighbours = 0; // number alive neighbours

    // see the state of my neighbour

    for(int i = 0; i < numNeighbours; i++)

        NumNeighbours += neighbour[i].state;

    // newState

    if(state == 1) {                // if alive

        if(NumNeighbours < 2)              // 1.Any live cell with fewer than two live neighbours dies

            newState = 0;

        if(NumNeighbours > 3)              // 2.Any live cell with more than three live neighbours dies

            newState = 0;

    }

    else {

        if(NumNeighbours == 3)            // 4.Any dead cell with exactly three live neighbours becomes a live cell

            newState = 1;

    }

}

完整代码:

package com.ggl.life;
import java.awt.*;

import java.awt.event.*;
import java.util.Random;

import javax.swing.*;


public class GameOfLife2 extends JFrame implements ActionListener {

    /**
     * 
     */
    public static Random random  = new Random();

    private static final long serialVersionUID = 1L;

    static final Color[] color = {Color.YELLOW, Color.BLACK};

    // size in pixel of every label

    static final int size = 15;

    static final Dimension dim = new Dimension(size, size);

    static final int GenDelay = 200;

    // the cells labels
    private CellsGrid[][] Cells;

    // timer that fires the next generation

    private Timer timer;

    // generation counter

    private int generation = 0;

    private JLabel CellsIteration = new JLabel("Generation: 0");

    // the 3 buttons

    private JButton clearBtn = new JButton("Clear"),

                    PauseBtn = new JButton("Pause"),

                    StartBtn = new JButton("Start");

    // the slider for the speed


    // state of the game (running or pause)

    private boolean gameRunning = false;

    // if the mouse is down or not

    private boolean mouseDown = false;



    GameOfLife2(int nbRow, int nbCol) {

        super(" New GameOfLife");

        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);



        // create the labels (2 more on each size) these wont be shown

        // but will be used in calculating the cells alive around

        Cells = new CellsGrid[nbRow+2][nbCol+2];

        for(int r = 0; r < nbRow+2; r++) {

            for(int c = 0; c < nbCol+2; c++) {

                Cells[r][c] = new CellsGrid();

            }


        }


        // panel in the center with the labels

        JPanel panel = new JPanel(new GridLayout(nbRow, nbCol, 1, 1));

        panel.setBackground(Color.BLACK);

        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));



        // add each label (not the one on the border) to the panel and add to each of them its neighbours

        for(int r = 1; r < nbRow+1; r++) {

            for(int c = 1; c < nbCol+1; c++) {

                panel.add(Cells[r][c]);

                Cells[r][c].addNeighbour(Cells[r-1][c]);
                //Cells[r][c].addNeighbour(getCellSafe(r-1, c)); // North

                Cells[r][c].addNeighbour(Cells[r+1][c]);    // South
              //Cells[r][c].addNeighbour(getCellSafe(r+1, c));

                Cells[r][c].addNeighbour(Cells[r][c-1]);    // West
              //Cells[r][c].addNeighbour(getCellSafe(r, c-1));

                Cells[r][c].addNeighbour(Cells[r][c+1]);    // East
              //Cells[r][c].addNeighbour(getCellSafe(r, c+1));

                Cells[r][c].addNeighbour(Cells[r-1][c-1]);  // North West
              //Cells[r][c].addNeighbour(getCellSafe(r-1, c-1));

                Cells[r][c].addNeighbour(Cells[r-1][c+1]);  // North East
              //Cells[r][c].addNeighbour(getCellSafe(r-1, c+1));

                Cells[r][c].addNeighbour(Cells[r+1][c-1]);  // South West
              //Cells[r][c].addNeighbour(getCellSafe(r+1, c-1));

               Cells[r][c].addNeighbour(Cells[r+1][c+1]);  // South East
              //Cells[r][c].addNeighbour(getCellSafe(r+1, +c));

            }


        }



        // now the panel can be added

        add(panel, BorderLayout.CENTER);



        // the bottom panel with the buttons the generation label and the slider

        // this panel is formed grid panels

        panel = new JPanel(new GridLayout(1,3));

       // another panel for the 3 buttons

        JPanel buttonPanel = new JPanel(new GridLayout(1,3));

        clearBtn.addActionListener(this);

        buttonPanel.add(clearBtn);

        PauseBtn.addActionListener(this);

        PauseBtn.setEnabled(false);           // game is pause the pause button is disabled

        buttonPanel.add(PauseBtn);

        StartBtn.addActionListener(this);

        buttonPanel.add(StartBtn);

        // add the 3 buttons to the panel

        panel.add(buttonPanel);

        // the generation label

        CellsIteration.setHorizontalAlignment(SwingConstants.CENTER);

        panel.add(CellsIteration);


        // in the JFrame

        add(panel, BorderLayout.NORTH);

        // put the frame on

        setLocation(20, 20);

        pack(); // adjust to the window size
        setVisible(true);

        // start the thread that run the cycles of life

        timer = new Timer(GenDelay , this);

    }

    private CellsGrid getCellSafe(int r0, int c0) {
        int r = r0  % Cells.length; // Cells.length is effectively nbRow
        if (r < 0) r += Cells.length; // deal with how % works for negatives
        int c = c0  % Cells[0].length; // Cells[0].length is effectively nbCol
        if (c < 0) c += Cells[0].length; // deal with how % works for negatives
        return Cells[r][c];
   }

//end of game of life

    // called by the Timer and the JButtons

    public synchronized void actionPerformed(ActionEvent e) {

        // test the JButtons first

        Object o = e.getSource();

        // the clear button

        if(o == clearBtn) {

            timer.stop();                   // stop timer

            gameRunning = false;            // flag gamme not running

            PauseBtn.setEnabled(false);       // disable pause button

            StartBtn.setEnabled(true);           // enable go button

            // clear all cells

            for(int r = 1; r < Cells.length ; r++) {

                for(int c = 1; c < Cells[r].length ; c++) {

                    Cells[r][c].clear();

                }

            }

            // reset generation number and its label

            generation = 0;

            CellsIteration.setText("Generation: 0");

            return;

        }

        // the pause button

        if(o == PauseBtn) {

            timer.stop();                   // stop timer

            gameRunning = false;            // flag not running

            PauseBtn.setEnabled(false);       // disable myself

            StartBtn.setEnabled(true);           // enable go button

            return;

        }

        // the go button

        if(o == StartBtn) {

            PauseBtn.setEnabled(true);                // enable pause button

            StartBtn.setEnabled(false);                  // disable myself

            gameRunning = true;                     // flag game is running

            timer.setDelay(GenDelay);

            timer.start();

           return;

        }

        // not a JButton so it is the timer

        // set the delay for the next time

        timer.setDelay(GenDelay);

        // if the game is not running wait for next time

        if(!gameRunning)

            return;

        ++generation;

        CellsIteration.setText("Generation: " + generation);

        for(int r = 0; r < Cells.length; r++) {

            for(int c = 0; c < Cells[r].length; c++) {

                Cells[r][c].checkState();

            }

        }

        for(int r = 0; r < Cells.length; r++) {

            for(int c = 0; c < Cells[r].length; c++) {

                Cells[r][c].updateState();

            }

        }

    }
    //end of action


    // to start the whole thing as a Java application

    public static void main(String[] arg) {

      SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                new GameOfLife2(50, 50);

            }

        });

    }



    // A class that extends JLabel but also check for the neigbour

    // when asked to do so

    class CellsGrid extends JLabel implements MouseListener {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        private int state, newState;

        private int numNeighbours;

        private CellsGrid[] neighbour = new CellsGrid[8]; // array of total neighbours with possibility of 8




        CellsGrid() {

            state = newState = 0;           // Dead

            setOpaque(true);                // so color will be showed

            setBackground(color[0]);        //set colour of dead cell

            addMouseListener(this);         // to select new LIVE cells

            this.setPreferredSize(dim);     //set size a new cells

        }

        // to add a neighbour

        void addNeighbour(CellsGrid n) {

            neighbour[numNeighbours++] = n;

        }

        // to see if I should live or not

        void checkState() {

            // number alive around

            int NumNeighbours = 0; // number alive neighbours

            // see the state of my neighbour

            for(int i = 0; i < numNeighbours; i++)

                NumNeighbours += neighbour[i].state;

            // newState

            if(state == 1) {                // if alive

                if(NumNeighbours < 2)              // 1.Any live cell with fewer than two live neighbours dies

                    newState = 0;

                if(NumNeighbours > 3)              // 2.Any live cell with more than three live neighbours dies

                    newState = 0;

            }

            else {

                if(NumNeighbours == 3)            // 4.Any dead cell with exactly three live neighbours becomes a live cell

                    newState = 1;

            }

        }

        // after the run switch the state to new state

        void updateState() {

            if(state != newState) {     // do the test to avoid re-setting same color for nothing  

                state = newState;

                setBackground(color[state]);

            }

        }



        // called when the game is reset/clear

        void clear() {

            if(state == 1 || newState == 1) {

               state = newState = 0;

                setBackground(color[state]);

            }

        }

        @Override

        public void mouseClicked(MouseEvent arg0) {

        }

        // if the mouse enter a cell and it is down we make the cell alive

        public void mouseEntered(MouseEvent arg0) {

            if(mouseDown) {

                state = newState = 1;

                setBackground(color[1]);               

            }

        }

        @Override

        public void mouseExited(MouseEvent arg0) {

        }

        // if the mouse is pressed on a cell you register the fact that it is down

        // and make that cell alive

        public void mousePressed(MouseEvent arg0) {

            mouseDown = true;

            state = newState = 1;

            setBackground(color[1]);

        }

        // turn off the fact that the cell is down

        public void mouseReleased(MouseEvent arg0) {

            mouseDown = false;

        }      

    }

}

作为UnholySheep,您需要学习%运算符。 %是计算除法又称为模的提醒的方法。 参见https://en.wikipedia.org/wiki/Modulo_operation

在这里应用它的最简单方法是这样的。 首先介绍getCellSafe方法

 private CellsGrid getCellSafe(int r0, int c0) {
      int r = r0  % Cells.length; // Cells.length is effectively nbRow
      if (r < 0) r += Cells.length; // deal with how % works for negatives
      int c = c0  % Cells[0].length; // Cells[0].length is effectively nbCol
      if (c < 0) c += Cells[0].length; // deal with how % works for negatives
      return Cells[r][c];
 }

然后使用这种方法直接访问,例如

     Cells[r][c].addNeighbour(getCellSafe(r-1, c);    // North

并摆脱nbColnbRow中的+ 2


更新: 修复L形

“ L形”的错误在“东南”行的副本中,其中您将c+1转换为+c

  Cells[r][c].addNeighbour(Cells[r+1][c+1]); // South East //Cells[r][c].addNeighbour(getCellSafe(r+1, +c)); 


更大的进步

通常,您的代码非常糟糕。 有很多逻辑重复(请参阅DRY原理 )和其他不良代码,例如庞大的ActionListener 这是我尝试清理一下的尝试:

 public class GameOfLife2 extends JFrame { /** * */ public static Random random = new Random(); static final Color[] color = {Color.YELLOW, Color.BLACK}; // size in pixel of every label static final int cellSize = 15; static final Dimension cellDim = new Dimension(cellSize, cellSize); static final int GenDelay = 200; // the cells labels private CellsGrid[][] Cells; // that fires the next generation private Timer timer; // generation counter private int generation = 0; private JLabel CellsIteration = new JLabel("Generation: 0"); // the 3 buttons private JButton clearBtn = new JButton("Clear"); private JButton PauseBtn = new JButton("Pause"); private JButton StartBtn = new JButton("Start"); private JButton StepBtn = new JButton("Step"); // the slider for the speed // state of the game (running or pause) private boolean gameRunning = false; GameOfLife2(int nbRow, int nbCol) { super("New GameOfLife"); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); Cells = new CellsGrid[nbRow][nbCol]; for (int r = 0; r < nbRow; r++) { for (int c = 0; c < nbCol; c++) { Cells[r][c] = new CellsGrid(); } } // panel in the center with the labels JPanel panel = new JPanel(new GridLayout(nbRow, nbCol, 1, 1)); panel.setBackground(Color.BLACK); panel.setBorder(BorderFactory.createLineBorder(Color.BLACK)); // add each label (not the one on the border) to the panel and add to each of them its neighbours for (int r = 0; r < nbRow; r++) { for (int c = 0; c < nbCol; c++) { CellsGrid curCell = Cells[r][c]; panel.add(curCell); for (int dr = -1; dr <= 1; dr++) { for (int dc = -1; dc <= 1; dc++) { CellsGrid neighbor = getCellSafe(r + dr, c + dc); if (neighbor != curCell) curCell.addNeighbour(neighbor); } } } } // now the panel can be added add(panel, BorderLayout.CENTER); // the bottom panel with the buttons the generation label and the slider Box headerPanel = Box.createHorizontalBox(); Box buttonPanel = Box.createHorizontalBox(); // old pre-Java 8 syntax //clearBtn.addActionListener(new ActionListener() //{ // @Override // public void actionPerformed(ActionEvent e) // { // clearGame(); // } //}); clearBtn.addActionListener((e) -> clearGame()); // holy Java 8 lambda syntax buttonPanel.add(clearBtn); PauseBtn.addActionListener((e) -> stopGame()); buttonPanel.add(PauseBtn); StartBtn.addActionListener((e) -> startGame()); buttonPanel.add(StartBtn); StepBtn.addActionListener((e) -> stepToNextGeneration()); buttonPanel.add(StepBtn); // the generation label CellsIteration.setHorizontalAlignment(SwingConstants.CENTER); headerPanel.add(Box.createHorizontalStrut(10)); headerPanel.add(buttonPanel); headerPanel.add(Box.createHorizontalStrut(10)); headerPanel.add(Box.createHorizontalGlue()); headerPanel.add(Box.createHorizontalStrut(10)); headerPanel.add(CellsIteration); headerPanel.add(Box.createHorizontalGlue()); // if you want "generation" label closer to center headerPanel.add(Box.createHorizontalStrut(10)); // in the JFrame add(headerPanel, BorderLayout.NORTH); // put the frame on setLocation(20, 20); gameRunning = false; generation = 0; updateGenerationTitle(); updateButtonsState(); pack(); // adjust to the window size setMinimumSize(new Dimension(600, 500)); setVisible(true); // start the thread that run the cycles of life timer = new Timer(GenDelay, (e) -> onTimerStep()); } private CellsGrid getCellSafe(int r0, int c0) { int r = r0 % Cells.length; // Cells.length is effectively nbRow if (r < 0) r += Cells.length; // deal with how % works for negatives int c = c0 % Cells[0].length; // Cells[0].length is effectively nbCol if (c < 0) c += Cells[0].length; // deal with how % works for negatives return Cells[r][c]; } private void updateButtonsState() { PauseBtn.setEnabled(gameRunning); StartBtn.setEnabled(!gameRunning); StepBtn.setEnabled(!gameRunning); } private void updateGenerationTitle() { CellsIteration.setText("Generation: " + generation); } private void startGame() { gameRunning = true; // flag game is running updateButtonsState(); timer.setDelay(GenDelay); timer.start(); } private void stopGame() { timer.stop(); // stop timer gameRunning = false; // flag not running updateButtonsState(); } private void clearGame() { stopGame(); // clear all cells for (int r = 0; r < Cells.length; r++) { for (int c = 0; c < Cells[r].length; c++) { Cells[r][c].clear(); } } // reset generation number and its label generation = 0; updateGenerationTitle(); } private void stepToNextGeneration() { ++generation; updateGenerationTitle(); for (int r = 0; r < Cells.length; r++) { for (int c = 0; c < Cells[r].length; c++) { Cells[r][c].checkState(); } } for (int r = 0; r < Cells.length; r++) { for (int c = 0; c < Cells[r].length; c++) { Cells[r][c].updateState(); } } } private void onTimerStep() { if (gameRunning) stepToNextGeneration(); } // to start the whole thing as a Java application public static void main(String[] arg) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GameOfLife2(50, 50); } }); } // A class that extends JLabel but also check for the neigbour // when asked to do so static class CellsGrid extends JLabel implements MouseListener { private boolean alive = false; private boolean newAlive = false; private final ArrayList<CellsGrid> neighbours = new ArrayList<>(8); // array of total neighbours with possibility of 8 CellsGrid() { setOpaque(true); // so color will be showed addMouseListener(this); // to select new LIVE cells setPreferredSize(cellDim); //set size a new cells updateColor(); } // to add a neighbour void addNeighbour(CellsGrid n) { neighbours.add(n); } // to see if I should live or not void checkState() { // number alive around int NumNeighbours = 0; // number alive neighbours // see the state of my neighbour for (CellsGrid neighbour : neighbours) NumNeighbours += neighbour.alive ? 1 : 0; // 1. Any live cell with fewer than two live neighbours dies // 2. Any live cell with two or three live neighbours lives on to the next generation. // 3. Any live cell with more than three live neighbours dies // 4. Any dead cell with exactly three live neighbours becomes a live cell if (alive) { newAlive = (NumNeighbours == 2) || (NumNeighbours == 3); } else { newAlive = (NumNeighbours == 3); } } // after the run switch the state to new state void updateState() { alive = newAlive; updateColor(); } // called when the game is reset/clear void clear() { alive = newAlive = false; updateColor(); } private void updateColor() { setBackground(color[alive ? 1 : 0]); } @Override public void mouseClicked(MouseEvent arg0) { } // if the mouse enter a cell and it is down we make the cell alive @Override public void mouseEntered(MouseEvent e) { boolean mouseDown = (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0; if (mouseDown) { alive = newAlive = true; updateColor(); } } @Override public void mouseExited(MouseEvent e) { } @Override public void mousePressed(MouseEvent arg0) { // state = newState = true; alive = newAlive = !newAlive; //invert state is more useful, so you can clear something updateColor(); } @Override public void mouseReleased(MouseEvent arg0) { } } } 

该代码似乎可以正常工作,包括滑翔机沿对角线绕场飞行。

一些注意事项:

  • 我将每个动作ActionListener拆分为独立的对象,并使用Java 8语法使它们简短易注册
  • 我介绍了一些updateXyzstepToNextGeneration方法来大大减少代码重复
  • MouseEvent已经提供了有关按钮是否按下的信息。
  • 我添加了“调试”按钮,这是调试您的代码所必需的。
  • 除了GridLayout还有其他布局
  • 在UI( JComponent )类中定义serialVersionUID并没有多大意义,因为它们实际上无法序列化(尽管它们确实实现了Serializable接口)。 另请参见Swing组件和序列化


更新更多评论答案

如果我想有一些预制的初始状态,我做了一个简单的类(更像structute) Point只有两个字段rowcolumn ,有一个List或它们的阵列,然后做了clear和遍历集合到使列表中的那些单元格活跃起来。 如果我想走得更远,我可能会再添加一个包装类(结构) SavedGame或一些可以节省widthheight东西,以及一些Point 并使整个UI动态化,而不是在构造函数中一次构建一次; 然后增加了从某些文件保存/读取此类配置的功能

至于随机,有一个java.util.Random类,它带有有用的int nextInt(int n)方法。 因此,我想问用户应该使多少个随机单元变为活动状态,然后循环运行直到达到该数量,并使用Random通过生成行和列来生成新的活动单元。 请注意,如果要填充大量的单元格,则可能需要检查所选的下一个随机单元格是否还活着,如果是,请再次“掷骰子”。 如果要填充更多,则需要将算法更改为更复杂,或者填充90%的单元可能要花很多时间。 一种想法可能是生成范围为(0;行*高度-已经存活的细胞数)的单个细胞索引,而不是独立的行和列,然后仅从左上角开始到右下角的细胞进行计数,仅计算死细胞,使相应的细胞存活。 这样,您可以确保每个新的活细胞都花费相同的时间来生成。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM