문제 링크
https://www.acmicpc.net/problem/17081
문제 선정 과정
별다른 알고리즘 없이, 문제에서 지시하는 내용들을 그대로 구현하면 되는 문제로 보였습니다.
다만 구현량이 꽤 많아 보이기 때문에, 객체 지향 프로그래밍을 지향하며 코드를 짜는 문제로 보였습니다.
문제 풀이 전략
객체 지향 프로그래밍 (Object-Oriented Programming, OOP)이란 컴퓨터 프로그래밍 패러다임의 일종으로,
프로그램을 수많은 객체(Object)로 나누고 이들 간의 상호작용으로 프로그램을 구성하는 방식을 의미합니다.
기존의 절차지향 프로그래밍은 구현해야 할 양이 조금만 많아져도 굉장히 복잡하고 난해하게 구성이 됩니다.
또한, 디버깅 과정에서도 어디서 오류가 났는지 찾기도 힘들고, 관련된 부분들을 찾고 고치기도 번거롭습니다.
이러한 문제점을 해결하기 위해 프로그램을 여러 개의 객체로 나누고,
객체 간의 상호작용으로 구성하는 것을 객체 지향 프로그래밍이라 합니다.
대략적으로 다음과 같이 객체들과 객체 간의 상호작용을 구성해볼 수 있습니다.
먼저 객체 정보들을 구성합니다.
플레이어는 유일하므로 따로 관리해주고, 나머지는 class를 활용해 구성해 주었습니다.
#include <iostream>
#include <string>
#include <map>
using namespace std;
int n,m;
string board[101];
string cmd;
int Y, X;
int init_y, init_x;
int monster_cnt;
class monster_info
{
public:
string name;
int att;
int def;
int hp;
int exp;
};
monster_info monster[101][101];
int item_cnt;
class item_info
{
public:
string item_type;
int att;
int def;
};
item_info item[101][101];
int Lv = 1;
int HP = 20, max_hp = 20;
int EXP = 0, max_exp = 5;
int ATT = 2, DEF = 2;
int weapon = 0;
int armor = 0;
map<string,bool> used;
int effect_cnt = 0;
map<char,int> dy, dx;
먼저 이동하는 데 필요한 dy, dx 변수를 설정해 주어야 합니다.
명령어가 문자열로 주어지므로, map을 활용하여 직관적으로 구성합니다.
void init()
{
dy['U'] = -1; dx['U'] = 0;
dy['D'] = 1; dx['D'] = 0;
dy['L'] = 0; dx['L'] = -1;
dy['R'] = 0; dx['R'] = 1;
}
다음으로 이동 상호작용을 구현합니다.
void move(int i)
{
int ty = Y + dy[char(cmd[i])];
int tx = X + dx[char(cmd[i])];
if(0 <= ty && ty < n && 0 <= tx && tx < m)
{
if(board[ty][tx] != '#')
{
Y = ty;
X = tx;
}
}
}
이동 후, 현재 위치에 가시가 있다면 체력 정보를 갱신해 줍니다.
이때 장신구 정보를 참조해 주어야 합니다.
게임 오버 발생 시 시스템이 감지할 수 있도록 반환값을 가지도록 구성해야 합니다.
int dead()
{
if(used["RE"] == true)
{
used["RE"] = false;
effect_cnt -= 1;
HP = max_hp;
Y = init_y;
X = init_x;
return 1;
}
else
{
HP = 0;
return -1;
}
}
int act_spike()
{
int damage = 5;
if(used["DX"] == true) damage = 1;
HP -= damage;
if(HP <= 0) return dead();
else return 0;
}
다음으로 몬스터와의 전투입니다.
여러 아이템들의 효과, 반환값 등을 모두 고려해 주어야 합니다.
int battle()
{
bool is_boss = (board[Y][X] == 'M' ? true : false);
if(used["HU"] == true && is_boss) HP = max_hp;
int damage = ATT + weapon;
if(used["CO"] == true)
{
if(used["DX"] == true) damage *= 3;
else damage *= 2;
}
int monster_init_hp = monster[Y][X].hp;
int &monster_hp = monster[Y][X].hp;
int monster_def = monster[Y][X].def;
int monster_att = monster[Y][X].att;
for(int i=0;;i++)
{
monster_hp -= max(1, damage - monster_def);
if(monster_hp <= 0)
{
board[Y][X] = '.';
exp_update();
if(used["HR"] == true) HP = min(HP+3, max_hp);
if(is_boss) return 0;
else return 1;
}
if(used["HU"] == true && is_boss && i == 0)
{
damage = ATT + weapon;
continue;
}
HP -= max(1, monster_att - (armor+DEF));
if(HP <= 0) return dead();
damage = ATT + weapon;
}
}
만약 현재 위치에 아이템이 있다면 이를 획득하는 코드도 구성해 주어야 합니다.
이때, 이 아이템이 무기인지, 장신구인지를 고려해 주어야 합니다.
void item_collect()
{
if(item[Y][X].item_type == "W") weapon = item[Y][X].att;
else if(item[Y][X].item_type == "A") armor = item[Y][X].def;
else
{
if(effect_cnt < 4 && used[item[Y][X].item_type] == false)
{
effect_cnt++;
used[item[Y][X].item_type] = true;
}
}
board[Y][X] = '.';
}
이제 부가적인 정보들을 구성해준 후, 객체 간의 상호작용을 시행하여 해결할 수 있습니다.
아래는 전체 코드입니다.
#include <iostream>
#include <string>
#include <map>
using namespace std;
int n,m;
string board[101];
string cmd;
int Y, X;
int init_y, init_x;
int monster_cnt;
class monster_info
{
public:
string name;
int att;
int def;
int hp;
int exp;
};
monster_info monster[101][101];
int item_cnt;
class item_info
{
public:
string item_type;
int att;
int def;
};
item_info item[101][101];
int Lv = 1;
int HP = 20, max_hp = 20;
int EXP = 0, max_exp = 5;
int ATT = 2, DEF = 2;
int weapon = 0;
int armor = 0;
map<string,bool> used;
int effect_cnt = 0;
map<char,int> dy, dx;
void init()
{
dy['U'] = -1; dx['U'] = 0;
dy['D'] = 1; dx['D'] = 0;
dy['L'] = 0; dx['L'] = -1;
dy['R'] = 0; dx['R'] = 1;
}
void input()
{
cin >> n >> m;
for(int i=0;i<n;i++)
{
cin >> board[i];
for(int j=0;j<m;j++)
{
if(board[i][j] == 'B') item_cnt++;
else if(board[i][j] == '&' || board[i][j] == 'M') monster_cnt++;
else if(board[i][j] == '@')
{
Y = i; init_y = i;
X = j; init_x = j;
board[i][j] = '.';
}
}
}
cin >> cmd;
for(int i=0;i<monster_cnt;i++)
{
int ty,tx;
cin >> ty >> tx;
ty--; tx--;
cin >> monster[ty][tx].name;
cin >> monster[ty][tx].att;
cin >> monster[ty][tx].def;
cin >> monster[ty][tx].hp;
cin >> monster[ty][tx].exp;
}
for(int i=0;i<item_cnt;i++)
{
int ty,tx;
cin >> ty >> tx;
ty--; tx--;
cin >> item[ty][tx].item_type;
if(item[ty][tx].item_type == "W") cin >> item[ty][tx].att;
else if(item[ty][tx].item_type == "A") cin >> item[ty][tx].def;
else cin >> item[ty][tx].item_type;
}
}
void move(int i)
{
int ty = Y + dy[char(cmd[i])];
int tx = X + dx[char(cmd[i])];
if(0 <= ty && ty < n && 0 <= tx && tx < m)
{
if(board[ty][tx] != '#')
{
Y = ty;
X = tx;
}
}
}
int dead()
{
if(used["RE"] == true)
{
used["RE"] = false;
effect_cnt -= 1;
HP = max_hp;
Y = init_y;
X = init_x;
return 1;
}
else
{
HP = 0;
return -1;
}
}
int act_spike()
{
int damage = 5;
if(used["DX"] == true) damage = 1;
HP -= damage;
if(HP <= 0) return dead();
else return 0;
}
void exp_update()
{
int update_exp = monster[Y][X].exp;
if(used["EX"] == true)
{
update_exp *= 6;
update_exp /= 5;
}
EXP += update_exp;
if(EXP >= max_exp)
{
EXP = 0;
max_exp += 5;
Lv += 1;
max_hp += 5;
ATT += 2;
DEF += 2;
HP = max_hp;
}
}
int battle()
{
bool is_boss = (board[Y][X] == 'M' ? true : false);
if(used["HU"] == true && is_boss) HP = max_hp;
int damage = ATT + weapon;
if(used["CO"] == true)
{
if(used["DX"] == true) damage *= 3;
else damage *= 2;
}
int monster_init_hp = monster[Y][X].hp;
int &monster_hp = monster[Y][X].hp;
int monster_def = monster[Y][X].def;
int monster_att = monster[Y][X].att;
for(int i=0;;i++)
{
monster_hp -= max(1, damage - monster_def);
if(monster_hp <= 0)
{
board[Y][X] = '.';
exp_update();
if(used["HR"] == true) HP = min(HP+3, max_hp);
if(is_boss) return 0;
else return 1;
}
if(used["HU"] == true && is_boss && i == 0)
{
damage = ATT + weapon;
continue;
}
HP -= max(1, monster_att - (armor+DEF));
if(HP <= 0) return dead();
damage = ATT + weapon;
}
}
void item_collect()
{
if(item[Y][X].item_type == "W") weapon = item[Y][X].att;
else if(item[Y][X].item_type == "A") armor = item[Y][X].def;
else
{
if(effect_cnt < 4 && used[item[Y][X].item_type] == false)
{
effect_cnt++;
used[item[Y][X].item_type] = true;
}
}
board[Y][X] = '.';
}
void print_result(int turn, int res_code)
{
if(res_code != -1) board[Y][X] = '@';
for(int i=0;i<n;i++) cout << board[i] << '\n';
cout << "Passed Turns : " << turn << '\n';
cout << "LV : " << Lv << '\n';
cout << "HP : " << HP << '/' << max_hp << '\n';
cout << "ATT : " << ATT << '+' << weapon << '\n';
cout << "DEF : " << DEF << '+' << armor << '\n';
cout << "EXP : " << EXP << '/' << max_exp << '\n';
if(res_code == 0) cout << "YOU WIN!";
else if(res_code == 1) cout << "Press any key to continue.";
else
{
cout << "YOU HAVE BEEN KILLED BY ";
if(board[Y][X] == '^') cout << "SPIKE TRAP";
else cout << monster[Y][X].name;
cout << "..";
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
input();
int turn;
for(turn = 0; turn < cmd.size(); turn++)
{
move(turn);
if(board[Y][X] == '^')
{
int res = act_spike();
if(res == -1)
{
print_result(turn+1, -1);
return 0;
}
}
else if(board[Y][X] == 'B') item_collect();
else if(board[Y][X] == '&' || board[Y][X] == 'M')
{
int res = battle();
if(res != 1)
{
print_result(turn+1, res);
return 0;
}
}
}
print_result(turn, 1);
return 0;
}
느낀 점
처음 문제를 접했을 때 막대한 구현량에 막막했으나, 객체 지향 프로그래밍을 학습하여 조금씩 해결하며 성취감을 느꼈습니다.
문제를 해결하면서 절차지향으로 해결하면 복잡한 문제들을 객체지향으로 해결하며 중요성을 체감했습니다.
객체 지향은 주로 해보지 않은 만큼, 상속, 캡슐화 등의 다양한 객체지향 개념들을 익혀보고 싶습니다.
'과학고 조기졸업 과제 > AP 프로그래밍과 문제해결' 카테고리의 다른 글
5. Discrete Convolution (0) | 2024.03.26 |
---|---|
3. 주식회사 승범이네 (0) | 2024.03.15 |
2. 교수님은 기다리지 않는다 (1) | 2024.03.14 |
1. 잔디밭과 개미굴 (0) | 2024.03.14 |