/*
* Problem :: 2174 / 로봇 시뮬레이션
*
* Kind :: Simulation
*
* Insight
* - 의외로 스펙이 좀 많다...?
* 고려해야할 점도 많다...?
* 실수할 수 있는 부분이 꽤 있다...?
* + 일단, 주어진 땅에서 y 좌표가 아래서 위로 증가하는 것으로
* 주어진 것은 크게 문제되지 않는다
* 항상 y 좌표가 위에서 아래로 증가하는 식으로 풀었는데...
* 땅을 위아래 뒤집어준다고 생각하면 된다
* + 로봇들의 데이터를 저장하는 전역배열이 필요하다
* 로봇의 번호, 좌표, 방향을 항상 추적해야 한다
* + 방향을 바꾸어주는 명령같은 경우
* 이를 따로 처리하는 함수를 만들기 보다는
* 열거형과 전역배열을 사용해서 처리해주자
* + 로봇의 데이터를 변수로 선언해서 가져와야할 때
* 참조변수로 선언해서 다루어주자
* + 몇 번 로봇이 또 다른 몇 번 로봇과 충돌했는지 쉽게 파악하기 위해서
* 로봇들의 위치를 저장하는 2차원전역배열이 필요하다
* # 위의 점들을 생각하며 구현해주자...
* -> 하드코딩은 안 한것 같은데 왜이리 코드가 길다냐 ㅠㅠ
*
* Point
* - 아무래도 명령을 수행하는 함수를 잘 만드는게 이 문제를 푸는데 있어 핵심이다
* + 이번에 풀었던 코드에서는 명령을
* (명령을 받는 로봇, 명령의 종류, 명령의 반복 횟수) 로 보아서
* 그 함수 안에서 반복까지 시켜주었는데
* 처음 풀었던 코드에서는 명령을 (명령을 받는 로봇, 명령의 종류) 로 보고
* 밖의 함수에서 반복시켜주었다
* # 최적화면에서는 전자의 경우가 <= 함수 호출 횟수가 적음
* 쉬운구현면에서는 후자의 경우가 <= F 명령 구현이 더 쉬움
* 더 좋은 것 같다
*
* - 좌표의 사용을
* 구조체에는 x,y 순서
* 함수에서 사용은 y,x 순서로 했더니
* 헷갈려서 좀 틀렸다... 다음부터는 문제의 양식보다는 일관성을 더 중요하게 다뤄야지
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
#include <algorithm>
using namespace std;
#define endl '\n'
// Set up : Global Variables
int A, B; /* 가로의 길이, 세로의 길이 */
int N, M; /* 로봇의 개수, 명령의 개수 */
int T[100+1][100+1]; /* T[i][j] = (i,j) 에 있는 로봇의 번호, 없으면 0 */
struct Robot { int no, x, y, dir; }; /* 구조체 Robot : 로봇의 데이터 */
Robot R[100+1]; /* R[i] = i 번 로봇의 데이터 */
struct Command { int no; char type; int rep; }; /* 구조체 Command : 명령 데이터 */
Command C[100+1]; /* C[i] = i 번째 명령 데이터 */
enum Direction { NORTH, WEST, EAST, SOUTH };
int dy[4] = {+1, 0, 0, -1}; /* 차례로 북쪽, 서쪽, 동쪽, 남쪽으로 이동시 y 좌표의 증감 */
int dx[4] = {0, -1, +1, 0}; /* 차례로 북쪽, 서쪽, 동쪽, 남쪽으로 이동시 x 좌표의 증감 */
enum Rotate { LEFT, RIGHT };
int D[4][2] = {
{WEST, EAST}, /* 북쪽의 왼쪽 = 서쪽, 북쪽의 오른쪽 = 동쪽 */
{SOUTH, NORTH}, /* 서쪽의 왼쪽 = 남쪽, 서족의 오른쪽 = 북쪽 */
{NORTH, SOUTH}, /* 동쪽의 왼쪽 = 북쪽, 동쪽의 오른쪽 = 남쪽 */
{EAST, WEST} /* 남쪽의 왼쪽 = 동쪽, 남쪽의 오른쪽 = 서족 */
};
struct Result { bool status; int X, Y; }; /* 구조체 Result : 명령 실행 결과 데이터 */
#define NONE (-1) /* 구조체 Result 에 쓰이는 상수 */
#define WALL 0 /* 구조체 Result 에 쓰이는 상수 */
#define ERROR false /* 구조체 Result 에 쓰이는 상수 */
// Set up : Functions Declaration
bool isValid(int y, int x);
Result exe(Command cmd);
Result typeL(Command cmd);
Result typeR(Command cmd);
Result typeF(Command cmd);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
cin >> A >> B;
cin >> N >> M;
for (int i=1; i<=N; i++) {
int x, y; char d;
cin >> x >> y >> d;
int dir;
switch (d) {
case 'N': dir = 0; break;
case 'W': dir = 1; break;
case 'E': dir = 2; break;
case 'S': dir = 3; break;
default: throw runtime_error("Invalid Direction");
}
R[i] = {i, x, y, dir};
T[y][x] = i;
}
for (int i=1; i<=M; i++) {
int no; char type; int rep;
cin >> no >> type >> rep;
C[i] = {no, type, rep};
}
// Process
bool isThereError = false;
Result res{}; /* 명령의 실행 결과 */
for (int i=1; i<=M; i++) {
res = exe(C[i]);
/* 결과의 상태가 ERROR 라면 */
if (res.status == ERROR) {
isThereError = true;
break;
}
}
// Control : Output
if (not(isThereError))
cout << "OK" << endl;
else {
/* 결과의 Y 값이 벽이라면 */
(res.Y == WALL) ? /* 삼항 연산자 사용 */
/* 로봇이 벽에 충돌한 경우 */
printf("Robot %d crashes into the wall\n", res.X) :
/* 로봇이 다른 로봇과 충돌한 경우 */
printf("Robot %d crashes into robot %d\n", res.X, res.Y);
}
}
// Helper Functions
bool isValid(int y, int x)
/* 주어진 좌표 (y,x) 가 유효하면 true 를 반환, 그 외 false 를 반환 */
{
return y >= 1 && y <= B && x >= 1 && x <= A;
}
Result exe(Command cmd)
/* 명령 cmd 를 실행한 결과를 반환 */
{
/* 명령의 type 에 따라 적절한 수행 함수를 호출 */
char type = cmd.type;
switch (type) {
case 'L': return typeL(cmd);
case 'R': return typeR(cmd);
case 'F': return typeF(cmd);
default: throw runtime_error("Invalid Type");
}
}
Result typeL(Command cmd)
/* 타입 L 명령을 실행하고 그 결과를 반환 */
{
Robot &r = R[cmd.no];
for (int i=0; i<cmd.rep; i++) {
r.dir = D[r.dir][LEFT];
} return {true, NONE, NONE};
}
Result typeR(Command cmd)
/* 타입 R 명령을 실행하고 그 결과를 반환 */
{
Robot &r = R[cmd.no];
for (int i=0; i<cmd.rep; i++) {
r.dir = D[r.dir][RIGHT];
} return {true, NONE, NONE};
}
Result typeF(Command cmd)
/* 타입 F 명령을 실행하고 그 결과를 반환 */
{
Robot &r = R[cmd.no];
int cy = r.y, cx = r.x; /* 처음 로봇의 위치 */
for (int i=0; i<cmd.rep; i++) {
/* 현재 로봇이 바라보는 방향으로 한 칸 이동 */
int ny = cy + dy[r.dir];
int nx = cx + dx[r.dir];
/* 이동한 칸이 주어진 땅의 밖으로 벗어난 칸이라면 */
if (not(isValid(ny, nx)))
/* 결과 : 에러발생, 충돌한 로봇 번호, 벽 */
return {ERROR, r.no, WALL};
/* 이동한 칸에 다른 로봇이 있다면 */
if (T[ny][nx] != 0)
/* 결과 : 에러발생, 충돌한 로봇 번호, 충돌당한 로봇 번호 */
return {ERROR, r.no, T[ny][nx]};
cy = ny, cx = nx; /* 로봇의 현재 위치 갱신 */
}
swap(T[r.y][r.x], T[cy][cx]); /* 땅에서 로봇의 현재 위치 갱신 및 저장 */
r.y = cy, r.x = cx; /* 로봇의 현재 위치 저장 */
return {true, NONE, NONE};
}