각종 후기/우아한테크코스

[우아한 테크코스 3기] LEVEL 1 회고 (43일차)

제이온 (Jayon) 2021. 3. 16.

안녕하세요? 제이온입니다.

 

오늘은 루터 회관에서 오프라인 수업을 받았고, 새 미션인 체스 미션을 진행하였습니다.

 

 

데일리 미팅

오늘 데일리 미팅 진행자는 피카였고, 신서유기 게임을 진행해 주셨습니다. 게임의 방식은 사회자가 두 글자를 말하면, 플레이어는 임의의 2글자를 말해서 말이 되는 4글자를 만드는 것이었습니다. 대략 1 ~ 2초 내에 대답을 해야 해서 꽤나 난이도가 높았습니다.

 

그래서 두 조 모두 한 바퀴 동안 모든 조원이 맞히기 못하였습니다. 보다 못한 피카는 카테고리를 사자성어로 정해 주었고, 제가 속하지 않은 조가 결국 성공하였습니다.

 

비록, 게임에서는 졌지만 매우 즐거웠습니다 ㅎㅎ

 

 

람다와 스트림 강의

오늘은 씨유가 람다와 스트림 관한 수업을 해 주셨습니다. 간단하게 내용을 설명해 주셨고, 스스로 학습을 해 볼 수 있도록 가벼운 예제 문제를 내 주셨습니다. 오랜만에 reduce()와 limit() 등 평소에 잘 사용하지 않았던 스트림 문법을 복습하는 좋은 기회였다고 생각합니다.

 

또한, 스트림은 원본 리스트에서 별도로 복사해서 만드는 것이므로 map을 통한 요소의 변화가 일어나도 원본 리스트에는 영향이 미치지 않는 것을 알게 되었습니다.

 

 

    public static List<Integer> doubleNumbers(List<Integer> numbers) {
        return numbers.stream()
            .map(number -> 2 * number)
            .collect(Collectors.toList());
    }

 

 

위와 같이 numbers의 요소를 2배씩 늘리는 연산을 하고 싶은 경우, 굳이 numbers의 복사 리스트를 새로 만들 필요가 없다는 것입니다.

 

 

페어 프로그래밍

오늘 체스 미션와 함께 하게 된 크루는 '아론'이었습니다. 아론은 데일리 미팅과 같은 조였기 때문에 어느 정도 친했고, 그만큼 개발을 편하게 할 수 있겠다는 생각이 들었습니다.

 

체스 미션은 총 5단계로 되어 있으며, 3단계까지 페어와 수행한 다음 코드리뷰를 신청하는 방식이었습니다. 그리고 이전까지는 미션 마감일이 목요일 오후 6시였지만, 이번에는 난이도가 높은 관계로 다음주 월요일 오후 6시까지로 연장되었습니다.

 

먼저, 1단계 내용부터 보겠습니다.

 

 

 

 

1단계는 단순히 체스판을 보여주는 것입니다. 그리고 위 사진에는 가로축과 세로축, 그리고 (rank) 출력문이 있는데 이것은 실제로 출력하지는 않습니다.

 

 

 

 

이렇게 간단한 게임 시작 출력문을 띄우고, start를 입력받으면 초기 체스판을 출력하는 것입니다.

 

 

처음에는 어렵지 않다고 생각하였으나, 초기 체스의 위치를 하드 코딩해야 한다는 것에 염증을 느껴서 아론과 많이 고생하였습니다. 마음 같아서는 key를 위치, value를 체스말로 정의한 Map에 하드 코딩으로 넣어줄까 생각도 하였습니다. 예를 들어, 현재 Rook는 (a, 1), (h, 1), (a, 8), (h, 8)에 있으므로 4줄 put을 해 주는 것이죠.

 

하지만, 아론과 저는 둘다 하드 코딩과 반복된 구조를 굉장히 싫어하였기 때문에 이를 어떻게든 추상화하려고 노력하였습니다.

 

 

먼저, initialize() 메소드가 반복되므로 이것을 인터페이스로 분리하기로 하였습니다.

 

 

public interface LocationInitializer {
    Map<Position, Piece> initialize();
}

 

 

이 인터페이스를 상속받아서 각자 체스 위치를 설정하도록 하였습니다.

 

 

public class PawnInitializer implements LocationInitializer {
    private static final List<String> HORIZONTALS = Arrays.stream(Horizontal.values())
            .map(Horizontal::getSymbol)
            .collect(Collectors.toList());
    private static final List<String> VERTICALS_WHITE = Collections.singletonList("2");
    private static final List<String> VERTICALS_BLACK = Collections.singletonList("7");

    @Override
    public Map<Position, Piece> initialize() {
        final Map<Position, Piece> chessBoard = new HashMap<>();
        for (String horizontal : HORIZONTALS) {
            VERTICALS_BLACK.forEach(vertical -> chessBoard.put(new Position(horizontal, vertical), new Pawn(true)));
            VERTICALS_WHITE.forEach(vertical -> chessBoard.put(new Position(horizontal, vertical), new Pawn(false)));
        }
        return chessBoard;
    }
}

 

 

예를 들어, 폰 체스 말은 가로축은 전체, 세로축은 2와 7에만 존재하므로 위와 같이 List를 정의하고 반복문을 돌면서 폰 체스말이 존재하는 위치를 Map으로 담을 수 있습니다.

 

같은 방식으로 나머지 모든 말에 대해서도 자기 위치를 정해줍니다.

 

 

public class BoardInitializer {
    private static final List<LocationInitializer> locationInitializers;
    private static final List<String> HORIZONTAL_RANGE = Arrays.stream(Horizontal.values())
            .map(Horizontal::getSymbol)
            .collect(Collectors.toList());
    private static final List<String> VERTICAL_RANGE = Arrays.stream(Vertical.values())
            .map(Vertical::getSymbol)
            .collect(Collectors.toList());

    static {
        locationInitializers = Arrays.asList(new PawnInitializer(), new RookInitializer(), new KingInitializer(),
                new QueenInitializer(), new BishopInitializer(), new KnightInitializer());
    }

    public static Map<Position, Piece> initializeBoard() {
        final Map<Position, Piece> chessBoard = new HashMap<>();
        for (String horizontal : HORIZONTAL_RANGE) {
            VERTICAL_RANGE.forEach(vertical -> chessBoard.put(new Position(horizontal, vertical), new Blank()));
        }
        locationInitializers.forEach(initializer -> chessBoard.putAll(initializer.initialize()));
        return chessBoard;
    }

}

 

 

그리고 BordInitializer 안에서 위와 같이 LocationInitializer 리스트를 만들고, 그 안에 각각 체스말들의 이니셜라이저를 넣어줍니다. 이제, 이 리스트 안에 요소를 돌면서 initialize()를 수행하면 각 체스말의 위치가 담긴 Map을 얻어올 수 있습니다. 그리고 조각나 있는 Map을 하나로 합치기 위하여 putAll() 메소드를 사용하였습니다.

 

참고로, 처음에는 모든 객체를 Blank()로 초기화하였는데 이것은 빈 공간을 표현하기 위함입니다.

 

 

체스말과 위치 객체를 정의하는 것은 그렇게 어렵지 않았지만, 위 매핑 과정이 굉장히 힘들었습니다. 그래도 아론과 제 성향이 비슷해서 열심히 잘 원하는 바를 이루어 낸 것 같습니다.

 

 

정리

오늘은 페어 프로그래밍을 하느라 루터 회관이 닫힐 때까지 달렸습니다. 매우 고된 일이었지만, 그럭저럭 1단계 미션을 마무리해서 만족합니다. 아직 갈 길이 멀지만, 첫 단추를 잘 끼웠고 구조도 어느 정도 추상화를 잘 시켰다는 생각이 듭니다.

 

내일은 온라인이지만 아론과 사당역에서 만나기로 하였습니다. 체스 미션 2단계까지 완수했으면 하는 바람입니다.

댓글

추천 글