Question

I wrote a simple implementation of the Game of life with java applets. Here's is the source code for the Applet and the Model.

When I click the button to get the next iteration these Exceptions get thrown.

Z:\GameOfLife>appletviewer driver.html
Exception in thread "AWT-EventQueue-1" java.lang.ArrayIndexOutOfBoundsException:
 65
        at GameOfLifeApplet.mouseClicked(GameOfLifeApplet.java:63)
        at java.awt.Component.processMouseEvent(Component.java:6219)
        at java.awt.Component.processEvent(Component.java:5981)
        at java.awt.Container.processEvent(Container.java:2041)
        at java.awt.Component.dispatchEventImpl(Component.java:4583)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4413)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThre    ad.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThre    ad.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Was it helpful?

Solution

Try this code that adds the button once rather than every call to paint(). Note that this source still throws AIOOBE if you click outside the grid (and not on the button), but that seems like a basic logic error you should investigate once the button is fixed.

// <applet code='GameOfLifeApplet' width=580 height=650></applet>
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class GameOfLifeApplet extends Applet implements MouseListener,ActionListener
{
  //the x and y coordinates to get the location of the clicked points
  private int xCo, yCo;
  private int diff_x, diff_y;
  private GameOfLife game = new GameOfLife();
  private Button nextButton = null;
  public void init()
  {
    setLayout(null);
    nextButton = new Button("Next Stage");
    diff_x = diff_y = 600 / game.getGridSize();
    nextButton.setLocation(250, 575);
    nextButton.setSize(120, 30);
    // add the button once only!
    add(nextButton);
    addMouseListener(this);
  }

  private void drawEmptyGrid(Graphics g)
  {
    g.setColor(Color.white);
    g.fillRect(0,0,600,600);
    g.setColor(Color.black);
    for(int i=0;i<game.getGridSize();++i)
    {
      g.drawLine(0,i*diff_x,600,i*diff_x);
      g.drawLine(i*diff_x,0,i*diff_x,600);
    }
    g.setColor(Color.white);
  }

  public void paint(Graphics g)
  {
    drawEmptyGrid(g);
    g.setColor(Color.red);
    for(int i=0;i<game.getGridSize();++i)
    {
      for(int j=0;j<game.getGridSize();++j)
      {
        if( game.grid[i][j] )
        {
          g.fillRect(i*diff_x,j*diff_y,diff_x,diff_y);
        }
      }
    }
    g.setColor(Color.white);
  }

 // This method will be called when the mouse has been clicked.
 public void mouseClicked (MouseEvent me) {

  // Save the coordinates of the click lke this.
  xCo = me.getX();
  yCo = me.getY();

  int x_init = xCo / diff_x;
  int y_init = yCo / diff_y;

  System.out.println(x_init + "x" + y_init);
  game.grid[x_init][y_init] = true;
  //show the results of the click
  repaint();

 }

 // This is called when the mous has been pressed
 public void mousePressed (MouseEvent me) {}

 // When it has been released
 // not that a click also calls these Mouse-Pressed and Released.
 // since they are empty nothing hapens here.
 public void mouseReleased (MouseEvent me) {}

 // This is executed when the mouse enters the applet. it will only
 // be executed again when the mouse has left and then re-entered.
 public void mouseEntered (MouseEvent me) {}

 // When the Mouse leaves the applet.
 public void mouseExited (MouseEvent me) {}

 public void actionPerformed(ActionEvent evt)
  {
  // Here we will ask what component called this method
    if (evt.getSource() == nextButton)
    {
      System.out.println("I got clicked!");
      game.nextIteration();
      repaint();
    }
  }

}

class GameOfLife
{
  private final int GRID_SIZE = 64;
  public boolean [][] grid = new boolean[GRID_SIZE][GRID_SIZE];
  //default constructor
  public GameOfLife()
  {
    for(int i=0;i<GRID_SIZE;++i)
    {
      for(int j=0;j<GRID_SIZE;++j)
    {
      grid[i][j] = false;
    }
    }
  }

  public int getGridSize()
  {
    return GRID_SIZE;
  }

  public int getLiveNeighbors(int i,int j)
  {
    int neighbors = 0;
    for( int tmp_i = i-1; tmp_i <= i+1; ++tmp_i )
    {
      for( int tmp_j = j-1; tmp_j <= j+1; ++tmp_j )
      {
        if( tmp_i < 0 || tmp_i >= GRID_SIZE || tmp_j < 0 || tmp_j >= GRID_SIZE )
        {}
        else
        {
          if( grid[tmp_i][tmp_j] )
          {
            neighbors++;
          }
        }
      }
    }
    return neighbors;
  }

  public void nextIteration()
  {
    boolean [][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];

    for(int i=0;i<GRID_SIZE;++i)
    {
      for(int j=0;j<GRID_SIZE;++j)
      {
        newGrid[i][j] = grid[i][j];
      }
    }

    for( int i=0;i<GRID_SIZE;++i)
    {
      for( int j=0;j<GRID_SIZE;++j)
      {
        int my_neighbors = getLiveNeighbors(i,j);
        if( !newGrid[i][j] && my_neighbors == 3)
        {
          grid[i][j] = true;
        }

        else if( newGrid[i][j] && ( my_neighbors == 2 || my_neighbors == 3 ) )
        {
          grid[i][j] = true;
        }

        else
        {
          grid[i][j] = false;
        }
      }
    }
    System.out.println("Change of assignment");
  }
}

Further tips

  1. Don't use AWT components in this millennium, use Swing instead.
  2. Don't use null layouts. The custom rendered area does not need it, and the button should be sized and positioned using a layout manager (with maybe a border to pad it out).

Update

This code implements the 2nd suggestion from above 'use layouts', but leaves it as an exercise for the reader to update the components to something that might be used in this millennium (i.e. Swing).

The source below 'cheats' in a sense to show the GUI at it's natural size. This is tricky to do in an applet, since the size is set by the HTML. But put the GUI into a Swing based JOptionPane and it can be put on-screen, packed to its natural size, in just a couple of lines of code.

Here is what it looks like at the 'natural size' (I played with some numbers, to make the GUI smaller).

GameOfLifeApplet

// <applet code='GameOfLifeApplet' width=320 height=350></applet>
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class GameOfLifeApplet extends Applet implements ActionListener
{
    private Button nextButton = null;
    private Ecosystem ecosystem;

    public void init()
    {
        add(getGui());
    }

    public Component getGui() {
        Panel gui = new Panel(new BorderLayout(3,3));
        ecosystem = new Ecosystem();
        gui.add(ecosystem, BorderLayout.CENTER);
        nextButton = new Button("Next Stage");

        Panel p = new Panel(new FlowLayout());
        p.add(nextButton);
        gui.add(p, BorderLayout.SOUTH);
            nextButton.addActionListener(this);
        return gui;
    }

    public void actionPerformed(ActionEvent evt)
    {
        // Here we will ask what component called this method
        if (evt.getSource() == nextButton)
        {
            System.out.println("I got clicked!");
            ecosystem.nextIteration();
            ecosystem.repaint();
        }
    }

    public static void main(String[] args) {
        GameOfLifeApplet gola = new GameOfLifeApplet();
        // quick cheat to get it on-screen (packed).
        javax.swing.JOptionPane.showMessageDialog(null,gola.getGui());
    }
}

class Ecosystem extends Panel implements MouseListener {
    private GameOfLife game = new GameOfLife();
    //the x and y coordinates to get the location of the clicked points
    private int xCo, yCo;
    private int diff_x, diff_y;
    private int size = 300;

    Ecosystem() {
        diff_x = diff_y = 600 / game.getGridSize();
        setPreferredSize(new Dimension(size,size));
        addMouseListener(this);
    }

    public void nextIteration() {
        game.nextIteration();
    }

    private void drawEmptyGrid(Graphics g)
    {
        g.setColor(Color.white);
        g.fillRect(0,0,size,size);
        g.setColor(Color.black);
        for(int i=0;i<game.getGridSize();++i)
        {
            g.drawLine(0,i*diff_x,size,i*diff_x);
            g.drawLine(i*diff_x,0,i*diff_x,size);
        }
        g.setColor(Color.white);
    }

    public void paint(Graphics g)
    {
        drawEmptyGrid(g);
        g.setColor(Color.red);
        for(int i=0;i<game.getGridSize();++i)
        {
            for(int j=0;j<game.getGridSize();++j)
            {
                if( game.grid[i][j] )
                {
                    g.fillRect(i*diff_x,j*diff_y,diff_x,diff_y);
                }
            }
        }
        g.setColor(Color.white);
    }

    // This method will be called when the mouse has been clicked.
    public void mouseClicked (MouseEvent me) {
        Point point = me.getPoint();

        // Save the coordinates of the click lke this.
        xCo = (int)point.getX();
        yCo = (int)point.getY();

        int x_init = xCo / diff_x;
        int y_init = yCo / diff_y;

        System.out.println(x_init + "x" + y_init);
        game.grid[x_init][y_init] = true;
        //show the results of the click
        repaint();
    }

    // This is called when the mous has been pressed
    public void mousePressed (MouseEvent me) {}

    // When it has been released
    // not that a click also calls these Mouse-Pressed and Released.
    // since they are empty nothing hapens here.
    public void mouseReleased (MouseEvent me) {}

    // This is executed when the mouse enters the applet. it will only
    // be executed again when the mouse has left and then re-entered.
    public void mouseEntered (MouseEvent me) {}

    // When the Mouse leaves the applet.
    public void mouseExited (MouseEvent me) {}
}

class GameOfLife
{
    private final int GRID_SIZE = 60;
    public boolean [][] grid = new boolean[GRID_SIZE][GRID_SIZE];

    //default constructor
    public GameOfLife()
    {
        for(int i=0;i<GRID_SIZE;++i)
        {
            for(int j=0;j<GRID_SIZE;++j)
            {
                grid[i][j] = false;
            }
        }
    }

    public int getGridSize()
    {
        return GRID_SIZE;
    }

    public int getLiveNeighbors(int i,int j)
    {
        int neighbors = 0;
        for( int tmp_i = i-1; tmp_i <= i+1; ++tmp_i )
        {
            for( int tmp_j = j-1; tmp_j <= j+1; ++tmp_j )
            {
                if( tmp_i < 0 || tmp_i >= GRID_SIZE || tmp_j < 0 || tmp_j >= GRID_SIZE )
                {}
                else
                {
                    if( grid[tmp_i][tmp_j] )
                    {
                    neighbors++;
                    }
                }
            }
        }
        return neighbors;
    }

    public void nextIteration()
    {
        boolean [][] newGrid = new boolean[GRID_SIZE][GRID_SIZE];

        for(int i=0;i<GRID_SIZE;++i)
        {
            for(int j=0;j<GRID_SIZE;++j)
            {
                newGrid[i][j] = grid[i][j];
            }
        }

        for( int i=0;i<GRID_SIZE;++i)
        {
            for( int j=0;j<GRID_SIZE;++j)
            {
                int my_neighbors = getLiveNeighbors(i,j);
                if( !newGrid[i][j] && my_neighbors == 3)
                {
                    grid[i][j] = true;
                }
                else if( newGrid[i][j] && ( my_neighbors == 2 || my_neighbors == 3 ) )
                {
                    grid[i][j] = true;
                }
                else
                {
                    grid[i][j] = false;
                }
            }
        }
        System.out.println("Change of assignment");
    }
}

Other matters

  1. The code moves the custom painting to a Panel. This gets around the common problems of painting directly to a top-level container. It also allows easy re-use of the same GUI in different containers. In this case both an applet, and for the 'application' (which would usually be put in a frame), a JOptionPane. It is now what is known as a 'hybrid applet/application' (easier for testing).
  2. The custom painted component Ecosystem (shrugs) informs the layout what size it prefers to be. This helps us to avoid needing to set the size or bounds of anything.
  3. The button will be exactly as big as it needs to be.

OTHER TIPS

First, I think you're reading the exception trace wrong. The exception is an ArrayIndexOutOfBoundsException and occurs on line 63 of GameOfLifeApplet.java. That your app is an applet or that the exception occurs on the thread AWT-EventQueue-1 bears no relevance at all.

The root cause is that you've not properly synchronized the model and view's idea of how many cells there are in your grid. At the least, you should consider checking that the user actually clicked inside the grid before accessing the array element.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top