|
|
c m s c 214
f a l l 2 0 0 1 |
Read the project 1 FAQ (currently empty), to see changes in the description.
Files are located in the "Proj/P1" directory of the posting account. More files will be added later on. Copy these files to a P1 directory in your own account.
Write a makefile, too.
However, this is only a "warm-up" project.
A common goal is to reach a certain level of a dungeon, where there might be a prize, or perhaps to kill a dragon. In the process, you tried to make your character rich and powerful, at least, more than other characters in the dungeon. Usually, characters would kill mythical beasts, and perhaps other characters in the game.
These games often permitted you to send messages to other people playing the game (thus "multi-user"). Therefore, you could work as a team with other people.
You will write a simple version of a MUD. In this version, there will be Players. A group of players is stored in a PlayerList which represents a team. The list of all teams is in a class AllTeamsList. A PlayerList contains a doubly linked list of Nodes which itself contains a pointer to a Player. Player will be dynamically allocated.
You will define most of the public methods for a Player. We provide you with data members.
The input will be written in something that resembles XML. XML is a markup language, which resembles HTML, except you can invent your own tags. Fortunately, it's rather easy to read XML, so you don't have to learn much to read it, especially, if you can already read HTML.
This contains a vector of PlayerList. A PlayerList is a team of players.
Used to parse the commands. The commans are part of an input file.
An iterator which can move through a PlayerList object (without modifying the Player objects within the list).
Parses the information about each room of a dungeon (using a static parse() method). Stores this information into a vector of Rooms.
An individual item that either a player can carry or lies in a room. The item consists of a name, a unique 4 digit ID number, its weight, and its worth, in gold pieces.
An iterator which can move through a PlayerList object (with the ability to modify the Player objects within the list).
Contains the first and last name of each player.
A node in a doubly linked list. Has a prev and next data member. Also holds a Player as data member.
Contains information about a player, including name, amount of gold pieces the player holds, the strength, and the amount of treasure. A PlayerList
contains a doubly linked list of Node. Each Node has a Player as data member.
Parses the part of input file that contains information about each player (using a static parse() method). Produces a vector of Players.
This is a "team". Consists of the name of a team, which is a C++ string, and a pointer to the first and last elements of doubly linked list of Nodes.
Contains the prototype of a function, readTag which reads an XML tag.
Parses the information about a team (which is really a PlayerList) from the input file, using a static parse() method.
Classes which I wrote copy cons/assign op/destructor ---------------------------------------------------- .....list of files....
<lower> ::== "a" | "b" | "c" | "d" | "e" | "f" |
"g" | "h" | "i" | "j" | "k" |
"l" | "m" | "n" | "o" | "p" |
"q" | "r" | "s" | "t" | "u" |
"v" | "w" | "x" | "y" | "z"
<upper> ::== "A" | "B" | "C" | "D" | "E" | "F" |
"G" | "H" | "I" | "J" | "K" |
"L" | "M" | "N" | "O" | "P" |
"Q" | "R" | "S" | "T" | "U" |
"V" | "W" | "X" | "Y" | "Z"
<alpha> ::== <lower> | <upper>
<digit> ::== "0" | "1" | "2" | "3" | "4" |
"5" | "6" | "7" | "8" | "9"
<digits> ::== [ <digit> ]+
<empty> ::==
<endl> ::== "\n"
<left> ::== "<"
<right> ::== ">"
<tag> ::== <left> <word> <right>
<under> ::== "_"
<blank> ::== " "
<blanks> ::== [ <blank> ]+
<mblanks> ::== [ <blank> ]+
<sep> ::== <blank> | <endl>
<seps> ::== [ <blank> ]+
<mseps> ::== [ <blank> ]*
<word> ::== <alpha> [ <alpha> | <digit> | <under> ]*
<start> ::== <dungeon><seps><teamlist><seps><playerlist><seps>
<commands><mseps>
<dungeon> ::== <left> "dungeon" <right> <seps>
<mseps> [ <room> ]+ <mseps> <seps>
<left> "/dungeon" <right>
<room> ::== <left> "room" <right> <seps>
<onename> <seps>
<listofitems> <seps>
<left> "/room" <right>
<onename> ::== <left> "name" <right> <seps>
<word>
<left> "/name" <right>
<listofitems> ::== <left> "itemlist" <right> <seps>
<oneitem> [ <seps> <oneitem> ]+
<left> "/itemlist" <right>
<oneitem> ::== <left> "item" <right> <seps>
<a_name> <seps> <an_id> <seps> <a_weight> <seps> <a_cost>
<seps>
<left> "/item" <right> <seps>
<listofteams> ::== <left> "tlist" <right> <seps>
[ <team_name> <seps> ]+
<left> "/tlist" <right>
<team_name> ::== <left> "name" <right> <seps>
<word> <seps>
<left> "/name" <right>
<list_of_players> ::== <left> "plist" <right> <seps>
[ <oneplayer> <seps> ]+
<left> "/plist" <right>
<oneplayer> ::== <left> "player" <right> <seps>
<onename> <seps> <onestrength> <seps>
<onegold> <seps>
<left> "/player" <right>
<onestrength> ::== <left> "strength" <right> <seps>
[ <digit> ]+ <seps>
<left> "/strength" <right>
<onegold> ::== <left> "gold" <right> <seps>
[ <digit> ]+ <seps>
<left> "/gold" <right>
<list_of_cmmds> ::== <left> "cmmdlist" <right> <seps>
[ <onecommand> <seps> ] +
<left> "/cmmdlist" <right>
<onecommand> ::== <left> "cmmd" <right> <seps>
<cmmd_choice> <seps>
<left> "cmmd" <right>
<firstname> ::== <word>
<lastname> ::== <word>
<personname> ::== <firstname> <seps> <lastname>
<teamname> ::== <word>
<roomname> ::== <word>
The entire list of commands for <cmmd_choice> is listed
below.
p1 < sample.inputOf the four parts in the input file, only the last part--the list of commands, causes your program to produce some output.
The following is a list of the commands your program must support. Each of these are one of the choices provided by cmmd_choice. All outputs will be listed in the output BNF.
JOIN_TEAM <seps> <personname> <seps> <teamname>
Initially, a player does not belong to any team. A player can join
any valid team. A player may switch from one team to another team.
You may assume that the first name and last name in the input
files are names found in the player list from the input file.
Furthermore, you may assume they join a valid team.
WHICH_TEAM <seps> <personname>
Prints out the current team of a player, if the player is on
a team. You may assume valid first name and last name.
GOTO_ROOM <seps> <personname> <seps> <roomname>
Have player with <personname> go to room <roomname>.
You may assume both the person's name and the room name is valid.
Initially, a person is not in any room. A person who goes to
a new room is assumed to leave the previous room they were in.
DISP_ROOM <seps> <roomname>
Display the contents of the room. This includes the items
in the room as well as the people in the room. You may assume
the room name is valid.
LIST_TEAM_MEMBERS <seps> <teamname>
Prints out all the team members in order of the player's
names. This is sorted by last names. If two players have the
same last name, then sort by the first name. For example,
if you have "Ani Jain" and "Ankan Jain", then "Ani Jain" would
be printed before "Ankan Jain" because the last names are the
same, but "Ankan" comes after "Ani".
The name comparisons are case sensitive, therefore "John" and "john" are considered different names. Let the built-in comparison operators for C++ strings handle the comparison. The same rules apply to team names (it's case sensitive).
You may assume that the team name is valid. Use an iterator to print out the names in forward order.
LIST_TEAM_MEMBERS_REV <seps> <teamname>
Prints out all the team members in reverse order of the player's
names. It should print in the reverse order from the previous
command. Use an iterator to print out the names in reverse order.
ADD_GOLD <seps> <teamname> <seps> <digits>
Give each member of the team the additional amount of gold specified
by <digits>. You may assume this number is positive.
PICKUP <seps> <personname> <seps> <itemname>
Have the person attempt to pick up the item. If the item
is not in the room, an error message should be printed. If
the item is too heavy (more than the person can carry, given
items they are already carrying), an error message should
be printed. If there are two items with the same name, then
the person should attempt to pick up the item with the smallest
ID number. For example, if there are two "rings",
the first with ID 1000 and the second with ID 2000, the person
should always attempt to pick up the one with ID 1000 since it
has the smaller ID.
DROP <seps> <personname> <seps> <itemname>
The person will attempt to drop an item in the current room.
You may assume the person is valid. However, they may not necessarily
be carrying the item mentioned. If the person carries two items
of the same name, then they should drop the one with the smaller
ID number.
DISP_TREASURE <seps> ( <personname> | <teamname> )
Display the treasure of a single player, or the treasure of
all members of a team. You may assume that a team never has
the same name as the first name of a player.
<word> [ <seps> <word> ]+
Any other command is considered invalid. Such a command
may contain one or more invalid words. You must ignore these
so that the next command can be read properly.
<join_team_out> ::== "[JOIN_TEAM] "
( <join_team_switch> | <join_team_first> |
<join_team_stay> ) <endl> <endl>
<join_team_switch> ::== "(" <playername> ") leaves team ("
<teamname> ") and joins team ("
<teamname> ")"
<join_team_first> ::== "(" <playername> ") joins team ("
<teamname> ")"
<join_team_stay> ::== "(" <playername> ") is already on team ("
<teamname> ")"
Initially, a player is not on any team. When
the player joins a team, the output should be <join_team_first>.
If the player is on a team, and switches to a different team, the output
should be <join_team_switch>. If a player is on a team
and attempts to join the team they are on, the output should be
<join_team_stay>.
<which_team_out> ::== "[WHICH_TEAM] "
( <which_team_valid> |
<which_team_invalid> ) <endl> <endl>
<which_team_valid> ::== "(" <playername> ") is on team ("
<teamname> ")"
<which_team_invalid> ::== "(" <playername> ") is not on any team"
If a player is on a team, print the message with
<which_team_valid>. If the player isn't on a team, print
the message with <which_team_invalid>.
<goto_room_out> ::== "[GOTO_ROOM] "
( <goto_room_switch> | <goto_room_first> |
<goto_room_stay> ) <endl> <endl>
<goto_room_switch> ::== "(" <playername> ") leaves room ("
<roomname> ") and enters room ("
<roomname> ")"
<goto_room_first> ::== "(" <playername> ") enters room ("
<roomname> ")"
<goto_room_stay> ::== "(" <playername> ") is already in room ("
<roomname> ")"
Initially, a player is not in any room. If they go to their
first room, use <goto_room_first>. If they
are switching from one room to a different room, use
<goto_room_switch>. If they are in one room and
attempt to go to the same room, use <goto_room_stay>.
<disp_room_out> ::== "[DISP_ROOM]" <endl>
<list_of_items_out> <endl> <endl>
<list_of_players_out> <endl> <endl>
<list_of_items_out> ::== "List of items in room: (" <roomname>
")" <endl>
"---------------------------" <endl>
( <no_items> | <item_list_out> )
<no_items> ::== " NO ITEMS IN ROOM"
<item_list_out> ::== <one_item_out> [ <endl> <one_item_out> ]+
<one_item_out> ::== "(" <digit> ") Name: " <word>
" Id: " <digit>{4,4}
" Weight: " <digits> " Cost: " <digits>
<list_of_items_out> ::== "List of players in room: (" <roomname>
")" <endl>
"---------------------------" <endl>
( <no_players> | <player_list_out> )
<no_items> ::== " NO PLAYERS IN ROOM"
<player_list_out> ::== <one_player_out> [ <endl> <one_player_out> ]+
<one_player_out> ::== " (" <digit> ") " <lastname> ", " <firstname>
<list_team_out> ::== "[LIST_TEAM_MEMBERS]" <endl>
( <team_list_out> | <no_team> ) <endl> <endl>
<no_team> ::== " NO MEMBERS ON THIS TEAM"
<team_list_out> ::== <player_list_out>
List the members of a team in sorted order by team member's names.
If the team has no members, use <no_team>. If it has
team members, use <player_list_out>.
<player_list_out> is defined in the previous section.
<list_team_rev_out> ::== "[LIST_TEAM_MEMBERS_REV]" <endl>
( <team_list_out> | <no_team> ) <endl> <endl>
List the members of a team in reverse sorted order by team
member's names. If the team has no members, use
<no_team>. If it has team members, use
<player_list_out>. <player_list_out>
and <no_team> are defined in earlier sections.
<add_gold_out> ::== "[ADD_GOLD] Each player on team ("
<teamname> ") receives ("
<digits> ") additional gold pieces"
<endl> <endl>
There is no error message since the command assumes
valid team names for inputs.
<pickup_out> ::== "[PICKUP] "
( <pickup_not_in_room> | <pickup_no_item> |
<pickup_too_heavy> | <pickup_success> )
<endl> <endl>
<pickup_not_in_room> ::== "(" <playername>
") is not in any room"
<pickup_no_item> ::== "(" <playername> ") " could not pick up ("
<itemname> "): (" <itemname> ") not in room"
<pickup_too_heavy> ::== "(" <playername> ") " could not pick up ("
<itemname> ":id " <digits>{4,4}
"): (" <itemname> ") too heavy"
<pickup_success> ::== "(" <playername> ") " successfully pick up ("
<itemname> ":id " <digits>{4,4}
") from room (" <roomname> ")"
Initially, the player is not in any room. So, there's no way
for a player to pick up an item. Use <pickup_not_in_room>.
If the item is not in the room, but the player is in a room, then
use <pickup_no_item>. If the item is in the room,
but it is too heavy to pick up that item (in addition to the other
items the player carries), use <pickup_too_heavy>.
Otherwise, use <pickup_success> to indicate the
player successfully lifted the item from the room.
<drop_out> ::== "[DROP] " ( <drop_not_carry> | <drop_success> )
<endl> <endl>
<drop_not_carry> ::== "(" <playername> ") is not carrying ("
<itemname> ")"
<drop_success> ::== "(" <playername> ") " dropped ("
<itemname> ":id " <digits>{4,4}
") in room (" <roomname> ")"
A player can only drop something that they are carrying. If they
aren't carrying the item, use <drop_not_carry>.
If they are carrying the item, use <drop_not_success>.
<disp_treasure_out> ::== "[DISP_TREASURE]"You can display treasure for a player or for a whole team. If you display for a player, then use <disp_player> otherwise, use <disp_team>. If there are no team members, you should use <has_no_members>.( <disp_player> | <disp_team> ) <endl> <endl> <disp_player> ::== " Treasure for player (" <playername> ")" <endl> " -----------------------------" <endl>" ( <player_treasure> | <player_notreasure> ) <endl> " -----------------------------" <endl>" " (" <playername> ") carries " <digits> " gold pieces" <player_notreasure> ::== " NO TREASURE" <player_treasure> ::== " " <one_item_out> [ " " <endl> <one_item_out> ]+ <disp_team> ::== "Treasure for team (" <teamname> ")" <endl> "-----------------------------" <endl>" ( <has_members> | <has_no_members> ) <has_members> ::== " Total gold: " <digits> <endl> <endl> ( <disp_player> [ <endl> <endl> <disp_player> ]+ ) <has_no_members> ::== " THIS TEAM HAS NO MEMBERS"
<invalid> ::== "[invalid] Command (" <word> ") is INVALID" <endl> <endl>
Any other one word "command" that is not listed, is considered invalid.
You should print out this error message in response.
Returns true, if the team is not in the list (i.e., no team with the same name as the team, teamIn) is in list. If not in the list, this wil add to the vector, and put it in sorted order based on team name (which means sorted in case-sensitive lexicographic order). Use STL sort algorithm to sort the teams.
Returns a pointer to the PlayerList object with the same name as teamName. If the team doesn't exist, return NULL.
Returns true if a team with the name, teamName, in list. Otherwise, return false.
Does a "sensible" print, so it may be used in the project somewhere. Used to implement operator<<.
You must use the private data members provided in the file, and implement the methods shown. In addition, you should write helper methods.
Add any sensible .h files at the top.
Unlike the other parsers, this parser is an object (it's not static). Like the other parsers, it does parse the fourth part of the input file (the list of commands) and runs each command as it processes it.
Initialize first and last to firstIn and lastIn, respectively. curr can be set to anything.
Sets curr to first.
Sets curr to last.
Sets curr to next node, even if it is NULL. Returns true if curr can be moved. If curr is NULL, then don't move curr, and return false.
Sets curr to previous node, even if it is NULL. Returns true if curr can be moved. If curr is NULL, then don't move curr, and return false.
Returns true, if curr points to a non-NULL node.
Returns Player object pointed to by curr. You can allow this function to "core dump", if curr is NULL. This means it's up to the user to make sure to only call this method when curr is pointing to something meaningful.
These files have been provided to you. Use DungeonParser.cpp to help you determine how to parse the other three parts of the input file. Note: these use static functions.
Each of these methods should be straight-forward to implement. Two items will be considered "equal" (determined using ==), if they same name and ID. One item, X, will be considered less than another item, Y, (using operator<) if either X's name precedes Y's name lexicographically, or if the two names are the same (using string's ==), then X is less than Y if X's ID is less than Y's ID.
The print() method should do something reasonable for printing output, and should be used by operator<< to print (don't make operator<< a friend of Item).
See ConstListIterator.h. The only difference between this class and ConstListIterator is getCurrent(). which returns a reference in ListIterator. Again, you can allow the method to core dump if curr is NULL.
Add const whereever it is appropriate.
These two files have been provided to you. All you have to do is to determine whether to write the copy constructor, destructor, and operator=. You shouldn't have to modify any methods.
These methods should be quite similar to IntNode.h. There should be minor modifications.
This will probably be the most difficult class (outside of the doubly linked list) to write. This is because the player must maintain a large amount of information. You may define the methods as you see fit. However, you are not permitted to return treasure, as a vector, by value, reference, or pointer. You can return a particular element of the vector if you want (the use may, for instance, specify an index).
Returns true if the player wasn't originally carrying the item. Add it to player's treasure (which should be sorted). Returns false, if player already carries item. You may add reasonable parameters to this method.
Returns true if the player is carrying the item, and then removes that item from treasure. Returns false, if player is not carrying the item. You may define reasonable parameters for this method.
Returns number of items carried by the player. Initially, it's 0.
Returns number of items carried by the player.
This parses the third part of the input file, which is the list of players. parse() is a static method. This method will return back a vector of Players. Use DungeonParser.h to guide you on how to write this class.
This represents a team. It consists of a team name, a pointer to the first and last node of a doubly linked list of Node's. It also contains the list size, which is the number of nodes in the list. You should not add any dummy nodes to this list, and it should not be circular linked list.
There are the methods it should support.
This is the constructor.
Adds a player to the list, only if there is not a player by the same name in the list. If successfully added, return true, otherwise return false.
Remove player from the list, but only if there is a player by the name, nameIn, in the list. If successfully removed, return true, otherwise return false.
Remove all elements from the list, and set the list size to 0.
Return the size (number of nodes) in the doubly linked list.
Return a list iterator. This iterator may be used to modify the Player objects in the doubly linked list.
Return a list iterator. This iterator may be used to manipulate the Player objects in the doubly linked list, but only because it returns copies of objects.
Return the team name.
Returns true, if the current team's name is "smaller" than the team name referenced by other. Use the < operator from string to do this comparison.
Returns true, if the current team's name is the same as the team name referenced by other. Use the < operator from string to do this comparison.
Pick a sensible way to print the list. Use this method to implement the output operator.
Use either a predecessor or successor locate to fine a player with this name. Return true, if such a player exists in the list, and false otherwise. You must use this method in add() and remove().
This file only has the standalone function, readTag. This is used by DungeonParser.cpp and should be used by all other files ending in Parser.
This class represents a room. You can add an item and remove an item to the room. This represents the items residing in the room. You must determine reasonable parameters for the remove method. getName() returns the name of the room.
This parses the second part of the file. It produces a AllTeamsList object as a result. This resulting object is a vector of PlayerList. Recall a PlayerList is a doubly linked list of Players. Again, the parse() method is static.
You must use this main.cpp. This contains main(). You should not need to make any modifications to this file.
If it is NOT necessary to implement these methods, then do NOT implement. By necessary, this means the class would behave incorrectly (especially, if, say, only the destructor were defined).
If you do choose to write these three methods, you should write the following helper methods.
all : p1 driver1 driver2 p1 : ...stuff... driver1 : ...stuff... driver2 : ...stuff...p1 must be the name of the executable that runs Part 2. It should not be called a.out or P1 or p1.x or p1.exe. It should simply be p1. If you fail to observe this, you will lose at least 30 points on the project.
driver1 should be your test driver executable for RankingList.cpp. driver2 should be your test driver for AllRankingLists.cpp.
I don't particular care what the drivers do, as long as they reasonably test the the classes mentioned. The goal is to make sure you write test drivers to test your code.
If your executable fails to produce an output that matches the primary output, then you will be unable to submit your project. You must then fix your code until it does match.
To submit, run the following command:
submit p1.tar 1 SUBMIT
where p1.tar can be replaced by whichever tar
file you have, and 1 refers to the current project,
and SUBMIT refers to a subdirectory that is created
by the submit program. You may name this subdirectory
any name you want. We use "SUBMIT" as a sample.
This directory should NOT exist prior to running the "submit" command. Either remove the directory and its contents if it does exist, or give it a new name.
If you forget the syntax, simply type "submit", hit return, answer the academic integrity question, and it will tell you the syntax.
Also, if you wish to find out why your project did not submit, go into the temporary directory just created. Look for a file called "214_p1_diff_file". That file will contain the diff between your output and the primary output. It should help you determine what went wrong.
VIOLATIONS OF ACADEMIC HONESTY INCLUDE:
IT IS THE RESPONSIBILITY, UNDER THE UNIVERSITY HONOR POLICY, OF ANY STUDENT WHO LEARNS OF AN INCIDENT OF ACADEMIC DISHONESTY TO REPORT IT TO THEIR INSTRUCTOR.