가로 A * 세로 B 크기의 필드에 N개의 로봇들이 있다. N개의 로봇들은 명령에 따라 움직인다. 명령은 다음과 같다.
Robot Number, Command, Repeat
Robot Number : 로봇의 배치 순서이다. 먼저 배치된 로봇이 1번이고 이후로 1씩 증가한다.
Command : 명령의 종류로 L은 반시계 90도, R은 시계 90도 회전이다. F는 앞으로 1칸 움직인다.
Repeat : 명령의 반복 횟수이다.
- 1 F 3은 1번 로봇을 앞으로 3칸 움직이는 것이다.
잘못된 명령을 내리는 경우도 생긴다. 잘못된 명령을 내리면 추가 명령을 하지 않으며 오류 내용을 출력한다.
벽에 충돌하는 경우 : 필드는 벽으로 둘러 쌓여 있다. 벽에 충돌하면 Robot X crashes into the wall을 출력한다.
다른 로봇과 충돌하는 경우 : 다른 로봇과 충돌하면 Robot X crashes into robot Y를 출력한다.
- X와 Y는 각각 로봇의 번호이다.
만약 모든 명령에 오류가 없다면 OK를 출력한다.
// 필드의 크기를 입력
StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
int A = Integer.parseInt(tokenizer.nextToken()); // 필드의 가로 크기
int B = Integer.parseInt(tokenizer.nextToken()); // 필드의 세로 크기
// 로봇 배치와 명령의 수를 입력
tokenizer = new StringTokenizer(reader.readLine());
int N = Integer.parseInt(tokenizer.nextToken()); // 로봇 배치 수
int M = Integer.parseInt(tokenizer.nextToken()); // 명령 수
// Command 센터 생성
CommandCenter commandCenter = new CommandCenter(A, B);
// N번의 로봇 배치
while (N-- > 0) {
// 로봇 배치 정보를 입력
tokenizer = new StringTokenizer(reader.readLine());
int x = Integer.parseInt(tokenizer.nextToken()); // 로봇의 x축 위치
int y = Integer.parseInt(tokenizer.nextToken()); // 로봇의 y축 위치
String direction = tokenizer.nextToken(); // 로봇의 방향
// 로봇 배치
commandCenter.deployRobot(x, y, direction);
}
// 오류 명령일 때 예외 처리
try {
// M번의 명령
while (M-- > 0) {
// 명령 정보 입력
tokenizer = new StringTokenizer(reader.readLine());
int rno = Integer.parseInt(tokenizer.nextToken()); // 로봇의 번호
String command = tokenizer.nextToken(); // 명령
int repeat = Integer.parseInt(tokenizer.nextToken()); // 반복 회수
// 로봇에게 명령
commandCenter.commandRobot(rno, command, repeat);
}
// 명령에 오류가 없으면 OK 출력
result.append("OK");
} catch (RobotException exception) {
// 명령에 오류가 있으면 오류문 출력
result.append(exception.getMessage());
}
// 명령을 처리하는 중앙 센터
public class CommandCenter {
private final List<Robot> robots = new ArrayList<>(); // 로봇 번호 = index + 1
private final Robot[][] robotMap; // 로봇 위치 = (row, column) = (x, y)
public CommandCenter(int maxX, int maxY) {
robotMap = new Robot[maxX][maxY];
}
/**
* 로봇을 배치한다.
* @param x 로봇의 x축 위치
* @param y 로봇의 y축 위치
* @param direction 로봇이 바라보는 방향
*/
public void deployRobot(int x, int y, String direction) {
Robot robot = new Robot(robots.size() + 1, x, y, direction);
robots.add(robot);
robotMap[x - 1][y - 1] = robot;
}
/**
* 로봇에게 명령을 내린다.
* @param rno 명령을 내리고자 하는 로봇의 번호
* @param command 명령
* @param repeat 명령 반복 횟수
*/
public void commandRobot(int rno, String command, int repeat) {
Robot robot = robots.get(rno - 1);
while (repeat-- > 0) robot.readCommand(command, robotMap);
}
}
public class Robot {
private final int rno; // 로봇의 번호
private int x, y; // 로봇의 위치
private Direction direction; // 로봇이 바라보는 방향
public Robot(int rno, int x, int y, String direction) {
this.rno = rno;
this.x = x;
this.y = y;
this.direction = Direction.createDirection(direction);
}
/**
* 명령을 해석하고 처리한다.
* @param command 명령
* @param robotMap 전체 로봇 위치 정보
*/
public void readCommand(String command, Robot[][] robotMap) {
// 반시계 방향으로 90도 회전
if (command.equals("L")) direction = direction.preDirection();
// 시계 방향으로 90도 회전
else if (command.equals("R")) direction = direction.postDirection();
// 바라보는 방향으로 전진
else if (command.equals("F")) moveForward(robotMap);
// throw new RobotException("Unresolved command");
}
/**
* 로봇이 바라보는 방향으로 1칸 전진한다.
* @param robotMap 전체 로봇 위치 정보
*/
private void moveForward(Robot[][] robotMap) {
int nextX = x + direction.getX(); // 전진한 후의 x축 위치
int nextY = y + direction.getY(); // 전진한 후의 y축 위치
// 전진 가능 여부를 검사한다.
try {
// 전진할 위치에 다른 로봇이 있으면 다른 로봇과의 충돌 예외 발생
if (robotMap[nextX - 1][nextY - 1] != null)
throw new RobotException(rno, robotMap[nextX - 1][nextY - 1].rno);
} catch (ArrayIndexOutOfBoundsException e) {
// 전진할 위치에 벽이 있으면 벽과의 충돌 예외 발생
throw new RobotException(rno);
}
// 충돌이 발생하지 않는다면 전진
robotMap[x - 1][y - 1] = null;
robotMap[(x = nextX) - 1][(y = nextY) - 1] = this;
}
}
public class RobotException extends RuntimeException {
public RobotException(String message) {
super(message);
}
/**
* 벽과의 충돌 예외를 던진다.
* @param rno 충돌하는 로봇의 번호
*/
public RobotException(int rno) {
super("Robot " + rno + " crashes into the wall");
}
/**
* 다른 로봇과의 충돌 예외를 던진다.
* @param from 충돌하는 로봇의 번호
* @param to 충돌 당하는 로봇의 번호
*/
public RobotException(int from, int to) {
super("Robot " + from + " crashes into robot " + to);
}
}
public enum Direction {
NORTH(0, "N", 0, 1),
EAST(1, "E", 1, 0),
SOUTH(2, "S", 0, -1),
WEST(3, "W", -1, 0);
private final int index; // 방향 번호
private final String direction; // 방향
private final int x, y; // 방향 정보
Direction(int index, String direction, int x, int y) {
this.index = index;
this.direction = direction;
this.x = x;
this.y = y;
}
/**
* 방향값을 통해 방향 방향 인스턴스를 생성한다.
* @param dir 방향값 (N, E, S, W)
* @return 방향 인스턴스
*/
public static Direction createDirection(String dir) {
for (Direction direction : values())
if (direction.direction.equals(dir)) return direction;
throw new RuntimeException("Wrong direction is given");
}
/**
* 반시계 방향으로 90도 회전한다.
* @return 회전한 방향
*/
public Direction preDirection() {
return values()[(this.index + values().length - 1) % values().length];
}
/**
* 시계 방향으로 90도 회전한다.
* @return 회전한 방향
*/
public Direction postDirection() {
return values()[(this.index + 1) % values().length];
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}