# CS50 Nuggets
## Implementation Spec
In this document we reference the [Requirements Specification](REQUIREMENTS.md) and [Design Specification](DESIGN.md) and focus on the implementation-specific decisions.
The Lecture unit noted that an [implementation spec](https://www.cs.dartmouth.edu/~cs50/Lectures/units/design.html#implementation-spec) may include many topics; not all are relevant to Nuggets.
Here we focus on the core subset:
- Data structures
- Control flow: pseudo code for overall flow, and for each of the functions
- Detailed function prototypes and their parameters
- Error handling and recovery
- Testing plan
## Data structures for Server, Grid, and Player-server
- We use an array (of size 26, the max number of players) to hold the player struct
- We create four types of data structs
- a grid module, a two-dimensional array of characters to hold the map of the game and integer values for number of rows and number of columns.
- a player module, which holds information about the clients who join the game (player ID, playername, goldcount, and address).
- a global game struct in `server.c`, to store the state of the game. It's members include the following:
- a main grid struct
- an original grid struct that remains for reference, it contains no gold piles or players
- an array of pointers to player structs
- an integer storing the current number of players that have joined
- an integer storing the amount of gold remaining
- an integer storing the number of gold piles spawned on the main grid
- a global client struct in `player.c`
- A struct `client` that is similar to `game` but stores the host port number, host name, address struct, number of gridRows, number of gridCols, the player's unique display grid, gold remaining in the game, the goldCount for the client's purse, the temporary gold received each time a player collects gold, the client's X and Y location, their playername, and their letter.
## Control flow (server)
The Nuggets game is implemented in a program `server.c` and joined by players or spectators in a complementary program `player.c` (this is the client program).
### Global Variables (server)
Nuggets will have one global variable in `server.c`, a `game` struct which will store the main grid, original grid, array of pointers to player structs, number of players that have joined so far, amount of gold remaining, and the number of gold piles spawned onto the main grid.
```c
static struct game {
grid_t* mainGrid;
grid_t* origGrid;
player_t** playerArray;
int currPlayers;
int goldRem;
int numPiles;
} game;
```
### Global Constants
We will have several global constants for the game.
`const int MaxNameLength = 50; `
`const int MaxPlayers = 26;`
`const int GoldTotal = 250;`
`const int GoldMinNumPiles = 10;`
`const int GoldMaxNumPiles = 30;`
### ***Server***
### main
The main initializes the game struct members, calls`parseArgs`,`initializeGame`, initializes the logging module (`log_init`), initializes the message module (`message_init`), calls `message_loop`, terminates the message module (`message_done`), terminates the log module (`log_done`), deletes the malloced members of the game struct (each player, the player array, origGrid, and mainGrid), then exits 0.
Note: the output of the logging module is sent to a file named "log" created in main, while the output of the message module is sent to stderr.
### parseArgs
Validates command-line arguments. Verifies that either 1 or 2 arguments were received. For the mapFile, checks if it is the pathname of a file that can be read (we assume it's a valid map). For the optional seed parameter, we check if the seed is a valid integer and if it is a positive integer. If the seed is not provided, its value is unchanged and remains -1 (as it was initialized to in main). If any errors are encountered, print a relevant error message and exit nonzero.
Pseudocode:
validate that either one or two arguments were received;
create filepointer to mapFile;
open the mapFile for reading;
if unssuccessful, print error message and return non-zero exit status;
close the fp;
if seed received,
convert it to an integer;
if seed is a negative integer
print error message and return non-zero exit status;
### initializeGame
Prepares the initial state of the game by 1) callibrating the random number generator (with a seed if provided), 2) loads teh map from the mapFile into the game grid structs (both origGrid and mainGrid), 3) spawns gold piles on mainGrid only. If any errors are encountered, prints relevant error message and exits non-zero.
Pseudocode:
if seed exists
call srand(seed) to generate the random sequence
if the seed does not exist
call srand(getpid())
find dimensions of map from mapFile
load the mapFile into mainGrid
load the mapFile into origGrid
spawn gold piles on mainGrid only
### message_loop
Given function that provides a continuous loop detecting input from stdin or on the network. We will not be using the timeout feature and will pass 0 for the timeout parameter and NULL for the handleTimeout handler function. We will also pass NULL to the handleInput handler function, as it will be implemented in player.c. We will also pass NULL to the arg parameter as all the relevant game information is stored in the gobal `game` struct. The server will implement a `handleMessage` function that will parse the received message and call appropriate helper functions.
### handleMessage
Helper function to handle messages received by `message_loop`. Parses the received message and calls the appropriate helper function to handle it, depending on the type of message received which is determined by the first word of the message. If a KEY message is received, calls`getPlayer` helper function to get the player struct associated with the address that sent the message (this is needed as a parameter in handleKey()). Each helper function will update the global game struct to reflect the new state of the game and send OK, DISPLAY, GRID, GOLD, and QUIT messages when appropriate. Each helper function, and by consequence `handleMessage`, will return true if all gold is collected meaning the game is over and`message_loop` should exit, and false otherwise. If a malformatted message is received, the server sends an ERROR message to the client, logs an error, and ignores the message.
Pseudocode:
validate address
check that gold remaining is not 0
if message starts with PLAY
call handlePlay
if handlePlay returns true
return true
else if message starts with SPECTATE
call getPlayer to get player struct associated with the address of the message sender
call handleSpectate
if handleSpectate returns true
return true
else if message starts with KEY
call handleKey
if handleKey returns true
return true
else if message malformatted
send ERROR message to client
log an error
ignore the message
return false
#### getPlayer
Helper function to return player struct associated with a given address, taken as a parameter. Returns this player struct if a match is found, returns NULL otherwise.
Pseudocode:
loop through array of players
call playerGetAddress to get the player address
if player address and given address are equal (use message_eqAddr to compare addresses)
return player struct
return NULL
#### handlePlay
Helper function to handle PLAY messages, indicating a player wants to join the game. Takes a message string as a parameter. If there are already `MaxPlayers` players, sends the client a QUIT message indicating the game is full, and does not add that player. If the player's real name is empty, sends the client a QUIT message indicating this error. Otherwise, adds the player to the game by creating a new player struct and adding it to the array of players held in the global `game` struct. Formats `real name` by deleting characters after `MaxNameLength` characters and replacing with an underscore `_` any character for which both `isgraph()` and `isblank()` are false, and stores it in the player struct. Sends OK, DISPLAY, GRID, and GOLD messages to client.
Pseudocode:
if there are already maxPlayers
send client QUIT message
return false
else
if the player's name is empty
send client QUIT message
return false
if the player's name consists of only spaced
send client QUIT message
return false
format realName
generate new player's letter
generate new player's random location
create a new player struct with all this information
add this new player struct to the playerArray at index currPlayers
call sendOk
call sendDisplay
call sendGrid
call sendGold
increment currPlayers
return false
#### handleSpectate
Helper function to handle SPECTATE messages, indicating a spectator wants to join the game. Takes spectator's address as a parameter. If there is already a specator, sends a QUIT message to the prior spectator and forgets it by deleting its struct. Creates a new spectator as a specialized player struct and adds it to the playerArray at the last index. Sends DISPLAY, GRID, and GOLD messages to spectator. Returns false.
Pseudocode:
If spectator already exists
send QUIT message to that spectator
forget that prior spectator by deleting struct
create a new spectator as a specialized player struct
add this struct to the playerArray at index MaxPlayers
send DISPLAY message to new spectator
send GRID message to new spectator
send GOLD message to new spectator
return false
#### handleKey
Helper function to handle KEY messages, indicating a player has pressed a key. Takes a player struct and the message as parameters. Updates the global `game` struct according to how the keystroke affects the game. When a spectator quits, their struct is deleted from the playerArray. When a player quits, their letter is removed from the grid, but their struct is NOT deleted because their information will still be included in the end of game summary. When a keystroke causes a player to collect gold, sends GOLD message to that client, as well as a GOLD message to all other clients. When a keystroke causes a player to move, sends DISPLAY message to all clients. This function implements the sprinting versus stepping functionality by checking for a capital letter key. This function also handles player collisions, when a player's move causes it to step on another player, by swapping these two players's locations in this event. Returns true if all gold is picked up and game is over, returns false otherwise.
Pseudocode:
if q
if spectator
send QUIT message
delete struct
if player
send QUIT message
remove player from grid
send new display to all players
else if upper case move key
if spectator
send error message
return false
while playerUpdateLocation is true
move player
if collision occurs
swap player locations
update grid
if all gold collected
call endGame()
return true
send display message to all players
else if lower case move key
if spectator
send error message
return false
if playerUpdateLocation returns true
move player
if collision occurs
swap player locations
update grid
if all gold collected
call endGame()
return true
send display message to all
else, its a malformatted message
send an error
log an error
return false
#### sendOk
Helper function to send OK message to player once they are added to the game. Takes the player struct as input, constructs an OK message in the format "OK playerLetter", and calls `message_send` to send the message.
Pseudocode:
get player address from player struct
get player letter from player struct
construct OK message using player letter
call message_send using player address and constructed OK message as parameters
#### sendGrid
Helper function to send GRID message in the format "GRID nrown ncols". Takes player struct as a parameter.
Pseudocode:
call grid_getNR to get number of rows
call grid_getNC to get number of columns
get address of player
construct a GRID message string (GRID nrows ncols)
call message_send using player address and GRID message string as parameters
#### sendGold
Helper function to send GOLD message in the format "GOLD n p r" (where n is gold just collected by player, p is players total purse, r is gold remaining). It takes a boolean value as a parameter to indicate whether or not the player has just picked up gold and a player struct. Returns true if no gold remaining, indicating that message_loop should exit and the game should end. Returns false otherwise.
Pseudocode:
if bool true
generate random number to represent amount of gold collected
save that number as n
call playerIncrementGold using the player struct and n as parameters
decrement the remaining gold member of the global game struct by n
if remaining gold <= 0
return true
else
n = 0;
save remaining gold value as int r
call playerGetGold to get purse, save it as int p
construct GOLD message string (GOLD n p r)
call message_send using the players address and the constructed GOLD message as parameters
return false
#### sendDisplay
Helper function to send DISPLAY message in the format "DISPLAY\nstring" (where string is that players visible map). Takes a player struct as a parameter to have access to that player's address and visible map through the playerGetVisibility() function.
Pseudocode:
get player's address
get player's visible map string by calling playerGetVisibility
construct DISPLAY message (DISPLAY\nvisibleMapString)
call message_send using player's address and DISPLAY message as parameters
### endGame()
Helper function to create and send the final quit message once game is over. An example of a formatted error message is given in the function header comment in `server.c`. This function includes two loops, one to calculate how many lines the message will be (based on the number of structs in the playerArray) in order to allocate enough memory for the message string, and a second loop to fill that string. The function then send the message to each player and frees this message string.
Pseudocode:
initialize a numLines counter to 0
loop through playerArray
if a spot is not null, increment the numLines counter
create the message string and allocate enough memory based on the number of the final numLines count
loop through playerArray
if a spot contains a player struct
extract its letter, gold count, and realNmae
add this info to the message string
loop through playerArray
send message to each player
free message string
### nameDelete()
Itemfunc to use in player_delete(). Checks that the given name is not NULL, then frees it. This function should only be passed to player_delete() if a player is being deleted and should NOT be used when a spectator is being deleted (because no name string was malloced when creating the specialized spectator struct).
Pseudocode:
if name is not NULL
free name
### Control Flow (client)
### Global Variables
```c
static struct {
char hostPort[maxPortSize];
char hostName[maxHostSize];
char visGrid[maxGridSize];
char playerName[maxPlayerSize];
char letter;
addr_t server;
int numRows;
int numCols;
int goldLeft;
int purse;
int goldReceived;
} client;
```
### Global Constants
`#define maxPortSize (5 + 1) // 16-bit 0 to 65535 (5 chararacters)`
`#define maxHostSize (253 + 1) // 253 ASCII characters`
`#define maxPlayerSize (65507 - 6 - 1) // message_maxBytes - strlen("PLAY ") + 1`
`#define maxGridSize (65507 - 10 - 1) // from ReqSpec: "NR x NC + 10 < message_MaxBytes."`
### ***Player***
### main
The `main` function initializes the logging module, and then calls `parseArgs`, `initializeNetwork`, `message_loop`, and `message_done`. Finally, it closes the logging module.
### parseArgs
Validates command-line arguments. Verifies that either two or three arguments were received (after ./player). Load hostname and port with first and second arguments. If third argument exists, set playername equal to it. If not, set playername for a spectator to null terminating character. If any issues, print message to stderr and exit nonzero.
Note: use strcpy() to set client char arrays with char*s.
Pseudocode:
constant char pointer for program name for first argument for printing usage errors
if number of args is not two or three,
print usage message to stderr and exit nonzero
else,
set client.hostName to first arugment
set client.hostPort to second arugment
if number of args is 3,
set client.playerName to third argument
else,
set client.playerName to '\0'
### initializeNetwork
Function to initialize the network components of the client struct and let server know if we are a Player or Spectator. If any trouble while trying to initialize network, print error message to stderr and exit nonzero.
Pseudocode:
initialize the message module
check that it worked, if not,
print error and exit nonzero
set &client.address with given hostName and hostPort
if we join as a SPECTATOR,
construct "PLAY playername" message
send PLAY message
initialize PLAY letter to 0
else,
print bad hostname/port error and exit nonzero
### message_loop
Given function that continually loops detecting input from player or on the network. We will not be using the timeout feature and will pass 0 for the timeout parameter and NULL for the handleTimeout handler function. However, unlike `server.c`, we will implement a `handleInput` handler function, which will read keyboard input (key presses) and call message_send to send a standardized message to the server (ex: “[playerAddress] inputted [key]”) for interpretation. Similarly to `server.c`, we will implement a `handleMessage` handler function, which will receive messages from the server and call helper functions to update our client's struct based on specific message prompts. We pass in NULL to the arg parameter because all the information and variables we need are stored and accessible via the client struct global variable.
### handleMessage
Helper function for `message_loop`, `handleMessage` will take a cursory glance at the message and call the appropriate helper function to handle it, depending on the type of message received which is determined by the first word of the message. For example, the server could send a `grid` message that client will need to display. Each helper function will update the global client struct to reflect the new state of the game. Each helper function will return true if `message_loop` should exit (e.g. if a fatal error occurs) and false otherwise. If a malformatted message is received from the server, the client prints the ERROR for the user, logs an error, and ignores the message.
Pseudocode:
if client.server is not a valid address,
log error
return true
if `from` is not the same address as client.server,
log error
return true
if message starts with GOLD
call handleGold
if handleGold returns true
return true
else if message starts with GRID
call handleGrid
if handleGrid returns true
return true
else if message starts with DISPLAY
call handleDisplay
if handleDisplay returns true
return true
else if message starts with OK
call handleOK
if handleOK return true
return true
else if message starts with QUIT
call handleQuit
if handleQuit returns true
return true
else if message start with ERROR
call handleError
if handleError returns true
return true
else if message malformatted
log the error
ignore the message
call update_display
return false
#### handleGold
Helper function for `handleMessage` to deal with the server sending the client gold information. Does so by updating the global constant struct client's goldCount. A gold message is in the following format (GOLD n r p) where n is the amount of gold collected, p is the amount of gold in that player's purse, and r is in amount of gold remaining in the game.
Pseudocode:
using sscanf on the message with format "GOLD %i %i %i",
set client.goldReceived to first int
set client.purse to second int
set client.goldLeft to third int
if sscanf did not successfully match each of the message parameters (its return value is not three),
send ERROR message to server
log error
ignore the message
else,
return false
#### handleGrid
Helper function for `handleMessage` to deal with the server sending the client grid information. Does so by updating the global constant struct client's number of rows and number of columns. A grid message is in the following format (GRID nrows ncols).
Pseudocode:
using sscanf on the message with the format "GRID %i %i",
set client->numRows equal to first int
set client->numCols equal to second int
if sscanf did not successfully match each of the message parameters (its return value is not two),
send ERROR message to server
log error
ignore the message
else,
call intialize_display()
return false
#### initializeDisplay
Void helper function that calls the necessary ncurses functions to initialize the screen and all that is needed is an additional function `updateDisplay` to update the display for the player. Should only be called once when we first receive the GRID message.
Pseudocode:
initialize the screen with initscr()
accept keystrokes immediately with cbreak()
don't echo characters to the screen with noecho()
cache window size with getmaxyx(stdscr, int dimY, int dimX)
while the screen is not large enough,
prompt to resize with mvprintw and say to press enter once resized
block until they press enter
reset the window dimensions with getmaxyx()
set colors
#### handleDisplay
Helper function for `handleMessage` to deal with the server sending the client a grid display. Does so by updating the global constant struct client's gridDisplay, calling updateDisplay, and then printing the visGrid string to the screen after the status display. A display message is in the following format (DISPLAY\nvisibleMapString).
Pseudocode:
set client.visGrid equal to content
updateDisplay to print display status line at the top
print the actual visible grid on the second row (right below display status line)
refresh the screen
return false
#### updateDisplay
Void helper function used to update the display status line based off of received GOLD and GRID messages. Handle spectator and players differently as well as whether or not a piece of gold has just been stepped on or an error message has just been received. Use mvprintw and then refresh() to repaint the display status line.
Pseudocode:
set cursor to 0,0
clear line
if spectator,
print special displays status line for spectator
refresh
else player,
if player letter has been set with OK message,
if they did not just step on gold,
print status line without new gold
else,
print status line with new gold
reset goldReceived
refresh
#### handleOk
Helper function for `handleMessage` to deal with the server sending the client an ok message with their letter. Does so by setting the global constant struct client's letter. An ok message is in the following format (OK L). Note: This presence or absence of this letter can then be used anywhere else to check if the player has been added to the game.
Pseudocode:
using sscanf on the message with the format "OK %c",
set client->letter equal to the character^
if sscanf did not successfully match the message parameter (its return value is not one),
send ERROR message to server
log error
return true
return false
#### handleQuit
Helper function for `handleMessage` to deal with the server sending the client a quit message followed by an explanation. Does so by printing the explantion followed by a newline, and returning true to break out of message_loop.
Pseudocode:
exit curses
print the explanation followed by a newline
exit message_loop and the program (return true)
#### handleError
Helper function for `handleMessage` to deal with the server sending the client an error message followed by an explanation. Does so by presenting the explanation to the user of client on the display's status line.
Pseudocode:
show the 'explanation' to the end of the display status line
log the error
return false
### handleInput
Helper function for `message_loop`, `handleInput` will take a cursory glance at keystroke input (single key presses) and send pressed keys to the server with a call to `message_send`. If a key is not recognized, print "unknown keystroke" to the display status line. For a player, valid keystrokes are `Q`, `h`, `l`, `j`, `k`, `y`, `u`, `b`, `n` (see requirements spec for what each key does). The corresponding Capitalized character for each of valid keystrokes just listed are also valid. For the spectator, all they can type is `Q`. We return true if any fatal errors occur or we want to break out of message_loop, else we return false to continue looping over stdin. Note: we assume that we are reading stdin in "character-oriented mode"
Pseudocode:
if client.server is not a valid address,
log error
return true
read once from stdin into char
if char is not EOF,
create buffer for length of message
add space for char and null char
actually build the message
call message_send with message
else,
call message_send with KEY Q
return false
### message_done
Given function to shut down message module. For more information see message.h.
## Other modules
### game
We create a game struct to store the main grid, an original grid that remains for reference, the array of players, and keep track of the amount of gold remaining. We will have one global variable of type `game_t`.
### client
We create a global client struct called `client` to store and update the information that is specific to each client, including the host port number, host name, host address, number of grid rows, number of grid columns, the player's unique display grid, gold remaining in the game, the gold count for the client's purse, the temporary gold received each time a player collects gold, the clients x location, the client's y location, their playername, and their letter.
### grid
We create a module `grid.c` to handle loading the map into the grid structure. The grid structure holds a 2D array of characters to represent the grid and an integer variable for the number of rows and two integer values to store the number of rows and columns. See `grid.h` for more in depth information on each function.
*See function prototypes in prototypes section below.*
Pseudocode for `grid_load`:
check if grid is valid
if it is not
return false
if it is
allocate memory for gridarray and its components
iterate through the map string and fill values of gridarray with characters
return true
Pseudocode for `grid_loadEmptyGrid`:
check if grid and parameters are valid
if it is not
return false
if it is
allocate memory for gridarray and its components
for the number of rows
allocate memory of length NC for string
fill it with empty chars
return true
Pseudocode for `grid_distribGold`:
go to a certain number of random spots in grid (more than min parameter and less than max)
add gold character there
return true if successful
return false if not
Pseudocode for `grid_updateGrid`:
Get players current location from player struct
Remove their character from that current location
Go to the x and y position passed in parameter
Check if there would be a collision with checkCollision
If no collision
put player character in that spot
If there is a collision
call handleCollision function
Pseudocode for `grid_addPlayer`:
validate parameters
if invalid return false
check if desired spot is a room
if it is, add player char to (x,y) coords
return true
if it is not
return false
Pseudocode for `grid_removePlayer`:
validate parameters
if invalid return false
set spot in grid equal to spot in original grid
return true
Pseudocode for `grid_pickUpGold`:
go to x and y position defined in parameters
if there is gold there
remove the gold character from that position
return true
return false
Pseudocode for `grid_print`:
iterate through the grid
print each string
Pseudocode for `grid_delete`:
iterate through the grid
free each item
free the grid
Rest of the funcitons are getters, just returns values.
### server-player
player holds x,y coordinates, name, port number
revealed map string variable, given a player what is their revealed map. Be a struct variable for player module.
We create a `player.c` module to hold information about each client who connects as a player/spectator.
`playerUpdateLocation`
`playerIncrementGold`
`playerGetVisibility`
`playerGetLocation`
`playerGetGold`
Initialization:
When a player is created, it's location is set to a random location on the map, it's gold is set to zero, and `revealed` initializes all gridpoints to false
Pseudocode `playerUpdateLocation`:
read direction code (character inputted by player)
check if can move in that direction
if yes:
update location (if lowercase, move one space. If uppercase, move as far as possible)
return true
if couldn't move, return false
Pseudocode for `playerIncrementGold`:
increase gold count by inputted amount
Pseudocode for `playerGetVisibility`:
create a new grid_t struct `tempMap` that is composed of empty spaces
for each gridpoint `target` of the game's `map`:
calculate slope from player's location
starting from the player, scan each gridpoint roughly on the line drawn
if a point along the way is a wall:
if the `target` point in `revealed` is false, do nothing (the point is already invisible)
if the `target` point in `revealed` is true:
if the `target` point in `map` is a goldPoint or other play, convert it to a normal floor character in `tempMap`
if no points along the way are a wall, set the corresponding `target` point on`tempMap` to the character of `map`
if a `target` point became visible for the first time, set its corresponding point in `revealed` to true
return `tempMap`
convert `tempMap` to string
free(tempMap)
send out string
Other methods are getters and setters
## Function prototypes
### server
Detailed descriptions of each function's interface is provided as a paragraph comment prior to each function's implementation in `server.c` and is not repeated here.
```c
typedef struct game_t* game;
int main(const int argc, char* argv[]);
static void parseArgs(const int argc, char* argv[],
char** mapFile, int* seed);
static void initializeGame(char* mapFile, int seed);
int message_init(FILE* logFP);
bool message_loop(void* arg, const float timeout,
bool (*handleTimeout)(void* arg),
bool (*handleInput) (void* arg),
bool (*handleMessage)(void* arg,
const addr_t from,
const char* message));
static bool handleMessage(void* arg, const addr_t from, const char* message);
static player_t* getPlayer(addr_t address);
static bool handlePlay(const char* message);
static bool handleSpectate(const addr_t from);
static bool handleKey(player_t* player, const char* message);
static bool sendGold(player_t* player, bool gotGold);
static void sendGrid(player_t* player);
static void sendDisplay(player_t* player);
static void sendOk(player_t* player);
void message_send(const addr_t to, const char* message);
void message_done(void);
```
### player
Detailed descriptions of each function's interface is provided as a paragraph comment prior to each function's implementation in `player.c` and is not repeated here.
```c
struct client;
int main(const int argc, char* argv[]);
static void parseArgs(const int argc, char* argv[]);
static void initializeNetwork();
bool message_loop(void* arg, const float timeout,
bool (*handleTimeout)(void* arg),
bool (*handleInput) (void* arg),
bool (*handleMessage)(void* arg,
const addr_t from,
const char* message));
static bool handleMessage(void* arg, const addr_t from, const char* message);
static bool handleGold(const char* content);
static bool handleGrid(const char* content);
static void initializeDisplay();
static bool handleDisplay(const char* content);
static void updateDisplay(const char* explanation);
static bool handleOk(const char* content);
static bool handleQuit(const char* content);
static bool handleError(const char* content);
static bool handleInput(void* arg);
```
### grid
Detailed descriptions of each function's interface is provided as a paragraph comment prior to each function's declaration in `grid.h` and is not repeated here.
```c
grid_t* grid_new(void);
bool grid_load(grid_t* grid, char* map, int NR, int NC);
bool grid_loadEmptyGrid(grid_t* grid, int NR, int NC);
int grid_distribGold(grid_t* grid, const int GoldMinNumPiles, const int GoldMaxNumPiles);
bool grid_addPlayer(grid_t* grid, char playerID, int x, int y);
bool grid_removePlayer(grid_t* grid, grid_t* origGrid, char playerID, int x, int y);
bool grid_updateGrid(grid_t* grid, grid_t* origGrid, int px, int py, char playerID, int x, int y);
void grid_print(FILE* fp, grid_t* grid);
void grid_delete(grid_t* grid);
char** grid_getGrid(grid_t* grid);
int grid_getNC(grid_t* grid);
int grid_getNR(grid_t* grid);
```
### server-player
```c
player_t* player_new(bool spectator, addr_t add, char letter, char* playerName, int r, int c, grid_t* grid);
void player_delete(player_t* player, grid_t* grid, void(*itemdelete)(void* item));
bool playerUpdateLocation(player_t* player, grid_t* grid, char key);
void playerSetLocation(player_t* player, int r, int c);
void playerIncrementGold(player_t* player, int amount);
char* playerGetVisibility(player_t* player, grid_t* grid, grid_t* origGrid);
int playerGetR(player_t* player)
int playerGetC(player_t* player)
int playerGetGold(player_t* player)
addr_t playerGetAddress(player_t* player)
char playerGetLetter(player_t* player)
char* playerGetFullName(player_t* player)
void playerPrintLocation(player_t* player)
```
## Error handling and recovery
All the command-line parameters are rigorously checked before any data structures are allocated or work begins; problems result in a message printed to stderr and a non-zero exit status.
Out-of-memory errors are checked for and result in a message printed to stderr and a non-zero exit status.
We anticipate out-of-memory errors to be rare and thus allow the program to crash (cleanly) in this way.
All code uses defensive-programming tactics to catch and exit, e.g., if a function receives bad parameters.
## Testing plan
Here is an implementation-specific testing plan.
### Unit testing
There are four units (server, client, grid, player).
The server represents the whole game and is covered below. The client is relatively small and enables players or spectators to connect and interact with the game.
We use `gridtest.c` to test the grid module. The gridtest will test the grid load function by loading a map of into the module, then testing functions such as the distributeGold and updateGrid function. After each test the function will use the gridprint method to print the grid to check if the tests were successful or not. Then gridtest will delete the grid.
### Integration/system testing
We write a script `testing.sh` that invokes the server and client several times, with a variety of command-line arguments with various erroneous arguments, testing each of the possible mistakes that can be made.
Verify correct behavior by studying the output.
See `testing.md` for more information.
For further testing we will write a "human script" or instructions on how to launch different kinds of games. This will be the main method of testing, playing the game and seeing if it functions correctly.
- For testing, we plan on breaking down the unit testing based on our above plan for the division of work
- Claire will use`gridtest.c` to test her grid module (*see above*)
- Camden will use `playertest.c` to test his players module
- Giorgie and Nick will work together to use a bash script `networktest.sh` to test the server and client programs
- For the integration testing, we will do that together as a team in a meeting once our unit tests all check out
- For the system test, Giorgie will create a simple bash script to test server with various invalid arguments and Nick will createa a similar bash script to test client with various invalid arguments