Question
In the following project you will complete an implementation of John Conway's famous Game of Life. The Game of Life is an example of a
In the following project you will complete an implementation of John Conway's famous Game of Life. The Game of Life is an example of a cellular automaton and is played on a (finite) grid of cells. Each cell is either alive or dead and has at most eight neighbors. The state of the cell changes from one generation to the next according to the following rules:
1. Birth Rule: A dead cell with exactly three living neighbors is reborn (vivified) in the next generation. Otherwise, a dead cell remains dead!
2. Survival Rule: A live cell with two or three living neighbors remains alive for the next generation. A live cell (blue) and its 8 neighboring cells (green)
3. Death Rule: A live cell with one or fewer living neighbors dies from loneliness in the next generation; a live cell with four or more living neighbors dies from overcrowding in the next generation.
4. Using rules 1 to 3, the states of all the cells in the next generation is calculated based on the current alive or dead states of all the grid cells. Then the states of all the cells are transformed to their new states.
5. Rules 1 to 4 are repeated as long as is desired!
An Example:
If the rules are applied to a row of three live blue cells (see figures below), then in the next generation
- The two end cells (red background) will die because they each have only one live neighbor.
- The two dead North and South cells (green background) will be reborn because they each have exactly three live neighbors
- The center live cell (gray background) survives because it has two live neighbors!
- In fact, the third generation will be the same as the first (why?). So this is an example of an oscillating configuration!
How to Run the Program
- Use the Go and Step buttons to cause the program to calculate and display future generations of the cells.
- Use the Reset button to restore the board to the initial configuration of cells.
- Use the Slow, Medium, Fast, Light Speed ComboBox to control and set the speed of the program.
- Use the Flat Grid, Torus Grid ComboBox to control the type of grid.
- In a flat grid, edge and corner cells have fewer than eight neighbors. Thus, the life and death behavior of edge and corner cells is different than other cells. The Game of Life Application
- In a torus grid, the bottom and top edges "wrap around" and are considered as identical. Similarly, the left and right edges are considered as identical too. Hence, the four corner cells all have each other as neighbors! Thus, the grid simulates a torus (a donut) and every cell has eight neighbors!.
- Use the Open button to open and read .life configuration files such as the example files.
- Use the Save button to save the current cell configuration in a .life text file
- Use the Fish, Plus, Glider, FlyingMachine ComboBox to choose an initial configuration. Run these examples on both Flat Grid and Torus Grid, and see the differences.
- Use the mouse (clicking on a cell toggles its state) to create your own initial configurations or to edit an existing configuration.
1. In Eclipse, create a new Java project called "Project 2", and import the above 6 files into a default package.
2. Compile and run the program. Make sure it works (it shows the game board).
3. Investigate all the source code, and draw a UML diagram of all classes. For each class, describe its data fields and methods. Presents the relationships among these classes,such as inheritance and associations (20 points).
4. To complete the project, you will first need to implement the following four private methods in GameOfLife.java for flat grid (20 points):
1. private void copyMap(boolean sourceMap[][])
2. private void clearMap(boolean targetMap[][])
3. private int getFlatNeighborCount(int row, int col)
4. private void nextGenerationForFlatGrid() Then you are asked to implement the game of life for torus grid (20 points).
5. Write the pre-post conditions for each method you implemented for torus grid in the java file (10 points).
6. Test your program with additional cell configuration files, write a test report to record the results (10 points):
- what is the configuration file you have tested.
- what is the observation. If it stops to change after some generations, describe the final shape.
The Cell Configuration .life Files
A cell configuration file can be made using text-only editor. The contents of the file are up to fifty lines of text consisting of only the '-' and '*' characters. Each line can contain up to fifty such characters. A '-' indicates that a cell is dead while the '*' indicates that a cell is alive. A character in the third line (from the top) and tenth column (from the left) corresponds to the cell with coordinates (2, 9). Thus, the first line of the file corresponds to the zero-th row cells and the first column of text corresponds to the zero-th column of cells. If a file contains fewer than fifty lines or fewer than fifty columns of text, the program will assume that the corresponding cells are dead!
Classes
import java.io.*; import javax.swing.*; import javax.swing.filechooser.FileFilter;
public class FileIO { protected int maxRowsRead = 0, maxColsRead = 0; protected boolean echoIO = false; protected JFileChooser chooser; // Remembers previous directory protected SimpleFileFilter lifeFilter;
FileIO(String fileExtension, String fileDescription) { chooser = new JFileChooser(); lifeFilter = new SimpleFileFilter(fileExtension, fileDescription); }
public File getFile() { File file = null; int returnVal;
chooser.setFileFilter(lifeFilter); returnVal = chooser.showOpenDialog(null); if (returnVal == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); System.out.println("You chose to open the file: "+ file.getName()+"."); } else System.out.println("No file selected.");
return file; }
public File putFile() { File file = null; int returnVal; chooser.setFileFilter(lifeFilter); returnVal = chooser.showSaveDialog(null); if (returnVal == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); System.out.println("You chose to save the file: "+ file.getName()+"."); } else System.out.println("Save was not approved.");
return file; }
public int getMaxRowsRead() { return maxRowsRead; }
public int getMaxColsRead() { return maxColsRead; }
public boolean read(boolean bray[][]) { try { File file = getFile(); if (file == null) return false; else { int inInt; char inChar; String inStr; int row = 0, col = 0;
FileInputStream inStream = new FileInputStream(file); InputStreamReader reader = new InputStreamReader(inStream); inInt = reader.read(); while ((inInt != -1)&&(row < bray.length)) { inChar = (char)inInt; switch (inChar) { case '-': if (col < bray[row].length){ bray[row][col] = false; col++; } break; case '*': if (col < bray[row].length){ bray[row][col] = true; col++; } break; case ' ': if (maxColsRead < col) maxColsRead = col; col = 0; row++; break; default: ; } if (echoIO) System.out.print(inChar); inInt = reader.read(); } // while maxRowsRead = row; if (echoIO) System.out.println(""); reader.close(); return true; } // else } catch (IOException e){ System.out.println("Error in reading."); return false; } }
public void write(boolean bray[][]) { try { File file = putFile(); if (file == null) System.out.print("File null"); else { FileOutputStream outStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(outStream); final int DASH = (int)'-'; final int ASTERISK = (int)'*'; final int NEW_LINE = (int)' ';
for (int row = 0; row < bray.length; row++){ for (int col = 0; col < bray[row].length; col++){ if (bray[row][col]) { writer.write(ASTERISK); if (echoIO) System.out.print('*'); } else { writer.write(DASH); if (echoIO) System.out.print('-'); } } writer.write(NEW_LINE); if (echoIO) System.out.println(); } writer.flush(); writer.close(); } } catch (IOException e) { System.out.println("Error in writing."); } }
private class SimpleFileFilter extends FileFilter {
protected String extensionStr=""; protected String fileDescriptionStr = "";
public SimpleFileFilter(String extensionStr, String fileDescriptionStr) { this.extensionStr = extensionStr; this.fileDescriptionStr = fileDescriptionStr; }
public String getExtension(File f) { String ext = null; String s = f.getName(); int i = s.lastIndexOf('.'); if (i > 0 && i < s.length() - 1) ext = s.substring(i+1).toLowerCase(); return ext; }
public boolean accept(File f) { if (f.isDirectory()) return true; else { String extension = getExtension(f); if (extension != null) return (extension.equals(extensionStr)); } return false; }
public String getDescription() { return fileDescriptionStr; } } }
import javax.swing.*; import java.awt.*;
public class GameBoard extends JComponent { public static final int BOARD_WIDTH = 500; public static final int BOARD_HEIGHT = 500; public static final int BOARD_MARGIN = 5; public static final int GAME_BOARD_BOTTOM = BOARD_HEIGHT + 2*BOARD_MARGIN; public static final int GAME_BOARD_RIGHT = BOARD_WIDTH + 2*BOARD_MARGIN; public static final int CELL_HEIGHT = 10; public static final int CELL_WIDTH = 10;
Dimension preferredSize; boolean boardArray[][]; Image gridImage511_By_511;
public GameBoard(boolean boardArray[][]){ preferredSize = new Dimension(GAME_BOARD_RIGHT + 1, GAME_BOARD_BOTTOM + 1); this.setSize(preferredSize.width, preferredSize.height); this.boardArray= boardArray; setBorder(BorderFactory.createMatteBorder(BOARD_MARGIN, BOARD_MARGIN, BOARD_MARGIN, BOARD_MARGIN, Color.BLUE)); setOpaque(true); gridImage511_By_511 = Toolkit.getDefaultToolkit().getImage(getClass().getResource("gameGrid.gif")); this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); }
public Dimension getPreferredSize() { return preferredSize; }
protected void paintComponent(Graphics g) { if (isOpaque()) { g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } g.drawImage(gridImage511_By_511,0,0, this); g.setColor(Color.BLUE); for (int row = 1; row <= boardArray.length; row++) for (int col = 1; col <= boardArray[0].length; col++) if (boardArray[row-1][col-1]) { g.fillRect(col*CELL_WIDTH-2, row*CELL_HEIGHT-2, CELL_WIDTH-BOARD_MARGIN, CELL_HEIGHT-BOARD_MARGIN); } }
public Point getCell(Point pt){ if (pt.x < BOARD_MARGIN) pt.x = BOARD_MARGIN; else if (pt.x >= GAME_BOARD_RIGHT-BOARD_MARGIN) pt.x = GAME_BOARD_RIGHT-BOARD_MARGIN-1;
if (pt.y < BOARD_MARGIN) pt.y = BOARD_MARGIN; else if (pt.y >= GAME_BOARD_BOTTOM-BOARD_MARGIN) pt.y = GAME_BOARD_BOTTOM-BOARD_MARGIN-1; return new Point((pt.x-BOARD_MARGIN)/CELL_WIDTH, (pt.y-BOARD_MARGIN)/CELL_HEIGHT); }
public void drawCell(Point cell){ int x = (cell.x+1)*CELL_WIDTH-BOARD_MARGIN; int y = (cell.y+1)*CELL_HEIGHT-BOARD_MARGIN; repaint(x,y,CELL_WIDTH,CELL_HEIGHT); } }
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.lang.Runnable; import java.lang.Thread;
public class GameGUI extends JFrame implements Runnable, ActionListener { public final int FLAT_GRID = 1; // One of two possible grid modes public final int TORUS_GRID = 2; // The second of two possible grid modes
protected GameInterface game; protected GameBoard board; protected JButton goButton, stepButton,resetButton, readInMapButton, saveButton; protected JComboBox gridTypeComboBox, speedComboBox, exampleComboBox; protected JLabel generationLabel, rowColLabel;
protected int gridMode = FLAT_GRID; protected Thread playGameThread = null; protected boolean gameStopped = true; protected int gameSpeed = 500;
public GameGUI(GameInterface game, boolean[][] map) { this.game = game; board = new GameBoard(map); setContentPane(makeContentPane()); pack(); setVisible(true); }
public Container makeContentPane() { Color birthColor = new Color(160, 160, 222); Color survivalColor = new Color(192, 192, 255); Color lightBlue = new Color(200, 200, 240); Color gray12 = new Color(222,222,222); setSize(550, 600);
//IO buttons readInMapButton = new JButton("Open Cell Configuration..."); readInMapButton.setActionCommand("Open"); readInMapButton.addActionListener(this); readInMapButton.setBackground(lightBlue);
saveButton = new JButton("Save Cell Configuration..."); saveButton.setActionCommand("Save"); saveButton.addActionListener(this); saveButton.setBackground(lightBlue);
String[] exampleStrs = {"Fish", "Plus", "Glider", "FlyingMachine", "ClearSpace"}; exampleComboBox = new JComboBox(exampleStrs); exampleComboBox.setSelectedIndex(1); exampleComboBox.addActionListener(this); exampleComboBox.setBackground(lightBlue);
JPanel IOPane = new JPanel(); IOPane.setBackground(lightBlue);
JPanel topPane = new JPanel(); topPane.setLayout(new BorderLayout()); topPane.add(IOPane,BorderLayout.CENTER); IOPane.add(readInMapButton); IOPane.add(saveButton); IOPane.add(exampleComboBox);
JPanel tempPane = new JPanel();
//Game generation control buttons
String[] gridStrs = {"Flat Grid", "Torus Grid"}; gridTypeComboBox = new JComboBox(gridStrs); gridTypeComboBox.setSelectedIndex(0); gridTypeComboBox.addActionListener(this); gridTypeComboBox.setBackground(gray12);
String[] speedStrs = {"Slow", "Medium", "Fast", "Light"}; speedComboBox = new JComboBox(speedStrs); speedComboBox.setSelectedIndex(1); speedComboBox.addActionListener(this); speedComboBox.setBackground(gray12);
stepButton = new JButton("Step"); stepButton.setActionCommand("Step"); stepButton.addActionListener(this); stepButton.setBackground(gray12);
goButton = new JButton("Go"); goButton.setActionCommand("Go"); goButton.addActionListener(this); goButton.setBackground(gray12);
resetButton = new JButton("Reset"); resetButton.setActionCommand("Reset"); resetButton.addActionListener(this); resetButton.setBackground(gray12);
JPanel buttonPane = new JPanel(); buttonPane.setBackground(gray12);
tempPane = new JPanel(); tempPane.setLayout(new BorderLayout()); tempPane.setBackground(gray12); tempPane.add(gridTypeComboBox, BorderLayout.NORTH); buttonPane.add(tempPane);
tempPane = new JPanel(); tempPane.setLayout(new BorderLayout()); tempPane.setBackground(gray12); tempPane.add(speedComboBox, BorderLayout.NORTH); buttonPane.add(tempPane);
tempPane = new JPanel(); tempPane.add(goButton); tempPane.setBackground(gray12); buttonPane.add(tempPane);
tempPane = new JPanel(); tempPane.add(stepButton); tempPane.setBackground(gray12); buttonPane.add(tempPane);
tempPane = new JPanel(); tempPane.add(resetButton); tempPane.setBackground(gray12); buttonPane.add(tempPane);
generationLabel = new JLabel("Generation: 0"); rowColLabel = new JLabel("[0,0]",JLabel.CENTER); rowColLabel.setForeground(Color.blue); tempPane = new JPanel(); tempPane.setLayout(new BorderLayout()); tempPane.setBackground(gray12); tempPane.add(generationLabel, BorderLayout.NORTH); tempPane.add(rowColLabel, BorderLayout.CENTER); buttonPane.add(tempPane);
JPanel bottomPane = new JPanel(); bottomPane.setLayout(new BorderLayout()); bottomPane.add(buttonPane, BorderLayout.CENTER); bottomPane.setBackground(gray12);
JPanel controlsPane = new JPanel(); controlsPane.setLayout(new BorderLayout()); controlsPane.add(topPane, BorderLayout.NORTH); controlsPane.add(bottomPane, BorderLayout.SOUTH);
JPanel mainPane = new JPanel(); mainPane.setLayout(new BorderLayout()); mainPane.add(controlsPane, BorderLayout.NORTH);
// Component board = game.getBoard(); board.addMouseListener(new gameMouseAdapter()); board.addMouseMotionListener(new gameMouseMotionAdapter()); tempPane = new JPanel(); tempPane.add(board); tempPane.setBackground(gray12); mainPane.add(tempPane, BorderLayout.SOUTH);
mainPane.setBorder(BorderFactory.createMatteBorder(1,1,2,2,Color.black));
addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
return mainPane; }
private void resetGoButton(boolean resetGenerationLabel) { goButton.setText("Go"); if (resetGenerationLabel) generationLabel.setText("Generation: 0"); gameStopped = true; playGameThread = null; }
private void step() { if (gridMode == FLAT_GRID) game.nextGenerationForFlatGrid(); else game.nextGenerationForTorusGrid(); this.repaint(); generationLabel.setText("Generation: " + game.getGeneration()); }
public void actionPerformed(ActionEvent e) { String command = e.getActionCommand();
if (command.equals("comboBoxChanged")){ if (e.getSource() == gridTypeComboBox){ resetGoButton(true); switch (gridTypeComboBox.getSelectedIndex()){ case 0: gridMode = FLAT_GRID; break; case 1: gridMode = TORUS_GRID; break; } } else if (e.getSource() == speedComboBox){ switch (speedComboBox.getSelectedIndex()){ case 0: gameSpeed = 1000; break; case 1: gameSpeed = 500; break; case 2: gameSpeed = 100; break; case 3: gameSpeed = 10; break; } } else if (e.getSource() == exampleComboBox){ switch (exampleComboBox.getSelectedIndex()){ case 0: game.readExample(1); break; case 1: game.readExample(2); break; case 2: game.readExample(3); break; case 3: game.readExample(4); break; case 4: game.readExample(0); break; } } } else if(command.equals("Step")) { resetGoButton(false); step(); } else if(command.equals("Go")){ if (goButton.getText().equals("Go")) { goButton.setText("Stop"); playGameThread = new Thread(this); gameStopped = false; playGameThread.start(); } else { resetGoButton(false); this.repaint(); } }else if(command.equals("Reset")){ resetGoButton(true); game.reset(); } else if(command.equals("Open")){ resetGoButton(true); game.readInMap(); } else if(command.equals("Save")){ if(!gameStopped) resetGoButton(true); game.writeMap(); } }
public class gameMouseAdapter extends MouseAdapter { public void mousePressed(MouseEvent e){ if (!gameStopped) resetGoButton(true); toggleCell(e.getPoint()); } }
// change the state of a cell public void toggleCell(Point clickedPt) { try{ Point cell = board.getCell(clickedPt); game.updateMap(cell.y, cell.x); board.drawCell(cell); } catch (ArrayIndexOutOfBoundsException e){ System.out.println("ERROR in ToggleCell. " + e); } }
public class gameMouseMotionAdapter extends MouseMotionAdapter { public void mouseMoved(MouseEvent e){ GameBoard board = (GameBoard)e.getComponent(); Point cell = board.getCell(e.getPoint()); rowColLabel.setText("[" + cell.y + ", " + cell.x + "]"); } }
public void run() { while(!gameStopped){ step(); try{ Thread.sleep(gameSpeed); } catch(InterruptedException e){} } } }
public interface GameInterface { public final boolean DEAD = false; // State of a dead cell public final boolean ALIVE = true; // State of a live cell public final int BIRTH_NBR_COUNTS = 3; // The neighbor counts that allow a dead // cell to be vivified in the next generation public final int SURV_NBR_COUNTS_2 = 2; // The neighbor counts that allow a live public final int SURV_NBR_COUNTS_3 = 3; // cell to survive to the next generation public final int MAX_ROWS = 50; // The maxium number of grid rows public final int MAX_COLS = 50; // The maxium number of grid columns
public void nextGenerationForFlatGrid(); // Run game of life for one step public void nextGenerationForTorusGrid();// Run game of life for one step
public int getGeneration(); // Get current generation number public void reset(); // Reset the map to the original map public void readExample(int n); // Read sample examples public void readInMap(); // Read in map from file public void writeMap(); // Write map to file public void updateMap(int row, int col); // Change the state of a cell
}
public class GameOfLife implements GameInterface {
private boolean[][] originalMap, // The initial cell configuration
map, // The current cell configuration
newMap; // The next generation configuration
private GameGUI gui;
private int generation = 0;
private FileIO fileIO;
// GameOfLife constructor
public GameOfLife() {
originalMap = new boolean[MAX_ROWS][MAX_COLS];
map = new boolean[MAX_ROWS][MAX_COLS];
newMap = new boolean[MAX_ROWS][MAX_COLS];
gui = new GameGUI(this, map);
gui.setTitle("CIS 181 Array Based Game Of Life");
fileIO = new FileIO("life", "Game of Life Text Files");
readExample(2);
}
// ====>>>>> Complete the methods below this line! <<<<<====
// copyMap:
// Precondtions: None.
// Postcondtion: 'map' is a deep copy of 'sourceMap'.
//
private void copyMap(boolean sourceMap[][]) {
// ==> 1. Add your code here!
}
// clearMap:
// Precondtions: None.
// Postcondtion: Sets all cells of the 'targetMap' to DEAD.
//
private void clearMap(boolean targetMap[][]) {
// ==> 2. Add your code here!
}
// getFlatNeighborCount:
// Precondtions: 0 <= row < MAX_ROWS and 0 <= col < MAX_COLS.
// Postcondtion: A count of all LIVE neighbors of the cell at [row, col] is
// returned where its neighbors are all the ADJACENT cells
// including those
// a) In the rows BELOW and ABOVE the cell (if any exist).
// b) In the columns LEFT and RIGHT of the cell (if any exist).
// Thus, a cell adjacent to a board edge (or corner) has
// fewer neighbors than other cells.
//
private int getFlatNeighborCount(int row, int col){
int count = 0;
// ==> 3. Add your code here!
return count;
}
// nextGenerationForFlatGrid:
// Precondtions: None
// Postcondtion: The next generation of live and dead cells is calculated using
// a) the FLAT neighbor counts.
// b) the current birth, survival and death count rules.
// c) the rules are applied to the counts obtained from the current
// generation's configuration of live and dead cells.
// The current 'map' is updated to the next generation's configuration
// of live and dead cells.
// d) the global variable 'generation' is increased by 1
//
public void nextGenerationForFlatGrid() {
// ==> 4. Add your code here!
}
// ==> 5. Implement the game of life for torus grid.
public void nextGenerationForTorusGrid() {}
// ====>>>>> Don't touch the code below this line! <<<<<====
// Return the next generation
public int getGeneration() {
return generation;
}
// Reset the map to the original map
public void reset() {
copyMap(originalMap);
generation = 0;
gui.repaint();
}
// Game of life examples 1-4: Fish, Plus, Glider, FlyingMachine
public void readExample(int n) {
System.out.println("Initializing with example " + n + " ...");
clearMap(originalMap);
switch (n) {
case 1: // Example 1: Fish
for (int col = 23; col <= 26; col++)
originalMap[13][col] = ALIVE;
originalMap[14][22] = ALIVE;
originalMap[14][26] = ALIVE;
originalMap[15][26] = ALIVE;
originalMap[16][22] = ALIVE;
originalMap[16][25] = ALIVE;
break;
case 2: // Example 2: Plus
for (int col = 6; col < 43; col++)
originalMap[24][col] = ALIVE;
for (int row = 6; row < 43; row++)
originalMap[row][24] = ALIVE;
break;
case 3: // Example 3: Glider
originalMap[14][23] = ALIVE;
originalMap[15][24] = ALIVE;
for (int row = 13; row <= 15; row++)
originalMap[row][25] = ALIVE;
break;
case 4: // Example 4: FlyingMachine
for (int col = 22; col <= 25; col++) {
originalMap[11][col] = ALIVE;
originalMap[19][col] = ALIVE;
}
for (int row = 14; row <= 16; row++)
for (int col = 17; col <= 18; col++)
originalMap[row][col] = ALIVE;
originalMap[15][19] = ALIVE;
for (int row = 12; row <= 18; row = row+2)
originalMap[row][21] = ALIVE;
originalMap[14][24] = ALIVE;
originalMap[16][24] = ALIVE;
originalMap[12][25] = ALIVE;
originalMap[13][25] = ALIVE;
originalMap[17][25] = ALIVE;
originalMap[18][25] = ALIVE;
break;
default: // Default Example: ClearSpace
break;
}
copyMap(originalMap);
generation = 0;
gui.repaint();
}
// Read map from file
public void readInMap() {
clearMap(originalMap);
if (fileIO.read(originalMap)) {
copyMap(originalMap);
generation = 0;
} else
readExample(2);
gui.repaint();
}
// Write map to file
public void writeMap() {
fileIO.write(map);
}
// Change the state of a cell
public void updateMap(int row, int col) {
map[row][col] = !map[row][col];
}
// Destroy the GUI window
public void destroy() {
gui.dispose();
}
// The main method of GameOfLife
public static void main(String[] args) {
GameOfLife game = new GameOfLife();
}
}
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started