Advent of Code 2023 Day 4 - Kotlin

cotton·2023년 12월 4일

Advent of Code 2023

목록 보기
4/4

🔥 매년 12월 1일부터 12월 25일까지 한 문제씩 프로그래밍 퍼즐을 제공하는 사이트다. Eric Wastl이 2015년에 만든 사이트로, 재림절 달력(Advent calendar)을 매일 열어보듯이 크리스마스까지 꾸준히 문제를 해결해나간다는 컨셉이다. 퍼즐은 UTC-5 기준으로 자정, 한국시간으로는 오후 2시에 공개된다.
출처 : 나무위키 - Advent of Code

Advent of Code가 2023년 돌아왔습니다! 25일간 프로그래밍 퍼즐을 풀어봅시다.

Jetbrains는 이번에도 어김없이 Kotlin을 통해 25일간의 AoC에 참가하면 코틀린 독점 상품을 받을 수 있는 기회를 준다고 합니다!

자세한 내용은 코틀린 블로그에 개제된 Tackle Advent of Code 2023 With Kotlin and Win Prizes! 를 참고하세요.

오늘은 네 번째 문제인 Scratchcard 문제를 풀어봤습니다.

Part 1

문제 풀이

간단하게 복권이라고 생각하면 됩니다.

Scratchcard는 외국의 연금 복권 같은 거라고 생각하면 편합니다.

Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11

| 를 기준으로 왼쪽은 당첨 번호, 오른쪽은 보유 번호입니다.

1개 당첨되면 1점을 주고, 그 이후에 1개 당첨될 때마다 점수가 2배씩 늘어납니다.

내 풀이

1개 당첨되면 1점을 주고, 그 이후에 1개씩 당첨될 때마다 점수가 2배 늘어난다?

즉 2의 제곱 수입니다. 2^(당첨 개수 - 1)

| 를 기준으로 왼쪽 리스트와 오른쪽 리스트를 가져오고 합친 이후, 중복을 제거한 리스트와 사이즈의 차를 가져옵니다. 이 값이 당첨된 개수입니다.

그 이후 2의 제곱 수 로직에 넣어 값을 도출합니다.

fun part1(input: List<String>): Int {
    var sum = 0
    input.forEach { str ->
        val regex = "\\d+".toRegex()
        val list = str.substringAfter(":").split("|").map {
            regex.findAll(it).toList().map { it.value.toInt() }
        }
        val winnings = list.first()
        val nums = list.last()
        val mergeList = winnings + nums
        val size = mergeList.size - mergeList.distinct().size
        sum += Math.pow(2.0, (size - 1).toDouble()).toInt()
    }
    return sum
}

Part 2

문제 풀이

Card 1에서 4개가 당첨되면, Card 2~5의 사본을 추가합니다. 즉 당첨된 개수의 다음 카드들을 1장씩 추가합니다. Card 2~5는 2개가 됩니다.

Card 2에서 2개가 당첨되면, 마찬가지로 Card 3~4을 1장씩 추가합니다. 이 때, Card 2는 Card 1에 의해 1개가 추가되었으므로 Card 3~4도 마찬가지로 2장이 추가됩니다.

이를 반복했을 때, 총 카드의 개수가 몇개가 되는지를 도출하는 문제입니다.

내 풀이

fun part2(input: List<String>): Int {
    val winCountList = MutableList(size = input.size) { 0 }

    input.forEachIndexed { idx, str ->
        val regex = "\\d+".toRegex()
        val gameList = str.substringAfter(":").split("|").map {
            regex.findAll(it).toList().map { it.value.toInt() }
        }
        val winnings = gameList.first()
        val nums = gameList.last()
        val mergeList = winnings + nums
        winCountList[idx] = mergeList.size - mergeList.distinct().size
    }

    val cardCountList = MutableList(size = input.size) { 1 }
    for (idx in input.indices) {
        val winCount = winCountList[idx]
        for (i in 0..<winCount) {
            val sIdx = idx + i + 1
            if (cardCountList.getOrNull(sIdx) != null) {
                cardCountList[sIdx] += cardCountList[idx]
            }
        }
    }
    return cardCountList.sum()
}
  1. 우선 각 카드의 당첨 개수 리스트를 생성합니다.
    • 당첨 개수를 저장할 리스트를 주어진 카드 문자열의 개수만큼 초기화합니다.
    • Part 1과 동일하게 당첨 숫자를 도출하고, 이를 리스트에 추가합니다.
    • 위를 반복하면 각 카드의 당첨 개수로 구성된 리스트를 만들 수 있습니다.
  2. 이번에는 카드의 개수를 저장할 리스트를 생성합니다.
    • 카드 개수를 저장할 리스트를 주어진 카드 문자열의 개수만큼 초기화합니다.
    • 0부터 당첨 개수의 IntRange를 기준으로 반복문을 돌리고 현재 인덱스에 해당 값을 더합니다.
    • 이를 반복합니다. 마지막 카드에 가까워질 수록 추가할 수 있는 카드가 없을 수 있으므로 getOrNull 를 이용해 크래시를 방지합니다.
    • 위를 반복하면 카드 개수 리스트를 만들 수 있습니다.
  3. 카드 개수 리스트의 총 합계를 반환합니다.

다른 사람들 풀이

🔥 다른 사람들의 풀이는 KotlinLang Slack에 참여하면 확인할 수 있습니다!

오늘 문제는 난이도가 그렇게 높지 않아 다른 사람들의 풀이도 비슷해 생략합니다!

profile
안드로이드 개발자

0개의 댓글