문제 링크: https://www.acmicpc.net/problem/17081

 

구현 문제인데요 정말 빡셉니다. 조건도 되게 많고, 방어구 공격력 전투 장신구 게임오버정보, ......

처음엔 진짜 게임 짜는 것처럼 시작했는데 C++로 알고리즘 말고 개발을 너무 오래 안했더니 OOP를 까먹었습니다 헐; 큰일났습니다 ㅠ

item 상속해서 방어구, 무기, 장신구 나누고 막 하려고 했는데.. 방법을 모르겠어요 허허 클래스 기초부터 다시 해야겠습니다ㅠㅠ

 

이 문제는 짜면서 실수할 수 있는 부분이 너무 많아서 뭐부터 적어야 할지 모르겠습니다.

문제에 나와있지 않은 조건은 다음과 같습니다

  • 상자를 열어서 무기, 방어구 교체시 원래 쓰고 있던건 땅바닥에 내려놓는게 아니고 그냥 없어짐
  • 가시 함정은 방어구와 상관 없이 무조건 5데미지 (DX 장신구 제외)
  • 4개 이상 장신구를 끼고 있을 때 장신구를 주웠을 때 or 이미 끼고 있는 장신구를 주웠을때 그냥 없어집니다

나머지는 게시판에 있는거 보고 체크해주세요

 

숏코딩 순위를 보니까 가장 짧은 코드 길이가 3119 바이트네요. 여기서 구현 난이도를 대충 아실 수 있습니다.

제 소스는 클래스화한 것 때문인지 10130 바이트입니다.

#pragma GCC optimize ("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,avx,avx2")

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>

using namespace std;

////////////////////////////////////////////////////////////////////////
// Note
//
// * Spike trap: 무조건 5 dmg (unless character has DX)
// * 상자를 열어서 무기/방어구 교체시 들고있던건 없어짐
// * 4개 이상 장신구를 끼고 있거나 이미 끼고 있는 장신구를 줏으면 그것도 없어짐
//
////////////////////////////////////////////////////////////////////////

enum dir { L, R, U, D };
enum effects {
	// 장신구 효과 flag
	HR = 0x0001,
	RE = 0x0002,
	CO = 0x0004,
	EX = 0x0008,
	DX = 0x0010,
	HU = 0x0020,
	CU = 0x0040
};
enum cell_type {
	EMPTY		= 0x0001,
	WALL		= 0x0002,
	MONSTER		= 0x0004,
	TRAP		= 0x0008,
	ITEM		= 0x0010
};

const int DIR[][2] = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };	//y, x

enum item_type { WEAPON, ARMOR, ACCESSORY };
struct item {
	int pos_y, pos_x;
	item_type type;
	int val;	// 무기: 공격력, 방어구: 방어력, 장신구: 효과
};


struct character {
	int start_y, start_x;
	int pos_y, pos_x;

	int lvl;		// 레벨
	int exp_cur;	// 현재 경험치
	int hp_cur;		// 현재체력
	int hp_max;		// 최대체력
	int atk;		// 공격력 (순수)
	int def;		// 방어력 (순수)
	int weapon;		// 무기 공격력
	int armor;		// 갑옷 방어력
	int accessory_flag;				// 장신구 (4개까지 가능. 중복은 X) -> flag
	int accessory_used_flag;		// 장신구 사용했으면 flag에 체크

	character() {}
	character(int _y, int _x) : start_y(_y), start_x(_x),
		pos_y(_y), pos_x(_x), exp_cur(0), lvl(1),
		hp_cur(20), hp_max(20), atk(2), def(2),
		weapon(0), armor(0), accessory_flag(0), accessory_used_flag(0) {}

	
	int get_def() { return def + armor; }
	int get_atk() {
		int base = atk + weapon;
		
		// 첫번째 공격 뻥튀기 -> CO, DX
		if ((accessory_flag & CO) && !(accessory_used_flag & CO)) {
			if (accessory_flag & DX)
				base *= 3;
			else
				base *= 2;

			accessory_used_flag |= CO;
		}

		return base;
	}

	// 전투 승리할 때마다 호출해야 함 (중요)
	void rest() {
		// HR 효과 적용
		if (accessory_flag & HR)
			hp_cur = min(hp_max, hp_cur + 3);
		 
		// CO, HU 복구
		accessory_used_flag &= ~CO;
		accessory_used_flag &= ~HU;
	}

	// exp만큼의 경험치를 획득
	// --
	// 레벨업하면 true
	bool obtain_exp(const int& exp) {
		// EX 효과
		if (accessory_flag & EX)
			exp_cur += floor(exp * 1.2);
		else
			exp_cur += exp;

		// 레벨업 처리
		if (exp_cur >= 5*lvl) {
			++lvl;
			exp_cur = 0;	// 남은 경험치는 버렷

			// 스텟 업 & 회복
			hp_max += 5;
			hp_cur = hp_max;
			atk += 2;
			def += 2;

			return true;
		}

		return false;
	}

	// 아이템(무기, 방어구, 장신구)을 획득
	void obtain_item(const item& it) {
		if (it.type == WEAPON) {
			weapon = it.val;
		}
		else if (it.type == ARMOR) {
			armor = it.val;
		}
		else {
			// 4개 이상 꼈거나 동일한거 낀 경우 X
			if (bitset<7>(accessory_flag).count() < 4 && !(accessory_flag & it.val)) {
				accessory_flag |= it.val;
			}
		}
	}

	// 전투에서 damage만큼의 데미지를 입음 (보스한테 온 데미지면 from_boss=true)
	// --
	// 죽으면 true
	bool attacked(const int& damage, bool from_boss) {
		// HU 효과
		if (from_boss && (accessory_flag & HU) && !(accessory_used_flag & HU)) {
			hp_cur = hp_max;
			accessory_used_flag |= HU;

			return false;
		}
		else {
			hp_cur -= max(1, damage-get_def());

			return hp_cur <= 0;
		}
	}

	// spike를 밟음
	// --
	// 죽으면 true
	bool attacked_by_spike() {
		if (accessory_flag & DX)
			hp_cur -= 1;
		else
			hp_cur -= 5;

		return hp_cur <= 0;

		/*if (hp_cur <= 0)
			return (false == reincarnate());
		else
			return false;*/
	}

	// RE 장신구 사용
	// --
	// 사용했으면 true 반환
	// 사용할 환경이 아니거나 없으면 false 반환
	bool reincarnate() {
		// RE 효과 발동
		if (hp_cur <= 0 && accessory_flag & RE) {
			pos_y = start_y;
			pos_x = start_x;
			hp_cur = hp_max;
			accessory_flag &= ~RE;	// 제거
			return true;
		}
		else
			return false;
	}

	void print() {
		cout << "LV : " << lvl << '\n'
			<< "HP : " << max(hp_cur, 0) << '/' << hp_max << '\n'
			<< "ATT : " << atk << '+' << weapon << '\n'
			<< "DEF : " << def << '+' << armor << '\n'
			<< "EXP : " << exp_cur << '/' << lvl * 5 << '\n';
	}
};

struct monster {
	int pos_y, pos_x;
	int hp_max;
	int hp_cur;
	int atk;
	int def;
	int exp;

	bool is_boss;
	string name;

	monster() {}
	monster(int r, int c, string s, int w, int a, int h, int e, bool boss)
	: pos_y(r), pos_x(c), name(s), atk(w), def(a), hp_cur(h), hp_max(h),
	exp(e), is_boss(boss) {}
	
	// damage 만큼의 데미지를 입음
	// 죽으면 true
	bool attacked(const int& damage) {
		hp_cur -= max(1, damage - def);
		return hp_cur <= 0;
	}
};

struct game {
	character hero;
	vector<monster> monsters;
	vector<item> items;
	vector<vector<cell_type>> cells;
	int passed_turns;
	string directions;

	game(): passed_turns(0) {}

	void read(int n, int m) {
		//read map
		cells.resize(n);
		for (auto& v : cells)
			v.resize(m);

		int monster_cnt = 0;
		int item_cnt = 0;

		char ch;
		pair<int, int> boss_pos;	// y, x
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j) {
				cin >> ch;

				switch (ch) {
				case '@':
					hero = character(i, j);
					cells[i][j] = EMPTY;
					break;

				case '.':
					cells[i][j] = EMPTY;
					break;

				case '#':
					cells[i][j] = WALL;
					break;

				case 'M':
					boss_pos = { i, j };
				case '&':
					++monster_cnt;
					cells[i][j] = MONSTER;
					break;

				case '^':
					cells[i][j] = TRAP;
					break;

				case 'B':
					++item_cnt;
					cells[i][j] = ITEM;
					break;
				}
			}
		}

		// read dirs
		cin >> directions;

		// monsters
		int r, c, w, a, h, e;
		string s;
		monsters.resize(monster_cnt);
		for (auto& mm: monsters) {
			cin >> r >> c >> s >> w >> a >> h >> e;
			--r; --c;
			mm = monster(r, c, s, w, a, h, e, (make_pair(r,c) == boss_pos));
		}

		// items
		string t;
		items.resize(item_cnt);
		for (auto& ii : items) {
			cin >> r >> c >> t >> s;
			--r; --c;

			ii.pos_y = r;
			ii.pos_x = c;

			if (t == "W") {
				ii.type = WEAPON;
				ii.val = atoi(s.c_str());
			}
			else if (t == "A") {
				ii.type = ARMOR;
				ii.val = atoi(s.c_str());
			}
			else {
				ii.type = ACCESSORY;
				if (s == "HR") ii.val = HR;
				else if (s == "RE") ii.val = RE;
				else if (s == "CO") ii.val = CO;
				else if (s == "EX") ii.val = EX;
				else if (s == "DX") ii.val = DX;
				else if (s == "HU") ii.val = HU;
				else ii.val = CU;
			}
		}
	}

	void print_map() {
		// 보스랑 캐릭터 좌표 빼놔야함
		int r = cells.size();
		int c = cells[0].size();

		int hero_y=-1, hero_x=-1;
		int boss_y=-1, boss_x=-1;

		if (hero.hp_cur > 0) {
			hero_y = hero.pos_y;
			hero_x = hero.pos_x;
		}

		for (auto& m : monsters)
			if (m.is_boss)
				boss_y = m.pos_y, boss_x = m.pos_x;

		for (int i = 0; i < r; ++i) {
			for (int j = 0; j < c; ++j) {
				if (i == hero_y && j == hero_x)
					cout << '@';
				else if (i == boss_y && j == boss_x)
					cout << 'M';
				else {
					switch (cells[i][j]) {
					case EMPTY:
						cout << '.';
						break;

					case WALL:
						cout << '#';
						break;

					case MONSTER:
						cout << '&';
						break;

					case TRAP:
						cout << '^';
						break;

					case ITEM:
						cout << 'B';
						break;
					}
				}
			}
			cout << '\n';
		}
	}

	void start() {
		bool win = false;
		bool lose = false;
		string by_whom = "";

		for (auto& c: directions) {
			/*cout << "-----" << passed_turns << '\n';
			print_map();
			hero.print();
			cout << "-----------\n";*/

			if (win || lose) break;


			// 일단 이동
			int dir_idx;
			if (c == 'L') dir_idx = L;
			else if (c == 'R') dir_idx = R;
			else if (c == 'U') dir_idx = U;
			else dir_idx = D;

			int next_y = hero.pos_y + DIR[dir_idx][0];
			int next_x = hero.pos_x + DIR[dir_idx][1];

			// 맵 바깥인 경우 or 그 방향에 벽이 있는 경우
			if ((next_y < 0 || next_y >= cells.size() || next_x < 0 || next_x >= cells[0].size()) ||
				(cells[next_y][next_x] == WALL)) {
				next_y = hero.pos_y;
				next_x = hero.pos_x;
			}

			hero.pos_y = next_y;
			hero.pos_x = next_x;

			// 이동 후 액션
			switch (cells[next_y][next_x]) {
			case MONSTER:
				for (auto it = monsters.begin(); it != monsters.end(); ++it) {
					auto& m = *it;
					if (m.pos_y == next_y && m.pos_x == next_x) {
						while (true) {
							if (m.attacked(hero.get_atk())) {
								// 몹 죽음
								if (m.is_boss) {
									win = true;
								}
								hero.obtain_exp(m.exp);
								cells[next_y][next_x] = EMPTY;
								monsters.erase(it);
								hero.rest();	// 전투휴무

								break;
							}

							if (hero.attacked(m.atk, m.is_boss)) {
								if (hero.reincarnate()) break;

								// 주인공 죽음
								lose = true;
								by_whom = m.name;

								break;
							}
						}

						break;
					}
				}
				break;

			case TRAP:
				if (hero.attacked_by_spike()) {
					if (hero.reincarnate()) break;

					// 가시에 죽음
					lose = true;
					by_whom = "SPIKE TRAP";
				}
				break;

			case ITEM:
				// 아이템 착용
				for (auto it = items.begin(); it != items.end(); ++it) {
					if (it->pos_y == next_y && it->pos_x == next_x) {
						hero.obtain_item(*it);
						items.erase(it);
						break;
					}
				}
				cells[next_y][next_x] = EMPTY;
				break;
			}

			++passed_turns;
		}

		
		// end check
		print_map();
		cout << "Passed Turns : " << passed_turns << '\n';
		hero.print();

		if (win) {
			cout << "YOU WIN!\n";
		}
		else if (lose) {
			cout << "YOU HAVE BEEN KILLED BY " << by_whom << "..\n";
		}
		else {
			cout << "Press any key to continue.\n";
		}
	}
};


int main()
{
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	//freopen("output.txt", "w", stdout);
#endif

	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	int n, m;
	cin >> n >> m;

	game g;
	g.read(n, m);
	g.start();

	return 0;
}
반응형

'Online Judge > 백준' 카테고리의 다른 글

[백준][C++] 1036: 36진수  (0) 2020.06.24
[백준][C++] 18119: 단어 암기  (0) 2020.06.21
[백준][C++] 18111: 마인크래프트  (0) 2020.06.15
[백준][C++] 2293: 동전 1  (0) 2020.06.14
[백준][C++] 1259: 팰린드롬수  (0) 2020.06.13