我有一个生命游戏的运行版本,但我无法弄清楚的一件事是如何包裹网格或棋盘,我猜测是必须与邻居计数和网格,我需要一种方法来表明数组包装。
规则:
生命游戏的宇宙是无限的二维 方形单元的正交网格,
每个都处于两种可能的状态之一,无论是活着的还是死的。
每个细胞与其八个邻居(细胞)相互作用 直接水平,
垂直或对角相邻。在每一个步骤, 发生以下转换:
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;
}
}
}
答案 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
并删除+ 2
和nbCol
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语法使它们在注册时变得简短而且很好updateXyz
和stepToNextGeneration
方法,以大幅减少代码重复MouseEvent
已经提供了按钮是否已关闭的信息。JComponent
)类中定义serialVersionUID没有多大意义,因为它们实际上不可序列化(尽管它们实现了Serializable
接口)。另请参阅Swing components and serialization <小时/> 更新以及更多评论答案
如果我想要一些预制的初始状态,我只用两个字段Point
和row
制作了一个简单的类(更像结构化)column
有一个List
或它们的数组,然后做了clear
并迭代了集合,使列表中的那些单元格生效。如果我想更进一步,我可能会添加一个包装类(结构)SavedGame
或其他东西来保存width
和height
以及{{1}的集合} S;并使整个UI动态而不是在构造函数中构建一次;然后添加了从某个文件
至于随机,有一个java.util.Random类,其中包含有用的Point
方法。所以我要问用户应该生成多少个随机单元,然后运行一个循环直到该数字并使用Random生成的新的活细胞通过生成行和列。请注意,如果要填充大部分单元格,可能需要检查所选择的下一个随机单元格是否已存活,如果是,则#34;掷骰子&#34;再一次。如果你想要更多填充,你需要将算法更改为更复杂或填充90%的单元格可能需要永远。一个想法可能是在范围内生成单个细胞索引(0;行*高度 - 已经活着的细胞的数量)而不是独立的行和列,然后只是从左上角到右下角开始计数只有死细胞和使相应的细胞生存。通过这种方式,您可以确保每个新的活细胞都需要相同的时间来生成。