# NUGGETS SERVER IMPLEMENTATION
## SERVER 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 spec will contain the following conditions:
- 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
- Grid
- char map[][]
- int nc
- int nr
- Game
- grid_t* full
- int remainingGold
- int remainingGoldPiles
- int numPlayers
- set of players
- bool currSpectator
- Player
- int purse
- grid_t* visible
- int currentx
- int currenty
- bool active
- char oldSpot
### Control flow
The server is implemented in one file ```server.c```, with the following functions:
#### main
The main function simply calls other modules, initializes the port (to listen to client input) and other functions. Pseudo code:
```
calls parseArgs
call game_grid with grid associated with the inputted file with random number of gold and random number of gold piles
initialize addr_t data structure
call log_init on stderr
call message_init on stderr and intialize it to ourPort
if failure to initialize port, return non-zero
initialize set of players (key = id(alphabet), item = name)
initialize a set of clients (struct of addr_t; key = same id as the players set, item = client address)
initialize the spectator client
call update_grid
call update_players
call message_done
```
#### parseArgs
Given arguments from the command line, extract them into function parameters; return only if successful.
- for ```map text file```, check if the file is readable
- if optional parameter ```seed``` provided then call ```srand``` on the seed
- if optional parameter ```seed``` not provided then call ```srand(getpid())```
- if any trouble is found, print an error to stderr and exit non-zero.
#### update_grid
Updates the grid according to the information received from the clients and monitors if all gold nuggets have been acquired. Pseudo code:
```
call message_loop with NULL handleTimer and NULL handleInput and handleMessage
call set_iterate() to iterate over every client in the clients set
call message_send on spectator client (DISPLAY\n string (game_grid))
```
#### update_players
Updates the players according to the information received from the clients. Pseudo code:
```
call message_loop with actionMessage and NULL handleInput and NULL handleTimer
call set_iterate() to iterate over every client in the clients set
call message_send on spectator client (DISPLAY\n string (game_grid))
```
#### helper functions:
- ```handleMessage```
Used in message_loop called by ```update_players``` and ```update_grid```. Pseudo code:
```
if client is NULL
log this error
return true
if there is still gold left to be collected
initialize client to 'from' (the second parameter of the function)
call parseMessage
if first word of message is PLAY
add the client to the set of clients
if game already has maximum number of players
call message_send on current client (QUIT Game is full, no more players can join)
else if no name provided
call message_send on current client (QUIT Sorry - you must provide player's name)
else
add the name and the next alphabet to the set of players (id starting at 'A')
call message_send on current client (OK (alphabet associated with the name in the set of players))
if new player
call game_newPlayer on the alphabet (id)
call message_send on current client (GRID nrows(grid_nr) ncols(grid_nc))
call message_send on current client (GOLD n(game_playerPurse) p(game_playerPurse) r(game_gold))
else if first word of message is SPECTATE
if there is a spectator already
call message_send on current spectator (QUIT You have been replaced by a new spectator)
initialize spectator to new client
call message_send on current client (GRID nrows(grid_nr) ncols(grid_nc))
call message_send on current client (GOLD n(0) p(0) r(game_gold))
else
call message_send on current client (ERROR explanation)
return false
else if all gold nuggets collected
call set_iterate on the clients set using quitfunc as helper method
call message_send on spectator client(QUIT Game Over: tabular data)
```
- ```actionMessage```
```
if client is NULL
log this error
return true
initialize client to from
call parseMessage
if first word is KEY
if client is not spectator
call game_update() on the game with the second word of the message
else if client is spectator
if second word is 'Q'
call message_send on spectator client (QUIT Thanks for watching)
else
call message_send on spectator client (ERROR explanation)
else
call message_send on client (ERROR Explanation)
return true
```
- ```itemfunc```
helper function for set_iterate used in update_grid and update_players. Pseudo code:
```
if key exists and item exists
get the player from the players set using the key
call message_send on the client (DISPLAY\n game_playerGrid())
```
- ```parseMessage```
helper function used in handleMessage and actionMessage. Pseudo code:
```
read the string message
when encountered first space, replace it with a null character
add both words to another array of strings
take the second word and if for a char in this string isgraph and isblank are both false, replace it with underscore
truncate this word to MaxNumLength characters and add it to the original array of strings
```
- ```quitfunc```
helper function called in handleMessage
```
for every key in client
call message_send on each client (QUIT Game Over: tabular data)
```
### Other modules
#### libcs50
We leverage the `set.c` module from libcs50. See that directory for module interfaces.
#### support
We will leverage the support module for `message.c` to handle all the message compilation and distribution processes of the information coming to and from the client among Internet hosts. Messages are limited to UDP packet size, may be lost, and may be reordered, but require no connection setup or teardown. We will also use `log.c` to handle logging messages to a file and `file.c` to support reading input from a word, line, or entire file.
#### Grid
The grid data structure will primarily be a 2D array of characters. The grid module will implement the following functions:
- grid_new
create a new grid struct 'grid'
count number of lines: this is grid->nr
create a char** map and malloc nr x sizeof(char*)
use file_readLine to store first line as a char*
find length of that line: this is grid->nc
for each row in NR, malloc nc x sizeof(char)
Loop over every element of the inputted file
Copy said element into the 2D array of characters
set the map to grid.map
return grid or NULL for memory allocation errors
- grid_empty
malloc a grid_t instance
malloc a 2D array of charaters of size NR times length of char*
for each row:
malloc space for a nc chars
for each gridpoint add an empty character ' '
return the grid struct with the char array or NULL if there were any memory allocation errors
- grid_get
return the character at the given x and y coordinates in the 2D char array of mentioned grid_t struct
- grid_set
set the grid value at given x and y coordinates in the 2D char arry to the given char
- grid_isVisible
given a full grid, and 2 points (i.e 2 pairs of x and y values - (currx, curry) and (otherx, othery))
Calculate the line properties - slope and intercept, using the 2 points
Go over evry viable point in the line between the 2 points:
If the point is not a '.' or '*' or '@' or between 'A' - 'Z':
return False
Return true
- grid_visible
Iterate through all the points in the older visible grid
If the value of the point is '@':
set the point to player's oldSpot
Else If the point has value '*':
Set the value in the new grid to a '.'
If the point is the current location:
set the grid value to '@'
If the point is visible from the current location (using grid_isVisible)
Set the point to its real value
- grid_nc
return the number of column
- grid_nr
return the number of rows of 2D char array
- grid_valid
get the character using grid_get
if the character is a '.' a '*' or a '#':
return true
return false
- grid_getString
Allocate memory for a string of size nr*nc+1 chars
Go over every line in the 2D array:
Go over every character in the line:
Calculate the proper index in the string
Add the character to the string
Add a '\n'
return the char array
#### Game
The game data structure will contain details about the game like its grid, the total gold, the number of players currently playing the game, a set of players, and a boolean indicating if the game has a spectator or not. The game module will implement the following functions:
- game_new
Allocate memory for a new game_t
Set the full grid struct to the result of calling grid_new on inputted file pointer
Set the remaining gold equal to the inputted total gold
Set remaining gold piles equal to inputted total gold piles
For each gold pile:
Get a random x between 0 and grid_nc()
Get a random y between 0 and grid_nr()
while (randx, randy) != '.':
Get a random x between 0 and nc
Get a random y between 0 and nr
Set the point in the grid to a '*'
set numPlayers to 0
set currSpectator to false
Intitialize a new set using set_new() in order to store the players
- game_grid
If the grid associated with inputted game_t* is not NULL
Get the grid in string form by using grid_getString
Return the string associated with inputted game_t* struct
else
Return NULL
- game_update
Get a pointer to the player_t struct using the playerKey from set of players
Get the current x and y values for player
If the action is 'u' (up):
Decrease the y value of the player by 1
Else If the action is 'd' (down):
Increase the y value of the player by 1
Else If the action is 'l' (left):
Decrease the x value of the player by 1
Else If the action is 'r' (right):
Increase the x value of the player by 1
If the new position (with updated x, y) is valid
Keep track of the player's old x and y values
set the new position of the player using player_setPoints
If the new position already has a player on it
Get that player's playerKey (the character on the grid at said point)
Use player_setPoints to change that player's points to the old points of current player
Update the grid to account for the new position of this player
else if the new position has gold:
Calculate the random gold value at that point
Subtract the gold value at point from total gold in the game and add it to the player's purse (player_updatePurse)
Decrement remainingGoldPiles
get the visible grid of the player
get the old spot value of the player
Update the visible grid of the player to account for the new changes by passing it to grid_visible
set the old spot of the player to the value of the grid at the new location of player
Update the grid to account for the new position of player
- game_newPlayer
set randx to random number inbetween 0 and NC
set randy to random number inbetween 0 and NR
while grid_get(randx, randy) != '.':
set randx to random number inbetween 0 and NC
set randy to random number inbetween 0 and NR
call player_new(NC, NR, randx, randy, full) to create a player struct
add that player_t* to the set of player_t* with provided char playerKey as key
increment numPlayers
use grid_set to add the player to the game's grid
return
- game_playerPurse
Get the pointer to the player struct associated with given playerKey
return the purse of said player struct by calling player_purse
- game_gold
Returns the gold remaining in game (that is not part of players' individual purses)
- game_randomGold
if remaining gold piles == 1
return remaining gold
else
int proportional = (remaining gold)/(remaining gold piles)
return proportional + random number in between -proportional/3 and proportional/3
- game_spectator
Return the bool currSpectator
- game_numPlayers
Return the int numPlayers
- game_removePlayer
use set_find to get the player_t* for given key
call player_quit to remove player
decrement numPlayers
- game_nc
return the nc from grid
- game_nr
return the nr from the game's grid by using grid_nr()
- game_delete
delete the game's grid using grid_delete
delete each of the players in the set using set_delete with player_delete as a helper function
free the game struct
#### Player
The player data structure tracks an individual's game information: purse, current position, and visited locations.
The player module will implement the following functions:
- player_new
create a new player_t*
set int purse to 0
set bool active to True
set currx to given randx
set curry to given randy
set the old spot value to the value in the full grid at the current position of player
use grid_visible to create visible grid from starting point and a new grid
return pointer to this player_t*
- player_purse
return the current int purse of the inputted player struct
- player_addToPurse
set the purse value of the inputted player_t struct pointer to the sum of the existing purse value and the inputted int value
- player_visible
return the grid_t* which contains the points in the map which given player has ever been able to see
- player_setPoint
change currx to newx
change curry to newy
- player_currx
return the current x coordinate of the player
- player_curry
return the current y coordinate of the player
- player_getOldSpot
return the character beneath the player's previous location
- player_setOldSpot
set given char to the value of player's old spot
- player_delete
free the memory allocated for the player's visible grid using grid_delete
free the memory allocated for given player struct
- player_quit
set player's active indicator to false
### Function prototypes
#### server.c
```c
void parseArgs(const int argc, char* argv[], char** filename, int seed);
void update_grid();
void update_players();
int main(const int argc, char* argv[]);
static bool handleMessage(void* arg, const addr_t from, const char* message);
static bool actionMessage(void* arg, const addr_t from, const char* message);
static void itemfunc(void* arg, const char* key, void* item)
static char** parseMessage(const char* message);
```
#### Grid
```c
grid_t* grid_new(FILE* fp);
grid_t* grid_empty(int nc, int nr)
void grid_visible(grid_t* full, int currx, int curry, grid_t* visible, char oldSpot);
bool grid_isVisible(grid_t* full, int currx, intcurry, int otherx, int othery);
char grid_get(grid_t* grid, int x, int y);
void grid_set(grid_t* grid, int x, int y, char newChar);
int grid_nc(grid_t* grid);
int grid_nr(grid_t* grid);
bool grid_valid(grid_t* grid, int x, int y);
char* grid_getString(grid_t* grid);
void grid_delete(grid_t* grid);
```
#### Game
```c
game_t* game_new(FILE* fp, int totalGold, int numGoldPiles);
char* game_grid(game_t* game);
void game_update(game_t* game, char playerKey, char action);
void game_newPlayer(game_t* game, char playerKey);
void game_removePlayer(game_t* game, char playerKey);
int game_playerPurse(game_t* game, char playerKey);
int game_gold(game_t* game);
int game_randomGold(game_t* game);
bool game_spectator(game_t* game);
int game_numPlayers(game_t* game);
int game_nc(game_t* game);
int game_nr(game_t* game);
void game_delete(game_t* game);
```
#### Player
```c
player_t* player_new(int NC, int NR, int x, int y, grid_t* full);
int player_purse(player_t* player);
void player_addToPurse(player_t* player, int value);
grid_t* player_visible(player_t* player);
void player_setPoint(player_t* player, int newx, int newy);
int player_currx(player_t* player);
int player_curry(player_t* player);
char player_getOldSpot(player_t* player);
void player_setOldSpot(player_t* player, char oldSpot);
void player_delete(void* arg);
void player_quit(player_t* player);
```
### Error handling and recovery
### Testing plan
#### Unit testing
We propose to first test for the command-line with various forms of incorrect command-line arguments to ensure that the command-line parsing and validation of those parameters, works correctly. The plan will test the following arguments:
1. No arguments
2. One argument
3. Three or more arguments
4. Invalid map file
5. Invalid seed
#### Integration/system testing
We also plan to have individual testing files for each of the data structure modules in order to test our functionality on a case-by-case basis as opposed to a combined test of the different modules. We anticipate this will make it easier and more effective to spot bugs in the data structures side of our code. As such, we anticipate the following additional testing files:
1. `gridtest.c`
2. `playertest.c`
3. `gametest.c`
Also, similar to the client test, we plan to test the implementation of the message module and the server's ability to communicate with the client. This will essentially be a test of the overall functionality of the nuggets game (ie. playing the game).