Automated Grading Log for CS113 -- HW5 userid: rrshah Name: Shah, Ronak Log created: Thu Oct 31 13:33:37 EST 1996 SUBMISSIONS =========== rrshah Submitted hw5-life.c at Thu Oct 17 20:52:35 1996 rrshah Submitted hw5-board.h at Thu Oct 17 20:52:34 1996 rrshah Submitted hw5-board.c at Thu Oct 17 20:52:34 1996 COMPILATION =========== gccx -Wall -c hw5-life.c gccx -Wall -c hw5-board.c gccx -Wall -o hw5-life hw5-life.o hw5-board.o if [ -f "hw5-life2.c" ]; then \ gccx -Wall -c hw5-board2.c; \ gccx -Wall -c hw5-life2.c; \ gccx -Wall -o hw5-life hw5-life2.o hw5-board2.o; \ fi EXECUTION TESTS ========= ===== ============================================================ hw5-life < /home/course/cs113/GradesF96/HW5/input/board3.dat This program will run through the game of life, using Conway's rules. Enter first the number of rows of the initial board, and on the subsequent lines, enter in the board, row by row. The last line should be the number of generations to cycle through. Enter the number of rows: Begin the entry of the board: t=0 ............................................... .............................................o. .ooooo.............oco.....................ooo. .............................................o. ............................................... t=1 ............................................... .ooo...............o........................o.. .ooo...............c........................oo. .ooo...............o........................o.. ............................................... t=2 ................................................ ...o............................................ ..o.o........................................oo. .o...o.............oco......................ooo. ..o.o........................................oo. ...o............................................ ................................................ t=3 ................................................. ...o............................................. ..ooo...............o.......................o.o.. .oo.oo..............c.......................o..o. ..ooo...............o.......................o.o.. ...o............................................. ................................................. t=4 ................................................. ..ooo............................................ .o...o.......................................o... .o...o.............oco.....................oo.oo. .o...o.......................................o... ..ooo............................................ ................................................. t=5 ................................................. ....o............................................ ...ooo........................................... ..o.o.o..............o.......................ooo. .ooo.ooo.............c.......................o.o. ..o.o.o..............o.......................ooo. ...ooo........................................... ....o............................................ ................................................. t=6 .................................................. ...ooo............................................ ..............................................o... .o.....o.....................................o.o.. .o.....o............oco.....................o...o. .o.....o.....................................o.o.. ..............................................o... ...ooo............................................ .................................................. t=7 ................................................... .....o............................................. .....o............................................. .....o.........................................o... ......................o.......................ooo.. .ooo...ooo............c......................oo.oo. ......................o.......................ooo.. .....o.........................................o... .....o............................................. .....o............................................. ................................................... t=8 .................................................. ...ooo............................................ .............................................ooo.. .o.....o....................................o...o. .o.....o............oco.....................o...o. .o.....o....................................o...o. .............................................ooo.. ...ooo............................................ Data files: ========== 5 ............................................... .............................................o. .ooooo.............oco.....................ooo. .............................................o. ............................................... 8 ============================================================ SOURCE FILES ====== ===== /* * File: hw5-life.c * Author: Ronak Shah * Class: CS 113, Fall 96 * Assignment: HW 5 * Due Date: 18 October 1996 * Last modified: 18 October 1996 * ----------------------------------------------------- * * USAGE: hw5-life < input.dat * ====== * * OVERVIEW: * ========= * This program runs Conway's game of life. It takes (usually * through redirection), a formatted input file specifying the * number of rows, the initial layout of the board, and the * number of generations through which the game should cycle. * * Algorithm Notes: * ================ * Very little is required from this client in order to perform * the vast majority of the work to produce the Game of Life. * Functions from hw5-board.c include the ability to read in a * board, take a board to the next generation, and print the * board. All that hw5-life needs to do is cycle the board * through the correct number of generations and print the board * when required. * * Known Bugs: * =========== * none. * * Enhancements: * ============= * * Error Handling: * =============== * none. * * Other Comments: * =============== * In the interface hw5-board.h, the struct BoardT is actually * a pointer to a struct, which makes the calling convention of * lifeBoard very simple. * */ #include #include "genlib.h" #include "simpio.h" #include "hw5-board.h" /*Function prototypes*/ void GiveInstructions(void); void PrintLifeBoard(boardT board, int nGeneration); int main(void) { boardT lifeBoard; /*The big variable that holds most the information*/ int nGenerations; /*number of generations to go through*/ int i; /*Give some instructions, even though everything uses redirection*/ GiveInstructions(); /*Setup the board, Read in the user-input, and fill the board.*/ SetUpNewBoard(lifeBoard); nGenerations = ReadBoard(lifeBoard); /*Print out the initial board, just for the users own reference.*/ PrintLifeBoard(lifeBoard, 0); /*Start the cycle of life, running for as many generations *as specified in the input file*/ for (i=0; i= nGenerations-5)) { /*The print function in the library prints ONLY the board, *and none of the other necc. outputs*/ PrintLifeBoard(lifeBoard, i+1); } } /*Now free up the memory the lifeBoard takes up. How this memory *is actually used is known only by hw5-board.c*/ DestroyBoard(lifeBoard); return(0); } /*Even though hw5-life is usually called through redirection, it's *nice to give a useful front end in case the user wants to enter *boards in by hand*/ void GiveInstructions(void) { printf("This program will run through the game of life, using Conway's\n"); printf("rules. Enter first the number of rows of the initial board,\n"); printf("and on the subsequent lines, enter in the board, row by row.\n"); printf("The last line should be the number of generations to cycle\n"); printf("through.\n\n"); } /*The PrintBoard function in hw5-board.c only prints the actual board. *Since we want the generation to be prefixed to the board, we wrap *PrintBoard in an application specific function PrintLifeBoard*/ void PrintLifeBoard(boardT lifeBoard, int nGeneration) { printf("t=%d\n", nGeneration); PrintBoard(lifeBoard); printf("\n\n"); } /* * File: hw5-board.h * Author: Ronak Shah * Class: CS 113 * Assignment: HW 5 * Due Date: 18 Oct 1996 * Last modified: 18 Oct 1996 * ----------------------------------------------------- * * OVERVIEW: * ========= * Declares the types and functions necc. to use Game of * Life capabilities. * * Known Bugs: * ============ * none. * * Error Handling: * =============== * none. Client is assumed to know what he/she is doing. */ #ifndef _board_h #define _board_h /*The boardT pointer-to-struct is the main storage structure for *hw5-board. It contains size information about the board, the *coordinates of the board center, and of course, the memory *address of the characters that make up the board contents.*/ typedef struct { int rows, cols; /*current size of the board*/ int centerRow, centerCol; /*Location of the center point*/ char *cellPtr; /*pointer to board matrix*/ } *boardT; /* * Function: SetUpNewBoard * Usage: SetUpNewBoard(board) * ------------------- * Initialize the board variable so that it can be passed to * ReadBoard. Must be the first call into hw5-board.c, otherise * the memory can't be allocated to hold the board information.*/ void SetUpNewBoard(boardT board); /* Function: ReadBoard * Usage: nGenerations = ReadBoard(board) * ------------------- * ReadBoard reads the board into the data structure. It interacts * with the input by requesting first the number of rows and figures * out the number of columns. It then initializes the cellPtr by * allocating memory to it, and then reads in all the data points. * The last line it reads contains the number of generations the * life program should run, and this is the value it returns.*/ int ReadBoard(boardT board); /* Function: SetBoardCell * Usage: SetBoardCell(board, row, col, value) * ------------------- * SetBoardCell does exactly that. It sets the cell (row,col) in * board to the character value. */ void SetBoardCell(boardT board, int row, int col, char value); /* Function: PrintBoard * Usage: PrintBoard(board) * ------------------- * Prints each cell in the board in table format.*/ void PrintBoard(boardT board); /* Function: NextGeneration * Usage: NextGeneration(board) * ------------------- * This function generates the next generation of life. The board * after the function call is surrounded by just one ring of '.'*/ void NextGeneration(boardT board); /* Function: DestroyBoard * Usage: DestroyBoard(board) * ------------------- * Frees the memory occupied by board and everything it points to. * Always call this before ending your program.*/ void DestroyBoard(boardT board); #endif /*--END OF FILE--*/ /* * File: hw5-board.c * Author: Ronak Shah * Class: CS 113, Fall 96 * Assignment: HW 5 * Due Date: 18 October 1996 * Last modified: 18 October 1996 * ----------------------------------------------------- * * OVERVIEW: * ========= * Defines functions needed to use Game of Life board. * * Algorithm Notes: * ================ * Most of the difficult functions involve copying the dynamically * allocated array into a different form. This type of code is * used most often in NextGeneration() and its child functions. The * algorithm to this involves: * 1)Saving a pointer to the old board contents * 2)Allocating memory for the new board contents (which may be a * different size) * 3)Walking through the arrays and copying the individual cells. * 4)Freeing the memory at the location specified by the pointer * in (1) * * Known Bugs: none. * ================= * * Error Handling: * =============== * none. * * Other Comments: * =============== * Most code is documented in-line. * */ #include #include #include "genlib.h" #include "simpio.h" #include "strlib.h" #include "hw5-board.h" /*Function Prototypes of all the internal functions *not exported via hw5-board.h*/ static void InitializeBoard(boardT board, int nRows, int nCols); static char *GetPointerToCell(char *cellPtr, int TotalCols, int row, int col); static void RecordBoardRow(boardT board, string contents, int row); static void PadBoard(boardT board); static void DoGeneration(boardT board); static bool IsAliveNextState(char *cellPtr, int TotalCols, int row, int col); static void CropBoard(boardT board); void SetUpNewBoard(boardT board) { /*We need to allocate the memory for the boardT structure itself, *before we even allocate any room for the internal Ptr*/ board = (boardT) GetBlock(sizeof(*board)); } int ReadBoard(boardT board) { int nRows, nCols; /*size needed for this board*/ int nCurrentRow; /*current row in while loop*/ string currentRowContents; /*First get the number of rows we have to deal with*/ printf("Enter the number of rows: "); nRows = GetInteger(); /*The first line will give us the number of columns, with which we *can initialize the board (and allocate *cellPtr). We use the *simple strlib interface now, although we can use the standard *string.h functions just as easily*/ printf("Begin the entry of the board: \n"); currentRowContents = GetLine(); nCols = StringLength(currentRowContents); /*Now simply allocate the array and set rows, cols with what we know.*/ InitializeBoard(board, nRows, nCols); /*start up the while loop that reads in the user data. Since we've *already read in a row, the loop should increment at the end, *allowing us to do what we need to do first. *Stop when you read in any number, which is the Generation count*/ nCurrentRow = 0; while (!isdigit(IthChar(currentRowContents, 0))) { /*RecordBoardRow will read in the current row.*/ RecordBoardRow(board, currentRowContents, nCurrentRow); /*get a new line and increment nCurrentRow*/ nCurrentRow++; currentRowContents = GetLine(); } /*By now the entire matrix has been set, including the board fields *specifying the center. All we need to do is return the number of *generations this thing's supposed to do stuff for. This integer *is stored in currentRowContents as a string. Just parse it out *and return it in one line*/ return(StringToInteger(currentRowContents)); } void SetBoardCell(boardT board, int row, int col, char value) { char *valuePtr; /*The address of specified cell*/ /*Get the address of where the cell is*/ valuePtr = GetPointerToCell(board->cellPtr, board->cols, row, col); /*and set its value*/ *valuePtr = value; } void PrintBoard(boardT board) { int i, j; /*iterators*/ char *valuePtr; /*Address of current cell*/ /*just loop through *cellPtr and printf chars to the screen*/ for (i=0; i < board->rows; i++) { for (j=0; j < board->cols; j++) { /*again, get the pointer to the current character cell, and *simply printf its dereferenced value*/ valuePtr = GetPointerToCell(board->cellPtr, board->cols, i, j); printf("%c", *valuePtr); } /*new line for a new row, of course*/ printf("\n"); } } void NextGeneration(boardT board) { /*make sure that the final matrix will be big enough by adding a *ring of '.' around our original array. This will allow us to call *IsAliveNextState() and not worry about overstepping the bounds of our *board. We will crop the board if necc. later.*/ PadBoard(board); /*call DoGeneration to set contents to their next state*/ DoGeneration(board); /*Now crop the board however much it needs to be cropped.*/ CropBoard(board); } void DestroyBoard(boardT board) { /*Need to work backwords as we free the two pointers, otherwise *we won't be able to free the cellPtr, since all reference would *be gone once we free board*/ free(board->cellPtr); free(board); } /*InitializeBoard is used in several functions to set the fields *rows, cols and cellPtr in a board. This is done by simple assignment *wrt the first two, but cellPtr needs to be dynamically allocated, *which InitializeBoard does using GetBlock. *InitializeBoard then sets all contents of the array to '.'*/ static void InitializeBoard(boardT board, int nRows, int nCols) { int i, j; /*row, col iterating variables*/ char *cellPtr; /*location of current cell in loop*/ /*simple enough to set the row and cols fields*/ board->rows = nRows; board->cols = nCols; /*we need to allocate enough memory for the board matrix pointer *cellPtr. Use GetBlock() from genlib.h to do the malloc and check *for not enough heap space in one line*/ board->cellPtr = (char *) GetBlock( sizeof(char) * nRows * nCols); /*Now the structure is set up. Just fill it all with '.' to make *ReadBoard a little easier to deal with. Note that the only fields *in board that aren't set now are centerRow, centerCol*/ for (i=0; i < board->rows; i++) { for (j=0; j< board->cols; j++) { /*First get a pointer to cell i,j using the ubiquitous GPTC *function, and set its value to a '.'*/ cellPtr = GetPointerToCell(board->cellPtr, board->cols, i, j); *cellPtr = '.'; } } } /*GetPointerToCell is lets us treat our one-dimensional dynamically *allocated array like a two dimensional array, in a certain sense. *It returnss a pointer to the location in memory that holds the value *for the board state at (row,col). it's simple to dereference that *pointer, if necc, to get the char at that location*/ static char *GetPointerToCell(char *cellPtr, int TotalCols, int row, int col) { /*We're using ordinal notation all the way through, so we can do *this function in just one line. *Using array notation, we can get the value of a cell with a bit *of arithmetic, and simply prefix a '&' to return its pointer.*/ return(&cellPtr[(row * TotalCols) + col]); } /*RecordBoardRow is used in ReadBoard() to parse the return of *GetLine() into our board format. It cycles through each character *in the input string contents, and SetBoardCell() it. It also looks *for the character marking the center of the board, and sets the values *of board->centerRow and board->centerCol*/ static void RecordBoardRow(boardT board, string contents, int row) { char ch; int i; /*cycle through each of the cells in the row*/ for (i=0; i < board->cols; i++) { ch = IthChar(contents, i); SetBoardCell(board, row, i, ch); /*check to see if this is the center. If so, set the fields in *board to record it*/ if (ch == 'c' || ch == ':') { board->centerRow = row; board->centerCol = i; } } } /*PadBoard is the first step of the NextGeneration function. Since it's *easiest to do the generation step if you can simply check each cell's *neighbors in each of the 8 compass point directions, PadBoard puts a *ring of '.' around the current board. The board becomes two rows taller *and two columns wider. *Also, if, in the next cycle the board spreads to the edges, PadBoard can *insure that you will always have at least 1 ring of '.' around the living *cells. *It uses the cellPtr copying scheme noted in Algorithm Notes, above*/ static void PadBoard(boardT board) { char *oldcellPtr; /*temporary holding place for board contents*/ char *oldValuePtr; /*address of a cell in the temporary contents*/ int i, j; /*store the old contents for future reference when we repopulate *our new cellPtr*/ oldcellPtr = board->cellPtr; /*set up new matrix values and allocate memory for a new set of *row,col contents*/ InitializeBoard(board, board->rows + 2, board->cols + 2); /*Now cycle through the old values, filling stuff into the new cell *contents. Remember the old board is 2 rows and cols smaller*/ for (i=0; i < (board->rows -2); i++) { for (j=0; j < (board->cols -2); j++) { oldValuePtr = GetPointerToCell(oldcellPtr, board->cols -2, i, j); /*Remember that the new contents list is shifted one cell*/ SetBoardCell(board, i+1, j+1, *oldValuePtr); /*now do a quick check for the center, and if necc., set these *values as well*/ if (*oldValuePtr == 'c' || *oldValuePtr == ':') { board->centerRow = i+1; board->centerCol = j+1; } } } /*Now just free up the memory we used for the temporary holding pointer*/ free(oldcellPtr); } /*DoGeneration actually increments the board one generation. It does so *by using the cellPtr copying scheme noted in Algorithm Notes. It walks *through the old array, finds whether it'll be alive or dead (with a call *to IsAliveNextState), and sets its value in the new array.*/ static void DoGeneration(boardT board) { char *oldcellPtr; /*The holding place for last generation's contents*/ char newState; /*The new state of the cell*/ int i, j; /*iterators*/ /*save a copy of the old contents and allocate memory for the new values*/ oldcellPtr = board->cellPtr; InitializeBoard(board, board->rows, board->cols); /*now walk through the entire old array, except for the outer ring, and *call IsAliveNextState to check whether it lives or dies.*/ for (i=1; i < (board->rows -1); i++) { for (j=1; j < (board->cols -1); j++) { /*Get the character corresponding to the new state and call *SetBoardCell to set the new pointer's value to it*/ if (IsAliveNextState(oldcellPtr, board->cols, i, j)) { /*Check whether or not it's the center*/ if (board->centerRow==i && board->centerCol==j) { newState = 'c'; } else { newState = 'o'; } } else { /*it's dead, so check if it's a : or a . */ if (board->centerRow==i && board->centerCol==j) { newState = ':'; } else { newState = '.'; } } /*Call SetBoardCell*/ SetBoardCell(board, i, j, newState); } } /*Free up the memory for the old cell pointer.*/ free(oldcellPtr); } /*IsAliveNextState determines whether a cell will live, die, be born *or stay the same in the Next State. Since PadBoard placed a ring of *'.' around the "real" cell contents, IsAliveNextState can determine *the number of neighbors by checking each of the 8 adjacent cells. It *uses a nested for loop for this. It then switches() the number of *neighbors alive and tests it with Conway's rules of life to determine *a boolean (true/false) value of whether or not the cell is alive.*/ static bool IsAliveNextState(char *cellPtr, int TotalCols, int row, int col) { int nNeighborsAlive=0; /*number of neighbors that are alive*/ char *currentCellPtr; /*Address of the current neighboring cell*/ bool wasAlive; /*whether or not the cell was alive last turn*/ int i, j; /*Go through all the adjacent cells, counting the number of *living cells*/ for (i=-1; i <= 1; i++) { for (j=-1; j <= 1; j++) { /* you don't want to count (row,col) itself, but you can set *wasAlive in this case*/ if (i==0 && j==0) { currentCellPtr = GetPointerToCell(cellPtr, TotalCols, row, col); /*Check whether the cell was alive or dead last turn*/ if (*currentCellPtr=='o' || *currentCellPtr=='c') { wasAlive = TRUE; } else { wasAlive = FALSE; } } else { /*Check the value of the current cell for life, and if so, *increment nNeighborsAlive*/ currentCellPtr = GetPointerToCell(cellPtr, TotalCols, row + i, col + j); if (*currentCellPtr=='o' || *currentCellPtr=='c') nNeighborsAlive++; } } } /*Return a TRUE/FALSE based upon whether or not it's going to be alive. *This is where Conway's Rules of life are actually coded*/ switch (nNeighborsAlive) { case 0: case 1: return(FALSE); case 2: return(wasAlive); case 3: return(TRUE); default: return(FALSE); } /*you'll never get here, but it keeps the compiler happy*/ return(FALSE); } /*CropBoard is a necc. function that keeps the board size manageable. *The successive calls to PadBoard make the DoGeneration() process simple, *but artificially increase the size of the board itself. CropBoard checks *the outermost rows and columns and deletes all but one ring of '.' around *the living cells. *It first determines the first row/col (working inwards) from each of the *four sides that has an alive cell in it. These rows/cols are the only *necc. ones, although CropBoard will keep a ring of '.' around the board. *It then uses the cellPtr copying scheme from above to reference the new, *cropped array.*/ static void CropBoard(boardT board) { int topRow=-1, bottomRow=board->rows; /*vertical limits*/ int leftCol=-1, rightCol=board->cols; /*horizontal limits*/ int oldRows=board->rows, oldCols=board->cols; /*old values of each*/ int i, j; char *currentCell; char *oldcellPtr; /*Temporary holding place of uncropped board*/ /*Our first job is to determine which rows and columns we need to keep. *to do this, we walk 'inwards' from each of the four sided, and find *where we should stop cropping. These stopping points are stored in *topRow through rightCol.*/ /*First walk down from the left-top to find the row of the first *non '.' or ':'*/ for (i=0; i < oldRows; i++) { for (j=0; j< oldCols; j++) { currentCell = GetPointerToCell(board->cellPtr, board->cols, i, j); if ((topRow==-1) && (*currentCell=='o' || *currentCell=='c')) { topRow = i-1; } } } /*walk up from the bottom to find bottomRow*/ for (i= (oldRows-1); i>=0; i--) { for (j=0; j < oldCols; j++) { currentCell = GetPointerToCell(board->cellPtr, board->cols, i, j); if ((bottomRow==(oldRows)) && (*currentCell=='o' || *currentCell=='c')) { bottomRow = i+1; } } } /*walk right from the left to find leftCol*/ for (j=0; j < oldCols; j++) { for (i=0; i < oldRows; i++) { currentCell = GetPointerToCell(board->cellPtr, board->cols, i, j); if ((leftCol==-1) && (*currentCell=='o' || *currentCell=='c')) { leftCol = j-1; } } } /*walk right from the left to find leftCol*/ for (j=(oldCols-1); j >=0 ; j--) { for (i=0; i < oldRows; i++) { currentCell = GetPointerToCell(board->cellPtr, board->cols, i, j); if ((rightCol==(oldCols)) && (*currentCell=='o' || *currentCell=='c')) { rightCol = j+1; } } } /*We now have limits for each side of the board. Just do the same *matrix copying technique we've used all along to chop out the unn. *rows. As if this function wasn't long enough already*/ oldcellPtr = board->cellPtr; InitializeBoard(board, bottomRow - topRow +1, rightCol - leftCol +1); for (i=topRow; i <= bottomRow; i++) { for (j=leftCol; j <= rightCol; j++) { currentCell = GetPointerToCell(oldcellPtr, oldCols, i, j); SetBoardCell(board, i-topRow, j-leftCol, *currentCell); } } free(oldcellPtr); } /*--END OF FILE--*/ -- END OF LOG FILE FOR rrshah --