CEng242 Homework 5
Due 15th May 2002
(Release 1.3)

In this homework you will implement a simple back-end for a strategy game simulation. You have the following class inheritance hierarchy to implement this simulation:
Inheritance Tree

Player class is the base class for all player types in the game. It contains the following definitions:

#define MAX(a,b) ((a)>(b)?(a):(b)) 
#define MIN(a,b) ((a)<(b)?(a):(b)) 

struct Hit {
        unsigned spell;
        unsigned strength; 
        Hit() { spell=strength=0;}
        Hit(unsigned p, unsigned s) { spell=p;strength=s;}
};

class Player {
        char name[40];
        char army[40];
protected:
        int moral;
        int health;
        virtual Hit shield(Hit h) { return h; }
        virtual Hit counterspell(Hit h) { return h; }
        virtual Hit hitpower(Hit h) { return h; }
        virtual Hit castspell(Hit h) { return h; }
public:
        virtual int isFellowship() { return 0;}
        virtual void addplayer(Player *) { throw(-1); }

        virtual Hit attackpower() {
                                        Hit t;
                                        t=hitpower(t);
                                        t=castspell(t);
                                        return t;
                                   }
        virtual Hit defensepower(Hit h) {
                                h=shield(h);
                                h=counterspell(h);
                                return h;
                                }
        Player(char n[], char a[]) {
                strncpy(name,n,40); name[39]=0;
                strncpy(army,a,40); army[39]=0;
                moral=health=10000;
        }
        virtual ~Player() { }
        virtual const char *getname() { return name;}
        const char *getarmy() { return army;}
        virtual Hit attack() ;
        virtual void defense(Hit);
        virtual void operator++() {
                                moral=MIN(10000,moral+250);
                                health=MIN(10000,health+250);
                          }
};

Each player has moral and health attributes determining its fighting power. Fighting is done in terms of Hits which are compositions of some spell power and physical hit power. When a player is requested to generate an attack, it should spend 100 of its moral and health where 100 is subtracted from its current moral and health respectively. If any of the attributes will be less than or equal to 0 by this loose it is set to 1 so that a player will not die in attacking. Otherwise Player will generate and return a hit determined by a call to attackpower().

Similarly when a player gets a hit h, defense(h) is called and this hit, spell and strength, is directly subtracted from its current moral and health respectively (spell reduces moral, strength reduces health). If any of moral or health is less than or equal to 0, player dies and reports it with raising an empty exception.

When a player takes rest, ++ operator can be called to increase his moral and health explicitly.

If you look at the attackpower() or defensepower() code, you will see some calls to abstract functions which are identity as default but implementable by the subclasses. Subclasses can provide different implementations for these methods to make the difference. shield() gets a hit from an enemy and reduces its strength according the specific defensive ability of the class. counterspell() similarly gets the hit and reduces the spell power of the hit. hitpower(h) gets a previous hit generated by a possibly previous function call and adds some to its strength according to abilities of the class. Similarly castspell() adds spell power to the hit. All these methods return the new (weakened or strengthened) hits.

You should inherit all player types drawn above from Player. All players require their constructors to pass name and army information to Player, and the implementations of some of the abstract methods and they require no additional member variable except the Fellowship class.

A Cavalry is a player with a horse and expert capability in defense. It can reduce the strength of a hit by 10% of its health. So shield() function will modify the parameter hit strength to MAX(strength-health/10, MIN(100, strength)). A Cavalry also has a capability of attacking so it can add 5% of its health to the hit strength with hitpower() method. So that the resulting hit strength will be incremented by health/20+1

A Swordsman is a player with a powerful sword. It can reduce the strength of a hit by 5% of its health in defense. shield() function will modify the hit strength to MAX(strength-health/20, MIN(100, strength)). A Swordsman is expert in attacking so it can add 10% of its health to the hit strength with hitpower() method. So that the resulting hit strength will be incremented by health/10+1

A Wizard is a player with ability of casting spells. He is harmless and helpless in terms of strength but his spells effects moral of the other players. It can reduce spell of a hit by 5% of its moral. So counterspell() function will modify the hit spell to MAX(spell-moral/20, MIN(100, spell)). A Wizard also has a capability of spell attack so it can add 10% of its moral to the hit spell with castspell() method. So that the resulting hit spell will be incremented by moral/10+1.

There are 3 player classes that are multiply inherited from 2 classes: Knight, Magicmaster and Magicsword. They should contain 1 copy of moral, health, name and army attributes. In addition to the constructor, they implement shield(), hitpower(), counterspell() and castspell() in a standard way. Any of these functions should get the hit result from same functions of its two superclasses and returns the best hit composition, (best spell and best strength) defense or attack. As a result, a multiply inherited player will have a superior powers to its base classes.

Fellowship is a different type of player which is actually a container of up to 5 players. Those players combine their power to get the best fighting performance. Fellowship does not use the inherited member variables from the player except army but keeps track of an array of Player pointers and number of players instead. When an attack is encountered the player with the best defensive power is chosen and it gets the hit. Best defensive power is determined according to the total positive score in defensive power after the hit.

When an attack is to be made, attacks of all fellowship members are summed and hit is determined accordingly. Fellowship implements a constructor which gets an army name and two player pointers who are the first 2 members of the fellowship (Fellowship(char [], Player*, Player *)).

Fellowship class overrides void addplayer(Player *) function to add a new member to the fellowship. Also it overrides const char *getname() method to list the member names of the fellowship instead of a single name. Returned string will be a comma separated [] bracketed list of member names. isFellowship() function will also be overwritten to return a non-zero value which will be the current number of players in the fellowship. So this function can be used to determine if a player is a fellowship and if a possible merge will overflow or not? Similarly ++ operator is converted into a call to ++ for all members.

In addition to players you will write an Arena class which will be the terrain of the simulation:

class Arena  {
        struct playerpos {        
                int x,y;
                Player *p;
        } players[100];
        int playercount;
public:
        Arena() { playercount=0; }
        ~Arena() { for (int i=0;i<playercount;i++) delete players[i].p;}
        void addplayer(Player *p, int px=0, int py=0) {
                        players[playercount].x=px;
                        players[playercount].y=py;
                        players[playercount++].p=p;
        }
        void dump();
        void cycle();
};

Critical part of the simulation is the cycle(). At each call of the cycle, all players in the array take turn one by one. At each turn of a player, name of the player is output and user is asked to enter a move option, one of the chars U, D, L, R up, down, right, left respectively and the player is moved to its new location. x increments in direction `right' and y increments in direction `up'. Moves to negative locations are silently ignored.

After the move is made, either the new location is empty or there is at most one player there. If the player belongs to a different army they fight each other. Fight starts from an attack() by the new comer. Returned hit is passed to the other by a defense() call. Then the seconds starts an attack. Fight continues infinitely until one of the player dies which is reported by a raised exception. When the exception is caught the defensing player is reported to be dead on standard output and it is deleted from the array and storage is deallocated by a delete.

When the new location contains a player from the same army, players are merged into a single Fellowship. If both players are standalone, they are merged into a new fellowship at the positin of the player with the turn. Otherwise, standalone player joins the fellowship or fellowships are merged. In all cases, the resulting fellowship will be in the position of the player in turn. Also in the name ordering standalone will be appended at the end of the fellowship. In case the fellowship has not enough space, and merge cannot fit in 5 player positions, move is silently ignored. After a merge, a Fellowship behaves like a single player.

Please note that the death of a fellowship member will be different. Death as a result of a defense() will be caught by the Fellowship::defense() and player is deleted from the fellowship instead. If fellowship shrinks into a single member fellowship, the pointer of that member is thrown in an exception so that the cycle can catch it, delete the fellowship and replace the fellowship position in the array by the member player so the fellowship is broken into a single player.

At the end of each cycle, all living players take a rest by the ++ call.

dump() will produce a report of all player names, armies and their current attack and defense power.

Input and Outputs:
When cycle is called, it will ask the next move for each player as:
Player "..." from "..." is at (..,..), next move?
When a player is attacking to the other:
Player "..." from "..." is hitting "..." from "..." with (...,...)
When a player dies:
Player "..." is death
When a fellowship is established:
Player "..." and "..." from "..." are forming a fellowship
When a player is joining a fellowship:
Player "..." from "..." is joining "..."
When two fellowships are merged:
Fellowships "..." and "..." from "..." are merging
When a fellowship is broken:
Player "..." from "..." stands alone now

For upcoming details follow the newsgroup.



1