Question: You will be given a base implementation of Conways Game of Life. Using the MVC pattern and Java Swing, turn the game into an interactive

You will be given a base implementation of Conways Game of Life. Using the MVC pattern and Java Swing, turn the game into an interactive GUI.

1) The provided Grid class will be the model in the MVC paradigm. Familiarize yourself with how this class works. Each iteration (call to update()), it counts the adjacent living cells for each cell on the board using itsCurrentState. It uses this information to produce the itsNextState state, then swaps the two arrays. At any time we can ask the Grid if a specific cell at (i,j) is alive or set it to be alive or dead. Begin by altering Grid to extend java.util.Observable. Recall that any methods that change the state of the object should make calls to setChanged() and notifyObservers().

2) Next, you will create a view for the game in Swing. The easiest way to achieve this is to extend the class JPanel. Your view should keep an instance of the Grid model is it viewing, which is passed in to its constructor. Use whatever colors you like to represent living and dead cells, but the size in pixels of each cell should be configurable as a parameter. Your GUI class should also implement java.util.Observer and be responsible for refreshing its display when its Grid model changes.

3) You should be able to click rectangles in the grid using your mouse to toggle them alive or dead. This is done by setting up a MouseListener (this.addMouseListener(...)) and querying the mouse event for its (x,y) coordinate. From this (x,y) coordinate in window-pixel space, you can infer the (i,j) cell in cell-coordinate space. These listeners serve as our MVC controllers.

4) Have the simulation begin running when the b key is pressed.

5) Create a main runner class to setup and run a 75-by-100 instance of Conways Game of Life GUI. You can use any reasonable size for the cells. Instantiate a new JFrame instance and add your custom view to it. Set it to visible and make sure your interactions are functional. Verify the behavior of your game against an online implementation.

Cell.java :

/** * A class representing a single cell in a Conway Grid. */ public class Cell {

/** Whether the cell is alive or not. */ private boolean isAlive;

/** * Creates a new, dead Cell. */ public Cell() { this.isAlive = false; }

/** * Checks whether the Cell is alive or not. * * @return Whether this Cell is alive. */ public boolean isAlive() { return this.isAlive; }

/** * Sets the alive status of this Cell. * * @param val the new alive value. True for alive or false for dead. */ public void setAlive(boolean val) { this.isAlive = val; } public String toString() { String returnVal = "-"; if (isAlive) returnVal = "O"; return returnVal; }

public boolean equals(Object o) { boolean returnVal = false; if (o instanceof Cell) returnVal = (this.isAlive == ((Cell)o).isAlive()); return returnVal; } } _________________________________________________ Grid.java

/** * This class represents a single instance of Conway's Game of Life. */ public class Grid {

/** The current state. */ private Cell[][] itsCurrentState;

/** The next state. */ private Cell[][] itsNextState;

/** * Constructs a new Grid with the specified number of rows and columns. * * @param numRows The number of rows. * @param numColumns The number of columns. */ public Grid(int numRows, int numColumns) { itsCurrentState = new Cell[numRows][numColumns]; itsNextState = new Cell[numRows][numColumns]; for (int row=0; row< itsCurrentState.length; row++) { for (int column=0; column < itsCurrentState[row].length; column++) { itsCurrentState[row][column] = new Cell(); itsNextState[row][column] = new Cell(); } } }

/** * Checks whether the cell at row, column is alive. * * @param row The row number to query. * @param column The column number to query. * @return Whether the requested cell is "alive". */ public boolean cellIsAlive(int row, int column) { return itsCurrentState[row][column].isAlive(); } /** * Sets the "alive" state of the cell at row , column * * @param row The row number. * @param column The column number. * @param val Whether the cell should be set to alive (true) or dead (false). */ public void setCellAlive(int row, int column, boolean val){ itsCurrentState[row][column].setAlive(val); } /** * Returns the number of rows in this grid. * * @return The number of rows in this grid. */ public int rowCount(){ return this.itsCurrentState.length; } /** * Returns the number of columns in this grid. * * @return The number of columns in this grid. */ public int columnCount(){ return this.itsCurrentState[0].length; }

/** * Advances the game of life a single step forward. */ public void update() { // loop over rows for (int row=0; row < itsCurrentState.length; row++) { // loop over columns for (int column=0; column < itsCurrentState[row].length; column++) { boolean isAliveNextRound = aliveNextRound(row,column); itsNextState[row][column].setAlive(isAliveNextRound);

} } swapStates(); } ////////////////////////////////////////////// //PRIVATE METHODS: //Not for the faint of heart!! //////////////////////////////////////////////

private boolean aliveNextRound(int row, int column) { boolean aliveNextRound = false;; boolean currentAliveState = itsCurrentState[row][column].isAlive(); int liveNeighbors = getCountOfLiveNeighbors(row,column); if (currentAliveState == true && liveNeighbors <2) aliveNextRound = false; else if ( currentAliveState == true && (liveNeighbors == 2 || liveNeighbors == 3) ) aliveNextRound = true; else if ( currentAliveState == true && liveNeighbors > 3) aliveNextRound = false; else if ( currentAliveState == false && liveNeighbors == 3) aliveNextRound = true; return aliveNextRound; }

private int getCountOfLiveNeighbors(int row, int column) { int numLiveNeighbors = 0; // check general case first - here we know we don't need // to check boundary conditions explicitly if (row != 0 && row != itsCurrentState.length - 1 && column != 0 && column != itsCurrentState[0].length - 1) {

for (int rowMod = -1; rowMod < 2; rowMod++) { for (int colMod = -1; colMod <2; colMod++) { if ( itsCurrentState[row+rowMod][column+colMod].isAlive() && !(rowMod == 0 && colMod ==0) ) // don't count myself numLiveNeighbors++; } }

} // end of general case calculation else { // now deal with edge cases int up = row-1; int down = row+1; int right = column+1; int left = column-1; if (row == 0) { // top edge case up = itsCurrentState.length-1; } else if (row == itsCurrentState.length-1) { // bottom edge case down = 0; }

if (column == 0) { // left edge case left = itsCurrentState[0].length-1; } else if (column == itsCurrentState[0].length-1) { // right edge case right = 0; }

int[][] neighborsToConsider = { {up,left}, {up,column}, {up,right}, {row,left}, {row,right}, {down,left},{down,column},{down,right} };

for (int neighborIndex = 0; neighborIndex < neighborsToConsider.length; neighborIndex++) { if (itsCurrentState[neighborsToConsider[neighborIndex][0]][neighborsToConsider[neighborIndex][1]].isAlive()) numLiveNeighbors++; }

} // end of edge cases clause return numLiveNeighbors; }

// this swaps current and next references private void swapStates() { Cell[][] temp = itsCurrentState; itsCurrentState = itsNextState; itsNextState = temp; } // @Override public String toString() { String returnVal = ""; for (int i=0; i for (int j=0; j returnVal += itsCurrentState[i][j]; returnVal += " "; } returnVal += " "; } return returnVal; }

public void gliderSetup() { itsCurrentState[5][5].setAlive(true); itsCurrentState[6][5].setAlive(true); itsCurrentState[7][5].setAlive(true); itsCurrentState[7][4].setAlive(true); itsCurrentState[6][3].setAlive(true); } }

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!