Java版生命游戏

时间:2022-01-05 19:07:29

在研究元胞自动机理论过程中,Conway发明生命游戏(Game of Life、GoL),在1970s风靡一时。
这是0人游戏,即按照初始的设置,游戏自动演化。在类似围棋的棋盘中,每一个格子可以是空格或者存在一个生命/细胞/Cell;每一个格子有8个相邻的格子(正上方、正下方、右侧、左侧、左上方、右上方、左下方以及右下方),相邻的格子中存活的生命数量称为其邻居(neighbor)数。在世代交替时,所有的格子根据其邻居数,诞生新生命、Cell保持存活或者Cell死亡。
生命演化规则:B3/S23
  • 一个生命如果恰好有2个或3个邻居,它会存活到下一个世代;否则,会因为孤独或拥挤而死亡。
  • 一个空格,如果恰好有3个邻居,则诞生一个新生命。

Java项目GameOfLife结构:

Java版生命游戏

1、Life

package net.hw.game;
/**
* Created by howard on 2018/2/8.
*/
public class Life {
private int maxRow;
private int maxCol;

private int[][] grid;

public Life(int maxRow, int maxCol) {
this.maxRow = maxRow;
this.maxCol = maxCol;
grid = new int[maxRow + 2][maxCol + 2];
for (int row = 0; row <= maxRow + 1; row++)
for (int col = 0; col <= maxCol + 1; col++)
grid[row][col] = 0;
}

public int[][] getGrid() {
return grid;
}

public void setGrid(int[][] grid) {
this.grid = grid;
}

public void update() {
int[][] newGrid = new int[maxRow + 2][maxCol + 2];

for (int row = 1; row <= maxRow; row++)
for (int col = 1; col <= maxCol; col++)
switch (getNeighborCount(row, col)) {
case 2:
newGrid[row][col] = grid[row][col]; // Status stays the same.
break;
case 3:
newGrid[row][col] = 1; // Cell is alive.
break;
default:
newGrid[row][col] = 0; // Cell is dead.
}

for (int row = 1; row <= maxRow; row++)
for (int col = 1; col <= maxCol; col++)
grid[row][col] = newGrid[row][col];
}

private int getNeighborCount(int row, int col) {
int count = 0;

for (int i = row - 1; i <= row + 1; i++)
for (int j = col - 1; j <= col + 1; j++)
count += grid[i][j]; // Increase the count if neighbor is alive.
count -= grid[row][col]; // Reduce count, since cell is not its own neighbor.

return count;
}
}

2、GameFrame

package net.hw.game;import javax.swing.*;import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.util.Arrays;/** * Created by howard on 2018/2/8. */public class GameFrame extends JFrame implements ActionListener {    private static GameFrame frame;    private JPanel backPanel, centerPanel, bottomPanel;    private JButton btnOK, btnNextGeneration, btnStart, btnStop, btnExit;    private JButton[][] btnBlock;    private JLabel lblRow, lblCol;    private JComboBox rowList, colList;    private boolean[][] isSelected;    private int maxRow, maxCol;    private Life life;    private boolean isRunning;    private Thread thread;    private boolean isDead;    public static void main(String arg[]) {        frame = new GameFrame("生命游戏V1.0");    }    public int getMaxRow() {        return maxRow;    }    public void setMaxRow(int maxRow) {        this.maxRow = maxRow;    }    public int getMaxCol() {        return maxCol;    }    public void setMaxCol(int maxCol) {        this.maxCol = maxCol;    }    public void initGUI() {        /**         * 设计地图生成器界面 *         */        if (maxRow == 0) {            maxRow = 20;        }        if (maxCol == 0) {            maxCol = 60;        }        life = new Life(maxRow, maxCol);        backPanel = new JPanel(new BorderLayout());        centerPanel = new JPanel(new GridLayout(maxRow, maxCol));        bottomPanel = new JPanel();        rowList = new JComboBox();        for (int i = 3; i <= 20; i++) {            rowList.addItem(String.valueOf(i));        }        colList = new JComboBox();        for (int i = 3; i <= 60; i++) {            colList.addItem(String.valueOf(i));        }        rowList.setSelectedIndex(maxRow - 3);        colList.setSelectedIndex(maxCol - 3);        btnOK = new JButton("确定");        btnNextGeneration = new JButton("下一代");        btnBlock = new JButton[maxRow][maxCol];        btnStart = new JButton("开始演化");        btnStop = new JButton("停止演化");        btnExit = new JButton("退出");        isSelected = new boolean[maxRow][maxCol];        lblRow = new JLabel("设置行数:");        lblCol = new JLabel("设置列数:");        this.setContentPane(backPanel);        backPanel.add(centerPanel, "Center");        backPanel.add(bottomPanel, "South");        for (int i = 0; i < maxRow; i++) {            for (int j = 0; j < maxCol; j++) {                btnBlock[i][j] = new JButton("");                btnBlock[i][j].setBackground(Color.WHITE);                centerPanel.add(btnBlock[i][j]);            }        }        bottomPanel.add(lblRow);        bottomPanel.add(rowList);        bottomPanel.add(lblCol);        bottomPanel.add(colList);        bottomPanel.add(btnOK);        bottomPanel.add(btnNextGeneration);        bottomPanel.add(btnStart);        bottomPanel.add(btnStop);        bottomPanel.add(btnExit);        // 设置窗口        this.setSize(900, 620);        this.setResizable(false);        this.setLocationRelativeTo(null); // 让窗口在屏幕居中        // 将窗口设置为可见的        this.setVisible(true);        // 注册监听器        this.addWindowListener(new WindowAdapter() {            public void windowClosed(WindowEvent e) {                System.exit(0);            }        });        btnOK.addActionListener(this);        btnNextGeneration.addActionListener(this);        btnStart.addActionListener(this);        btnStop.addActionListener(this);        btnExit.addActionListener(this);        for (int i = 0; i < maxRow; i++) {            for (int j = 0; j < maxCol; j++) {                btnBlock[i][j].addActionListener(this);            }        }    }    public GameFrame(String name) {        super(name);        initGUI();    }    public void actionPerformed(ActionEvent e) {        if (e.getSource() == btnOK) {            frame.setMaxRow(rowList.getSelectedIndex() + 3);            frame.setMaxCol(colList.getSelectedIndex() + 3);            initGUI();            life = new Life(getMaxRow(), getMaxCol());        } else if (e.getSource() == btnNextGeneration) {            makeNextGeneration();        } else if (e.getSource() == btnStart) {            isRunning = true;            thread = new Thread(new Runnable() {                @Override                public void run() {                    while (isRunning) {                        makeNextGeneration();                        boolean isSame = true;                        try {                            Thread.sleep(500);                        } catch (InterruptedException e1) {                            e1.printStackTrace();                        }                        isDead = true;                        for(int row = 1; row <= maxRow; row++) {                            for (int col = 1; col <= maxCol; col++) {                                if (life.getGrid()[row][col] != 0) {                                    isDead = false;                                    break;                                }                            }                            if (!isDead) {                                break;                            }                        }                        if (isDead) {                            JOptionPane.showMessageDialog(null, "生命消失了~");                            isRunning = false;                            thread = null;                        }                    }                }            });            thread.start();        } else if (e.getSource() == btnStop) {            isRunning = false;            thread = null;        } else if (e.getSource() == btnExit) {            System.exit(0);        } else {            int[][] grid = life.getGrid();            for (int i = 0; i < maxRow; i++) {                for (int j = 0; j < maxCol; j++) {                    if (e.getSource() == btnBlock[i][j]) {                        isSelected[i][j] = !isSelected[i][j];                        if (isSelected[i][j]) {                            btnBlock[i][j].setBackground(Color.BLACK);                            grid[i + 1][j + 1] = 1;                        } else {                            btnBlock[i][j].setBackground(Color.WHITE);                            grid[i + 1][j + 1] = 0;                        }                        break;                    }                }            }            life.setGrid(grid);        }    }    private void makeNextGeneration() {        life.update();        int[][] grid = life.getGrid();        for (int i = 0; i < maxRow; i++) {            for (int j = 0; j < maxCol; j++) {                if (grid[i + 1][j + 1] == 1) {                    btnBlock[i][j].setBackground(Color.BLACK);                } else {                    btnBlock[i][j].setBackground(Color.WHITE);                }            }        }    }}


运行程序,结果如下:

Java版生命游戏

可以设置行数与列数:

Java版生命游戏

设置生命初始布局:

Java版生命游戏

单击【下一代】按钮:

Java版生命游戏

再次单击【下一代】按钮:

Java版生命游戏

重新设置生命初始布局,单击【开始演化】,生命会自动按照规则进行演化,直到单击【停止演化】为止。

Java版生命游戏