Java游戏生活环绕

时间:2017-03-16 13:56:45

标签: java arrays conways-game-of-life

我有一个生命游戏的运行版本,但我无法弄清楚的一件事是如何包裹网格或棋盘,我猜测是必须与邻居计数和网格,我需要一种方法来表明数组包装。

  

规则:

     

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

     

每个都处于两种可能的状态之一,无论是活着的还是死的。

     

每个细胞与其八个邻居(细胞)相互作用   直接水平,

     

垂直或对角相邻。在每一个步骤,   发生以下转换:

     

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

     

2.有三个以上活着的邻居的活细胞死亡,好像过度拥挤一样。

     

3.任何有两三个活邻居的活细胞都会留在下一代。

     

4.只有三个活着的邻居的任何死亡细胞都会成为活细胞。

     

初始模式构成系统的种子。首先   通过同时应用上述规则来创建生成   种子出生和死亡的每个细胞同时发生。

以下是与网格/电路板相关的一些代码;叫 CellsGrid Cells;

  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;

        }      

    }

}

1 个答案:

答案 0 :(得分:0)

作为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

并删除+ 2nbCol

中的nbRow

<小时/> 更新:修复L形

&#34; L形&#34;的错误在你的#34;东南&#34;线的副本中您将c+1转换为+c

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

<小时/> 更大的改进

通常你的代码非常糟糕。有很多逻辑重复(请参阅DRY principle)和其他错误代码,例如巨大的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已经提供了按钮是否已关闭的信息。
  • 我添加了&#34; Step&#34;我调试代码所需的按钮。
  • 除了GridLayout
  • 之外还有其他布局
  • 在UI(JComponent)类中定义serialVersionUID没有多大意义,因为它们实际上不可序列化(尽管它们实现了Serializable接口)。另请参阅Swing components and serialization

<小时/> 更新以及更多评论答案

如果我想要一些预制的初始状态,我只用两个字段Pointrow制作了一个简单的类(更像结构化)column有一个List或它们的数组,然后做了clear并迭代了集合,使列表中的那些单元格生效。如果我想更进一步,我可能会添加一个包装类(结构)SavedGame或其他东西来保存widthheight以及{{1}的集合} S;并使整个UI动态而不是在构造函数中构建一次;然后添加了从某个文件

保存/读取此类配置的功能

至于随机,有一个java.util.Random类,其中包含有用的Point方法。所以我要问用户应该生成多少个随机单元,然后运行一个循环直到该数字并使用Random生成的新的活细胞通过生成行和列。请注意,如果要填充大部分单元格,可能需要检查所选择的下一个随机单元格是否已存活,如果是,则#34;掷骰子&#34;再一次。如果你想要更多填充,你需要将算法更改为更复杂或填充90%的单元格可能需要永远。一个想法可能是在范围内生成单个细胞索引(0;行*高度 - 已经活着的细胞的数量)而不是独立的行和列,然后只是从左上角到右下角开始计数只有死细胞和使相应的细胞生存。通过这种方式,您可以确保每个新的活细胞都需要相同的时间来生成。