问题
This is homework. I included relevant code at the bottom.
Problem: In an attempted to allow the user to resize the grid, the grid is now being drawn severely overpopuated.
Screen Shots: "Overpopulation" - http://i.imgur.com/zshAC6n.png "Desired Population" - http://i.imgur.com/5Rf6P42.png
Background: It's a version of Conway's Game of Life. In class we completed 3 classes: LifeState which handles the game logic, LifePanel which is a JPanel that contains the game, and a driver that created a JFrame and added the LifePanel. The assignment was to develop it into a full GUI application with various requirements. My solution was to extend JFrame and do most of my work in that class.
Initializing the LifePanel outside of the actionlistener yields normal population, but intializing the LifePanel in the actionlistener "overpopulates" the grid.
Question: Why is the overpopulation occurring?
LifePanel class
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 class
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;
}
}
}
Thanks for reading all this, and in advance for any help/advice you offer.
EDIT: Added screen shots and refined question.
回答1:
Based on how you initialize 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());
what you call "overpopulation" is the expected state. The above code will set about 1/2 of the cells to "alive" (or "occupied"), which is what your "overpopulated" state looks like.
The "desired population" screenshot contains many "life" artifacts such as "beehives", "gliders", "traffic lights", etc, and was either manually constructed or is the result of running several iterations on an initially 50% random population. With a 50% occupied population the first generation will result in wholesale clearing ("death") of many, many cells due to the proximity rules.
Most crucially, consider that, when starting up, your program does not paint the initial configuration. At least one iteration occurs before the first repaint()
call.
I don't think your code is broken at all, just your expectation for what the initial population looks like.
来源:https://stackoverflow.com/questions/16048239/game-of-life-in-java-overpopulation-but-cant-figure-out-why