[코드트리] 싸움땅 (삼성 SW 역량테스트 2022 하반기 오전 1번)
https://www.codetree.ai/training-field/frequent-problems/problems/battle-ground
(r, c)에는 여러 개의 총이 있을 수 있으므로, 2차원 좌표에 1차원 배열을 추가하여 총을 관리한다.
int GUN[MAX][MAX][MAX * MAX];
int gIndex[MAX][MAX];
플레이어 정보에는 좌표와 방향, 능력치(s), 들고 있는 총의 power(gun)를 구조체로 관리한다.
typedef struct st
{
int r;
int c;
int dir;
int s;
int gun;
}PLAYER;
PLAYER player[30 + 5];
방향을 처리하는 배열은 다음과 같이 선언한다.
// ↑, →, ↓, ←
int dr[] = { -1, 0, 1, 0 };
int dc[] = { 0, 1, 0, -1 };
점수를 저장할 배열은 다음과 같다.
int SCORE[30 + 5];
input에서 총 개수 초기화, 총의 power 입력, SCORE 초기화, player 정보 입력을 한다.
void input()
{
scanf("%d %d %d", &N, &M, &K);
for (int r = 1; r <= N; r++)
for (int c = 1; c <= N; c++)
gIndex[r][c] = 0;
for (int r = 1; r <= N; r++)
{
for (int c = 1; c <= N; c++)
{
scanf("%d", &GUN[r][c][0]);
if (GUN[r][c][0]) gIndex[r][c] = 1;
}
}
for (int m = 1; m <= M; m++) // player 번호는 1번 부터
{
SCORE[m] = 0;
int x, y, d, s;
scanf("%d %d %d %d", &x, &y, &d, &s);
player[m].r = x;
player[m].c = y;
player[m].dir = d;
player[m].s = s;
player[m].gun = 0;
}
}
main에서 simulate 후에 SCORE를 출력한다.
int main(void)
{
// scanf("%d", &T);
T = 1;
for (int tc = 1; tc <= T; tc++)
{
input();
for (int k = 0; k < K; k++) simulate();
for (int m = 1; m <= M; m++) printf("%d ", SCORE[m]);
putchar('\n');
}
return 0;
}
구현
매 라운드 마다 현재 player의 위치를 2차원 배열 curPos에 player 번호를 기록하고 시작한다.
changeDir은 벽에 부딪히는 경우 방향을 바꾸기 위한 배열이다.
void simulate()
{
int curPos[MAX][MAX] = { 0 };
int changeDir[] = { 2, 3, 0, 1 }; // 방향 변경
// 현재 player의 위치 표기
for (int m = 1; m <= M; m++)
curPos[player[m].r][player[m].c] = m;
...
1-1. 플레이어를 이동하고 벽에 부딪힌 경우, 방향을 바꿔서 이동한다.
// 1-1. 플레이어 순차적으로 이동
PLAYER p = player[m];
int nr, nc;
nr = p.r + dr[p.dir];
nc = p.c + dc[p.dir];
// 1-1. 격자를 벗어나는 경우 반대 방향으로 변경 후 이동
if (nr < 1 || nc < 1 || nr > N || nc > N)
{
player[m].dir = p.dir = changeDir[p.dir];
nr = p.r + dr[p.dir];
nc = p.c + dc[p.dir];
}
이동할 방향에 다른 플레이어가 없는 경우 (curPos[nr][nc] == 0), 현재 플레이어를 이동한다.
(curPos 갱신, player 좌표 갱신)
이동할 방향에 (nr, nc)에 총이 있는 경우 가장 센 총을 선택하고 교체한다.
// 2-1. 이동할 방향에 플레이어가 있는지 체크, 없는 경우
if (curPos[nr][nc] == 0)
{
// 지도에서 player 이동
curPos[p.r][p.c] = 0;
curPos[nr][nc] = m;
// 실제 player 이동
player[m].r = nr;
player[m].c = nc;
if (gIndex[nr][nc]) // 총이 있는 경우
{
int playerGun = player[m].gun;
int gunIndex = getMaxPowerGunIndex(nr, nc);
if (playerGun < GUN[nr][nc][gunIndex])
{
int tmp = player[m].gun;
player[m].gun = GUN[nr][nc][gunIndex];
GUN[nr][nc][gunIndex] = tmp;
}
}
}
getMaxPowerGunIndex는 (r, c)에서 가장 power가 큰 총의 index를 return한다.
int getMaxPowerGunIndex(int r, int c)
{
int max = -1;
int count = gIndex[r][c];
int index = 0;
for (int i = 0; i < count; i++)
{
if(max < GUN[r][c][i])
{
max = GUN[r][c][i];
index = i;
}
}
return index;
}
이동할 방향에 player가 있다면, 싸움을 해야 한다.
이동할 플레이어를 curPos에서 삭제하고, battle 함수를 이용해 winner와 loser를 구분한다.
(battle 결과에 따라 두 플레이어의 움직일 위치가 결정되므로 curPos만 먼저 삭제한다.)
else // 2-2. player가 있는 경우
{
int fighter = curPos[nr][nc]; // 만나게 된 player
int mGun = player[m].gun; // 움직인 player의 총
int mSkill = player[m].s; // 움직인 player의 skill
int fGun = player[fighter].gun;
int fSkill = player[fighter].s;
int loser, winner;
// map에서 이동한 플레이어 삭제
curPos[player[m].r][player[m].c] = 0;
if (battle(player[m], player[fighter]) == 0) // m이 이긴 경우
{
winner = m;
loser = fighter;
}
else
{
winner = fighter;
loser = m;
}
...
battle 함수는 다음과 같다.
플레이의 능력치가 모두 다르기 때문에 반드시 결판이 난다.
// p1이 이기면 0, p2가 이기면 1
int battle(PLAYER p1, PLAYER p2)
{
int gun1 = p1.gun;
int s1 = p1.s;
int gun2 = p2.gun;
int s2 = p2.s;
int sum1 = gun1 + s1;
int sum2 = gun2 + s2;
if (sum1 > sum2) return 0;
if (sum1 < sum2) return 1;
if (s1 > s2) return 0;
if (s1 < s2) return 1;
return -1; // error
}
문제에 제시된 대로 점수를 누적한다.
// 2-2-1. 점수 획득
SCORE[winner]
+= ((player[winner].gun + player[winner].s)) - ((player[loser].gun + player[loser].s));
패배한 player는 총을 버린다.
버린 총은 GUN[nr][nc]에 추가한다.
그리고 패배한 player를 움직이고, 좌표를 갱신한다.
움직일 공간이 없으면 오른쪽 방향으로 회전해서 움직일 수 있는 방향을 찾아야 한다.
움직인 곳에 총이 있다면 가장 power가 높은 총을 얻는다.
// 2-2-2. 패배한 플레이어
int loserGun = player[loser].gun;
player[loser].gun = 0;
GUN[nr][nc][gIndex[nr][nc]++] = loserGun;
for (int d = 0; d < 4; d++)
{
int lnr, lnc;
lnr = nr + dr[player[loser].dir];
lnc = nc + dc[player[loser].dir];
if (lnr < 1 || lnc < 1 || lnr > N || lnc > N || curPos[lnr][lnc] != 0)
player[loser].dir = (player[loser].dir + 1) % 4;
else
{
curPos[nr][nc] = 0;
curPos[lnr][lnc] = loser;
player[loser].r = lnr;
player[loser].c = lnc;
if (gIndex[lnr][lnc]) // 총이 있는 경우
{
int gunIndex = getMaxPowerGunIndex(lnr, lnc);
if (player[loser].gun < GUN[lnr][lnc][gunIndex])
{
player[loser].gun = GUN[lnr][lnc][gunIndex];
GUN[lnr][lnc][gunIndex] = 0;
}
}
break;
}
}
이긴 플레이어는 현재 위치에서 가장 좋은 총으로 변경하고 curPos와 player의 좌표를 갱신한다.
// 2-2-3. 이긴 플레이어는 더 좋은 총으로 변경.
int gunIndex = getMaxPowerGunIndex(nr, nc);
if (player[winner].gun < GUN[nr][nc][gunIndex])
{
int tmp = player[winner].gun;
player[winner].gun = GUN[nr][nc][gunIndex];
GUN[nr][nc][gunIndex] = tmp;
}
// 이긴 플레이어의 이동.
curPos[nr][nc] = winner;
player[winner].r = nr;
player[winner].c = nc;
전체 코드는 다음과 같다.
#include <stdio.h>
#define MAX (20 + 5)
int T;
int N, M, K, X, Y;
int GUN[MAX][MAX][MAX * MAX];
int gIndex[MAX][MAX];
typedef struct st
{
int r;
int c;
int dir;
int s;
int gun;
}PLAYER;
PLAYER player[30 + 5];
// ↑, →, ↓, ←
int dr[] = { -1, 0, 1, 0 };
int dc[] = { 0, 1, 0, -1 };
int SCORE[30 + 5];
void input()
{
scanf("%d %d %d", &N, &M, &K);
for (int r = 1; r <= N; r++)
for (int c = 1; c <= N; c++)
gIndex[r][c] = 0;
for (int r = 1; r <= N; r++)
{
for (int c = 1; c <= N; c++)
{
scanf("%d", &GUN[r][c][0]);
if (GUN[r][c][0]) gIndex[r][c] = 1;
}
}
for (int m = 1; m <= M; m++) // player 번호는 1번 부터
{
SCORE[m] = 0;
int x, y, d, s;
scanf("%d %d %d %d", &x, &y, &d, &s);
player[m].r = x;
player[m].c = y;
player[m].dir = d;
player[m].s = s;
player[m].gun = 0;
}
}
void printMap(int map[MAX][MAX])
{
putchar('\n');
for (int r = 1; r <= N; r++)
{
for (int c = 1; c <= N; c++)
printf("%d ", map[r][c]);
putchar('\n');
}
putchar('\n');
}
int getMaxPowerGunIndex(int r, int c)
{
int max = -1;
int count = gIndex[r][c];
int index = 0;
for (int i = 0; i < count; i++)
{
if(max < GUN[r][c][i])
{
max = GUN[r][c][i];
index = i;
}
}
return index;
}
// p1이 이기면 0, p2가 이기면 1
int battle(PLAYER p1, PLAYER p2)
{
int gun1 = p1.gun;
int s1 = p1.s;
int gun2 = p2.gun;
int s2 = p2.s;
int sum1 = gun1 + s1;
int sum2 = gun2 + s2;
if (sum1 > sum2) return 0;
if (sum1 < sum2) return 1;
if (s1 > s2) return 0;
if (s1 < s2) return 1;
return -1; // error
}
void simulate()
{
int curPos[MAX][MAX] = { 0 };
int changeDir[] = { 2, 3, 0, 1 }; // 방향 변경
// 현재 player의 위치 표기
for (int m = 1; m <= M; m++)
curPos[player[m].r][player[m].c] = m;
for (int m = 1; m <= M; m++)
{
// 1-1. 플레이어 순차적으로 이동
PLAYER p = player[m];
int nr, nc;
nr = p.r + dr[p.dir];
nc = p.c + dc[p.dir];
// 1-1. 격자를 벗어나는 경우 반대 방향으로 변경 후 이동
if (nr < 1 || nc < 1 || nr > N || nc > N)
{
player[m].dir = p.dir = changeDir[p.dir];
nr = p.r + dr[p.dir];
nc = p.c + dc[p.dir];
}
// 2-1. 이동할 방향에 플레이어가 있는지 체크, 없는 경우
if (curPos[nr][nc] == 0)
{
// 지도에서 player 이동
curPos[p.r][p.c] = 0;
curPos[nr][nc] = m;
// 실제 player 이동
player[m].r = nr;
player[m].c = nc;
if (gIndex[nr][nc]) // 총이 있는 경우
{
int playerGun = player[m].gun;
int gunIndex = getMaxPowerGunIndex(nr, nc);
if (playerGun < GUN[nr][nc][gunIndex])
{
int tmp = player[m].gun;
player[m].gun = GUN[nr][nc][gunIndex];
GUN[nr][nc][gunIndex] = tmp;
}
}
}
else // 2-2. player가 있는 경우
{
int fighter = curPos[nr][nc]; // 만나게 된 player
int mGun = player[m].gun; // 움직인 player의 총
int mSkill = player[m].s; // 움직인 player의 skill
int fGun = player[fighter].gun;
int fSkill = player[fighter].s;
int loser, winner;
// map에서 이동한 플레이어 삭제
curPos[player[m].r][player[m].c] = 0;
if (battle(player[m], player[fighter]) == 0) // m이 이긴 경우
{
winner = m;
loser = fighter;
}
else
{
winner = fighter;
loser = m;
}
// 2-2-1. 점수 획득
SCORE[winner]
+= ((player[winner].gun + player[winner].s)) - ((player[loser].gun + player[loser].s));
// 2-2-2. 패배한 플레이어
int loserGun = player[loser].gun;
player[loser].gun = 0;
GUN[nr][nc][gIndex[nr][nc]++] = loserGun;
for (int d = 0; d < 4; d++)
{
int lnr, lnc;
lnr = nr + dr[player[loser].dir];
lnc = nc + dc[player[loser].dir];
if (lnr < 1 || lnc < 1 || lnr > N || lnc > N || curPos[lnr][lnc] != 0)
player[loser].dir = (player[loser].dir + 1) % 4;
else
{
curPos[nr][nc] = 0;
curPos[lnr][lnc] = loser;
player[loser].r = lnr;
player[loser].c = lnc;
if (gIndex[lnr][lnc]) // 총이 있는 경우
{
int gunIndex = getMaxPowerGunIndex(lnr, lnc);
if (player[loser].gun < GUN[lnr][lnc][gunIndex])
{
player[loser].gun = GUN[lnr][lnc][gunIndex];
GUN[lnr][lnc][gunIndex] = 0;
}
}
break;
}
}
// 2-2-3. 이긴 플레이어는 더 좋은 총으로 변경.
int gunIndex = getMaxPowerGunIndex(nr, nc);
if (player[winner].gun < GUN[nr][nc][gunIndex])
{
int tmp = player[winner].gun;
player[winner].gun = GUN[nr][nc][gunIndex];
GUN[nr][nc][gunIndex] = tmp;
}
// 이긴 플레이어의 이동.
curPos[nr][nc] = winner;
player[winner].r = nr;
player[winner].c = nc;
}
}
// printf("round end\n"); printMap(curPos);
}
int main(void)
{
// scanf("%d", &T);
T = 1;
for (int tc = 1; tc <= T; tc++)
{
input();
for (int k = 0; k < K; k++) simulate();
for (int m = 1; m <= M; m++) printf("%d ", SCORE[m]);
putchar('\n');
}
return 0;
}