Game of Life. I've isolated where the problem is

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • slapsh0t11
    New Member
    • Oct 2009
    • 16

    Game of Life. I've isolated where the problem is

    So, I've been working on this Game of Life (http://www.bitstorm.or g/gameoflife/) project, and all the code has been written. However, it will not run. First, I will post the error message and the fragment of code I think to be the cause of the problem. Then, I will post the code to all 3 of my java files in their entirety should you wish to reference them. Any help would be much appreciated.

    Also, if it would help fix my error, I would like guidance as to make these cells live on a toroid, meaning that any cells that hit the bottom pop up at the top and any cells that hit the left edge come out on the right.

    Here's the error message:
    Code:
    Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
    	at Cell.prepareNextTurn(Cell.java:82)
    	at GameStart.runastep(GameStart.java:42)
    	at Display.paint(Display.java:57)
    	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    	at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
    	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
    	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    	at java.awt.EventQueue.dispatchEvent(Unknown Source)
    	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    	at java.awt.EventDispatchThread.run(Unknown Source)

    Here's the code fragment I believe to be the problem:
    Code:
    		int[][] adjacent = {{1,1},{1,0},{0,1},{1,-1},{-1,1},{0,-1},{-1,0},{-1,-1}};
    		
    		for(int i = 0; i < adjacent.length; i++)
    		{
    			//System.out.println("myX" + myX + " myY" + myY + " context" + context.length);
    			if(context[myY + adjacent[i][0]][myX + adjacent[i][1]].getAlive())
    			{
    				myNeighbors++;
    			}

    Here's my Cell.java File:

    Code:
    import java.awt.Color;
    import java.awt.Graphics;
    
    /**
     * @author administrator
     *
     * TODO To change the template for this generated type comment go to
     * Window - Preferences - Java - Code Style - Code Templates
     */
    public class Cell {
    	public static int cellCounter = 0;    // Each time a new cell is created, this is incremented
    	private int myX, myY;		// x,y position on grid
    	private boolean myAlive;	// alive (true) or dead (false)
    	private int myNeighbors;	// count of neighbors with respect to x,y
    	private boolean myAliveNextTurn;	// Used for figuring out state in next iteration
    	private Color myColor;		// Based on alive/dead rules
    	private final Color DEFAULT_ALIVE = Color.ORANGE;
    	private final Color DEFAULT_DEAD = Color.GRAY;
    	private Cell[][] context; // this is how we can check our neighbors!
    	
    	public Cell(Cell[][] thegrid) {
    		// Object() is called by default
    		myAlive = false; // dead
    		myColor = Color.GRAY;
    		context = thegrid;
    	}
    	
    	public Cell(int x, int y, boolean alive, Color color, Cell[][] thegrid) {
    		myAlive = alive;
    		myColor = color;
    		myX = x;  myY = y;
    		cellCounter++;
    		context = thegrid;
    	}
    	
    	public boolean getAlive() { return myAlive; }
    	public int getX() { return myX; }
    	public int getY() { return myY; }
    	public Color getColor() { return myColor; }
    	
    	
    	public void setAlive (boolean alive, Color color) {
    		myColor = color;
    		myAlive = alive;
    	}
    	
    	public void setAlive (boolean alive) { 
    		if (alive) {
    			setAlive(true, DEFAULT_ALIVE);
    		} else {
    			setAlive(false, DEFAULT_DEAD);
    		}
    	}
    	
    	public void toggle(){
    	  //System.out.println("ok, what do you want to happen to cell[" + myX +"][" + myY +"]?");
    		if(context[myY][myX].getAlive())
    		{
    			context[myY][myX].setAlive(false);
    		}
    		else
    		{
    			context[myY][myX].setAlive(true);
    		}
    	}
    	
    
    	/**************this method has to check all the neighbors to 
    	 * count how many are living cells. Depending on the answer,
    	 * and whether or not the cell is CURRENTLY alive, it will
    	 * determine whether it will be alive or not NEXT turn
    	 */
    	
    	public void prepareNextTurn()
    	{
    		//System.out.println("you need to fill this in!");
    		int[][] adjacent = {{1,1},{1,0},{0,1},{1,-1},{-1,1},{0,-1},{-1,0},{-1,-1}};
    		
    		for(int i = 0; i < adjacent.length; i++)
    		{
    			//System.out.println("myX" + myX + " myY" + myY + " context" + context.length);
    			if(context[myY + adjacent[i][0]][myX + adjacent[i][1]].getAlive())
    			{
    				myNeighbors++;
    			}
    		}
    		System.out.println(myX);
    		System.out.println(myY);
    		if(context[myX][myY].getAlive())
    		{
    			if(myNeighbors < 2 || myNeighbors > 3)
    			{
    				context[myY-1][myX-1].toggle();
    			}
    			else
    			{
    				//stay alive
    			}
    		}
    		else
    		{
    			if(myNeighbors == 3)
    			{
    				context[myY][myX].toggle();
    			}
    			else
    			{
    				//stay dead
    			}
    		}
    	}
    	
    	
    	/**********************bookkeeping
    	 * this method updates the turn,
    	 */
    	
    	public void setNextTurn()
    	{
    		//System.out.println("you also need to fill this in!");
    
    	}
    	
    		
    	
    	public void setColor (Color color) { myColor = color; }
    	
    	
    	/****maybe I should have the method for drawing a cell in the Display class!
    	   but this is ok and needs no modification 
    	   **************/
    	
    	public void draw (int x_offset, int y_offset, int width, int height, Graphics g) {
    		// I leave this understanding to the reader
    		int xleft = x_offset + 1 + (myX*(width+1));
    		int xright = x_offset + width + (myX*(width+1));
    		int ytop = y_offset + 1 + (myY*(height+1));
    		int ybottom = y_offset + height + (myY*(height+1));
    		Color temp = g.getColor();
    
    		g.setColor(myColor);
    		g.fillRect(xleft, ytop, width, height);
    			}
    }
    Here's my starter file, GameStart.java:

    Code:
    import java.awt.Color;
    import javax.swing.JFrame;
    import java.util.Random;
    
    public class GameStart {
    	public static final int ROWS = 80, COLS = 100;
    	public static Cell[][] cell = new Cell[ROWS][COLS];
    	public static Display display = new Display(cell);
    	
    	public static void main(String[] args) {
    		initialize();
    		// Bring up a JFrame with squares to represent the cells
    		display.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    		display.setVisible(true);
    	}
    	
    	public static void initialize() {
    		for (int i = 0; i < ROWS; i++) {
    			for (int j = 0; j < COLS; j++) {
    				cell[i][j] = new Cell(j, i, false, Color.GRAY, cell);
    				if (java.lang.Math.random() < 0.25){
    					cell[i][j].setAlive(true);
    					}
    
      /*****one way to seed the Grid with some random data:
    				if (java.lang.Math.random() < 0.25){
    				cell[i][j].setAlive(true);
    				}
    	 ********************but you could do many other things, too!   **/			
    				
    		    }
    		}
    		System.out.println(Cell.cellCounter);	// demonstrates use of a class variable
    			
    	}
    	
    	public static void runastep(){
    	    //first run  through grid calculating what next turn status will be
    	    for (int i = 0; i < ROWS; i++) {
    			for (int j = 0; j < COLS; j++) {
    			 // what do you think should be here?
    				cell[i][j].prepareNextTurn();
    			 }
    		}
    		
    		//then go back and run grid, moving next turn status into place
    		
    		for (int i = 0; i < ROWS; i++) {
    			for (int j = 0; j < COLS; j++) {
    		    //what do you think should be here?
    		    cell[i][j].setNextTurn();
    			}
    		}
    		
    		
    		
    	}
    }

    And finally, here's the class that handles the display, Display.java:

    Code:
    import java.awt.*;
    import java.awt.event.*;
    
    import javax.swing.*;
    
    // Note that the JFrame is set up to listen for mouse clicks
    // and mouse movement.  To achieve this, the MouseListener and
    // MousMotionListener interfaces are implemented and there is additional
    // code in init() to attach those interfaces to the JFrame.
    
    public class Display extends JFrame implements MouseListener, MouseMotionListener {
    	private final int DISPLAY_WIDTH = 750;
    	private final int DISPLAY_HEIGHT = 600;
    	private final int X_GRID_OFFSET = 25;  // 10 pixels from left
    	private final int Y_GRID_OFFSET = 40;  // 10 pixels from top
    	private final int CELL_WIDTH = 5;
    	private final int CELL_HEIGHT = 5;
    	private final Cell[][] myCell;
    	private StartButton startStop = new StartButton();
    	private boolean paintloop = false;
    	
    	public Display(Cell[][] cell) {
    		myCell = cell;
    		init();
    	}	
    
    	public void init() {
    		setBackground(Color.WHITE);		
    		setSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
    		getContentPane().setLayout(null);
    		
    		// Example of setting up a button.
    		// See the StartButton class nested below.
    		add(startStop);
    		startStop.setBounds(100,500,72,36);
    		startStop.setVisible(true);
    		
    		addMouseListener(this);
    		addMouseMotionListener(this);
    		repaint();
    	}
    	
    	public void paint(Graphics g) {
    		g.setColor(Color.BLACK);
    		drawGrid(g);
    		drawCells(g);
    		drawComponents();  // buttons, etc., go here
    		if (paintloop) {
    			try {
    				
    				Thread.sleep(50);  //you can change this value!
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			//nextGeneration();
    			GameStart.runastep();  //this method will run one generation
    			repaint();
    			
    		}
    	}
    	
    	void drawComponents() {
    		startStop.repaint();
    	}
    	
    	void drawGrid(Graphics g) {
    		for (int i = 0; i <= GameStart.ROWS; i++) {
    			g.drawLine(X_GRID_OFFSET, 
    					   Y_GRID_OFFSET + (i*(CELL_HEIGHT+1)),
    					   X_GRID_OFFSET + GameStart.COLS*(CELL_WIDTH+1), 
    					   Y_GRID_OFFSET + (i*(CELL_HEIGHT+1)));
    		}
    		for (int i = 0; i <= GameStart.COLS; i++) {
    			g.drawLine(X_GRID_OFFSET + (i*(CELL_WIDTH+1)), 
    					   Y_GRID_OFFSET,
    					   X_GRID_OFFSET + (i*(CELL_WIDTH+1)), 
    					   Y_GRID_OFFSET + GameStart.ROWS*(CELL_HEIGHT+1));
    		}
    	}
    	
    	void drawCells(Graphics g) {
    		// Have each cell draw itself
    		for (int i = 0; i < GameStart.ROWS; i++) {
    			for (int j = 0; j < GameStart.COLS; j++) {
    				// The cell cannot know for certain the offsets nor the height
    				// and width; it has been set up to know its own position, so
    				// that need not be passed as an argument to the draw method
    				myCell[i][j].draw(X_GRID_OFFSET,Y_GRID_OFFSET,CELL_WIDTH,CELL_HEIGHT,g);
    			}
    		}
    	}
    	
    	void painter() { repaint(); }
    	
    	
    	/*************************THIS IS AN INNER CLASS*************************
    	 * that is, a class defined within a class, all it does is run the start
    	 * button.  When start is clicked, the "paintloop" variable in the
    	 * Display object is set to true; when stop is clicked, it's set to false.
    	 * when paintloop is true, the game will be running, when it's false
    	 * it won't (and perhaps the board may be edited)
    	 */
    	class StartButton extends JButton implements ActionListener {
    		StartButton() {
    			super("Start");
    			addActionListener(this);
    		}
    
    		public void actionPerformed(ActionEvent arg0) {
    			if (this.getText().equals("Start")) {
    				paintloop = true;
    				setText("Stop");
    			} else {
    				paintloop = false;
    				setText("Start");
    			}
    			painter();
    		}		
    	}	
    
    	
    	/*********************************************************
    	 * Note that the Display is ALSO listening for mouse clicks!
    	 * there is a MouseEvent class that contains information about
    	 * MouseClicks; all we need to know is the locaton of the click
    	 * IF IT HAPPENS WHEN THE START BUTTON HAS NOT BEEN PRESSED,
    	 * THIS WILL ALLOW THE USER TO set cells to be ALIVE or DEAD
    	 */
    	
    	public void mouseClicked(MouseEvent me) { 
    		if (paintloop==false){
    			//CONVERTS THE LOCATION OF THE CLICK IN PIXELS TO THE
    			//CORRECT ROW AND COLUMN IN THE GRID
    			int  x = (me.getX() - X_GRID_OFFSET-1)/(CELL_WIDTH+1);
    			int y =  (me.getY() - Y_GRID_OFFSET - 1)/(CELL_HEIGHT+1);
    		    //be sure the click is actually IN the grid 
    			//to avoid ArrayIndexOutOfBounds Exception
    			if ((0<=x)&& (x< myCell.length)&& (0<=y)&&(y<myCell[0].length)){
    				myCell[y][x].toggle();
    				repaint();
    			}
    			//System.out.println("clicked on (" + x + ","+ y + ") element");
    
    		}
    		
    	}
    
    	/*************************
    	 * a MouseListener must include ALL the methods for the MouseListener
    	 * interface; but we don't actually need to do anything for these:
    	 */
    	public void mouseEntered(MouseEvent arg0) { }
    
    	public void mouseExited(MouseEvent arg0) { }
    
    	public void mousePressed(MouseEvent arg0) { }
    
    	public void mouseReleased(MouseEvent arg0) { }
    
    	public void mouseDragged(MouseEvent arg0) { }
    
    	public void mouseMoved(MouseEvent arg0) { }
    
    }

    Thanks in advance for your help!
  • mrjohn
    New Member
    • May 2009
    • 31

    #2
    It does look like looking for cells "off the edge" is what is causing the null pointer exception. I do have one question though. Is context supposed to be context[x][y] or context[y][x]? Because you seem to use context[myX][myY] as well as context[myY][myX]. I recommend making your code use just the Cartesian X/Y version, as it makes the grid more intuitive.

    As far as going "around the world" goes, consider the fact that once you the cross the far side of the map (Element 99), the next element is 0, instead of 100.
    So if the number of the destination cell on the X-axis is greater or equal to the grid's length, you need to subtract the length of the map.
    If the number is less than 0, you need to add the length of the map.

    Same goes for the Y-axis.

    Comment

    • slapsh0t11
      New Member
      • Oct 2009
      • 16

      #3
      Yes, I see that. I actually caught that mistake minutes before I read your post. :) Thank you so much for the help though. Your advice on the toroidal structure was very useful. Much thanks.

      Comment

      Working...