Game-of-Life-WSU-CEG4180  v0.1
RefactoringtheGameofLife
GameOfLifeGrid.java
Go to the documentation of this file.
1 /* This page is part of the Game of Life source code */
2 
3 /**
4  * Copyright 1996-2004 Edwin Martin <edwin@bitstorm.nl>
5  * @author Edwin Martin
6  */
7 
8 package org.bitstorm.gameoflife;
9 
10 import java.awt.Dimension;
11 import java.util.Enumeration;
12 import java.util.Hashtable;
13 
14 /**
15  * Contains the cellgrid, the current shape and the Game Of Life algorithm that changes it.
16  *
17  * @author Edwin Martin
18  */
19 public class GameOfLifeGrid implements CellGrid {
20  private int cellRows;
21  private int cellCols;
22  private int generations;
23  private static Shape[] shapes;
24  /**
25  * Contains the current, living shape.
26  * It's implemented as a hashtable. Tests showed this is 70% faster than Vector.
27  */
28  private Hashtable currentShape;
29  private Hashtable nextShape;
30  /**
31  * Every cell on the grid is a Cell object. This object can become quite large.
32  */
33  private Cell[][] grid;
34 
35  /**
36  * Contructs a GameOfLifeGrid.
37  *
38  * @param cellCols number of columns
39  * @param cellRows number of rows
40  */
41  public GameOfLifeGrid(int cellCols, int cellRows) {
42  this.cellCols = cellCols;
43  this.cellRows = cellRows;
44  currentShape = new Hashtable();
45  nextShape = new Hashtable();
46 
47  grid = new Cell[cellCols][cellRows];
48  for ( int c=0; c<cellCols; c++)
49  for ( int r=0; r<cellRows; r++ )
50  grid[c][r] = new Cell( c, r );
51  }
52 
53  /**
54  * Clears grid.
55  */
56  public synchronized void clear() {
57  generations = 0;
58  currentShape.clear();
59  nextShape.clear();
60  }
61 
62  /**
63  * Create next generation of shape.
64  */
65  public synchronized void next() {
66  Cell cell;
67  int col, row;
68  int neighbours;
69  Enumeration enum;
70 
71  generations++;
72  nextShape.clear();
73 
74  // Reset cells
75  enum = currentShape.keys();
76  while ( enum.hasMoreElements() ) {
77  cell = (Cell) enum.nextElement();
78  cell.neighbour = 0;
79  }
80  // Add neighbours
81  // You can't walk through an hashtable and also add elements. Took me a couple of ours to figure out. Argh!
82  // That's why we have a hashNew hashtable.
83  enum = currentShape.keys();
84  while ( enum.hasMoreElements() ) {
85  cell = (Cell) enum.nextElement();
86  col = cell.col;
87  row = cell.row;
88  addNeighbour( col-1, row-1 );
89  addNeighbour( col, row-1 );
90  addNeighbour( col+1, row-1 );
91  addNeighbour( col-1, row );
92  addNeighbour( col+1, row );
93  addNeighbour( col-1, row+1 );
94  addNeighbour( col, row+1 );
95  addNeighbour( col+1, row+1 );
96  }
97 
98  // Bury the dead
99  // We are walking through an enum from we are also removing elements. Can be tricky.
100  enum = currentShape.keys();
101  while ( enum.hasMoreElements() ) {
102  cell = (Cell) enum.nextElement();
103  // Here is the Game Of Life rule (1):
104  if ( cell.neighbour != 3 && cell.neighbour != 2 ) {
105  currentShape.remove( cell );
106  }
107  }
108  // Bring out the new borns
109  enum = nextShape.keys();
110  while ( enum.hasMoreElements() ) {
111  cell = (Cell) enum.nextElement();
112  // Here is the Game Of Life rule (2):
113  if ( cell.neighbour == 3 ) {
114  setCell( cell.col, cell.row, true );
115  }
116  }
117  }
118 
119  /**
120  * Adds a new neighbour to a cell.
121  *
122  * @param col Cell-column
123  * @param row Cell-row
124  */
125  public synchronized void addNeighbour(int col, int row) {
126  try {
127  Cell cell = (Cell)nextShape.get( grid[col][row] );
128  if ( cell == null ) {
129  // Cell is not in hashtable, then add it
130  Cell c = grid[col][row];
131  c.neighbour = 1;
132  nextShape.put(c, c);
133  } else {
134  // Else, increments neighbour count
135  cell.neighbour++;
136  }
137  } catch (ArrayIndexOutOfBoundsException e) {
138  // ignore
139  }
140  }
141 
142  /**
143  * Get enumeration of Cell's
144  * @see org.bitstorm.gameoflife.CellGrid#getEnum()
145  */
146  public Enumeration getEnum() {
147  return currentShape.keys();
148  }
149 
150  /**
151  * Get value of cell.
152  * @param col x-coordinate of cell
153  * @param row y-coordinate of cell
154  * @return value of cell
155  */
156  public synchronized boolean getCell( int col, int row ) {
157  try {
158  return currentShape.containsKey(grid[col][row]);
159  } catch (ArrayIndexOutOfBoundsException e) {
160  // ignore
161  }
162  return false;
163  }
164 
165  /**
166  * Set value of cell.
167  * @param col x-coordinate of cell
168  * @param row y-coordinate of cell
169  * @param c value of cell
170  */
171  public synchronized void setCell( int col, int row, boolean c ) {
172  try {
173  Cell cell = grid[col][row];
174  if ( c ) {
175  currentShape.put(cell, cell);
176  } else {
177  currentShape.remove(cell);
178  }
179  } catch (ArrayIndexOutOfBoundsException e) {
180  // ignore
181  }
182  }
183 
184  /**
185  * Get number of generations.
186  * @return number of generations
187  */
188  public int getGenerations() {
189  return generations;
190  }
191 
192  /**
193  * Get dimension of grid.
194  * @return dimension of grid
195  */
196  public Dimension getDimension() {
197  return new Dimension( cellCols, cellRows );
198  }
199 
200  /**
201  * Resize grid. Reuse existing cells.
202  * @see org.bitstorm.gameoflife.CellGrid#resize(int, int)
203  */
204  public synchronized void resize(int cellColsNew, int cellRowsNew) {
205  if ( cellCols==cellColsNew && cellRows==cellRowsNew )
206  return; // Not really a resize
207 
208  // Create a new grid, reusing existing Cell's
209  Cell[][] gridNew = new Cell[cellColsNew][cellRowsNew];
210  for ( int c=0; c<cellColsNew; c++)
211  for ( int r=0; r<cellRowsNew; r++ )
212  if ( c < cellCols && r < cellRows )
213  gridNew[c][r] = grid[c][r];
214  else
215  gridNew[c][r] = new Cell( c, r );
216 
217  // Copy existing shape to center of new shape
218  int colOffset = (cellColsNew-cellCols)/2;
219  int rowOffset = (cellRowsNew-cellRows)/2;
220  Cell cell;
221  Enumeration enum;
222  nextShape.clear();
223  enum = currentShape.keys();
224  while ( enum.hasMoreElements() ) {
225  cell = (Cell) enum.nextElement();
226  int colNew = cell.col + colOffset;
227  int rowNew = cell.row + rowOffset;
228  try {
229  nextShape.put( gridNew[colNew][rowNew], gridNew[colNew][rowNew] );
230  } catch ( ArrayIndexOutOfBoundsException e ) {
231  // ignore
232  }
233  }
234 
235  // Copy new grid and hashtable to working grid/hashtable
236  grid = gridNew;
237  currentShape.clear();
238  enum = nextShape.keys();
239  while ( enum.hasMoreElements() ) {
240  cell = (Cell) enum.nextElement();
241  currentShape.put( cell, cell );
242  }
243 
244  cellCols = cellColsNew;
245  cellRows = cellRowsNew;
246  }
247 }
synchronized void resize(int cellColsNew, int cellRowsNew)
synchronized boolean getCell(int col, int row)
synchronized void setCell(int col, int row, boolean c)
synchronized void addNeighbour(int col, int row)
GameOfLifeGrid(int cellCols, int cellRows)