[Baekjoon] 3190번: 뱀 - Java

2023. 2. 10. 15:00Computer Sciences/Problem Solve

https://www.acmicpc.net/problem/3190

 

3190번: 뱀

 'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임

www.acmicpc.net

문제 설명

문제 이해 자체는 어렵지 않으나 구현 및 시뮬레이션 문제라 시간이 걸린다. 그리고 방향 전환 알고리즘에 대한 감이 없다면 처음에는 조금 시간이 걸릴 수 있다.

또한 뱀의 길이 및 방향 전환 관리를 위해 덱, 큐 자료구조를 사용했다.

풀이 방법

구현 문제라 딱히 설명할 것은 없다. 전체 코드는 다음과 같다.

package baekjoon.queue;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class BOJ3190 {
    public static void main(String[] args) throws IOException {
        Game game = new Game();
        game.setGame();
        int playTime = game.playGame();
        System.out.println(playTime);
    }

    static class Game {
        private int playTime = 0;
        private int boardSize;
        private int[][] board;

        // 오른쪽, 아래, 왼쪽, 위
        private int[] dx = {0, 1, 0, -1};
        private int[] dy = {1, 0, -1, 0};
        private int currentDirection = 0; // 기본 - 우측이동

        private Queue<DirectionInfo> directionInfos = new LinkedList<>();
        private LinkedList<Position> snakeBodies = new LinkedList<>();

        public void setGame() throws IOException {
            snakeBodies.offer(Position.of(1, 1)); // 시작 위치는 [1, 1]

            try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
                boardSize = Integer.parseInt(br.readLine());
                board = new int[boardSize + 1][boardSize + 1];

                // 사과 채우기
                int K = Integer.parseInt(br.readLine());
                for (int i = 0; i < K; i++) {
                    int[] position = Arrays.stream(br.readLine().split(" "))
                            .mapToInt(Integer::parseInt).toArray();
                    board[position[0]][position[1]] = 1;
                }

                // 방향 전환 정보 저장
                int L = Integer.parseInt(br.readLine());
                for (int i = 0; i < L; i++) {
                    String[] tokens = br.readLine().split(" ");
                    int X = Integer.parseInt(tokens[0]);
                    char C = tokens[1].charAt(0);

                    DirectionInfo directionInfo = DirectionInfo.of(X, C);
                    directionInfos.offer(directionInfo);
                }
            }
        }

        public int playGame() {
            while (true) {
                playTime++;

                Position head = snakeBodies.peek();
                Position newHead = Position.of(head.getX() + dx[currentDirection],
                        head.getY() + dy[currentDirection]);

                if (isBumpedAgainstBorder(newHead))
                    break;

                if (isTouchedItself(snakeBodies, newHead))
                    break;

                if (isExistApple(board, newHead)) {
                    eatApple(board, newHead);
                } else {
                    moveTail(snakeBodies);
                }

                snakeBodies.offerFirst(newHead);

                if (isItTimeForChangeOfDirection(playTime, directionInfos)) {
                    switch (getNextDirection()) {
                        case 'D':
                            currentDirection = (currentDirection + 1) % 4;
                            break;
                        case 'L':
                            currentDirection = (currentDirection + 3) % 4;
                            break;
                        default:
                            break;
                    }
                }
            }
            return playTime;
        }

        private char getNextDirection() {
            return directionInfos.poll().getDirection();
        }

        private void moveTail(LinkedList<Position> snakeBodies) {
            snakeBodies.pollLast();
        }

        private void eatApple(int[][] board, Position newHead) {
            board[newHead.getX()][newHead.getY()] = 0;
        }

        private boolean isExistApple(int[][] board, Position newHead) {
            return board[newHead.getX()][newHead.getY()] == 1;
        }

        private boolean isItTimeForChangeOfDirection(int playTime,
                Queue<DirectionInfo> directionInfos) {
            return !directionInfos.isEmpty() && directionInfos.peek().getTime() == playTime;
        }

        private boolean isTouchedItself(LinkedList<Position> snakeBodies, Position newHead) {
            return snakeBodies.stream().anyMatch(
                    (body) -> body.getX() == newHead.getX() && body.getY() == newHead.getY());
        }

        private boolean isBumpedAgainstBorder(Position newHead) {
            return newHead.getX() <= 0 || newHead.getY() <= 0 || newHead.getX() > boardSize
                    || newHead.getY() > boardSize;
        }
    }

    final static class Position {
        private int x;
        private int y;

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        private Position(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public static Position of(int x, int y) {
            return new Position(x, y);
        }
    }

    final static class DirectionInfo {
        private int time;
        private char direction;

        public int getTime() {
            return time;
        }

        public char getDirection() {
            return direction;
        }

        private DirectionInfo(int time, char direction) {
            this.time = time;
            this.direction = direction;
        }

        public static DirectionInfo of(int time, char direction) {
            return new DirectionInfo(time, direction);
        }
    }
}