Assignment - Software Design and Implementation
In your first assignment, you focused a great deal on data structures and performance. In this second assignment we will focus more on issues around software design and implementation.
The task in this case is to implement a basic game that you may (or may not) decide to build upon beyond this course.
You may find startup datafiles in the same folder on google drive as this document at:
https://drive.google.com/open?id=0BzM8LtxnJauTRFpDbFpOeWwwc0k
and you may find the initial class diagram here:
https://drive.google.com/open?id=0BzM8LtxnJauTcmlTNm94dm5obDA
in dia format or here:
https://drive.google.com/open?id=0BzM8LtxnJauTVU1ET1lBTmZRTlU
in png format. I have also provided startup header files here:
https://drive.google.com/open?id=0BzM8LtxnJauTZTVUa3M5ek92czQ
Description of the game:
Create a game prototype using modern C++. This should be a text based game. The game is laid out as a map. The map is composed of areas which have at least one entrance and exit. Each area is composed of rooms This game map should be represented by a stl container of area objects. The various objects that are part of the game map will have links between them represented by pointers between the objects. If any of your owning containers need pointers to objects (such as for polymorphism) you should use containers of safe pointer objects. However avoid additional memory allocation as much as possible as your program will run faster.
A character has various attributes (ints) that determine their effectiveness in battle (no requirement to implement actual battle but you do need to have values recorded for these stats. The stats include (all will have a value between 5 and 25):
- Int: the strength of the player's magic
- Wis: the strength of the player's healing powers
- Str: the higher the value, the more impact of each blow
- Dex: the larger the value, higher the probability that the player can dodge the blows
- Con: the larger the value, the lower the impact of each blow that lands
Each character will also have the following health status points:
- Hit points: the health value of the player
- Mana: the amount of magic that can be cast: each spell will deduct from this amount
- Moves: the number of moves the character can make before they are exhausted.
(each game "tick" (one second in the game) the count of these is increased up to the maximum of 25. Each tick their hit points should increase by their constitution divided by ten rounded down to the nearest whole number. Each tick their mana should increase by their wisdom divided by 10 rounded down to the nearest whole number. Each tick their moves should increase by their dexterity divided by ten rounded down to the nearest whole number. Each time that a character moves from one room to the next the number of moves they have left should be deducted by one. If their moves go to 0 then they will not be able to move until they have recuperated and if they try a message saying "you are too exhausted" should be printed.
You should provide a login screen for players to log into the game which will drop the player into midgard temple which is the default starting point for each player session. The player must be able to interact with their environment in the following ways. Please note that implementing these in the player object is probably not the best way. As we are only working with one player, it makes sense for your view module to send these requests to the controller which will make the right requests to the model. The model will then be asked by the view for updated information to display.
- "look" without any parameters: display the name and description of the current room, its contents and the available exits (if the door to that other room is closed, the exit will not be displayed.
- "look ": look in the direction specified and see the name of the room there or a message "door is closed" if the door is closed.
- open : open the door in the direction specified.
- close : close the door in the direction specified.
- "look item" where "item" is the name of the item to be looked at: name and description of the item as well as the type of item. For example:
Name: Shining Sword
Type: weapon
Description: A shining sword which will blind the opponent with every hit.
Name: Helm of the Gods
Type: helmet
Description: this helmet protects the wearer from any unnatural curses.
- "wear item": wear an item in a wear location where "item" is the name of the item. The item must currently be in the player's inventory at the time the command is issued.
- "Remove item": remove an item currently being worn and place it in the player's inventory.
- north, south, east, west, up, down: move through the exit in the direction specified. If the door is closed, a message stating this should be printed and the attempted move should fail.
- list: list the items for sale - must be in the presence of a shopkeeper.
- buy - : buy the item specified by name or number from the list of items for sale: must be in the presence of a shopkeeper.
- Account: display the number of gold coins the player has on hand.
- quit: quit the game
Finally, the user of your program must be able to specify at startup the format and location of the data files (the directory). The format of the files may be binary or ascii. Example data files in both ascii and binary formats will be provided to you.
Initial game design
I have provided you with an initial game design which will be available from the same folder as this assignment specification as both a dia file and a png file. Each of which starts as "a2_startup" with the appropriate extension for filetype. I will also provide this as a part of the git repository for this course at https://github.com/muaddib1971/cpt323 and navigate to the assignments/a2 folder from there.
The initial design I have given you is incomplete and incorrect and deliberately so. I have given guidance in most of those cases but there may be some where I have missed this. If there is any ambiguity, please verify my intent. There were a number of students in assignment 1 who made assumptions about aspects of the specifications that confused them. In most cases my guidance should be fairly clear but like everyone else, I am human. Don't be afraid to ask. In particular, just because we have discussed the main safe pointers such as unique_ptr and shared_ptr, don't automatically use these unless you need to. Remember that they allocate space on the heap and thus may slow down the performance of your program unnecessarily because pointers are hard for modern systems to cache.
I have used the model-view-controller design pattern for the initial class diagram I have given you. The idea behind this pattern is reasonably simple. An application can be modeled as a data layer, a event processor and a series of displays and that's just what this design pattern is all about: the player is presented with a view which is managed by a display manager of some kind. All requests to change the system are forwarded on to the controller which handles events for the system and any requests to change the state of the data are then sent along to the model. This separation of concerns is a fairly common approach to modern software and not just gui applications. Have a look at my initial class design for further information.
Provided Data
In both the repository specified above and on google drive, I will provide a (valid as far as my own testing has gone) set of data files in the "data_files" directory. As we get closer to the due date I will also release sample files in binary format. Please get your program to work with this sample data. Once you have succeeded in this, think about the kinds of invalid data that may appear in such a file. You will be required to reject all invalid data files.
The ascii file formats:
There are the following files which represent the state of the game at load time. All of these files are "tab delimited" which means that the fields on each line are separated by a tab character (the "\t" character. In the descriptions below, anywhere I specify "\t" this means the tab character. You should treat each of these files like a database table - each line (row) in the file is unique and you should validate for this. These files are:
- areas.txt: This file lists all the areas defined in the game. Unfortunately due to time constraints, our game only has two areas at this stage: Midgard and Wyvern Tower. The area file is formatted as follows:
The area id\tAreaname\tArea description
- rooms.txt: This file lists all the rooms in the mud. It is a little more complicated than the areas file. There are three tab separated integers followed by two tab separated strings.
For example:
1\t1\t1\tThrone Room\tThis is the throne room for midgard. People come here to have an audience with the king.
The numbers from right to left represent:
- The global room number: a number that uniquely identifies each room in the game. This number cannot be repeated for any other room in the game.
- The local room number: a number that uniquely identifies each room in the current area, so on its own it does not make a room unique in the game.
- The area number: id for the area this room belongs to. If we treat the local room number and the area number as a pair, this pair must be unique in the game.
- The room name - this is not required to be unique.
- The room description which is required to be unique.
- exits.txt: this file represents the connections between rooms as each room may have up to 6 exits. This file has two numbers followed by a direction and a boolean value, all separated by tabs, for example:
1\t2\tSOUTH\ttrue
This means that there is an exit from room 1 to room 2 in the south direction. The door between these rooms defaults to being opened (all doors between rooms can be manually opened and closed by a player - a door is closed if in the data loaded in the third argument is false, rather than true as it is with this exit listed here).
- items.txt: this file has the following format for each line: the itemid as an integer followed by the wear_location (can be one of: WRISTS, ARMS, HANDS, LEGS, FEET, HEAD, TORSO, SHIELD, WEAPON (weapons can only be wielded in your main hand with your shield held in your off-hand), followed by the market price in gold pieces. For example:
9\tWEAPON\tThieve's Dagger\tThis dagger looks great for backstabbing as a thief or an assassin.\t26
Unlike other game entities there can be multiple copies of these in the game - so there can be multiple copies of the "axe of slicing" being used at the same time in the game. Remember that items represent multiple potential classes so don't just load these in as "items" - think about a reasonable approach to inheritance here.
- players.txt: this text file contains the information specific to a player. It's a little more complicated than the other files we have examines but it is only because we are trying to manage various data for the player. In the players.txt file there are two players listed, wilma and fred. I'll explain wilma's data.
Here's the data for wilma (or one generation of it):
1\twilma\t3899433464\t-1\t1\tNULL\tNULL\t4\tNULL\tNULL\t7\tNULL\t10\t0\t500\t10\t6\t19\t19\t20\t23\t5\t9
This means that wilma is player 1 in the system (her unique id), her username is wilma, the hash of her password is 3899433464 (the actual password is pebbles and fred's password is dino), wilma is currently not in a particular room (in that case, just load the player in room 1 in Midgard), the next nine offsets represent the items that wilma is currently wearing. She is wearing item number 1 (bracelets of power) on her wrists, item number 4 (leggings of strength) on her legs, item number 7 (vest of fire) on her torso and she is wielding item 10 (mace of bashing). The 0 that is next represents that her inventory is empty. If it was not empty, the number her would represent how many items were in her inventory and so you would look up that man items and make sure they were in there when this player is loaded. The next number represents her gold pieces on hand (500), The next 5 numbers represent her constant attributes for the game: her intelligence is 10, her wisdom is 6, her strength is 19, her dexterity is 19, her constitution is 20. The final three numbers relate to her health status. 23 hit points, 5 mana, 9 moves. These will go up in value over time as I have specified earlier.
These areas and concepts used in this software are largely borrowed from software projects from the early ‘90s such as DIKU and ROM (both were muds (multi-user dungeons - after dungeons and dragons)). A web search for "dikumud" or "rom mud" will show you some of that history. The data formats I have chosen to represent that information however is completely my own.
Please note that all commands should work whether the player enters them in upper or lower case. In all cases a partial match for a command should suffice. For example "l up" and "L UP" and "look u" and "look up" are all interchangeable ways to look at the exit up from here.
Below are the allocation of marks for each section:
1. Command Line arguments
When the program is started it must be invoked at least with how to load the data via either the --ascii_load or --binary_load arguments (not both, of course). These specify whether to load from an ascii or binary source. If loading from an ascii source, the path specified must be a directory - use boost::filesystem to check this. If loading from a binary file, the filename must point to a file which has been saved in binary format - I will specify that further in the requirement "loading of data(binary) below.
Your program must also handle two further optional arguments: --ascii_save and --binary_save. These are optional and if both are left out, your program should default to saving changes to the file that data was originally loaded from. However if one or more are specified (there's no reason that both cannot be specified) then just save to the names specified. Of course, the destination specified by --ascii_save (if it already exists) must be a directory and for --binary_save must be a file.
2. Loading and saving of data robustly (ascii)
Your program should load the provided data into classes specified to achieve the required functionality. Your loading and saving of ascii data must be robust. It must not corrupt the files that are saved and all reads and writes must be validated.
3. Loading and Saving of data (binary)
Please ensure you understand the difference between saving / loading as binary and ascii. Binary formats mean that you save to disk the bit patterns of what is held in memory. If there are any pointers in the structures you are saving, these will need to be serialised first. It is advisable to have a serializable version of each of your classes or have that constructed by your saver/loader classes.
For example, the format for the binary file will be as follows - please note that everything here is written in binary, not ascii:
- The number of areas in the world as an integer
- For each area: write the id, name, description, the number of entrances followed the start room id, end room id and direction. The number of exits followed by the start room id, end room id and direction (remember that these are the global, not local room ids).
- Next you will write the number of rooms that the area contains followed by serialisation of the room data in a similar way as I described above the serialisation of area data.
- Next, you will write the number of items in the game (the number of items in the item list, not the copies) followed by a serialisation of the item data in the same way as above.
- Finally you will write the number of players followed by a serialisation of the player data.
- Please note that what this means is that the data classes must be the same as those provided in the class diagram I have provided or this will not work - each of the fields needs to be presented in the same order as represented on the class diagram. I will provide you with a sample binary file to load so you can test that your algorithm is correct. I expect this part to be one of the more difficult components of the assignment. The plan at this point is to provide you with sample binary files by the beginning of week 9.
4. Correct use of inheritance
Use inheritance whereever you can but in particular I require you to design an inheritance heirarchy for items (it doesn't make sense in terms of object oriented design to have a single item class). The same is true for input/output classes.
5. Correct use of operator overloading
All your data classes should have operator<< and operator>> overloaded to simplify the reading and writing of data in ascii format.
6. Correct use of exception handling.
All error handling and validation should be done by the use of the exception handling mechanisms rather than returning bool or enum state values. All exceptions which are thrown must be caught. It is optional whether you implement your own exception classes or use c++'s built in exception classes.
7. Correct implementation of Model-View-Controller design pattern
The class design you have been provided with has a partially implemented mvc pattern. Complete this pattern and only use the mvc mechanisms to communicate between components to reduce coupling.
8. Handling of logins
Either fred or wilma should be able to login to your system. The user that starts the program needs to be presented with a login screen that looks like the following:
Welcome to Explore World
------------------------------------
Please enter your username: fred
Please enter your password: dinner
Error: invalid password entered. Please try again.
Please enter your username: fred
Please enter your password: dino
Welcome to Explore World. We hope you enjoy your stay!
Please note that your program should convert the password entered into a hash (an integer) using std::hash before sending it from the view to the other mvc components.
You are provided with two players in the startup data. wilma whose password is pebbles and fred whose password is dino.
9. Displaying of rooms and items (the look command)
You need to display rooms whenever the player enters a new room or when they issue the look command with no arguments. For example, the room display for the throne room would be:
The Throne Room
-------------------------
This is the throne room for midgard. People come here to have an audience with the king.
Exits: [SOUTH: Entrance Chamber]
On that last line within the square brackers ([]) should be a comma separated list of all visible rooms. Exits for which the door is closed do not have to be displayed.
Also, if the player enters "look south" the output would be:
You can see the Entrance Chamber south from here.
Finally you need to implement "look item" where the item should be in your inventory to look at it.
"look bra" (without the quotes) should try to match against the first item in your inventory that starts with the letters "bra". So if you had the bracelets of power in your inventory it would say:
Name: Bracelets of Power
Type: Armor
Description: some shiny bracelets are sitting here. They look like they might protect you in battle.
10. Moving between rooms (navigation of the map)
Your player should be able to move between rooms. If there is an exit in the direction the player tries to move, simply follow the exit pointers in that room to navigate to the next room. You should handle them typing in the complete direction or any part thereof. So if the player wants to go north, entering north, no, N or n should all work.
There are two error conditions you need to handle here:
- If the door is closed, print "The door is closed."
- If there is no exit in that direction, print "There is no exit there."
As part of this requirement you are required to handle requests to open a door such as "open east" which would open the door to the east or say "the door is already open" if it is already open. Partial matches should also work here: open e should work as well as open east.
Moving from one room to the next must deduct one point from the move points for the player.
11. Shopkeepers and buying of items
When a player is in the same room as a shopkeeper, the following commands should work:
- List: displays a list of the wares that the shopkeeper has for sale.
- Buy - or buy should buy the item with that number from the shopkeeper. If a phrase rather than a number is used, you should look for a partial match and match with the first item on the list that matches the letters entered.
- You should load a shopkeeper in the rooms "The Armory" and "The Weaponshop" in Midgard and only display the items for sale that match the item type (weapons for the weaponshop and armor for the armory.
So in the weaponshop you see the following list of items:
Items for sale:
------------------
ID Name Price
1 Thieve's dagger 26
2 mace of bashing 27
3 axe of slicing 39
4 spear of piercing 20
Implementation of the "account" command will also be assessed as part of this option.
12. Wearing items
A player may issue the command to "wear item" where item may be a partial match with any item in their inventory. If you find an item that matches what they have entered and that wear slot is currently empty, assign that item to that wear slot. If that place is currently occupied display a message "that wear location is currently occupied."
You should allow a player to issue a remove command as well such as "remove bracelet" or "rem bra" which should take the bracelets of power out of their equipment list and place it in their inventory.
13. Stats handling
At the end of each mud "tick" (about a minute of normal time) the player's health stats (hp, mana, move) should increase by floor(governing_stat/10) points. Where the governing stat for hp is constitution, the governing stat for mana is wisdom and the governing stat for moves is dexterity.
14. Make / CMake build files
You must write a Makefile or a cmake file to be used with compiling your program. You must compile each object file separately and then link them together. You must pass the appropriate flags into the appropriate phase of compilation. Please note that the requirement of -Wall -pedantic -std=c++14 or -std=c++1z still holds for this assignment. Please ensure your program compiles and runs without problem on titan/jupiter/saturn as that is where we will test your program.
15. Report - You will need a class diagram of your final program. You will also need to justify any design decisions you have made. This means your report needs to be reasonably substantial - you should do some research to back up your decisions and be able to talk about the design patterns you have used, or other theoretical bases for your final design. "I thought this was the right thing to do" is not a justification - you need to give me the theory behind it. This report must be submitted as a pdf to the turnitin submission box provided in blackboard by the due date.
- Compile errors and warnings - a 5 marks deduction for compiler warnings, a 10 mark penalty for small compiler errors that your marker can fix quickly. If we cannot get your program to compile, your program will get a maximum of 40% of the marks available for the assignment.
- Unnecessary use of C functions
- Use of pointers for memory allocation with new / delete - use unique_ptr / shared_ptr when you need to but it is better to allocate objects on the stack when you can.
- Fatal runtime errors such as segmentation faults, bus errors, infinite loops, etc. Sections affected will get a maximum of 40%. If there are components we cannot run due to fatal runtime errors then those sections will also get a maximum of 40% of the marks available for that section even if the code is correct. It should be runnable and testable.
Extensions of Time
You may apply for an extension of time by emailing the instructor at least one week before the due date but please note that I will only give extensions for extenuating circumstances. Common requests for extensions such as "My hard disk crashed and I lost all my work." won't be accepted and we advise you to keep an up to date backup of all relevant source files. Likewise, applications for extensions based on working overtime or having a heavy study load will not be granted.
Attachment:- cplus.zip