본문 바로가기

강의자료/Class

011. 종합문제 - 월드클래스를 작성하시오.

◈ 이 글은 강의를 위하여 제가 직접 작성한 내용입니다. 따라서 퍼가실 경우, 출처를 명확히 해주시기 바랍니다!!
◈ This ariticle was written by me for teaching. So if you want to copy this article, please clarify the source!!


선수 지식
2개의 클래스가 서로를 참조할 경우, 작성하는 순서에 주의하여야 한다.
// 클래스끼리 상호참조할 경우, 주의할 점.
class B;
class A
{
public:
	//B b;			// 에러.
	B* b;

	void Move()
	{
		cout << "class A move..." << endl;
	}

	// 	void Show()		// 에러.
	// 	{
	// 		b->Show();
	// 	}

	void Show();
};

class B
{
public:
	A a;

	void Show()
	{
		a.Move();
	}
};

void A::Show()
{
	b->Show();
}

문제편 - 아래 해답편의 코드를 미리 실행해보고 그 결과를 보면서 클래스를 작성하시오.
// 제한된 크기를 가지는 월드(World) 클래스를 생성.
// 월드 위를 돌아다니는 캐릭터(Character) 클래스를 생성.
// 캐릭터가 가지는 속성은 이름, 현재좌표, 속도, 시야, 체력, 공격력이다.
// 캐릭터는 오크(Orc)와 트롤(Troll)의 2가지 타입으로 만든다.
// 모든 캐릭터는 월드내에서만 움직이도록 한다.
// 속도는 -1부터 1까지의 값이 랜덤하게 지정되도록 한다.
// 5번 움직일 때마다 속도는 랜덤하게 변경된다.
// 이동 중 오크는 월드의 경계선을 만나면 반대로 반사되어 움직인다.
// 이동 중 트롤은 월드의 경계선을 만나면 반대쪽 경계선에서 나타난다.

// 모든 캐릭터는 월드를 이동하며 자신의 시야에 들어오는 캐릭터를 보면 자신의 공격력으로 공격한다.
// 오크는 사각형 시야를 가진다.
// 트롤은 원형 시야를 가진다.

// 죽은 캐릭터는 이동과 공격 모두 불가능하다.
// 죽은 캐릭터를 공격하는 것도 불가능하다.
// 모든 캐릭터는 자신의 상태(보통, 공격, 죽음)를 화면에 표시해야 한다.

// 실제 구현.
// (30,20)의 크기를 가지는 월드를 생성한다.
// 10마리의 캐릭터를 랜덤하게 생성한다.
/*
이름에는 생성된 순서대로 번호를 부여.
좌표는 월드내부에 랜덤한 좌표로 설정.
체력은 20~50사이로 설정.
공격력은 5~15사이로 설정.
시야는 1~2사이로 설정.
*/
// 5마리의 캐릭터는 1초에 한번씩 월드를 이동하며 서로를 공격하도록 하고, 그 모습을 화면에 표시한다.
// 위의 과정을 단 1마리가 남을때까지 반복하고 1마리가 남으면 프로그램을 종료한다.
#include 
#include 
#include 
#include 
using namespace std;

// 제한된 크기를 가지는 월드(World) 클래스를 생성.
// 월드 위를 돌아다니는 캐릭터(Character) 클래스를 생성.
// 캐릭터가 가지는 속성은 이름, 현재좌표, 속도, 시야, 체력, 공격력이다.
// 캐릭터는 오크(Orc)와 트롤(Troll)의 2가지 타입으로 만든다.
// 모든 캐릭터는 월드내에서만 움직이도록 한다.
// 속도는 -1부터 1까지의 값이 랜덤하게 지정되도록 한다.
// 5번 움직일 때마다 속도는 랜덤하게 변경된다.
// 이동 중 오크는 월드의 경계선을 만나면 반대로 반사되어 움직인다.
// 이동 중 트롤은 월드의 경계선을 만나면 반대쪽 경계선에서 나타난다.

// 모든 캐릭터는 월드를 이동하며 자신의 시야에 들어오는 캐릭터를 보면 자신의 공격력으로 공격한다.
// 오크는 사각형 시야를 가진다.
// 트롤은 원형 시야를 가진다.

// 죽은 캐릭터는 이동과 공격 모두 불가능하다.
// 죽은 캐릭터를 공격하는 것도 불가능하다.
// 모든 캐릭터는 자신의 상태(보통, 공격, 죽음)를 화면에 표시해야 한다.

// 실제 구현.
// (30,20)의 크기를 가지는 월드를 생성한다.
// 10마리의 캐릭터를 랜덤하게 생성한다.
/*
		이름에는 생성된 순서대로 번호를 부여.
		좌표는 월드내부에 랜덤한 좌표로 설정.
		체력은 20~50사이로 설정.
		공격력은 5~15사이로 설정.
		시야는 1~2사이로 설정.
*/
// 5마리의 캐릭터는 1초에 한번씩 월드를 이동하며 서로를 공격하도록 하고, 그 모습을 화면에 표시한다.
// 위의 과정을 단 1마리가 남을때까지 반복하고 1마리가 남으면 프로그램을 종료한다.

//////////////////////////////////////////////////////////////////////////

void main()
{
	srand((unsigned)time(NULL));

	World map(30, 20);

	Orc::SetShape("☆", "★", "♨");
	Troll::SetShape("♡", "♥", "@");

	for(int i=0; i < 10; i++)
	{
		int px = rand()%(map.right()-map.left()) + map.left();
		int py = rand()%(map.bottom()-map.top()) + map.top();
		int hp = rand()%31+20;
		int power = rand()%11+5;
		int radius = rand()%2+1;

		Character* cha;
		char name[256];
		if (rand()%2 ==0)
		{			
			sprintf_s(name, _countof(name), "[Orc]%02d", i);
			cha = new Orc(name, px, py, radius, hp, power);
		}
		else 
		{
			sprintf_s(name, _countof(name), "[Troll]%02d", i);
			cha = new Troll(name, px, py, radius, hp, power);
		}

		map.AddCharacter(cha);
	}

	while(true)
	{
		_sleep(1000);
		system("cls");

		map.Move();
		map.Show();

		cout << " >> 남은 몬스터 수 : " << map.RemainCharacter() << endl;
		if(map.RemainCharacter() == 1) break;
	}
}
해답편
#include 
#include 
#include 
#include 
using namespace std;

// 제한된 크기를 가지는 월드(World) 클래스를 생성.
// 월드 위를 돌아다니는 캐릭터(Character) 클래스를 생성.
// 캐릭터가 가지는 속성은 이름, 현재좌표, 속도, 시야, 체력, 공격력이다.
// 캐릭터는 오크(Orc)와 트롤(Troll)의 2가지 타입으로 만든다.
// 모든 캐릭터는 월드내에서만 움직이도록 한다.
// 속도는 -1부터 1까지의 값이 랜덤하게 지정되도록 한다.
// 5번 움직일 때마다 속도는 랜덤하게 변경된다.
// 이동 중 오크는 월드의 경계선을 만나면 반대로 반사되어 움직인다.
// 이동 중 트롤은 월드의 경계선을 만나면 반대쪽 경계선에서 나타난다.

// 모든 캐릭터는 월드를 이동하며 자신의 시야에 들어오는 캐릭터를 보면 자신의 공격력으로 공격한다.
// 오크는 사각형 시야를 가진다.
// 트롤은 원형 시야를 가진다.

// 죽은 캐릭터는 이동과 공격 모두 불가능하다.
// 죽은 캐릭터를 공격하는 것도 불가능하다.
// 모든 캐릭터는 자신의 상태(보통, 공격, 죽음)를 화면에 표시해야 한다.

// 실제 구현.
// (30,20)의 크기를 가지는 월드를 생성한다.
// 10마리의 캐릭터를 랜덤하게 생성한다.
/*
		이름에는 생성된 순서대로 번호를 부여.
		좌표는 월드내부에 랜덤한 좌표로 설정.
		체력은 20~50사이로 설정.
		공격력은 5~15사이로 설정.
		시야는 1~2사이로 설정.
*/
// 5마리의 캐릭터는 1초에 한번씩 월드를 이동하며 서로를 공격하도록 하고, 그 모습을 화면에 표시한다.
// 위의 과정을 단 1마리가 남을때까지 반복하고 1마리가 남으면 프로그램을 종료한다.

//////////////////////////////////////////////////////////////////////////

class World;

class Character
{
public:
	enum Type {ORC, TROLL};
	enum State { NORMAL = 0, ATTACK, DEAD, STATE_SIZE };
	enum MoveChange { MOVE_INIT = 0, MOVE_CHANGE = 5 };

protected:
	string name;
	State state;
	int x;
	int y;
	int dx;
	int dy;
	int radius;

	int hp;
	int power;

	int move_count;

public:
	Character(char* name, int x, int y, int radius, int hp, int power)
		: name(name), x(x), y(y), dx(0), dy(0), radius(radius), hp(hp), power(power), move_count(MOVE_CHANGE)
	{
		state = NORMAL;
	}

	virtual void Move(World*) = 0 
	{
		if(isDead() == false)
		{
			this->set_state(NORMAL);

			if(move_count == MOVE_CHANGE)
			{
				dx = rand()%3 - 1;
				dy = rand()%3 - 1;
				move_count = MOVE_INIT;
			}

			x += dx; 
			y += dy;

			move_count++;
		}
	}
	virtual void Show() = 0;
	virtual bool isInsideVision(Character* other) = 0;

	void Attack(Character* target) 
	{
		this->set_state(ATTACK);
		target->Hit(power);
	}
	void Hit(int power)
	{
		this->hp -= power;
		if(isDead()) this->set_state(DEAD);
	}
	bool isDead() { return hp <= 0; }
	
	string& get_name() { return name; }
	int get_power() { return power; }
	int get_x() { return x; }
	int get_y() { return y; }
	void set_state(State state) { this->state = state; }
	virtual Type get_type() = 0;
};

class Orc : public Character
{
private:
	static string shape[STATE_SIZE];

public:
	static void SetShape(char* shape_normal, char* shape_attack, char* shape_dead)
	{
		Orc::shape[NORMAL] = shape_normal;
		Orc::shape[ATTACK] = shape_attack;
		Orc::shape[DEAD] = shape_dead;
	}
	
public:
	Orc(char* name, int x, int y, int radius, int hp, int power)
		: Character(name, x, y, radius, hp, power)
	{
	}
	virtual void Move(World* world);
	virtual void Show()
	{
		cout << shape[state];
	}
	virtual bool isInsideVision(Character* other)
	{
		int left = x - radius;
		int right = x + radius;
		int top = y - radius;
		int bottom = y + radius;

		int px = other->get_x();
		int py = other->get_y();

		if(left <= px && px <= right && top <= py && py <= bottom)
			return true;
		else
			return false;
	}
	virtual Type get_type() { return ORC; }
};

string Orc::shape[3];

class Troll : public Character
{
private:
	static string shape[STATE_SIZE];

public:
	static void SetShape(char* shape_normal, char* shape_attack, char* shape_dead)
	{
		Troll::shape[NORMAL] = shape_normal;
		Troll::shape[ATTACK] = shape_attack;
		Troll::shape[DEAD] = shape_dead;
	}

public:
	Troll(char* name, int x, int y, int radius, int hp, int power)
		: Character(name, x, y, radius, hp, power)
	{
	}
	virtual void Move(World* world);
	virtual void Show()
	{
		cout << shape[state];
	}
	virtual bool isInsideVision(Character* other)
	{
		int a = other->get_x() - this->get_x();
		int b = other->get_y() - this->get_y();

		int lengthSQ = a*a + b*b;
		int radiusSQ = radius*radius;

		return lengthSQ <= radiusSQ;
	}
	virtual Type get_type() { return TROLL; }
};

string Troll::shape[3];

class World
{
private:
	Character* character[100];
	int count;

	const int size_x;
	const int size_y;

public:
	World(int size_x, int size_y) : count(0), size_x(size_x), size_y(size_y)
	{
	}
	~World()
	{
		for(int i=0; iisDead()) continue;

			character[i]->Move(this);
			
			for(int m=0; m < count; m++)
			{
				if(i==m) continue;
				if(character[m]->isDead()) continue;
				if(character[i]->isInsideVision(character[m]))
				{
					character[i]->Attack(character[m]);
					//cout << "WORLD : " << character[i]->get_name() << "(이)가 " 
					//	<< character[m]->get_name() << "에게 " << character[i]->get_power() << "의 대미지를 입혔습니다." << endl;
				}
			}
		}
	}
	void Show()
	{
		// 위쪽 가장자리를 그린다.
		cout << endl << "┏"; for(int i=1;i <= size_x;i++) cout << "┳"; cout << "┓" << endl;

		// 중간부분을 그린다.
		for(int y=1; y <= size_y; y++) 
		{ 
			// 왼쪽 가장자리를 그린다.
			cout << "┣" ;
			// 안쪽을 그려준다.
			for(int x=1; x <= size_x; x++)
			{
				bool exist_character = false;

				for(int i=0; i < count; i++)
				{
					if(character[i]->get_x() == x && character[i]->get_y() == y)
					{
						character[i]->Show();
						exist_character = true;
						break;
					}
				}

				if(exist_character == false) cout << "╋";
			} 
			// 오른쪽 가장자리를 그린다.
			cout << "┫" << endl;
		}

		// 아랫쪽 가장자리를 그린다.
		cout << "┗"; for(int i=1; i <= size_x; i++) cout << "┻"; cout << "┛" << endl;
	}
	int RemainCharacter()
	{
		int deadcount = 0;
		for(int i=0; i < count; i++)
		{
			if(character[i]->isDead()) 
				deadcount++;
		}
		return (count-deadcount);
	}

	int left() { return 1; }
	int right() { return size_x; }
	int top() { return 1; }
	int bottom() { return size_y; }
};

void Orc::Move(World* world)
{
	Character::Move(world);
	if(x+dx < world->left())	dx = -dx;
	if(x+dx > world->right())	dx = -dx;
	if(y+dy < world->top())		dy = -dy;
	if(y+dy > world->bottom())	dy = -dy;
}

void Troll::Move(World* world)
{
	Character::Move(world);
	if(x < world->left())		x = world->right();
	if(x > world->right())		x = world->left();
	if(y < world->top())		y = world->bottom();
	if(y > world->bottom())		y = world->top();
}

void main()
{
	srand((unsigned)time(NULL));

	World map(30, 20);

	Orc::SetShape("☆", "★", "♨");
	Troll::SetShape("♡", "♥", "@");

	for(int i=0; i < 10; i++)
	{
		int px = rand()%(map.right()-map.left()) + map.left();
		int py = rand()%(map.bottom()-map.top()) + map.top();
		int hp = rand()%31+20;
		int power = rand()%11+5;
		int radius = rand()%2+1;

		Character* cha;
		char name[256];
		if (rand()%2 ==0)
		{			
			sprintf_s(name, _countof(name), "[Orc]%02d", i);
			cha = new Orc(name, px, py, radius, hp, power);
		}
		else 
		{
			sprintf_s(name, _countof(name), "[Troll]%02d", i);
			cha = new Troll(name, px, py, radius, hp, power);
		}

		map.AddCharacter(cha);
	}

	while(true)
	{
		_sleep(1000);
		system("cls");

		map.Move();
		map.Show();

		cout << " >> 남은 몬스터 수 : " << map.RemainCharacter() << endl;
		if(map.RemainCharacter() == 1) break;
	}
}