~macaptain/advent-of-code

31a03ca57ebe77dc5dfa2917497755e3f58dbe5c — Michael Captain 2 years ago 2a4b1aa
Complete 2021 Day 16
A src/main/advent2021/Day16.kt => src/main/advent2021/Day16.kt +112 -0
@@ 0,0 1,112 @@
package advent2021

import common.productOf
import common.takeWhileInclusive
import java.lang.Exception

class Packet(val binary: String) {
    fun version() = binary.take(3).toInt(2)
    fun typeId() = binary.substring(3, 6).toInt(2)

    fun literalValue(): Long? {
        if (typeId() != 4) return null
        val groups = binary.drop(6).chunked(5)
        return groups.asSequence().takeWhileInclusive { it.first() == '1' }.map {
            if (it.count() < 4) return@map 0
            it.substring(1)
        }.joinToString("").toLong(2)
    }

    fun lengthTypeId(): Int? {
        if (typeId() == 4) return null
        return binary[6].digitToInt(2)
    }

    private fun subpacketsLength(): Int? {
        if (lengthTypeId() != 0) return null
        return binary.substring(7, 7 + 15).toInt(2)
    }

    private fun numSubpackets(): Int? {
        if (lengthTypeId() != 1) return null
        return binary.substring(7, 7 + 11).toInt(2)
    }

    fun subpackets(): List<Packet> {
        subpacketsLength().let {
            if (it != null) return parseMultiple(binary.substring(22, 22 + it))
        }
        numSubpackets().let {
            if (it != null) return parseMultiple(binary.substring(18), numSubpackets())
        }
        return listOf()
    }

    fun allSubpackets(): List<Packet> {
        return listOf(this) + subpackets().flatMap { it.allSubpackets() }
    }

    fun length(): Int {
        val headerLength = 6
        if (numSubpackets() != null) return headerLength + 1 + 11 + subpackets().sumOf { it.length() }
        subpacketsLength().let { if (it != null) return headerLength + 1 + 15 + it }
        // literal value
        val groups =
            binary.drop(headerLength).chunked(5).asSequence().takeWhileInclusive { it.first() == '1' }.joinToString("")
        return headerLength + groups.count()
    }

    fun eval(): Long {
        val typeId = typeId()
        val subpackets = subpackets()
        return when(typeId) {
            0 -> subpackets.sumOf { it.eval() }
            1 -> subpackets.productOf { it.eval() }
            2 -> subpackets.minOf { it.eval() }
            3 -> subpackets.maxOf { it.eval() }
            4 -> literalValue()!!
            5 -> if (subpackets[0].eval() > subpackets[1].eval()) 1 else 0
            6 -> if (subpackets[0].eval() < subpackets[1].eval()) 1 else 0
            7 -> if (subpackets[0].eval() == subpackets[1].eval()) 1 else 0
            else -> throw Exception("unknown type id $typeId")
        }
    }

    companion object {
        fun fromHex(hex: String): Packet {
            return Packet(hex.map {
                it.digitToInt(16).toString(2).padStart(4, '0')
            }.joinToString(""))
        }

        fun parseMultiple(binary: String, numPackets: Int? = null): List<Packet> {
            val packets = mutableListOf<Packet>()
            var subbinary = binary
            while (subbinary.isNotEmpty()) {
                if (numPackets != null && packets.count() == numPackets) return packets
                val packet = Packet(subbinary)
                val length = packet.length()
                val trimmedPacket = Packet(subbinary.substring(0, length))
                packets.add(trimmedPacket)
                subbinary = subbinary.substring(length)
            }
            return packets
        }
    }
}

fun solve16a(transmission: String): Int {
    val packet = Packet.fromHex(transmission)
    return packet.allSubpackets().sumOf { it.version() }
}

fun solve16b(transmission: String): Long {
    val packet = Packet.fromHex(transmission)
    return packet.eval()
}

fun main() {
    val hex = input("day16").first()
    println(solve16a(hex)) // 897
    println(solve16b(hex)) // 9485076995911
}
\ No newline at end of file

M src/main/common/Extensions.kt => src/main/common/Extensions.kt +8 -1
@@ 43,4 43,11 @@ fun <T> List<T>.elementPairs() = sequence {

fun <T> Sequence<T>.repeat() = sequence { while (true) yieldAll(this@repeat) }

fun Iterable<Int>.product() = this.fold(1) { elt, acc -> elt * acc }
\ No newline at end of file
fun Iterable<Int>.product() = this.fold(1) { elt, acc -> elt * acc }
fun <T> Iterable<T>.productOf(selector: (T) -> Long): Long {
    var product = 1L
    for (element in this) {
        product *= selector(element)
    }
    return product
}
\ No newline at end of file

A src/main/resources/advent2021/day16_input.txt => src/main/resources/advent2021/day16_input.txt +1 -0
@@ 0,0 1,1 @@
620D7800996600E43184312CC01A88913E1E180310FA324649CD5B9DA6BFD107003A4FDE9C718593003A5978C00A7003C400A70025400D60259D400B3002880792201B89400E601694804F1201119400C600C144008100340013440021279A5801AE93CA84C10CF3D100875401374F67F6119CA46769D8664E76FC9E4C01597748704011E4D54D7C0179B0A96431003A48ECC015C0068670FA7EF1BC5166CE440239EFC226F228129E8C1D6633596716E7D4840129C4C8CA8017FCFB943699B794210CAC23A612012EB40151006E2D4678A4200EC548CF12E4FDE9BD4A5227C600F80021D08219C1A00043A27C558AA200F4788C91A1002C893AB24F722C129BDF5121FA8011335868F1802AE82537709999796A7176254A72F8E9B9005BD600A4FD372109FA6E42D1725EDDFB64FFBD5B8D1802323DC7E0D1600B4BCDF6649252B0974AE48D4C0159392DE0034B356D626A130E44015BD80213183A93F609A7628537EB87980292A0D800F94B66546896CCA8D440109F80233ABB3ABF3CB84026B5802C00084C168291080010C87B16227CB6E454401946802735CA144BA74CFF71ADDC080282C00546722A1391549318201233003361006A1E419866200DC758330525A0C86009CC6E7F2BA00A4E7EF7AD6E873F7BD6B741300578021B94309ABE374CF7AE7327220154C3C4BD395C7E3EB756A72AC10665C08C010D0046458E72C9B372EAB280372DFE1BCA3ECC1690046513E5D5E79C235498B9002BD132451A5C78401B99AFDFE7C9A770D8A0094EDAC65031C0178AB3D8EEF8E729F2C200D26579BEDF277400A9C8FE43D3030E010C6C9A078853A431C0C0169A5CB00400010F8C9052098002191022143D30047C011100763DC71824200D4368391CA651CC0219C51974892338D0

A src/test/advent2021/Day16KtTest.kt => src/test/advent2021/Day16KtTest.kt +86 -0
@@ 0,0 1,86 @@
package advent2021

import org.junit.jupiter.api.Test

import org.junit.jupiter.api.Assertions.*

internal class Day16KtTest {

    val exampleA1 = "8A004A801A8002F478"
    val exampleA2 = "620080001611562C8802118E34"
    val exampleA3 = "C0015000016115A2E0802F182340"
    val exampleA4 = "A0016C880162017C3686B18A3D4780"

    @Test
    fun literalValuePacketParsing() {
        val hex = "D2FE28"
        val packet = Packet.fromHex(hex)
        assertEquals("110100101111111000101000", packet.binary)
        assertEquals(6, packet.version())
        assertEquals(4, packet.typeId())
        assertEquals(2021, packet.literalValue())
    }

    @Test
    fun lengthTypeZeroOperatorPacketParsing() {
        val hex = "38006F45291200"
        val packet = Packet.fromHex(hex)
        assertEquals("00111000000000000110111101000101001010010001001000000000", packet.binary)
        assertEquals(1, packet.version())
        assertEquals(6, packet.typeId())
        assertEquals(null, packet.literalValue())
        assertEquals(0, packet.lengthTypeId())
        assertEquals(2, packet.subpackets().count())
        assertEquals(10, packet.subpackets().first().literalValue())
        assertEquals("11010001010", packet.subpackets().first().binary)
        assertEquals(20, packet.subpackets().last().literalValue())
        assertEquals("0101001000100100", packet.subpackets().last().binary)
    }

    @Test
    fun lengthTypeOneOperatorPacketParsing() {
        val hex = "EE00D40C823060"
        val packet = Packet.fromHex(hex)
        assertEquals("11101110000000001101010000001100100000100011000001100000", packet.binary)
        assertEquals(7, packet.version())
        assertEquals(3, packet.typeId())
        assertEquals(null, packet.literalValue())
        assertEquals(1, packet.lengthTypeId())
        assertEquals(3, packet.subpackets().count())
        assertEquals(1, packet.subpackets()[0].literalValue())
        assertEquals("01010000001", packet.subpackets()[0].binary)
        assertEquals(2, packet.subpackets()[1].literalValue())
        assertEquals("10010000010", packet.subpackets()[1].binary)
        assertEquals(3, packet.subpackets()[2].literalValue())
        assertEquals("00110000011", packet.subpackets()[2].binary)
    }

    @Test
    fun example16a() {
        assertEquals(16, solve16a(exampleA1))
        assertEquals(12, solve16a(exampleA2))
        assertEquals(23, solve16a(exampleA3))
        assertEquals(31, solve16a(exampleA4))
    }

    val exampleB1 = "C200B40A82"
    val exampleB2 = "04005AC33890"
    val exampleB3 = "880086C3E88112"
    val exampleB4 = "CE00C43D881120"
    val exampleB5 = "D8005AC2A8F0"
    val exampleB6 = "F600BC2D8F"
    val exampleB7 = "9C005AC2F8F0"
    val exampleB8 = "9C0141080250320F1802104A08"

    @Test
    fun example16b() {
        assertEquals(3, solve16b(exampleB1))
        assertEquals(54, solve16b(exampleB2))
        assertEquals(7, solve16b(exampleB3))
        assertEquals(9, solve16b(exampleB4))
        assertEquals(1, solve16b(exampleB5))
        assertEquals(0, solve16b(exampleB6))
        assertEquals(0, solve16b(exampleB7))
        assertEquals(1, solve16b(exampleB8))
    }
}
\ No newline at end of file