|
|
c m s c 214
f a l l 2 0 0 1 |
Late days will be 5 points off (1 day late), 10 points off (2 days), and 15 points off (3 days).
This was posted November 8, 2001.
If you do not pass the primary by this date, you will be given a grade of F, regardless of any other grades you may have received in the class. This policy only applies to project 4.
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.
You will also make Player into an abstract base class and derive four classes from this: Mage, Wizard, Fighter, and Knight.
You will also make Item into an base class (but not abstract) and derive two classes from this: Ring and Sword.
You should put as many public methods into Player as possible, so that derived classes don't duplicate code.
Player should store a new private data member called experiencePts which is initially set to 0. As a player gains experience points, they will move from novice to expert.
Experience points is computed as follows: it is the number of gold pieces plus the worth of all treasure being carried plus the experience gained from disabling a robot. The experience gained from disabling a robot will be the total amount of treasure that the robot was holding onto (the player can gain even more experience points, by picking up the treasure that the robot has).
While a player is still a novice, dropping treasure will decrease the experience points of a player. However, once a player becomes an expert, there's no need to track the players experience points. It will stay fixed at the value when the player become an expert.
Here are some more details for each class.
getTitle() for a Fighter will return "Fighter" as a string.
getPower() for a Fighter will return "punches" if he carries no weapon. A Fighter may carry only one sword. If he carries a sword, then the sword will have a getPower() method, and the fighter will use that when attacking robots.
clone() will produce a dynamically allocated copy of type Fighter *. However, clone() must have a return type of Player * (it's inherited, remember?).
isExpert() const will return false, since a fighter is a novice.
A Fighter will have one private data member, a Sword * pointer. It will either be NULL, or it will be a pointer to a dynamically allocated sword. Even though it won't really be used in practice, the destructor for the Fighter must deallocate the sword object, if it exists.
A Fighter can only carry one sword at a time. He must drop the sword, then pick up a new sword, to get a new sword.
getTitle() for a Knight will return "Knight" as a string.
getPower() for a Knight will return "kicks" if s/he carries no weapon. A Knight may carry up to two swords. If s/he carries a sword, then the sword will have a getPower() method, and the knight will use that when attacking robots. If s/he carries two swords, then s/he will attack a robot twice, once with each sword.
clone() will produce a dynamically allocated copy of type Knight *. However, clone() must have a return type of Player *.
isExpert() const will return true, since a knight is an expert.
A Knight will have one private data member, an array of two Sword * pointer. You may choose how to store the two swords (they will point to dynamically allocated swords). Even though it won't really be used in practice, the destructor for the Knight must deallocate the sword object, if it exists.
getTitle() for a Mage will return "Mage" as a string.
getPower() for a Mage will return "zaps" if s/he isn't wearing a ring. A Mage may carry only one ring. If s/he is wearing a ring, then the ring will have a getPower() method, and the mage will use that power when attacking robots.
clone() will produce a dynamically allocated copy of type Mage *. However, clone() must have a return type of Player *.
isExpert() const will return false, since a mage is a novice.
A Mage will have one private data member, a Ring * pointer. It will either be NULL, or it will be a pointer to a dynamically allocated ring. Even though it won't really be used in practice, the destructor for the Mage must deallocate the ring object, if it exists.
A Mage can only carry one ring at a time. S/he must drop the ring, then pick up a new ring, to get a new ring.
getTitle() for a Wizard will return "Wizard" as a string.
getPower() for a Wizard will return "hurls fireballs at" if s/he carries no rings. A Wizard may carry up to two rings. If s/he carries a ring, then the ring will have a getPower() method, and the mage will use the powers of the rings when attacking robots. If s/he wears two rings, then s/he will attack a robot twice, once with the power of each ring.
clone() will produce a dynamically allocated copy of type Wizard *. However, clone() must have a return type of Player *.
isExpert() const will return true, since a wizard is an expert.
A Wizard will have one private data member, an array of two Ring * pointer. You may choose how to store the two rings (they will point to dynamically allocated ring). Even though it won't really be used in practice, the destructor for the Wizard must deallocate the ring objects, if it exists.
They will both have a data member called power (a string), which will be the phrase used when attacking robots.
Think of this as "just in time" project descriptions!
You will need to redo the part of the parser which reads in Items. In particular, rings and swords now have more information than other items.
Here are examples:
<name> CloakRoom </name>
<itemlist>
<item>
<name> ring </name>
<type> lightning </type>
<power> sends lightning bolts at </power>
<damage> 30 </damage>
<id> 2001 </id>
<weight> 20 </weight>
<cost> 300 </cost>
</item>
<item>
<name> potion </name>
<id> 2002 </id>
<weight> 15 </weight>
<cost> 350 </cost>
</item>
<item>
<name> sword </name>
<type> giant </type>
<power> clubs </power>
<damage> 15 </damage>
<id> 2003 </id>
<weight> 105 </weight>
<cost> 1000 </cost>
</item>
</itemlist>
</room>
Both rings and swords will have additional parts that
other items do not. In particular, there is a UNIQUE type
to the sword (thus, only one "giant" sword). power
refers to a phrase that will be used when attacking a robot.
damage refers to the number of points of damage on
a robot, which you can assume is positive.
As a suggestion, you may want to write a static "parse()" function for Item, Ring, and Sword and have the parseDungeon() call those parse functions. The main drawback with static functions is that they can't be virtual since there's no object to refer to. Nevertheless, as long as they are public, you can still have a derived class static function refer to a base class static function (or vice versa, but that's unusual).
For example, if Bill Cheng has 1000 (now 1500!) pieces of gold, and his type is "Fighter", he will actually start off as a Knight.
The second key issue is how to store the information about players. Currently, this information is stored in an ArrayList of Players. That will have to change to store an ArrayList of Player *, to correctly handle polymorphishm.
The question is how to use the clone() feature of ArrayList. Right now, I think I may have a clone() feature for a Team, which creates a copy of Team, including a copy of Team. This copy of Team will be a deep copy, meaning it will call clone() on all the Nodes.
For this particular project, clone() is far from useful because most people have used pointers in their Teams that point to Players in the ArrayList of Players. This means the ArrayList is maintaining the Players, not the Team.
Ideally, I would have Team contain pointers to Players which is dynamically allocates, and thereofre, Team would be in charge of making copies of Players.
So, for now, you can keep the Array List of Player * pointers (recall they have to be pointers so they can store Fighters, etc). I will work out a way to make Team create a copy just for the sake of making clone() useful, but the normal copy constructor merely copies pointers for Players (thus, does a shallow copy when it comes to Player * pointers). (Those details will come in a further task).
Here is an example of a player list feom the input file. If
Samir Khuller had 1000 (now, 1500!!) gold pieces, his type would still be
listed as a Fighter, but you would create a Knight instead.
<plist>
<player>
<name> Samir Khuller </name>
<type> Fighter </type>
<strength> 400 </strength>
<money> 100 </money>
</player>
<player>
<name> Leana Golubchik </name>
<type> Mage </type>
<strength> 350 </strength>
<money> 200 </money>
</player>
What changes do you need to make ArrayList work with pointers? Fortunately, only one small change. The class can stay the same, but you should write sort214Ptr which will sort arrays of pointers (while using sort214Ptr with arrays of objects). The code should be very similar, but dereference the pointer so you compare objects. However, swap pointers (instead of copying objects).
Anything storing Player objects (such as the BST) will now need to store Player * pointers.
Thus, instead of saying:
[ATTACK] (Bill Gasarch) hurls icebolts at (Marty Cyborg)
you print:
[ATTACK] Mage Level 0 (Bill Gasarch) hurls icebolts at (Marty Cyborg)
Thus, the title is "Mage", followed by the "Level", followed by
the level number. Robots, on the other hand, still print the same
way.
Name: ring Id: 2001 Weight: 20 Cost: 300 Type: lightning Power: "sends lightning bolts at"Both Rings and Swords will print Type and Power. The phrase for the power is surrounded by single quotes.
Other items (potions, scrolls, etc) will still print in the usual fashion, as they did before.
<join_team_out> ::== "[JOIN_TEAM] "
( <join_team_first> |
<join_team_stay> ) <endl> <endl>
<join_team_error> ::== "Robot (" <robotname>
") can only join (TeamRobot)"
<join_team_first> ::== "Robot (" <robotname> ") joins team ("
<teamname> ")"
<join_team_stay> ::== "Robot (" <robotname> ") is already on team ("
<teamname> ")"
(11/24) Once a robot is disabled, there will be no commands to cause a robot to join a team. Print the message <join_team_error> if a robot is made to join either a Player team or a non-existent team. If a robot is not on a team, then joins "TeamRobot", print <join_team_first>. If a robot is currently on "TeamRobot" and tries to join it again, print <join_team_stay>.
Initially, robots are already on a team (TeamRobot). This is UNLIKE players, which do not start off on any team. Thus, if you ask which team a robot is on, they start on "TeamRobot". They may not join any other team (NEW! 11/18).
This command won't be tested with robots except on the extra credit. The command is only modified slighty with players (using the title and level above)
<which_team_out> ::== "[WHICH_TEAM] "
( <which_team_valid> |
<which_team_invalid> ) <endl> <endl>
<which_team_valid> ::== "(" <robotname> ") is on team ("
<teamname> ")"
<which_team_invalid> ::== "(" <robotname> ") is not on any team"
Again, this won't be tested with robots, except on extra credit.
(11/24) This command won't be tested on disabled robots. A robot initially starts on "TeamRobot". If a robot is on this team, then print <which_team_valid>, otherwise print <which_team_invalid>.
This command will not be tested on "TeamRobot" except on the extra credit.
(11/24) We won't test this command when there are disabled robots (technically, they should be removed from the team, but again, don't worry about it).
<pickup_special> ::== "[PICKUP] " ( <player_holds_max> | <player_wields> )
<player_holds_max> ::== <playername> " already wields " <number>
( "ring(s)" | "sword(s)" )
<player_wields> ::== <playername> " now wields: "
"(" <word> ( "ring)" | "sword)" )
[ "and (" <word> ( "ring)" | "sword)" ) ]?
[ <upgrade> ]?
<upgrade> ::== <playername> " is now upgraded to " <playername>
<playername> ::== [ "Mage" | "Wizard" | "Fighter" | "Knight" ]
" Level " <digits> " ("<firstname>
" " <lastname> ")"
If a player already holds the maximum allowable rings/swords and
it attempting to pick up another ring/sword, then
it prints <player_holds_max>, where it prints out 1 if the
player is a novice and 2 if the player is an expert. If the
player successfully picks up an ring, then it prints
<player_wields>.
Notice that <playername> has been redefined. It puts the title, the word "Level" and the level. The message is printed at the level prior to picking up the item.
After picking up the item, if a player then has 1500 points, then print the <upgrade> message.
If a player is picking up their weapon (Wizard picks up ring or Knight picks up a sword), they will pick it up in "queue" order. Thus, the first ring picked up by a Wizard becomes the first ring printed. The second ring picked up will be the second ring printed. If the first ring picked up is "ice" and the second is "fire", then a wizard wields a "(ice ring) and (fire ring)". If the wizard drop the ice ring, the fire ring becomes the primary ring, and if the wizard then picks up the ice ring, the wizard wields "(fire ring) and (ice ring)". At this point the first ring is the fire (it moved to the primary ring) and the ice ring is secondary.
(11/24) The <upgrade> line is only used in the extra credit when a player is upgraded from novice to expert. It is not printed if a player merely advances a level (for example, Fighter Level 0 to Fighter Level 1).
Notice the messages differ for "weapons" (for wizards/mages, the weapon is a ring; for fighters/knights, it's a sword) as opposed to normal items. When a "normal" item is picked it up, it uses the old BNF from Project 3 and earlier.
If a fighter/knight picks up a ring or a mage/qizard picks up a sword, then it will again, merely print the old message as it did in previous projects (thus, no adjectives).
Finally, if either a robot or players drop a ring or sword,
the message should say:
<drop_success> ::== "[DROP] " ( <robotname> | <playername> )
" dropped ("
<word> " " <itemname> ":id " <digits>{4,4}
") in room (" <roomname> ")"
<robotname> ::== "Robot " ("<firstname>
" " <lastname> ")"
where <word> refers to the "type" of the item dropped (thus,
"ice", "fire", etc.). This type is always printed for all rings
and swords.
There is a difference in the command to drop. If a player wants to drop their weapon (Mage/Wizard drops a ring or Fighter/Knight drops a sword), they must specify the type.
Thus the input command is:
DROP <firstname> <lastname> <itemname> <type>
The type would be left out if a Mage were dropping a sword
or a Fighter were dropping a ring. If a Wizard has two rings
and drops one, the remaining ring becomes the primary ring.
If a Knight has two swords and drops one, the remaining sword becomes
the primary sword.
If a wizard/mage drops a ring, it must print the type of the ring in front of the ring. If a fighter/knight drops a sword, it must print the type of the sword in front of the sword. Otherwise, dropping any other item does not print its type.
<disp_treasure> ::== " Treasure for player (" <playername> ")" <endl>
" ----*----*----*----*----*----*" <endl>
(<items>+ | " NO TREASURE"<endl> )
" ----*----*----*----*----*----*" <endl>
<playername> " wields : " <weapon> [ " and " <weapon> ]? <endl>
"and carries " <digits> " gold pieces " <endl>
"and has " <digits> " experience points" <endl><endl>
The gold pieces will be the actual gold pieces (not the total amount
of treasure held) carried by the player.
Normal items are printed as before. Rings and swords must additionally print "Type: " followed by the type, then "Power: " followed by the power.
Note: Rings carried by fighters/knights are part of the treasure (between the two dashed lines) as are swords carried by mages/wizards. However, rings carried by mages/wizards are not part of the displayed treasure. You can see this information displayed underneathe where it says a player "wields" certain rings or swords.
For a team, you display all players, using the BNF from above. The total gold will refer to the total gold pieces (and gold pieces only---not the worth of the treasure) carried by the entire team.
As extra credit, display the robot teams treasure. Since robots carry no gold, the total gold will be displayed as 0.
ATTACK <firstname> <lastname> <firstname> <lastname>where you give the player name first and the robot name second. We won't test for other combinations (i.e., we won't test for one players attacking another player, or a robot attacking either a robot or a player).
There are several possibilities.
<attack_cmmd> ::== "[ATTACK] " ( <robot_down> | <not_same_room> | <outside> |
<robot_is_hit> )
<robot_down> ::== <robotname> " is already disabled" <endl> <endl>
<not_same_room> ::== <playername> " and " <robotname>
" are not in the same room" <endl> <endl>
<outside> ::== <playername> " and " <robotname>
" are outside the dungeon" <endl> <endl>
<robot_is_hit> ::== <playername> <power> <robotname>
<endl>
( <robot_hurt> | <robot_disabled> )
<robot_hurt> ::== <robotname> " has " <digits>
" hit points " <endl> <endl>
<robot_disabled> ::== <robotname> " is disabled!" <endl>
<playername> " gains " <digits>
" experience points"<endl>
<playername> " has " <digits>
" experience points"<endl>
[ <upgrade> ]?
However, there are occasions when you want to copy the entire BST, including the Player objects to create a complete copy. For example, sometimes the BST will completely maintain those objects.
Under such circumstances, it's important to not only copy the Node, but also the Player object pointed to by the Node. Since those players can be fighters, mages, etc.
The command will only be applied to player teams.
Here's the command.
CLONE <teamname>
This must create a complete dynamic copy of the team, including
cloning all player objects. It will then do a PRINT_TREE function
(meaning it prints the tree). Then, you will destroy the entire
tree (all nodes, and players).
You must be very careful with the destruction. Calling the destructor may not be enough (since most people do not delete the player pointers, allowing the ArrayList to manage that).
TAs will check to see if the destructor is called. Here is
one way to code part of it at a higher level.
void CommandParser::cloneOp( Team *teamPtr )
{
Team *newTeamPtr = teamPtr->clone(); // clones
cout << "[CLONE]" << endl;
cout << " Tree for team: (" << newTeamPtr->getName() << ")" << endl;
cout << " ----*----*----*----*----*----*" << endl;
newTeamPtr->printTree(); // prints it up
cout << endl;
newTeamPtr->destruct(); // destroys everything, including player obj
delete newTeamPtr;
}
Notice that this will only destroy a copy of the player objects.
Thus, the original ones that may appear in an ArrayList should still
be there.
The output should be identical to a PRINT_TREE function.
submit p4.tar 4 SUBMITYour executable must be called exactly p4. Any other name will cause your program not to be submitted.
You can name the tar whatever you want. However, you should wait until a primary input and output have been posted. (If you submit before that, then if your project does not pass the primary input/output, you will receive a poor grade on the project, most likely a zero).
Please do not submit until a primary input and output have been posted. If you do, you may receive a 0 for failing to pass the primary.