#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#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 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);
                          }
		virtual ~Player() {}
};

class Cavalry:virtual public Player {
protected:
	virtual Hit shield(Hit h);
	virtual Hit hitpower(Hit h);
public:
	Cavalry(char n[],char a[]):Player(n,a){}
};

Hit Cavalry::shield(Hit h)
{
	h.strength=MAX(int(h.strength-health/10),MIN(100,h.strength));
	return h;
}

Hit Cavalry::hitpower(Hit h)
{
	h.strength+=health/20+1;
	return h;
}

class Swordsman:virtual public Player {
protected:
	virtual Hit shield(Hit h);
        virtual Hit hitpower(Hit h);
public:
	Swordsman(char n[],char a[]):Player(n,a){}
};

Hit Swordsman::shield(Hit h)
{
	h.strength=MAX(int(h.strength-health/20),MIN(100,h.strength));
	return h;
}

Hit Swordsman::hitpower(Hit h)
{
	h.strength+=health/10+1;
	return h;
}

class Wizard:virtual public Player {
protected:
	virtual Hit castspell(Hit h);
        virtual Hit counterspell(Hit h);
public:
	Wizard(char n[],char a[]):Player(n,a){}
};

Hit Wizard::counterspell(Hit h)
{
	h.spell=MAX(int(h.spell-moral/20),MIN(100,h.spell));
	return h;
}

Hit Wizard::castspell(Hit h)
{
	h.spell+=moral/10+1;
	return h;
}

class Knight:public Cavalry,public Swordsman {
protected:
	Hit shield(Hit h){h.strength=MAX(int(h.strength-health/10),MIN(100,h.strength)); return h;}
        Hit hitpower(Hit h){h.strength+=health/10+1; return h;}
public:
	Knight(char n[],char a[]):Cavalry(n,a),Swordsman(n,a),Player(n,a){}
};

class Magicmaster:public Cavalry,public Wizard {
protected:
	Hit shield(Hit h){	h.strength=MAX(int(h.strength-health/10),MIN(100,h.strength));
				return h; }
        Hit counterspell(Hit h) {h.spell=MAX(int(h.spell-moral/20),MIN(100,h.spell));
				return h; }

        Hit hitpower(Hit h) {h.strength+=health/20+1; return h; }
        Hit castspell(Hit h) {h.spell+=moral/10+1; return h; }
public:
	Magicmaster(char n[],char a[]):Cavalry(n,a),Wizard(n,a),Player(n,a){}
};

class Magicsword:public Swordsman,public Wizard {
protected:
	Hit shield(Hit h) {	h.strength=MAX(int(h.strength-health/20),MIN(100,h.strength));
				return h; }
        Hit counterspell(Hit h) {h.spell=MAX(int(h.spell-moral/20),MIN(100,h.spell));
				return h; }
        Hit hitpower(Hit h) {h.strength+=health/10+1; return h; }
        Hit castspell(Hit h) {h.spell+=moral/10+1; }
public:
	Magicsword(char n[],char a[]):Swordsman(n,a),Wizard(n,a),Player(n,a){}
};

class Fellowship:public Player {
	Player *myplyrs[5];
	int playercount;
	friend void mergefellowships(Player *,Player *);
public:
	Fellowship(char [],Player *,Player *);
	void addplayer(Player *);
	const char *getname();
	int isFellowship() { return playercount;}
	void operator++();
	Hit attack();
	void defense(Hit);
};

void mergefellowships(Player *f1,Player *f2)
{
	int i,j;
	Fellowship *ff1,*ff2;
	ff1=(Fellowship *)f1;
	ff2=(Fellowship *)f2;
	for(i=0;i<5;i++)
		if(ff1->myplyrs[i])
			for(j=0;j<5;j++)
				if(!ff2->myplyrs[j])
				{
					ff2->myplyrs[j]=ff1->myplyrs[i];
					ff1->myplyrs[i]=NULL;
				}
}

Fellowship::Fellowship(char newname[],Player *p1,Player *p2):Player("",newname)
{
	playercount=2;
	myplyrs[0]=p1;
	myplyrs[1]=p2;
	for(int i=2;i<5;i++)
		myplyrs[i]=NULL;
}

const char *Fellowship::getname()
{
	char *st;
	st=(char *)malloc(205*sizeof(char));
	st[0]='[';
	st[1]=0;
	for(int i=0;i<5;i++)
		if(myplyrs[i])
		{
			strcat(st,myplyrs[i]->getname());
			strcat(st,",");
		}
	st[strlen(st)-1]=']';
	return st;
}

void Fellowship::addplayer(Player *p)
{
	for(int i=0;i<5;i++)
		if(!myplyrs[i])
			myplyrs[i]=p;
		playercount++;
}

void Fellowship::operator++()
{
	for(int i=0;i<5;i++)
		if(myplyrs[i])
			myplyrs[i]->operator++();			
}

Hit Fellowship::attack()
{
	Hit h,h2;
	h.spell=h.strength=0;
	for(int i=0;i<5;i++)
		if(myplyrs[i])
		{
			h2=myplyrs[i]->attack();
			h.spell+=h2.spell;
			h.strength+=h2.strength;
		}
		return h;
}

void Fellowship::defense(Hit h)
{
	unsigned int min=2000,bestdefender;
	Hit h2;
	for(int i=0;i<5;i++)
		if(myplyrs[i])
		{
			h2=myplyrs[i]->defensepower(h);
			if(h2.spell+h2.strength<min)
			{
				min=h2.spell+h2.strength;
				bestdefender=i;
			}
		}

		try{
			myplyrs[bestdefender]->defense(h);
		}catch(double){
			cout<<"Player "<<myplyrs[bestdefender]->getname()<<" is death"<<endl;
			delete myplyrs[bestdefender];
			myplyrs[bestdefender]=NULL;
			playercount--;
			if(playercount==1)
				for(int j=0;j<5;j++)
					if(myplyrs[j])
					{
						cout<<"Player "<<myplyrs[j]->getname()<<" from "<<myplyrs[j]->getarmy()<<" stands alone now"
							<<endl;
						throw myplyrs[j];
					}
		}
}

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();
};

Hit Player::attack()
{
	if(moral<=100) 
		moral=1;
	else
		moral-=100;
	if(health<=100)
		health=1;
	else
		health-=100;
	return attackpower();
}

void Player::defense(Hit h)
{
	h=defensepower(h);
	if((int)h.spell>=moral || (int)h.strength>=health)
		throw 1.2;
	moral-=h.spell;
	health-=h.strength;
}

void Arena::dump()
{
	int i;
	Hit h;
	for(i=0;i<playercount;i++)
		if(players[i].p)
		{
			cout<<(players[i].p)->getname()<<" "
			<<(players[i].p)->getarmy()<<" at "
			"<"<<players[i].x<<","<<players[i].y<<"> ";
			h=players[i].p->defensepower(Hit(10000,10000));
			cout<<"("<<10000-h.spell<<","<<10000-h.strength<<") (";
			h=players[i].p->attackpower();
			cout<<h.spell<<","<<h.strength<<")"<<endl;
		}
}

void Arena::cycle()
{
	int i,j;
	char ch,myname[40];
	Player **k;
	Hit h;
	
	for(i=0;i<playercount;i++)
	{
		if(players[i].p)
		{
			cout<<"Player "<<(players[i].p)->getname()<<" from "<<(players[i].p)->getarmy()
				<<" is at "<<"("<<players[i].x<<","<<players[i].y<<"), next move?";
			cin>>ch;
			switch(ch){
                                                case 'U': players[i].y++; break;
                                                case 'D': players[i].y--; break;
                                                case 'L': players[i].x--; break;
                                                case 'R': players[i].x++; break;
                                                }			
			for(j=0;j<playercount;j++)
				if(players[j].p && players[i].x==players[j].x && players[i].y==players[j].y)
					if(strcmp((players[i].p)->getarmy(),(players[i].p)->getarmy()))
					{
							try{
								for(;;)
								{
									strcpy(myname,(players[j].p)->getname());
									k=&players[j].p;
									h=(players[i].p)->attack();
									cout<<"Player "<<(players[i].p)->getname()
										<<" from "<<(players[i].p)->getarmy()
										<<" is hitting "<<myname
										<<" from "<<(players[j].p)->getarmy()
										<<" with "<<"("<<h.spell<<","<<h.strength
										<<")"<<endl;
									(players[j].p)->defense(h);
									strcpy(myname,(players[i].p)->getname());
									k=&players[i].p;
									h=(players[j].p)->attack();
									cout<<"Player "<<(players[j].p)->getname()
										<<"	from "<<(players[j].p)->getarmy()
										<<" is hitting "<<myname
										<<" from "<<(players[i].p)->getarmy()
										<<" with "<<"("<<h.spell<<","<<h.strength
										<<")"<<endl;
									(players[i].p)->defense(h);
								}
							}catch(double db){
									if(db<1.5)
									{
										cout<<"Player "<<myname<<" is death"<<endl;
										delete (*k);
										*k=NULL;
									}
								}catch(Player *p1){
									delete *k;
									*k=p1;
								}
					}
					else
					{
						switch(ch){
                                                case 'U': players[i].y--; break;
                                                case 'D': players[i].y++; break;
                                                case 'L': players[i].x++; break;
                                                case 'R': players[i].x--; break;
                                                }
						if(!players[i].p->isFellowship() && !players[j].p->isFellowship())
						{
							Player *newp;
							char c[40];
							strncpy(c,players[i].p->getarmy(),40); c[39]=0;
							cout<<"Player "<<players[i].p->getname()<<" and "
								<<players[j].p->getname()<<" from "<<c
								<<" are forming a fellowship"<<endl;
							newp=new Fellowship(c,players[i].p,players[j].p);
							addplayer(newp,players[j].x,players[j].y);
							players[i].p=NULL;
							players[j].p=NULL;
						}
						else
							if(!players[i].p->isFellowship())
							{
								if(players[j].p->isFellowship()<5)
								{
									cout<<"Player "<<players[j].p->getname()<<" from "
										<<players[j].p->getarmy()<<" is joining "
										<<players[i].p->getname()<<endl;
									players[i].p->addplayer(players[j].p);
									players[j].p=NULL;
								}
							}
							else
								if(!players[j].p->isFellowship())
								{
									if(players[i].p->isFellowship()<5)
									{
										cout<<"Player "<<players[i].p->getname()<<" from "
											<<players[i].p->getarmy()<<" is joining "
											<<players[j].p->getname()<<endl;
										players[j].p->addplayer(players[i].p);
										players[i].p=NULL;
									}
								}
								else
									if(players[i].p->isFellowship()+players[j].p->isFellowship()<=5)
									{
										cout<<"Fellowships "<<players[i].p->getname()<<" and "
											<<players[j].p->getname()
											<<" are forming a fellowship"<<endl;
										mergefellowships(players[i].p,players[j].p);
										players[i].p=NULL;
									}
					}
		}
	}
	for(i=0;i<playercount;i++)
		if(players[i].p)
			(players[i].p)->operator++();
}

int  main()
{
	Arena Taklamakan;

	Taklamakan.addplayer(new Swordsman("Zivet","Burbons"),1,1);
	Taklamakan.addplayer(new Cavalry("Olget","Burbons"),2,2);
	Taklamakan.addplayer(new Knight("Fesget","Burbons"),1,2);
	Taklamakan.addplayer(new Magicmaster("Kaburgaet","Burbons"),2,1);
	Taklamakan.addplayer(new Magicsword("Izgaraet","Burbons"),3,1);

	Taklamakan.addplayer(new Swordsman("Kripgin","Klingons"),6,1);
	Taklamakan.addplayer(new Swordsman("Cargingin","Klingons"),6,2);
	Taklamakan.addplayer(new Knight("Dozlangin","Klingons"),7,2);
	Taklamakan.addplayer(new Cavalry("Kapostangin","Klingons"),8,1);
	Taklamakan.addplayer(new Magicsword("Heberdugin","Klingons"),5,1);

	Taklamakan.addplayer(new Swordsman("Anason","Balkons"),6,5);
	Taklamakan.addplayer(new Cavalry("Garson","Balkons"),6,6);
	Taklamakan.addplayer(new Knight("Fason","Balkons"),7,6);
	Taklamakan.addplayer(new Magicmaster("Tirbuson","Balkons"),8,5);
	Taklamakan.addplayer(new Wizard("Kornison","Balkons"),8,5);
	Taklamakan.addplayer(new Knight("Klakson","Balkons"),5,6);

	while (1) {
	try {
		Taklamakan.cycle();
	} catch(...) {
		cerr << "Unexpected error!\n";
	}
		Taklamakan.dump();
	}
	return 0;
}
