Design

The Nuggets game requires two standalone programs, the client and the server. We have further broken down these programs into smaller modules.

Client

The client acts in one of two modes:

  1. spectator, the passive spectator mode
  2. player, the interactive game-playing mode

Functional decomposition into modules

Simply a client module, handling all client functionality.

Major data structures

client

The client data structure holds all important values for the client as a global variable:

  • nrows - stores number of rows in screen
  • ncols - stores number of cols in screen
  • justCollected - stores number of gold just picked up for status line
  • purse - player purse for status line
  • goldRemaining - number of nuggets remaining
  • map - the string representation of the map
  • playername - the name of player, null if spectator
  • playerID - player’s id for status line
  • hostname - hostname of server
  • port - port of server

Server

Functional decomposition into modules

  • The grid module keeps a 2-dimensional array of gridpoints, where each gridpoint stores information about what character it is (".", “#”, etc.), what player is on it (if applicable) and how much gold it contains.
  • The game module keeps track of the game grid, along with an array of the players (and any spectators) that are in the game. As outlined above, each player stores its (row, column) position, purse size, playerID, and the portion of the game grid that is currently visible to him/her.

Major data structures

game

The game module will exist within the server to hold a state of Nuggets as it is being played, complete with the current game grid, the players, spectator, and more.

  • Grid - stores data about points across map of the game
  • array of players - stores each of the players that are in the game
  • lastPlayerNum - Array index of player last added
  • spectator - stores unique spectator player data, if one exists
player

The player module will handle various aspects of players within the game. Its uses will include storing the regions of the grid visible to the player and formatting it for clients, tracking a player’s position and how much gold they have collected, storing their name for the leaderboard, and more.

  • name - stores the player’s name
  • playerID - the player’s unique character playerID
  • 2d character array map - represents the portion of the grid visible to the player
  • purse - stores amount of gold the player has
  • row - stores the player’s row
  • col - stores the player’s column
  • address - player’s address in the network, so we can identify players as we receive client messages
grid

The grid will represent the data stored on the map and at each point on the map. The grid module loads a mapfile into the game’s memory, scatters gold across open rooms, and facilitates players’ interaction with the map on the server’s end as they search for nuggets.

  • 2d array of gridpoints which store data about each point on the grid
  • number of rows in the grid
  • number of columns in the grid
  • number of roomSpots in the grid
  • number of gold pieces remaining in the grid

Implementation

The game was implemented in C, and uses the ncurses library in order to provide the text display (see https://github.com/srb-private-org/nuggets-game) .

Diagram of Client/Server Implementation

Diagram of Client/Server Implementation

Client

Data structures

client:

A global struct to track the size of client’s window, their status line in the game (which will either print the amount of gold they have or any error messages that come up), and the map that’s visible to them.

static struct client {
	int nrows; 
	int ncols;
  	int justCollected;
  	int purse;
  	int goldRemaining;
  	char* map;
  	char* playername;
  	char playerID;
	bool connected;
  	char* hostname;
  	char* port;
}

Definition of function prototypes

A function to parse the command-line args, initialize the network:

static void parseArgs(const int argc, char* argv[], char** hostname, char** port,
                      char** playername);

This function initializes the ncurses objects needed by the client, including the display.

static void ncurses_init();

This function updates the display to correspond with the current state of the client.

static void updateDisplay(const char* statusLine);

This function is used in conjunction with message_loop to handle input.

static bool handleInput(void* arg);

This function sends the key input to the server.

static bool sendKey(const addr_t to, int key);

This function sends the player (or spectator) message to the server.

static bool sendPlayMessage(const addr_t to, char* playerName);

This function handles the message from server.

static bool handleMessage(void* arg, const addr_t from, const char* message);

This function handles the OK message.

static bool handleOK(const char* message);

This function handles the GRID message.

static bool handleGrid(char* message);

This function handles the GOLD message.

static bool handleGold(char* message);

This function handles the DISP message.

static bool handleDisp(char* message);

This function handles the QUIT message.

static bool handleQuit(char* message);

This function handles the ERROR message.

static bool handleError(char* message);

This function handles unrecognized messages.

static bool handleUnrec(char* message);

This function returns player status line.

static char* getPlayerStatus();

This function returns spectator status line.

static char* getSpectatorStatus();

Detailed pseudo code

main:
parse & validate command line args with parseArgs
initialize log module
form server address from args
set ncurses (including the display) up with ncurses_init
// do not init ncurses yet ; do so once we know we have received grid data from server, so we know we have a connection
call sendPlayMessage with the playerName (which is null if the client is spectating)
begin message_loop, passing display and functions for handling input and messages
exit curses
shut down communication with server
parseArgs:
if command line does not have two or three arguments
	exit nonzero
if we cannot set an address to the given hostname and portnumber using message_setAddr
	exit nonzero
ncurses_init:
initialize the screen
call cbreak
call noecho
create a new global struct client
store the number of rows and columns on the screen in client's nrows and ncols
set the color pair to yellow and black
updateDisplay:
print the status line at the top of screen
print the map under the statusline
call refresh to update the screen
handleInput:
set an adrr_t* to the void pointer passed in
if that addr_t* is NULL
	print a message to stderr
	return true
if that address is not a valid address
	print a message to stderr 
	return true
if the `connected` bool in client var hasn't been set to true
	log the error
	return true to exit

set int c equal to the next keyboard press
return sendKey of c
sendKey:
if the server passed in is NULL
	print an error message to log
	return true
send message to server with form "KEY k"
return false
sendPlayMessage:
if server is null
	print error message to stderr
else if playerName is null
	send "SPECTATE" message to server
else
	send message to server of form "PLAY (playerName)
handleMessage:
if server is null
	print error message to stderr
	return true
if first word is OK
	call handleOK with messages
else if first word is GRID
	return handleGrid with message
else if first word is GOLD
	return handleGold with message
else if first word is DISPLAY
	return handleDisp  with message
else if first word is QUIT
	return handleQuit with message
else if first word is ERROR
	return handleError with message
else
	return handleUnrec with mesaage
handleOK:
parse the player ID from the message and add it to the struct
return false
handleGrid:
call ncurses_init() since we know we are now connected
set the global client var's connected field to true
parse nrows and ncols from the message
	while client struct's nrows and ncols are less than nrows + 1 and ncols, respectively
		print a window sizing message to log
		tell the user to enlarge the window and try again
return false
handleGold:
parse number nuggets collected
parse number nuggets in purse 
parse number nuggets still to be found
update the client struct's statusLine with these values
call updateDisplay on the client struct
return false
handleDisp:
starting after the first newline character, parse grid display string from message
set client struct's map equal to that string
update the map with ncurses
return false
handleQuit:
end curses
parse the message text following the word "QUIT" into a string
print this string to stdout followed by newline
return true
handleError:
parse explanation into string
write error message to log
set client's status line equal to the text in the error message
call updateDisplay
return false
handleUnrec:
log error message with log.h since unhandled message
return false
getPlayerStatus:
return formatted status line for player in memory
getSpectatorStatus:
return formatted status line for spectator in memory

Server

Data structures

Game Data Structure

This data structure will store info about the current game being played, including the game grid, an array containing slots for MaxPlayers (26) players in the game. Within the server module, game data will be stored globally so that it need not be passed to and validated by every function.

struct gameData {
  grid_t* grid;
  player_t* players[MaxPlayers];
  player_t* spectator;
  int nextPlayerIndex;
}

Definition of function prototypes

A function to parse & validate the command-line arguments and initialize the game struct

static int parseArgs(const int argc, char* argv[]);

A function to initialize the game data struct and the grid & player data therein.

static void initializeGame(char* mapFile);

A function to delete the game data and the grid and player data therein.

static void gameData_delete();

A function to send the game’s grid dimensions to clients

static void sendGridDimensions(const addr_t to);

A function to send gold information to clients.

static void sendGoldInfo(const addr_t to, int justCollected, int purse, int remaining);

A function to send the current display to clients.

static void sendDisplay(const addr_t to, char* stringifiedMap);

A function to send QUIT messages with explanations to clients.

static void sendQuit(const addr_t to, const char* explanation);

A function for handling messages from the client

static bool handleMessage(void* arg, const addr_t from, const char* message);

A function to handle SPECTATE messages.

static void handleSpectateMessage(const addr_t from);

A function to handle PLAY messages.

static void handlePlayMessage(const addr_t from, const char* message);

A function to handle KEY keystroke messages.

static bool handleKeyMessage(const addr_t from, const char* message);

A function to handle movement key presses

static bool handleMovementCase(const int rowChange, const int columnChange, player_t* player);

A function to handle unrecognized messages.

static void handleUnrecognizedMessage(addr_t to, const char* message);

A function to get a player pointer from its address.

static player_t* getPlayerFromAddress(addr_t address);

A function to send error messages to clients

static void sendError(addr_t to, const char* message);

A function to send OK messages to clients

static void sendOK(const addr_t to);

A function to check if a line is blank

static bool isBlankLine(const char* string);

A function to format a player name.

static void formatName(char* name);

A function to convert a player’s index in the players array into an ID

static inline char indexToPlayerID(const int index);

A function to send a game over quit message to each player.

static void gameOver();

A function to update each player’s map and send it to them

static void sendUpdatedMaps();

Detailed pseudo code

main
start logging to stderr
call parseArgs() with the given cmd line args
start messages module, validate init
call message_loop() with handleMessage, store result in bool
call gameOver()
call message_done()
call log_done()
call gameData_delete()
return zero if message loop worked, otherwise nonzero
parseArgs:
validate commandline args
if wrong number of args is given
	exit with proper usage message
if seed provided
	verify it is a valid seed number
	seed the random-number generator with that seed
else
	seed the random-number generator with getpid()
verify map file can be opened for reading
call initializeGame() with the validated map file name
initializeGame
allocate memory for global gameData variable
call grid_loadMap with given mapFile
call grid_placePiles on the map to scatter gold across the grid
allocate space for MaxPlayers in game data
set spectator pointer to null in game data
set nextPlayerIndex to zero in game data
handleMessage
given a message and sender
if the message matches "SPECTATE"
	call handleSpectateMessage() with sender address
elif the message begins with "PLAY "
	call handlePlayMessage() with sender address and message
elif the message begins with "KEY "
	call handleKeyMessage() with sender address and message
	return true to exit loop if key press resulted in game ending
else
	call handleUnrecognizedMessage, with message
return false
sendGridDimensions
get number of rows, cols stored in game data grid
build string of form "GRID numberRows numberColumns"
send the string to the given `to` address
sendGoldInfo
given positive ints justCollected, purse, remaining
build string of form "GOLD justCollected purse remaining"
send string to given `to` address
sendDisplay
given a player
call player_mapToString with the player and game grid dimensions
form the message "DISPLAY\n" followed by the map string
send the message to the player's address
free the map string allocated in memory
sendQuit
send "QUIT " followed by given explanation to `to` address
handleSpectateMessage
if game's spectator is not NULL
	sendQuit with relevant explanation to old spectator's addr
	call player_delete on old spectator
	set spectator pointer in game data to NULL
call player_new with null name, given `from` address, grid dimensions, (-1,-1) as row,col, true for isSpectator
if we created the player without any issues
	store it in gameData spectator pointer
call grid_updateSpectatorMap on the spectator with the game grid
call sendGridDimensions to spectator's addr with game grid dimensions
call sendGoldInfo to spectator address with justCollected = 0, purse = 0, and remaining gold in grid
call sendDisplay with pointer to spectator
handlePlayMessage
if the game's nextPlayerIndex equals MaxPlayers
	call sendQuit to the new player, explaining that the game's full
	return
store the given username in memory
if isBlankLine() returns true for the username
	sendQuit to player with explanation
	free the username
	return
call formatName on the username
create a new player with player_new
if we failed create the player
	free the username, log error, and return
store the player at the nextPlayerIndex in gameData
increment game data's nextPlayerIndex
call grid_addPlayerRandom to place the player at random room spot in grid
call grid_updatePlayerMap
sendGridDimensions() to player
sendGoldInfo() to palyer with justCollected = 0, purse = 0, and remaining gold
sendDisplay() to player
update spectator's and each player's map, send it to them
handleKeyMessage
parse next letter 
if we have more than a letter or no letter
	sendQuit, with explanation
	return false (don't exit message loop)
call getPlayerFromAddress() with address to matching player pointer
if we failed to find a match
	log the error
	return false
take the key from the message
switch with key char as conditional
	case Q
		call sendQuit, w/ explanation
		if the player pointer points to the same place as spectator
			sendQuit with "Thanks for watching!"
			call player_delete on spectator
			set spectator pointer to NULL
		else
			call grid_deletePlayer()
			sendQuit with "Thanks for playing!"
			// do NOT delete quit players - only spectator
			// we need to keep player data for the leaderboard!
			update player's maps with player gone
			send new map to each player
			update spectator map
			send new map to spectator
		return false (don't exit msg loop)
	cases (h, l, j, k, y, u, b, n) or any of their capitalized forms
		return handleMovementCase with the address, player, and key
	default case (invalid)
		call handleUnrecognizedMessage
		sendError with "unknown keystroke"
return false
handleMovementCase
given from address, player, key pressed
save the player's current purse value
store the result of the player's attempt to move from grid_movePlayer
if the player successfully moved
	if the player found gold
		get the remaining gold in the grid
		if there's no remaining gold
			return true (to exit message loop & trigger game over)
		else
			calculate gold just collected by subtracting old purse from new purse
			sendGoldInfo to player with just collected gold, new purse, remaining gold
			loop through player array
				if the player is not null
					sendGoldInfo to the player, with 0 just collected, their purse, gold remaining
			if we have a spectator
				sendGoldInfo, with 0 just collected, 0 purse, gold remaining
	loop through each player
		if the player's not null
			call grid_updatePlayerMap on the player with game's grid
			call sendDisplay to that player
	if we have a spectator
		call grid_updateSpectatorMap
		call sendDisplay to the specator
return false
handleUnrecognizedMessage
log that we could not recognize message
sendError
send message of form "ERROR explanation" to client
sendOK
send message of form "OK k" to client, where k is a given char
gameOver
create special game over string to send, calculating buffer large enough to hold it
loop through player array in game data
	if the player's not null
		add a single line to the game over string with their ID, purse, and name
loop through player array in game data
	if the player is not null
		sendQuit to their address with the game over string 
if we have a spectator
	sendQuit to their address with game over string
indexToPlayerID
if the index is NOT within the range of 0..MaxPlayers
	return null character
return 'A' + index given
isBlankLine
for each char in given string
	if isspace(char) does not return true
		return false
return true
formatName
if the length of the name exceeds MaxNameLength
	set the character in the name string at MaxNameLength to be the terminating null char
loop through index in name, until we hit its length
	if neither isgraph(char) and isblank(char)
		set that char in the string to be an underscore
gameData_delete
if the grid in the game data is null
	log error, exit program
for each index in player array from [0..MaxPlayers)
	if the player's not null
		call player_delete on each player
free player array's memory
call player_delete on the spectator, if we have one
call grid_delete on the grid stored in game data
free global gameData's allocated memory
getPlayerFromAddress
if we cannot validate the address with message_isAddr
	return null player pointer
if player array in game data is not null
	loop through each player
		if the player isn't null
			if message_eqAddr(given address, player address)
				return pointer to that player
if we have a spectator
	if message_eqAddr(given address, spectator address)
		return pointer to the spectator
return null
sendUpdatedMaps
for each player from 0 to MaxPlayers in gameData
	if the player is not null
		update player's map
		send it to them
if the spectator in gameData isn't null
	update spectator's map
	send it to them

Grid module

The grid module allows us to construct and track a grid of gridpoints, where each gridpoint stores the amount of gold it has, what player is on it (if applicable), and what character the it should display. grid also provides several methods that interact with the player module, allowing the grid of gridpoints to be continually manipulated as players move around and collect gold.

Data structures

gridpoint

Stores the status of a given gridpoint in the grid. Will constantly be updated as players move onto gridpoints, off of gridpoints, and collect gold.

struct gridpoint{
  int goldCount;
  char gridChar;
  player_t* player;
}
grid

Stores a grid of gridpoints and the amount of gold remaining in these gridpoints, along with the grid’s number of rows and columns. This struct is a crucial part of Server’s game struct, as it stores the state of a given nuggets game.

struct grid{
  gridpoint_t** gridpoints;
  int goldRemaining;
  int numRows;
  int numCols;
  int numRooms;
}

Definition of function prototypes

A function to load a text file into a struct grid.

grid_t* grid_loadMap(char* fileName);

A function to insert a player at a given position in a grid. Returns true if player was inserted, false otherwise.

bool grid_addPlayer(grid_t* grid, player_t* player, int row, int col);

A function to insert a player at a random room spot in the grid.

void grid_addPlayerRandom(grid_t* grid, player_t* player);

A function to delete a given player from a grid. Returns true if the player could be deleted, false otherwise.

bool grid_deletePlayer(grid_t* grid, player_t* player);

A function to generate an array of ’num’ pointers to 2-slot arrays representing random (row, column) room spot coordinates in the grid.

int** grid_randomRoomSpots(grid_t* grid, int num);

A function to place n piles of gold (where n is a randomly selected integer between min and max), containing a total of goldTotal gold pieces, at random room spots in a grid.

void grid_placePiles(grid_t* grid, int min, int max, int goldTotal);

A function to move a player by one unit in a given direction, then modify the player’s purse accordingly. Returns a status code based on the result of the move attempt. 0 = did not move, 1 = moved, 2 = moved and found gold, 3 = moved and hit other player

int grid_movePlayer(grid_t* grid, player_t* player, char dxn);

We use the following static local function to help implement grid_movePlayer.

static int movePlayerHelper(grid_t* grid, player_t* player, int row change, int colChange);

We use the following static local function to help implement the run feature within grid_movePlayer

static int runHelper(grid_t* grid, player_t* player, int row change, int colChange);

A function to add any gold at a player’s position to their purse. Returns true if gold is collected, and false if not.

bool grid_collectGold(grid_t* grid, player_t* player);

A function to tell us whether a given gridpoint is visible to a player.

bool grid_isVisible(grid_t* grid, player_t* player, int row, int col);

A static function to help us determine whether a gridpoint is visible if the line between it and a player is straight.

static bool visibleHelperStraight(grid_t* grid, int playerRow, int playerCol, int row, int col);

A static function to help us determine whether a player can see through an intersection.

static bool
intersectionHelper(grid_t* grid, double currentRow, double currentCol, bool isRowCheck)

A function to modify a player’s map to reflect what they can currently see of the grid.

void grid_updatePlayerMap(grid_t* grid, player_t* player);

A function to update the spectator’s map to reflect the state of the game.

void grid_updateSpectatorMap(grid_t* grid, player_t* spectator);

A function to delete a grid.

void grid_delete(grid_t* grid);

Detailed pseudo code

grid_loadMap:
if the text file can be opened for reading
	create a new grid structure
	count the number of rows and columns in the text file passed into the function, add them to the grid structure as numRows and numCols
	create an empty array gridpoints of dimensions numRows * numCols
	allocate memory for each row, column in grid
	for each character in each line of the map text file
    	set the corresponding gridpoint in 'gridpoints' to have its gridChar equal to that character
	add 'gridpoints' to the grid structure
	return the grid structure
else
	return null
grid_addPlayer:
if parameters are not null or out of bounds
	identify the gridpoint in 'gridpoints' at the row and column specified
	if this gridpoint is an empty room spot
		change the player at this gridpoint to the player who is being added
		set the player's row, col to the row, col of this point
		return true
return false
grid_addPlayerRandom:
if parameters are not null
	use grid_randomRoomSpots to generate an array of random room spots of size 1
	take the spot at first index in the array
	set the player's row, col to be the room spot's row, col
	free the room spot 
	free the array holding the single room spot
grid_deletePlayer:
if the parameters are not null or out of bounds
	go to the gridpoint at grid[player's row][player's column]
	if that gridpoint's playerID equals the given playerID
		set the gridpoint's playerID to null character
		set the player's row, col to -1, -1
		return true
return false
grid_randomRoomSpots:
given a non null grid and specified number of room spots to return
create an array of int pointers with length numRows * numCols 
keep track of the current index position in the pointer array
for each row in the grid
	for each col in the grid
		if the isEmptyRoomSpot() returns true for the point there
		set current index in ptr array to store that row, col
		increase index for position in array
create an array of pointers, whose length is the specified number of room spots
iterate from 0 to the given number of room spots to find
	pick a random room spot from the array of all room spots from above
	for each preceding index in the array of random rooms
		if the random room spot we picked is the same spot as this index
			store that its a duplicate
			break out of this inner loop
	if the spot is a duplicate
		try again at this index in next iteration
	else
		save the unique random room spot we found in our array of pointers

free each row in the array of all room spots
free the array of all room spots

return our array of random room spots
grid_placePiles:
ensure params are not null or out of bounds
create an int array 'piles' with its number of slots being a randomly selected number between min and max, inclusive
for each integer between [0, goldTotal)
	pick a random slot between [0, number of slots)
    increment the count of the random slot in 'piles'
call grid_randomRoomSpots to produce an array of *(row, column)* coordinate pairs that's the same length as 'piles'
for each point in the array of room spots
    set the goldCount of the corresponding room spot equal to the number at this slot 'piles'
set goldRemaining equal to goldTotal given
free each random room row
free the random room array
free the array of piles
grid_movePlayer:
// makes use of static local funcs, movePlayerHelper and runHelper
using a switch conditional structure based on given key
	case h
		return movePlayerHelper with dir = left
	case H
		return runHelper with dir = left
	case l
		return movePlayerHelper with dir = right
	case L
		return runHelper with dir = right
	case j
		return movePlayerHelper with dir = down
	case J
		return runHelper with dir = down
	case k
		return movePlayerHelper with dir = up
	case K
		return runHelper with dir = up
	case y
		return movePlayerHelper with dir = up, left
	case Y
		return runHelper with dir = up, left
	case u
		return movePlayerHelper with dir = up, right
	case U
		return runHelper with dir = up, right
	case b
		return movePlayerHelper with dir = down, left
	case B
		return runHelper with dir = down, left
	case n
		return movePlayerHelper with dir = down, right
	case N
		return runHelper with dr = down, right
	default
		return that we did not move
movePlayerHelper
calculate new row, col
if they are out of bounds
	return that we did not move
if the spot we are moving to is not a room spot or a passage spot
	return that we did not move
if another player is on that spot
	swap places with that player
	update their row, col
	store that we collided
update player's row, col with new values
if grid_collectGold returns true at this spot
	return that we found gold
if we collided
	return that we collided
return that we moved
runHelper
init last attempt to move's result as didNotMove
while we are able to move, try to move
	keep track of last attempt to move's result
	if we found gold or hit a player
		break
return the last attempt's result
grid_collectGold:
if the parameters are not null
	identify the gridpoint that the player is located on
	if that gridpoint has goldCount greater than 0
		increment the player's purse by the gridpoint's goldCount
		decrement the grid's goldRemaining by the gridpoint's goldCount
		set the gridpoint's goldCount to 0
		return true
	else
		return false
grid_isVisible:
ensure params are not null and within bounds
store change in row, col from player to given row, col
if the change in row or col is zero
	call visibleHelperStraight

calculate change in row over change in column between points
calc change in col over change in row between points

// case 1
if the point is below, to the right of the player
	current row = player row + change in row / change in col
	iterate through each column from player -> target col or out of bounds
		call intersectionHelper at the current row, current col
		if we can't see through the intersection
			return false
		increment current row by change in row / change in col
	current col = player's col + change in col / change in row
	iterate through each row from player -> target row or OOB
		call intersectionHelper at current row, col
		if we can't see thru intersect
			return false
		increment current col by change in col / change in row
	return true // nothing obstructed us, we can see it

// case 2
if point is below, to the left
	perform same operation as case 1, but decrement col from player to target, and since change in row / change in col will be < 0, subtract it from currentRow instead of add 
	(return false if at any point we determine view is obstructed) otherwise true

// case 3
if point is above, to the left
	perform same operation as case 2, except decrement row from player to target, substracting change in col / change in row from currentCol
	(return false if at any point we determine view is obstructed) otherwise true

// case 4
if point is above, to the right
	perform same operation as case 1, except decrement row from palyer to target, substracting change in col / change in row from currentCol
	(return false if at any point we determine view is obstructed) otherwise true

return false
visibleHelperStraight
if change in row is zero
	if player col < target col
		loop from player to target
			if at any point view is obstructed, return false
	else
		loop from target to player
			if at any pt view's obstructed, return false
	return true
if change in col is zero
	if player row < target row
		loop from player to target
			if at any pt view is obstructed, return false
	else
		loop from target to palyer
			if at any pt view is obstructed, return false
	return true
return true
intersectionHelper
if we are checking the intersection between/on rows
	if the current row's an integer
		if we can't see through the pt at current row, col
			return false
	else
		if we cannot see through both the pt at floor(row), col and floor(row) + 1, col
			return false
else
	if the current col's an int
		if we can't see through pt at current row, col
			return false
	else
		if we cannot see through both row, floor(col) and row, floor(col) + 1
			return false
return true
grid_updatePlayerMap:
for each row in 'gridpoints'
	for each gridpoint in the row
		if isVisible of the gridpoint is true
			if that gridpoint has another player on it
				set the corresponding char in the first player's map to the other player's playerID
			else
				if that gridpoint's gold count > 0
					set the player's map's corresponding char to gold symbol
				else
					set the corresponding char in the player's map to the gridChar of that gridpoint
		else
			if the player's map char is gold or another player
				set the player's map char to whatever the corresponding gridpoint char is
grid_updateSpectatorMap:
if the grid and spectator passed in are valid
	for each row in the grid's gridpoint array
		for each gridpoint in that row
			if that gridpoint has a player on it
				set the corresponding gridpoint in the spectator's map to this player's player ID
			else
				if the gridpoint's gold count > 0
					set the spectator's char there to gold symbol
				else
					set the spec's char there to this gridpoint's gridChar
grid_delete:
for each row in 'gridpoints'
	for each gridpoint in that row
		delete the gridpoint
delete the array of gridpoints
delete the grid

Player

Data structures

The player module makes use of a player structure, which stores a 2d array of characters representing the portion of the map visible to the player, an int ‘purse’ storing the amount of gold the player has collected, a two-slot int array ‘point’ representing the row and column of the player, a char* representing the player’s name, a char playerID representing the player’s ID, and an addr_t representing the player’s address. If the player is a spectator, the purse and point will be set to NULL.

typedef struct player {
  char** visibleMap; 
  char* name;
  char playerID;
  int purse;
  int row;
  int col;
  addr_t playerAddress;
} player_t;

Definition of function prototypes

A function player_new to allocate a new player struct.

player_t* player_new(char* name, addr_t address, char playerID, int nRows, int nColumns, int startRow, int startColumn);

A function player_setMap to set the player’s visibleMap

void player_setMap(player_t* player, char** map);

Function to set player’s row

void player_setRow(player_t* player, int row);

Func to set player’s column

void player_setCol(player_t* player, int col);

A function player_addToPurse to add some gold to a player’s purse

void player_setPurse(player_t* player, int goldAmount);

A function player_getName to get the player’s name.

char* player_getName(player_t* player);

A function player_getMap to get the player’s visibleMap

char** player_getMap(player_t* player);

A function player_getID to get the player’s playerID

char** player_getID(player_t* player);

A function player_getPoint to get the player’s point:

int* player_getPoint(player_t* player);

A function player_getPurse to get the player’s purse:

int player_getPurse(player_t* player);

A function player_getAddr to get the player’s address

addr_t player_getAddr(player_t* player);

A function player_mapToString to prepare the map to be sent by the server to a client.

char* player_mapToString(player_t* player, int numberOfRows, int numberOfColumns);

A function player_delete to delete an allocated player struct

void player_delete(player_t* player);

Detailed pseudo code

player_new

allocate a new player struct
give the player struct the given name, address, starting row & column
allocate player's map based on given row and column sizes
initialize all other values to default

player_setMap

if the given map and player are not NULL
	set that player's visibleMap to the given map

player_addToPurse

if the given player is not NULL
	increase that player's purse by specified amount

player_getMap

if the given player is not NULL
	return that player's map

player_getPoint

if the given player is not NULL
	return that player's point

player_getID

if the given player is not NULL
	return that player's playerID

player_getPurse

if the given player is not NULL
	return that player's purse

player_getAddr

if the player is not NULL
	return their address

player_mapToString

allocate a `char*` whose size = (number of rows * (number of cols + 1)) + 1
string_index = 0
for each row
	for each column
		if the point on the player's map is the player's ID
			set the char here in the string to be @
		else
			set the char here in the string to be the char at this point on the map
		increment string_index
	set current string_index to new line char
	increment string_index
set last index in string to be null char
return result char

player_delete

deallocate player's map
deallocate player struct