Objectives
of this lecture
The
Game of life problem
Initialize the array map to contain the initial configuration
a.
count the number of living neighbors
b.
if the count is 0, 1, 4, 5 ,6 ,7 or 8 , set the
corresponding cell in array newmap to
dead; if the count is 3, set the corresponding cell in array newpap to living; if the count is 2, set
the corresponding cell in array newmap to the same status as the current
cell.
/*
Simulation of Conway's game of Life on a bounded grid
Pre: The
user must supply an initial configuration of living cells
Post:
The program prints a sequence of maps showing the changes in
the configuration according to the rules
for the game
Uses:
functions Initialize,WriteMap,NeighborCount, and UserSaysYes
*/
#include "common.h" /*common include files
and definitions */
#include "life.h" /*Life's defines, typedefs, and
prototypes*/
void
main(void)
{ int
row, col;
Grid map; /* current generation */
Grid newmap; /* next generation */
Initialize(map);
WriteMap(map);
printf("This is the initial
configuration you have chosen.\n"
"Press <Enter> to
continue.\n");
while(getchar() != '\n')
;
do {for (row = 1; row <= MAXROW; row++)
for (col = 1; col <= MAXCOL;
col++)
switch(NeighborCount(map, row,
col)) {
case 0:
case 1:
newmap[row][col]= DEAD;
break;
case 2:
newmap[row][col]=
map[row][col];
break;
case 3:
newmap[row][col]= ALIVE;
break;
case 4:
case 5:
case 6:
case 7:
case 8:
newmap[row][col]= DEAD;
break;
}
CopyMap(map, newmap);
WriteMap(map);
printf("Do you wish to view the
next generations");
} while (UserSaysYes());
}
#include
<stdio.h>
#include
<stdlib.h>
typedef
enum boolean { FALSE, TRUE } Boolean;
void Error(char
*); //display an error message and halts program
void
Warning(char *); // Error: report program error.
#define
MAXROW 20 /* maximum row
range */
#define
MAXCOL 60 /* maximum column
range */
typedef
enum state { DEAD, ALIVE } State;
typedef
State Grid[MAXROW+2][MAXCOL+2];
void CopyMap(Grid map, Grid newmap);
Boolean
UserSaysYes(void);
void Initialize(Grid map);
int NeighborCount(Grid map, int row, int
column);
void WriteMap(Grid map);
Good
Programming principles
You will study the construction of the functions
required for the completion of the life game problem (see section 1.4) of the
book. However, we can derive the
following good programming principles from what we have done so far:
1.
Analysis of the problem: This should always be the stating
point. Before you start anything, make
sure you thoroughly understand the specification of the problem you are about
to solve.
2.
Design the solution: Never start coding any problem without first
using pencil and paper to design the solution.
For reasonably small programs such as the life game problem, algorithms
may suffice. However, for large software
system, software engineering methods
must be applied.
3.
Document program specification: The conditions required to hold when a
program begins and when it finishes called respectively, the pre-conditions and
post-conditions should be documented at the beginning of every function.
4.
Document List of functions used: These should also be documented at the
beginning of every functions
5.
Use meaningful names for identifiers: Always give names to your variables,
functions, user-defined types, etc, such that the names suggest their purpose.
6.
Documentation:
Give concise and descriptive comments throughout your program. As a guide:\
Ø Indicate
at the beginning, the purpose of the function and for main program you may
include the author’s name, Date last modified, version number, etc.
Ø Explain
the purpose of each significant section and indicates its end
Ø Update
your documentation as you modify the code.
7.
Indentation:
Insert spaces, black lines and indentation to group related statements
especially structured statements such as if,
while and for.
8.
Top-down design:
This is the key to writing large programs. Try to look at the problem at a high level and break it into
smaller components so that the individual components are treated one at a
time. This approach has many advantages
and in fact is a must in mordern software development. Some of the advantages are:
Ø Easy to
code: --concentrate on a simple task at a time
Ø Easy to
detect and correct errors
Ø Re-userbility:
--The same sub-task is often required in solving other problems
Ø Maintainability:- Easy to modify a small component
Ø Division
of Work:- different programmers can be
working on different tasks
Ø Etc.
9.
Stubs:
After coding the main program, most programmers will wish to complete the
coding of functions to see if the system will work. For large systems, this is usually not possible, in fact not
advisable. It is much easier to debug
the main program (and indeed each function) as soon as it is written. To compile the program correctly however,
there must be something in place of each function that is used. Thus, we must put at least dummy functions –
called stubs. E.g:
/* Initialize:
initialize grid map. */
void Initialize(Grid map)
{
}
/* WriteMap: write grid map. */
void WriteMap(Grid map)
{
}
/* NeighborCount: count neighbors of row,col. */
int NeighborCount(Grid map, int row,
int col)
{
return 1;
}
Even with these stubs, we can at
least compile the program and make sure that the declaration of types and
variables are syntactically correct.
However, normally, each stub should
at least print a message to indicate that the function was invoked.
10.
Drivers:
For large projects, it is also desirable to test each function as soon
as it is written. To achieve this,
short main programs are written to provide the necessary input for the
function, call the function and print the result. Such a program is called a driver for the function. For example, the following is a driver for
the NeighborCount function:
/* Driver: test NeighborCount().
Pre: The user must supply an initial configuration of living cells.
displays the values returned.
*/
int main(void)
{
Grid
map;
int i, j;
Initialize(map);
for (i = 1; i <= MAXROW; i++) {
for (j = 1; j <= MAXCOL; j++)
printf("%3d", NeighborCount(map, i, j));
printf("\n");
}
return 0;
}
11.
Program Testing:
After coding all the functions and assembling them into a complete
system, it is necessary to test the entire system by using carefully chosen
test data. While selecting test data,
take note of the following guides:
Ø Easy
values:-choose data values that can easily be checked
Ø Typical
Values:-choose data that will normaly be used with the program
Ø Extreme
Values:- check the boundary (limit) values
Ø Illegal
values:-ensure that the program will not crash on encountering illegal values.