CSC230 Homework 5

This homework is to be done individually.

You may use any functions in the standard library on this assignment.

Update Sat 2015-07-11 5:40pm Update Fri 2015-07-10 11:34am Update Tue 2015-07-09 4:06pm Update Thu 2015-07-09 11:07am Update Tue 2015-07-07 11:47pm Update Tue 2015-07-07 2:04pm

This assignment includes extra credit -- all portions of this document relating to the extra credit will be in a green box like this one. You can safely skip these boxes until you're ready to look into pursuing the extra credit. Warning: Don't let your pursuit of extra credit mess up your primary program!

Note: this is the first semester this particular assignment is being used. Please report any unclear explanations, mistakes, or issues to the instructors.

Learning Outcomes

Quester: A Text Adventure in C


Tennis for Two, 1958. Source.


Zork, 1981. Source.
Computers (and before that, vaguely computer-like electronic things) have invariably been put to the task of gaming, no matter their limitations. When a computer interface was nothing more than a grid of a few lightbulbs, it was made to play Nim. When a computer interface was nothing more than an oscilloscope, it was made to play tennis (see right). And when a computer interface was just a text terminal like the kind we're emulating to this day, the first text adventures were born. The very first was Adventure, and probably the most well known was Zork.

In addition to being an important historical landmark on the way to today's pinnacle of gaming, text adventures can also feature a ton of fairly rigorous programming concepts, including dynamic data structures, packed binary file storage, length-prefixed strings and structures, and function pointer lookup tables.

In this assignment, you're going to write a simple text adventure engine capable of loading an adventure world from file and interactively allowing the player to explore. The game is called Quester, and the object is to find the key and unlock the chest full of treasure without being killed by the hideous Javgoblin.

Program Requirements

The game will accept a world file as its only argument; if run without argument, it displays the following usage message: "QUESTER: a CSC230 game!\n"
"\n"
"Syntax:\n"
" quester <world-file>\n"
"\n"
The world is laid out as a connected network of "rooms" (though a "room" could be described as being outside, or flying, or whatever). Each room has up to four exits in each of the cardinal directions: north, east, south, and west. These exits can be one-way or two-way, and they need not even make geometric sense. In other words, these rooms are NOT on a regular grid like the cellular automata of HW4; rather, each exit is a "link" to another room.

In each room, there can be any combination of the following four "objects":

Upon starting, the game prints a description of the starting room, and then a prompt. The user can input one of a limited number of commands, such as move in a direction ("east", "north", etc.), pick up an item ("get key", "get weapon"), attack an enemy ("attack"), or open the chest ("open chest"). There are a few meta-commands as well, such as "inventory" and "quit".

Added 2015-07-11 5:40pm The prompt presented to the user is "> ". (Note: this means that automated test output will appear to have a prompt followed immediately by a command's response (e.g. "> You travel east."), since inputs are not echoed to the log during automated testing.) In order to make the interface case insensitive, commands shall be made lower case before being interpreted.

The player must locate the key, then the chest, and open the chest to obtain the treasure. However, the world may include one or more enemies; to dispatch these enemies, the player will need to find a weapon. It may not be clear where the enemy is, as all the player can tell is if an enemy is in a given direction, not how far.

Play continues until (a) the user unlocks the chest with the key and wins, (b) the user is killed by an enemy, or (c) the user quits with the "quit" command. After any of these cases, the program exits, and the exit status code indicates how the game ended.

Below is an example playthrough using the minimal testing world file test_world_1.dat in which the player wins: = Start room = This world is very minimal. This room has the weapon. You spot a sharp, crescent-shaped blade resting at your feet. Exits: east (but you see a shadow moving in the distance) > get weapon You pick up the crescent blade. > east You travel east. = Enemy/key room = This room has the enemy. I hope you got the weapon. After you kill it, you can get the key. A bloodthirsty Javgoblin snarls at you, ready to strike! You spot a key glimmering on the ground. Exits: north west > attack You look the monster in the eye and leap forward while thrashing your crescent blade wildly. You somehow cleave your foe in half, which turns out to be its main weakness. It is dead. I guess this is an ethical thing you did? > get key You pick up the key. > north You travel north. = Chest room = This room has the chest. Using the key, open the chest and win. Also, this room has a one-way exit north to the start. A large oak chest sits here. Exits: north south > open chest This is it! You insert the key into the lock on the chest, and give it a turn. You hear a click, and the top bursts open revealing two bearded men from the '70s, each made of solid gold! You WIN! Goodbye.

For full marks to be awarded, your implementation must meet the following requirements:

Design

The world

The design of the program shall make heavy use of structures, pointers, and dynamic memory allocation. Each room will be stored in memory as a heap-allocated structure, with pointer links between rooms representing exits. A master table of rooms will be kept in a heap-allocated array called the world.

The world will be read from a binary file. The detailed structure is given later in this document, but at a high level, the file will make use of length-prefixed records. Typically, we've been using delimited records, meaning that a record continues until a special character or string is encountered. For example, our strings tend to be null-terminated, and data we've read from the console or ASCII files has been delimited by spaces or newlines. Delimited records like this have an advantage in that they can be of any size with very little overhead; the downside is that mistakes involving the delimeter can lead to malfunctions or security vulnerabilities.

Contrast this with length-prefixed records, in which the first few bytes stored are the length of the structure that follows. This means that no terminator or delimeter is needed, so there's nothing at the end to overwrite and cause problems. Length-prefixed records are often easier to read in binary files, because after you get the length, you know exactly how much further to read, and often can even do it with a single fread call. On the downside, you have to agree ahead of time what the maximum length of your record is going to be, and make your length prefix big enough to store that number. This can cause growing pains when software ends up needing even longer records down the road. (For a detailed treatment of the topic, see this excellent article by Joel Spolsky.)

The detailed design of the world file is given in the Implementation section of this document.

The interface

Commands will be read from standard input, and will be delimited by newlines. Unlike most text adventures, we will not be implementing a parser (i.e. our program will not understand "get" as a distinct verb; instead, it will just know "get key" as a single discrete command). In order to make our interface easily extensible, we will create a command table which maps commands to functions reference via function pointers. This use of a function lookup table is very common and very powerful, allowing commands to be mapped to a wide variety of operations without the need for a single giant sequential if statement.

You will be given freedom in your design to add additional commands (though these will not be tested). Also, we will relax the diff check when it comes to spacing and optional text colorization.

While navigating, the interface will print a description of the room, which will include its name, description, and objects that may be present (including enemies), and a list of exits from the room (north, east, south, or west). Further, the interface will warn the player if an enemy is present in any of the available directions, but this information will not indicate how far the enemy may be. For example, consider the following map: [___]-[enemy] | [___] | [enemy]-[here]-[___]-[___]-[___]-[enemy] | [___] | [enemy] Each bracket pair is a room. If the player is at the position marked [here], then an enemy will be reported east, south, and west, since there's an enemy directly in "line of sight" in these directions. However, no enemy will be reported in the north. To discover that enemy, the player will have to go north twice, then the enemy will be reported to the east. In other words, enemies are never reported "around corners".

NOTE: There is a potential issue with this design. It is possible for rooms in a given direction to form a loop, making the traversal for the enemy check into an infinite loop. The proper solution would be some form of loop detection, usually by marking the rooms traversed in some way. That is NOT a requirement of this assignment, and worlds with loops in a single direction will not be tested.

The specifics of the interface will be discussed in the Implementation section of this document.

Code structure

Because of the size of the assignment, the program will be broken down into separate modules: player.[ch]1, world.[ch]1, and quester.c. Further, a skeleton of the assignment is provided in the starter kit.

The main function is to be contained in quester.c, which should be fairly lightweight, likely just handling command line argument parsing and the game logic loop. Everything having to do with loading the world from file and navigating it is handled by the world.[ch] module. The player's location, inventory, and actions are handled in player.[ch].

1 The [ch] is regular expression notation for a 'character class', meaning 'either c or h', and is often used by C programmers to refer to code and header files simultaneously.

Extra credit

This assignment includes several opportunities for extra credit. As these are not core requirements, less detail and direction are given, and students are invited to research, explore, and experiment toward a solution.

Do not let pursuit of extra credit endanger the core requirements! Extra credit will only be considered when the majority of core requirements have been met.

You may pursue ONE (1) extra credit opportunity at most. If you pursue more than one, indicate which you'd like graded in a readme.txt file, otherwise we will grade a random one.

Cheating solver

Possible points: +15

Develop an additional program called cheat_quester which takes a world file as an input parameter and outputs on stdout a sequence of commands that will result in winning if winning is possible for a given world. For worlds where winning is impossible (e.g. there's no chest anywhere, or the only key and weapon are behind an enemy you can't get around, etc.), simply emit the "quit" command. In order words, develop a program that when run as follows will cause quester to exit with status 0 (win) in any possible case, and status 2 (quit) otherwise: $ ./chest_quester someworld.dat | ./quester someworld.dat

Note: Traversing a graph such as the world structure is usually covered in CSC316. You'll want to look into graph traversal algorithms. Be careful with worlds that have loops, though...

To apply for this extra credit, include a readme.txt file that indicates that you've written chest_quester. Additionally, include any extra documentation that may be needed to test it, and note any known problems it may have.

Fair solver

Possible points: +30

Develop an additional program called ai_quester which runs quester as a subprocess, redirecting its stdout and stdin to allow bi-directional programmatic control. Without peeking at the world file, ai_quester should use the output of quester to play as best it can. Our game provides incomplete information about the game world, so it will not be possible to win in all possible cases as with the cheating solver. Full credit will only be given for a play algorithm that meaningfully uses quester output to guide its choices.

The syntax for the program will be: $ ./ai_quester someworld.dat

Note: Launching a sub-process and taking bi-directional control of its stdin and stdout is an advanced technique usually introduced in CSC246 or later, so you'll have to do some digging to learn more, and there are pitfalls to watch out for.

To apply for this extra credit, include a readme.txt file that indicates that you've written ai_quester. Additionally, include any extra documentation that may be needed to test it, and note any known problems it may have.

World editor

Possible points: +15

Develop an additional program called world_maker which, by some means of your own design, allows users to create world files. The editor must be able to represent everything that can be in a world file (rooms, text fields, exits, and objects). Solutions may implement some form of command line interface or take as input the product of existing software.

For credit to be awarded, the editor must automatically determine the number of rooms, the length prefixes of room names and descriptions, the indexes of the rooms in the exit linkages, and how exits and object IDs correlate to numeric values. In other words, the included make_test_world.c program is woefully inadequate to be considered, since it generates worlds solely from inline code (it doesn't take input) and it requires the user (who's actually the programmer) to determine things like exit indices by hand.

To apply for this extra credit, include a readme.txt file that indicates that you've written world_maker. You must include complete documentation on how to use it -- documentation will be considered incomplete if we cannot learn how to make any possible world using your editor. Additionally, include any extra information that may be needed to test it, and note any known problems it may have.

Additional commands and/or objects

Possible points: +10

Without modifying the existing ones, add additional commands or objects to the game. Any changes you make must NOT affect the output of the test cases which use the standard worlds and commands.

Commands: These must be non-trivial -- adding a "wave" command that just prints "You wave hello.\n" will not suffice. The determination of non-triviality will be at the discretion of the instructors.

Objects: These must affect gameplay in some way -- adding a cool hat that does nothing will not suffice. Further, if you decide to add additional objects, you will have to create new world files to test these objects. Lastly, you can only use the 4 unused bits of the objects field, because you may NOT alter the world file format to allow for more.

If you are unsure if a change you'd like to make meets the requirements above, ask the instructor.

To apply for this extra credit, include a readme.txt file that indicates that you've added additional functionality. Describe what the functionality is, how to use it, how it affects gameplay, and how the changes are reflected in world files. If the changes you make require differing world files to see, include at least two world files of your own creation for testing, and note these in the readme file. Finally, include any extra documentation that may be needed to test it, and note any known problems it may have.

Something else?

If you have a different idea you'd like to propose, ask the instructor in person.

Implementation

The world representation: world.[ch]

The world module (world.c and world.h) will be responsible for loading the world and managing the in-memory data structure that represents it. This will make heavy use of structures, pointers, and dynamic memory allocation. First, the world will be referenced via a struct world accessed via struct world* pointer. The structure is defined as: struct world { struct room** rooms; int num_rooms; }; The rooms field shall be a dynamically allocated array of struct room* pointers, each pointing to a separate dynamically allocated struct room. See figure:

In turn, struct room is defined as follows: struct room { char name[MAX_NAME_LENGTH+1]; char* desc; char objects; // bit mask struct room* exits[MAX_EXITS]; };

Here, name is a character array inside the struct, whereas desc is a pointer to a separate heap-allocated string of arbitrary size. The objects is a bit field indicating the presence of any of the game objects, and exits is a set of 4 pointers for each of the cardinal directions -- these point to other rooms, or NULL if there's no exit in that direction. See figure:

As an example, consider the following potential world:

This world would be represented by the following set of structures:

The world structure and its pointers have been shaded blue to distinguish them, since they're just memory management book keeping; all the actual work of the game takes place in the unshaded pointers among the rooms.

Everything having to do with loading the world is to be implemented in world.[ch]. The only "public" functions are those listed in world.h:

The world file format

The world file consists of:

Each room record consists of:

A length-prefixed string is defined as:

Lastly, each exit linkage consists of:

See the following diagram (not to scale):

Note that file format means you cannot simply fread whole rooms directly into a struct room. Rather, you'll have to read in each field separately, making necessary conversions as you go. There are three key differences between the on-disk and in-memory representations of the world:

  1. The on-disk strings are length-prefixed and do not have null terminators, whereas the in-memory strings must have null terminators and not have a length prefix.
  2. The exits are stored in a separate region on-disk, whereas they're stored in the struct room in memory.
  3. Exit linkages on-disk are stored as integer "room indexes", i.e. references to the order in which the rooms appear in the file. Exit linkages in-memory must be pointers directly to the destination room's struct room.

Also, note that exits are one-way by default. To create an exit that the user can traverse both directions requires two linkages running opposite directions. In other words, if you want a two-way exit between A and B, you need both A⇒B and A⇐B.

As an example, consider the following hex dump: 00000000 02 00 00 00 08 00 00 00 52 6f 6f 6d 20 6f 6e 65 |........Room one| 00000010 18 00 00 00 54 68 69 73 20 69 73 20 72 6f 6f 6d |....This is room| 00000020 20 6e 75 6d 62 65 72 20 6f 6e 65 2e 07 08 00 00 | number one.....| 00000030 00 52 6f 6f 6d 20 74 77 6f 1b 00 00 00 57 65 6c |.Room two....Wel| 00000040 63 6f 6d 65 20 74 6f 20 74 68 65 20 73 65 63 6f |come to the seco| 00000050 6e 64 20 72 6f 6f 6d 2e 08 00 00 01 00 01 01 00 |nd room.........| 00000060 00 00 03 |... |

Deconstructing it:

When loading a world in load_world(), if an error occurs, the function returns NULL, and a global char* named load_world_error_msg is set to a string describing the problem.

Possible error messages include:

Command parsing and actions: player.[ch]

The player module (player.c and player.h) will keep track of the player's current location (a struct room* pointer) and inventory and handle execution of commands (though not the actual task of reading the command from the console).

This is achieved through three "public" functions:

The implementation of do_command() shall internally make use of a command lookup table, which correlates commands to a function pointer. This table is defined as follows: // associates a command with an action struct command_action { char* command; result_t (*action)(); }; // THE MASTER COMMAND/ACTION TABLE static const struct command_action commands[] = { // movement { "north", action_north }, { "n", action_north }, { "east", action_east }, { "e", action_east }, { "south", action_south }, { "s", action_south }, { "west", action_west }, { "w", action_west }, // interaction { "get key", action_get_key }, { "get weapon", action_get_weapon }, { "get blade", action_get_weapon }, ....... and so on ........ }; All the functions being referenced have the prototype result_t action_whatever(). This means that do_command() simply needs to compare the provided command to those in the table, and call the corresponding function when it finds a match (returning INVALID if no match could be found, i.e. if the user typed an invalid command). Note that multiple commands can map to the same action; this allows you to specify synonyms for commands, such as both "n" and "north" meaning to go north.

Game logic

NOTE: To allow for easy copy/pasting, these flowcharts are also available as a PDF.

To allow you to get a feel for the behavior of the game, we've included a reference implementation; this is a compiled copy of the instructor solution. Note that this document remains the authoritative source of program requirements -- consider the reference implementation to be a secondary resource.

Below is a flowchart illustrating the behavior of the front-end code, quester.c. Note how it relies on the do_command function for the actual processing.

Below are flowcharts for the actual action functions in the player module.













Magic Numbers

All "magic numbers" that you use, (for example the default values for the probabilities and exit codes), must be defined as constants. For example:

#define CONSTANT_NAME 3

Get your environment set up

Note: If you successfully cloned your GitHub repo for a previous homework, you can skip to the section "Get the starter files". Make sure that you're developing in the 5_homework directory!

Start by cloning the provided CSC230 GitHub repo in your local AFS (or development space) using the following commands:

$ unset SSH_ASKPASS
$ git clone https://<unity_id>@github.ncsu.edu/engr-csc230-summer2015/<repo_name>.git

This will create a directory with your repo's name. If you cd into the directory, you should see directories for each of the homeworks for the class. You'll want to do all of your development in 5_homework. If you do NOT see the 5_homework directory, enter the following command to make the directory:

$ mkdir 5_homework

Get the starter files

Next, you will need to copy the hw5_starter.tgz from the course locker into your 5_homework directory. Use the following command:

$ wget

Untar using the command:

$ tar xvzf hw5_starter.tgz

About the starter kit

The starter kit includes the following:

You must use the appropriate file names to allow for automated grading.

Reminder: A header file is a place to store information that is shared between different C modules. In this case, this includes function prototypes, certain type definitions, and any cross-module global variables. Do not modify the provided functions and struct names in the header file! You may add members to existing structs, as long as (a) you don't break compatibility with the posted world file format and (b) you keep the same pointer-based in-memory layout.

NOTE: The provided files are NOT fully commented and may not conform to all style rules for the course. You must update the files to meet the style guidelines! (Exception: you can ignore make_test_world.c, which is provided for your convenience.) You MAY use magic numbers in your test program.

Implementation Success Strategies

The teaching staff have the following recommendations for this assignment:

Pushing changes


This lad knows the value of keeping his github repository up to date! Source.

When you are ready to commit to your local repository, execute:

$ git add .
$ git commit -am "a meaningful message for future you"

When you are ready to submit to the remote repository for automated grading or teaching staff feedback, execute:

$ git push

NOTE: It is considered good practice in industry to commit locally very frequently, and to push whenever you have a functioning unit of work. This is doubly true when you have a continuous integration system like Jenkins to constantly provide feedback on compilation and test completion!

Tools

Valgrind

Valgrind is a tool that can be used to find memory leaks and heap memory problems. Use the following command for Valgrind after building the executable:

$ valgrind --leak-check=yes ./quester test_world_1.dat

Do not attempt to run valgrind on any of the make commands above. Instead of running valgrind on your program, you will be running valgrind on make (which amusingly enough has memory leaks). You must verify that your unit testing of your list library has no memory leaks.

Valgrind output looks like the following:

==5403== Memcheck, a memory error detector ==5403== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==5403== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==5403== Command: ./quester ==5403== Test output prints here (including interactive console input/output) ==5403== ==5403== HEAP SUMMARY: ==5403== in use at exit: 0 bytes in 0 blocks ==5403== total heap usage: 69 allocs, 69 frees, 9,098 bytes allocated ==5403== ==5403== All heap blocks were freed -- no leaks are possible ==5403== ==5403== For counts of detected and suppressed errors, rerun with: -v ==5403== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)

You want to see 0 errors from 0 contexts. If there is memory in the "definitely lost", "indirectly lost", and "possibly lost" categories (not shown above - the categories are only shown when there are possible memory leaks), you will have an error. The "still reachable" category means that the memory wasn't freed before the program stopped execution. Since that memory is freed when program execution stops, we will not deduct for memory in that category (and Valgrind doesn't report an error), but we encourage you to free all memory before stopping program execution.

A limitation of Valgrind is that it can only detect memory leaks on actual executions. Having no memory leaks reported for the provided tests and your tests does not mean that there is not a memory leak in your program. The teaching staff will also inspect your code for potential memory leaks. The best way to avoid memory leaks is to free all dynamically allocated memory on all paths in the program (e.g., don't forget to free memory on error paths or to free memory in the test code!).

make_test_world

A simple utility to create world files has been included, make_test_world.c. There's no need to document or modify this code unless you wish to. Note that the utility is "dumb", i.e. it you have to figure out the room count and room id's yourself for it to produce correct output. The kit comes configured to build this tool when you type make; the Makefile is also configured to run the tool to produce test files:

strip_escape_codes.pl and relaxed_diff

strip_escape_codes.pl is a simple utility written in Perl to remove ANSI color escape codes from any input it gets, either stdin or filename arguments. You may find it useful if you decide to use color, but still want to be able to diff against known outputs. Example:

relaxed_diff is a front-end to diff that will ignore both whitespace differences and ANSI color escape sequences. Options are the same as diff, except the order in which they're specified is rigid: Syntax: ./relaxed_diff <file1> <file2> [extra_option] The tests in test.sh use relaxed_diff, as do the instructor tests. This means that you can change the spacing or coloration of your output without failing tests.

Testing

Testing materials provided

The following materials are included in the starter kit: These system test cases are broken down as follows: Note: Most sequences above end in an extra 'quit' command to attempt to exit gracefully at the end, even if the program should have exited due to a win/lose condition earlier. Correct implementations won't hit this last quit command; this is reflected in example output.

Additional testing requirements

At this point, you should have a decent understanding of good testing practices. As such, the testing requirement will be stated simply:
  1. You must develop a new legal world file.
  2. You must develop at least two (2) system test cases against this world file, each terminating in a different exit status. Add them to test.sh.
  3. You must develop at least three (3) unit tests in quester_test.c. The code already present calls a few player functions, but does not check the results in any way; you'll fix that. The exact nature of your unit tests and the manner in which they report to the console is left to you.

Checking Jenkins Feedback

We have created a Jenkins build job for you. Jenkins is a continuous integration server that is used in industry to compile, build, and test applications under development. We will be using Jenkins to compile, build, and test your homework submissions to GitHub, which will provide early feedback on the completeness (does your implementation meet the requirements) and quality (does your implementation correctly implement the requirements).

Your Jenkins job is associated with your GitHub repository and will poll or query GitHub every two minutes for changes. After you have pushed code to GitHub, Jenkins will notice the change and automatically start a build process on your code. The following actions will occur:

Jenkins will record the results of each execution. To obtain your Jenkins feedback, do the following tasks:

Instructions for Submission

The following files MUST be pushed to your assigned CSC230 GitHub repository:

By submitting the actual results from the tests, you will prove that you have tested your program with the minimum set of the provided acceptance tests. Automated grading will not run on your program without at least one generated actual result file (it doesn't mean your tests have to pass, just that you've attempted the tests locally before pushing to your repo).

Additional Considerations

Follow the CSC 230 Style Guidelines.  Make sure your program compiles on the common platform and Jenkins cleanly (no errors or warnings), with the required compile options.

There are certain learning outcomes and basic software engineering skills that this assignment is assessing. See the rubric below for deductions may be applied to your submission as enforcement of good software engineering practices and assignment intentions.

Make sure that you push your code to the GitHub repository provided for you! Pushing your code to GitHub is your submission!

There is a 24 hour window for late submissions.  After the main deadline, continue to submit to GitHub. We will use the last commit to GitHub before the late deadline for grading and the timestamp of that commit will determine a deduction, if any.

Rubric

Quester

+5 for compiling on the common platform with gcc –Wall –std=c99 options, with no warnings

+50 for passing teaching staff unit tests. The precise weighting of the test cases is at the discretion of the instructor (for example, over 200 test cases act on a test world 1; this will be balanced against the one test case that runs against test world 2).

+5 for developing a new legal test world

+10 for at least two (2) high quality system test cases using the new world; tests are to be added to test.sh

+10 for at least three (3) high quality unit tests in quester_test.c.

+10 for passing the student written tests listed above.

+20 for comments and style

-5 for meaningless or missing comments

-5 for inconsistent formatting

-5 for magic numbers (other than 0 as a loop bound, +1, -1, and minor increments/decrements - if the value has meaning (say as a state or a resolution) it should be given a name (e.g., a macro, an enum, etc.). Magic numbers as expected test output are allowed in list_test.c! Good tests MUST have concrete expected values!

-5 for correct header information

Extra credit

+15 extra credit for the cheating solver cheat_quester.
      OR
+30 extra credit for the fair solver ai_quester.
      OR
+15 extra credit for the world editor world_maker.
      OR
+10 extra credit for additional commands/objects.

Total Points Possible: 110 (without extra credit)

Deductions (we will not go negative with your score)

-20 for late submission

-20 points: a memory leak is reported via valgrind tool (see tools section) OR as observed via a test or inspection by teaching staff

-20 points: for any observed buffer overflow OR security vulnerability

-60 points: not implementing the dynamically allocated structures described

-10 for a submission where a file is misnamed