Java中的生命游戏,人口过剩但无法弄清楚原因

时间:2013-04-16 22:24:42

标签: java conways-game-of-life

这是作业。我在底部包含相关代码。

问题: 在尝试允许用户调整网格大小时,网格现在被严重过度绘制。

屏幕截图: “人口过剩” - http://i.imgur.com/zshAC6n.png “所需人口” - http://i.imgur.com/5Rf6P42.png

背景 这是康威生命游戏的一个版本。在课堂上,我们完成了3个类:处理游戏逻辑的LifeState,包含游戏的JPanel的LifePanel,以及创建JFrame并添加LifePanel的驱动程序。任务是将其开发成具有各种要求的完整GUI应用程序。我的解决方案是扩展JFrame并完成我在该课程中的大部分工作。

在actionlistener之外初始化LifePanel会产生正常填充,但是在actionlistener中初始化LifePanel会“过度填充”网格。

问题:为什么人口过剩?

LifePanel类

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

public class LifePanel extends JPanel implements MouseListener
{
private int row;
private int col;
private int scale;
private LifeState life;
boolean state;
boolean wrap;
int delay;
Timer timer;

public LifePanel(int r, int c, int s, int d)
{
    row = r;
    col = c;
    scale = s;
    delay = d;
    life = new LifeState(row,col);
    Random rnd = new Random();
    for(int i=0;i<row;i++)
        for(int j=0;j<col;j++)
            life.setCell(i,j,rnd.nextBoolean());
    timer = new Timer(delay, new UpdateListener());
    setPreferredSize( new Dimension(scale*row, scale*col));
    addMouseListener(this);
    timer.start();
}

public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    for(int i=0;i<row;i++)
        for(int j=0;j<col;j++)
            if(life.getCell(i,j))
                g.fillRect(scale*i,scale*j,scale,scale);
}

public int getRow() {
    return row;
}

public void setRow(int row) {
    this.row = row;
}

public int getCol() {
    return col;
}

public void setCol(int col) {
    this.col = col;
}

public int getScale() {
    return scale;
}

public void setScale(int scale) {
    this.scale = scale;
}

public int getDelay() {
    return delay;

}

public void setDelay(int delay) {
    this.delay = delay;
    timer.setDelay(delay);
}

public void pauseGame(){
    timer.stop();
}
public void playGame(){
    timer.restart();
}
public void setInitState(boolean set){
    state = set;
    if(state){
      timer.stop();
    }
}
public void setWrap(boolean set){
    wrap = set;
    if(wrap){
    //implement allow wrap
    }
}

@Override
public void mouseClicked(MouseEvent e) {
  if(state){
    int x=e.getX(); 
    int y=e.getY();
    boolean isFilled;
    isFilled = life.getCell(x,y);
    //Test pop-up
    JOptionPane.showMessageDialog(this, x+","+y+"\n"+life.getCell(x,y));
    if(isFilled){
      life.setCell(x,y,false);
    }else{
      life.setCell(x,y,true);
    }
    repaint();
  }
}


@Override
public void mousePressed(MouseEvent e) {}

@Override
public void mouseReleased(MouseEvent e) {}

@Override
public void mouseEntered(MouseEvent e) {}

@Override
public void mouseExited(MouseEvent e) {}

private class UpdateListener implements ActionListener
{
    public void actionPerformed(ActionEvent e)
    {
        life.iterate();
        repaint();
    }
}


}

LifeFrame类

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 

public class LifeFrame extends JFrame implements ActionListener{ 

JMenuBar menuBar; 
JMenu mainMenu, helpMenu; 
JMenuItem restartItem, quitItem, helpItem; 
JButton stopButton, playButton, pauseButton, startButton; 
CardLayout cardLayout = new MyCardLayout(); 
CardLayout cardLayout2 = new MyCardLayout(); 
SetupPanel setupPanel; //panel for input 
LifePanel gamePanel;  //game panel 
JPanel controls = new JPanel(); //controls for game 
JPanel controls2 = new JPanel(); //controls for input panel 
JPanel cardPanel = new JPanel(cardLayout); 
JPanel cardPanel2 = new JPanel(cardLayout2); 
int gridRow=480; 
int gridCol=480; 
int scale=1; 
int delay=2;
boolean setState = false; 
boolean setWrap = false; 

public LifeFrame() { 
    setTitle("Game of Life"); 
    setLayout(new BorderLayout()); 

    //Add the Panels 
    setupPanel = new SetupPanel(); 
    gamePanel = new LifePanel(gridRow,gridCol,scale,delay); 
    cardPanel.add(setupPanel, "1");
    cardPanel.add(gamePanel, "2");
    add(cardPanel, BorderLayout.NORTH); 

    cardPanel2.add(controls2, "1"); 
    cardPanel2.add(controls, "2"); 
    add(cardPanel2, BorderLayout.SOUTH); 
    //init menu 
    menuBar = new JMenuBar(); 

    //button listener setup 
    stopButton = new JButton("Stop"); 
    pauseButton = new JButton("Pause"); 
    playButton = new JButton("Play"); 
    startButton = new JButton("Start"); 
    stopButton.addActionListener(this); 
    pauseButton.addActionListener(this); 
    playButton.addActionListener(this); 
    startButton.addActionListener(this); 
    //menu listener setup 
    restartItem = new JMenuItem("Restart", KeyEvent.VK_R); 
    quitItem = new JMenuItem("Quit", KeyEvent.VK_Q); 
    helpItem = new JMenuItem("Help", KeyEvent.VK_H); 
    restartItem.addActionListener(this); 
    quitItem.addActionListener(this); 
    helpItem.addActionListener(this); 
    //add buttons 
    controls.add(stopButton); 
    controls.add(pauseButton); 
    controls.add(playButton); 
    controls2.add(startButton); 
    //build the menus 
    mainMenu = new JMenu("Menu"); 
    mainMenu.setMnemonic(KeyEvent.VK_M); 
    helpMenu = new JMenu("Help"); 
    helpMenu.setMnemonic(KeyEvent.VK_H); 
    menuBar.add(mainMenu); 
    menuBar.add(helpMenu); 
    setJMenuBar(menuBar); 
    //add JMenuItems 
    restartItem.getAccessibleContext().setAccessibleDescription("Return to setup screen"); 
    mainMenu.add(restartItem); 
    mainMenu.add(quitItem); 
    helpMenu.add(helpItem); 


    this.addWindowListener(new WindowAdapter(){ 
        public void windowClosing(WindowEvent e){ 
            System.exit(0); 
        } 
    }); 

    pack(); 
    setLocationRelativeTo(null); 
    setVisible(true); 
    setDefaultCloseOperation(EXIT_ON_CLOSE); 
} 
@Override 
public void actionPerformed(ActionEvent e) { 
    try{ 
    gridRow = setupPanel.getRowSize(); 
    gridCol = setupPanel.getColSize(); 
    scale = setupPanel.getScale(); 
    delay = setupPanel.getDelay(); 
    setWrap = setupPanel.getSetWrap(); 
    setState = setupPanel.getSetState(); 
    }catch (NumberFormatException n){ 
        JOptionPane.showMessageDialog(LifeFrame.this, "Make sure the fields contain only digits and are completed!"); 
        return; 
    } 
    if(e.getSource() == pauseButton){ 
        gamePanel.pauseGame(); 
    }else if(e.getSource() == playButton){ 
        gamePanel.playGame(); 
    }else if(e.getSource() == quitItem){ 
        System.exit(0); 
    }else if(e.getSource() == restartItem || e.getSource() == stopButton){ 
        cardLayout.show(cardPanel, "1"); 
        cardLayout2.show(cardPanel2, "1"); 
        pack();
        setLocationRelativeTo(null);
    }else if(e.getSource() == helpItem){ 
        String helpText = "Help\nPlease make sure every field is completed and contains only digits\nCurrent Stats:\nGrid Size: "+gamePanel.getRow()+" by "+gamePanel.getCol()+"\nScale: "+ gamePanel.getScale() +"\nDelay: "+gamePanel.getDelay()+"\nManual Initial State: "+setState+"\nEnable Wrapping: "+setWrap;
        JOptionPane.showMessageDialog(LifeFrame.this, helpText); 
    }else if(e.getSource() == startButton){ 

        gamePanel = new LifePanel(gridRow,gridCol,scale,delay); 
        cardPanel.add(gamePanel, "2");
        /*
         * Alternate solution, throws array index out of bounds due to array usage in the LifePanel, but properly 
         * populates the grid.
         * 
        gamePanel.setRow(gridRow);
        gamePanel.setCol(gridCol);
        gamePanel.setScale(scale);
        gamePanel.setDelay(delay);
        */
        if(setWrap){ 
            gamePanel.setWrap(true); 
            gamePanel.playGame(); 
        }else if(setState){ 
            gamePanel.setInitState(true); 
        }else{ 
            gamePanel.setWrap(false); 
            gamePanel.setInitState(false); 
            gamePanel.playGame(); 
        }
        gamePanel.repaint(); 
        cardLayout.show(cardPanel, "2"); 
        cardLayout2.show(cardPanel2, "2"); 
        pack();
        setLocationRelativeTo(null);
    } 
} 
public static class MyCardLayout extends CardLayout { 

    @Override 
    public Dimension preferredLayoutSize(Container parent) { 

        Component current = findCurrentComponent(parent); 
        if (current != null) { 
            Insets insets = parent.getInsets(); 
            Dimension pref = current.getPreferredSize(); 
            pref.width += insets.left + insets.right; 
            pref.height += insets.top + insets.bottom; 
            return pref; 
        } 
        return super.preferredLayoutSize(parent); 
    } 

    public Component findCurrentComponent(Container parent) { 
        for (Component comp : parent.getComponents()) { 
            if (comp.isVisible()) { 
                return comp; 
            } 
        } 
        return null; 
    } 

} 
}

感谢您阅读所有这些内容,并提前获取您提供的任何帮助/建议。

编辑:添加了屏幕截图和精致的问题。

1 个答案:

答案 0 :(得分:2)

根据您初始化LifePanel

的方式
Random rnd = new Random();
for(int i=0;i<row;i++)
    for(int j=0;j<col;j++)
        life.setCell(i,j,rnd.nextBoolean());

你所谓的“人口过剩”是预期的状态。上面的代码将大约1/2的单元格设置为“活着”(或“占用”),这就是你的“人口过剩”状态。

“所需人口”截图包含许多“生命”工件,如“蜂箱”,“滑翔机”,“红绿灯”等,并且是手动构建的,或者是最初50%运行多次迭代的结果随机人口。由于邻近规则,第一代将占据50%的占用人口,导致许多许多小区的批发清算(“死亡”)。

最重要的是,考虑到启动时,程序不会绘制初始配置。在第一次repaint()调用之前至少进行一次迭代。

我认为你的代码根本没有破坏,只是你对初始人口的期望。