CSCI 1301: Introduction to Computing and Programming Fall 2015 Lab 07: Conway’s Game of Life Introduction Conway's Game of Life is a simple cellular automaton devised by John Conway in 1970. It consists of a two dimensional grid of cells that evolves over generations. In a given generation, each cell may be "alive" or "dead", and whether the cell is alive or dead in the next generation is completely determined by both its current state and the current states of the cells adjacent to it (the exact rules are specified below). In the above grid, each square represents a cell, with dead cells shown in black and live ones shown in white. The center square is alive in the current generation, and it has 5 dead neighbors and 3 live ones. According to the rules of the game, that center cell survives to the next generation. The cell in the bottom right of the grid is dead in the next generation, however. For a more detailed discussion of the game, see http://www.conwaylife.com. In this lab, you will implement routines needed to populate the grid and update it in each generation. You will be given a jar file (Java Archive File) called Grid.jar (you may download it from the labs and projects webpage) that contains a class called Grid, which will enable you to create Grid objects (much as you have created Scanner objects), and you will be able to retrieve and set cell values in the grid. In order to use Grid.jar, you’ll need to follow the instructions found in the link “Adding Jar Files to Projects” located on the labs and projects page. For the lab, you will create a source file called Life.java. When executed, your program will create the grid and use loops and decision statements to update it according to the rules of the game. Initially (at generation 0), each cell of the grid should be given a random value taken from the set {0,1} (We take 0 to mean “dead” and 1 to mean “alive”). For subsequent generations, the value of a given cell x is determined by the following rules: 1. If x is alive and has exactly 2 or 3 live neighbors, then x survives to the next generation. Otherwise it dies. 2. If x is dead and has exactly 3 live neighbors, then x is alive in the next generation. Otherwise it remains dead. Lab Objectives By the end of the lab, you should be able to: § Use for and while statements to create loops in Java programs. § Create nested loops. What to Submit You should submit an error free copy of the program Life.java. Instructions 1. In the main method of your source file, you should create a Grid object, specifying the integer grid size and the cell size (both of these are positive integer values). int gridSize = 200; CSCI 1301: Lab 07 Page 2 int cellSize = 3; Grid grid = new Grid(gridSize, cellSize, "The Game of Life"); grid.setDelay(10); The grid size indicates how many rows and columns the grid will have, and the cell size indicates how many pixels wide and tall each cell will be. It is recommended that you use 3 for the cell size and 200 for the grid size. The shown String object is the title for the grid. Once created, you should see something like the following. The setDelay method specifies a delay, in milliseconds, between updates after each generation. It makes the program run slower or faster. You are free to experiment with this value as you test your program, but you should set it to 10 before submitting your program to eLC-new. Without the delay, your program would consume a significant proportion of your computer’s CPU resources (the program will still be relatively inefficient, but the delay minimizes this to some extent). 2. The grid consists of rows and columns. Each cell can be represented by an ordered pair (row, col). Cell (0,0) is located at the top left corner of the grid. The pair (2,3) indicates the cell at row 2, column 3. An example of the ordered pairs representing cells in a 6x6 grid is shown below. 3. Each cell of the grid can be painted a particular color, taken from the below list. Black, White, Red, Blue, Green, Yellow, Orange, Cyan, Dark Gray, Light Gray, Magenta, Pink We will refer to each color using an integer: 0 is Black, 1 is White, and Pink is 11. For the game of life, we will use black to represent dead cells and white to represent live ones. CSCI 1301: Lab 07 Page 3 To do this, create two int variables: int aliveColor = 1; int deadColor = 0; 4. To color a particular cell of the grid, you use the grid object’s setPos method. // Set cell at (row,col) to value v, where v is a color id (0-11). void setPos(int row, int col, int v) 5. Once the position of one or more cells has been set, you must invoke grid.update(). This will cause all of the updates you have specified to be applied to the grid, and the new state of the grid is displayed on screen. If you don't do this, the image will never change. 6. To practice coloring cells, use a for loop to draw a white line from (10, 25) to (10, 75). Remember to invoke invoke grid.update() after you have updated the cells in question. 7. To erase the grid, you may the grid method clearGrid(). This sets every cell of the grid to 0. Once you are satisfied that you can successfully draw to the grid, invoke clearGrid() to remove the line you have just drawn. 8. Before you can begin the game, you must prepopulate the grid with random values. That is, each cell should be randomly assigned either a 1 or 0 (or whatever the values aliveColor and deadColor are). A random integer can be obtained using the Random class of the java.util package (you must import this class in order to use it in your program). An instance of the class can be created by executing the following statement: Random r = new Random(); After it’s created, you can use it to assign a value with a certain probability. For instance, if( r.nextInt(100) > 49 ) grid.setPos(row, col, aliveColor); else grid.setPos(row, col, deadColor); The method call r.nextInt(100) will return an int greater than or equal to 0 but less than 100. 9. In order to populate the grid with random values, you must use a nested loop—an outer loop to iterate over the rows of the grid, and an inner loop to iterate over the columns (or vice versa). Both while loops or for loops could be used, but it’s more natural in this case to use for loops. Since the grid in this case is a square, the lower bounds for both loops should be 0, and the upper bounds should gridSize-1. That is, you should color all cells from (0,0) to (gridSize -1, gridSize -1). CSCI 1301: Lab 07 Page 4 10. After you have assigned random values to the grid, you must invoke the method initialize(); grid.initialize(); This will cause the pseudorandom values you have set to be recorded and displayed. 11. After you have initialized the grid, you are ready to begin the Game of Life. First note that the game of life runs in cycles (generations), and the game never terminates (unless the user ends the program’s process). What this means is that you should use an infinite loop, that is, a loop in which the controlling expression always evaluates to true. 12. In a given generation, every cell is updated according to the rules of the game. That is, the value of a cell at generation n+1 is determined solely by the values of its neighbors at generation n. In order to update a cell, you will need the following methods in addition to setPos: int getPos(int row, int col) //Retrieve the value of cell (row,col). int matchingNeighbors(int row, int col, int val) //Return the # of neighbors of (row,col) having value val Since we are interested in cells that are alive, val above should be aliveColor. 13. In each generation, every cell of the grid must be updated. A nested loop is again needed to do this. You must write code to do this. When updating a cell value, you must use decision statements to determine what value it should be assigned in the next generation (based upon its current value and the current values of its neighbors). 14. Importantly, the Grid class has been defined so that it is possible to set a given cell to a new value without affecting the calculations determining whether other cells live or die. Changing the value of a cell does not take effect until the update() method is called, and so it is possible to set a cell’s value but still use its previous value in calculations about other cells. 15. You should invoke the grid.update() method immediately after the loop statement assigning new values to the grid has completed. 16. If you have implemented your loops correctly and the previous steps, you should see an evolving pattern that looks something like the first image below, and then it evolves to a pattern similar to the second image below: CSCI 1301: Lab 07 Page 5 If you haven’t implemented your code correctly, you’ll likely see either a black screen or else a screen that looks like static on a television. Possible reasons for this are: 1) you forgot to invoke grid.update() correctly 2) there is a problem with how the loops are defined; or 3) there is a problem with the Boolean CSCI 1301: Lab 07 Page 6 expressions responsible for deciding life and death in a generation. You must make certain your completed program outputs an evolved pattern similar to the one above (this is worth the majority of points for this lab). During your lab, you may ask your lab instructor to verify that your evolved output looks correct. eLC-new Submission and Grading After you have completed and thoroughly tested Life.java, upload and submit it to eLC. Always double check that your submission was successful on eLC! The lab will be graded according to the following guidelines. • A score between 0 and 100 will be assigned. • If the source file(s) are not submitted before the specified deadline’s late period ends (48 hours after the deadline), or if they do not compile, a 0 is assigned. Deductions will be made for each unexcused absence from lab and each day submitted late per syllabus policies. • If the required comment for all labs describing the program and the academic honesty statement is not included at the top of the file, then 10 points will be deducted. Note: this required comment can be found in Lab 02. • 100 points are awarded for a correctly functioning program. This will be ascertained by running the program and visually inspecting that the output’s evolved pattern is similar to the pattern shown in the example above. Otherwise, if the output’s evolved pattern does not resemble the pattern shown above, then a check that nested loops were used to attempt this lab. If nested loops are present, then a score of 40 points will be awarded.