
쉬운 문제지만 설명이 길어서 일단 쫄았다. 근데 설명이 긴 이유가 어려워서 긴 게 아니라, 설명이 아주 자세해서 긴 것이었다. 그리고 나는 최단거리라는 말만 나오는 쪼는 경향이 있는데 다행히 대각선 뭐 이런 조건 없이 위아래로 왔다 갔다 하고 비교 횟수도 2번 밖에 되지 않아서 잘 풀 수 있었다.
손과 버튼의 위치는 2차원 배열에서 ROW, COL로 표현하였고, 손가락의 위치를 담을 변수와 타겟 위치를 담을 static 변수를 먼저 만들었다. 작동 순서는 어느 손인지 알아내고 → 움직이고(업데이트하고) → 결과출력하는 것으로 구상을 해서 각 메소드를 만들었다.
setHanded()handed는 왼손/오른손잡이를 나타내는 변수다. 초기 parameter로 넘어오는 값인데, 이미 인수로 넘어왔던 값을 또 인수로 넘기기 싫고, 함수 단계가 깊어질 것 같아서 static으로 선언했다.
출력에서 원하는 형태인 R이나 L로 바로 반환하기 위해 본 메소드에서 적절하게 변환해서 저장한다.
setTargetPosition()타켓(number)의 좌표를 만들어 static 변수에 저장하는 메소드다. ROW는 위에서부터 0~3, COL은 왼쪽부터 0~2로 지정했다. 번호가 일정하게 3 단위로 나뉘어져 있는 것이 눈에 잘 보여서 바로 몫과 나머지를 활용해서 ROW와 COL 번호를 알아낼 수 있었다.
다른 메소드들은 딱히 설명이 없어도 직관적으로 이해할 수 있을 것 같다. 클린코드 책을 읽은 이후 함수가 4줄 이내로 정리되고 indent도 한 칸으로 유지가 되어 있으면 기분이 좋다.
moveHandToTarget()에서 배열 내의 값들을 각각 업데이트하지 않고 currentLeft = target 이딴 식으로 배열 자체를 assign했다. 이런 어이 없는 실수를 스스로 발견하지도 못하고 디버깅까지 찍고 나서야 알아챘다. 집중하자.수정 전
private String getNextMoveHand(int number) {
    switch (number) {
        case 1:
        case 4:
        case 7:
            return "L";
        case 3:
        case 6:
        case 9:
            return "R";
        default:
            return getNextMoveHandByDistance();
    }
}
수정 후
private String getNextMoveHand(int number) {
    return switch (number) {
        case 1, 4, 7 -> "L";
        case 3, 6, 9 -> "R";
        default -> getNextMoveHandByDistance();
    };
}
인텔리제이가 알아서 고쳐줬다. 역시 똑똑한 IDE다.
Spring Boot 쓰면서 리턴문 자체에 람다식을 넣는 것을 몇 번 봤는데 아직 람다 문법을 정확하게 몰라서 IDE가 고치는 수준만 이해할 수 있다. 부끄럽다. 람다문 공부를 해야겠다.
class Solution {
    static final int ROW = 0;
    static final int COL = 1;
    static int[] currentLeft = {3, 0};
    static int[] currentRight = {3, 2};
    static int[] target = new int[2];
    static String handed;
    public String solution(int[] numbers, String hand) {
        setHanded(hand);
        StringBuilder sb = new StringBuilder();
        for (int number : numbers) {
            setTargetPosition(number);
            String nextMoveHand = getNextMoveHand(number);
            moveHandToTarget(nextMoveHand);
            sb.append(nextMoveHand);
        }
        return sb.toString();
    }
    private void setHanded(String hand) {
        if (hand.equals("right")) handed = "R";
        else handed = "L";
    }
    private String getNextMoveHand(int number) {
        return switch (number) {
            case 1, 4, 7 -> "L";
            case 3, 6, 9 -> "R";
            default -> getNextMoveHandByDistance();
        };
    }
    private String getNextMoveHandByDistance() {
        int leftDistance = getDistanceForLeft();
        int rightDistance = getDistanceForRight();
        return getCloserHand(leftDistance, rightDistance);
    }
    private void setTargetPosition(int number) {
        target[ROW] = (number - 1) / 3;
        target[COL] = (number + 2) % 3;
        if (number == 0) {
            target[ROW] = 3;
            target[COL] = 1;
        }
    }
    private int getDistanceForLeft() {
        int rowDistance = Math.abs(currentLeft[ROW] - target[ROW]);
        int colDistance = Math.abs(currentLeft[COL] - target[COL]);
        return rowDistance + colDistance;
    }
    private int getDistanceForRight() {
        int rowDistance = Math.abs(currentRight[ROW] - target[ROW]);
        int colDistance = Math.abs(currentRight[COL] - target[COL]);
        return rowDistance + colDistance;
    }
    private String getCloserHand(int leftDistance, int rightDistance) {
        if (leftDistance > rightDistance) return "R";
        else if (leftDistance < rightDistance) return "L";
        else return handed;
    }
    private void moveHandToTarget(String nextMove) {
        if (nextMove.equals("R")) {
            currentRight[ROW] = target[ROW];
            currentRight[COL] = target[COL];
        } else if (nextMove.equals("L")) {
            currentLeft[ROW] = target[ROW];
            currentLeft[COL] = target[COL];
        }
    }
}
카카오 인턴 코테 대비를 위해 2020 인턴 코테 기출을 다시 풀기 시작했다. int[]로 position을 미리 계산해놓으니 코드가 더 직관적으로 변했다.
반 년 전에 짠 코드보다 지금 짠 코드가 확실히 더 나은 것 같다. getDistanceForLeft(), getDistanceForRight() 머 이렇게 같은 내용의 함수를 두 개로 분리해놓은 것도 파라미터를 활용했으면 더 간단했을텐데 쓸데없이 static으로 사용하느라 코드가 더 복잡해졌던 것 같다.
class Solution {
    
    private static final int ROW = 0;
    private static final int COL = 1;
    private int[] currentLeft = {3, 0};
    private int[] currentRight = {3, 2};
    public String solution(int[] numbers, String hand) {
        StringBuilder sb = new StringBuilder();
        for (int number : numbers)
            sb.append(push(number, hand));
        return sb.toString();
    }
    private char push(int number, String handed) {
        int[] target = getPosition(number);
        char hand = whichHand(target, handed);
        moveHandToNumber(target, hand);
        return hand;
    }
    private char whichHand(int[] target, String handed) {
        if (target[COL] == 0) return 'L';
        else if (target[COL] == 2) return 'R';
        char hand;
        int leftDist = dist(currentLeft, target);
        int rightDist = dist(currentRight, target);
        if (leftDist < rightDist) hand = 'L';
        else if (leftDist > rightDist) hand = 'R';
        else hand = (handed.equals("left") ? 'L' : 'R');
        return hand;
    }
    private int dist(int[] current, int[] target) {
        return Math.abs(current[ROW] - target[ROW])
                + Math.abs(current[COL] - target[COL]);
    }
    private int[] getPosition(int targetNumber) {
        int[] position = new int[2];
        if (targetNumber == 0) {
            position[ROW] = 3;
            position[COL] = 1;
        } else if (targetNumber % 3 == 1) {
            position[ROW] = targetNumber / 3;
            position[COL] = 0;
        } else if (targetNumber % 3 == 2) {
            position[ROW] = targetNumber / 3;
            position[COL] = 1;
        } else if (targetNumber % 3 == 0) {
            position[ROW] = targetNumber / 3 - 1;
            position[COL] = 2;
        }
        return position;
    }
    private void moveHandToNumber(int[] target, char hand) {
        if (hand == 'L') currentLeft = target;
        else if (hand == 'R') currentRight = target;
    }
}