day(16): add solution
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
6b17fef240
commit
1f8d32f78c
3 changed files with 253 additions and 0 deletions
|
@ -5,6 +5,9 @@ plugins {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation("org.junit.jupiter:junit-jupiter:5.7.0")
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|
150
src/year2021/day16/Day16.kt
Normal file
150
src/year2021/day16/Day16.kt
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package year2021.day16
|
||||||
|
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
enum class PacketType(val id: Byte) {
|
||||||
|
Sum(0),
|
||||||
|
Product(1),
|
||||||
|
Minimum(2),
|
||||||
|
LiteralValue(4),
|
||||||
|
Maximum(3),
|
||||||
|
GreaterThan(5),
|
||||||
|
LessThan(6),
|
||||||
|
EqualTo(7),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LengthType(val id: Byte) {
|
||||||
|
TotalLength(0),
|
||||||
|
Count(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Byte.toPacketType(): PacketType = PacketType.values().first { it.id == this }
|
||||||
|
private fun Byte.toLengthType(): LengthType = LengthType.values().first { it.id == this }
|
||||||
|
|
||||||
|
data class Packet(
|
||||||
|
val version: Byte,
|
||||||
|
val type: PacketType,
|
||||||
|
val bytes: List<Byte> = emptyList(),
|
||||||
|
val packets: List<Packet> = emptyList()
|
||||||
|
) {
|
||||||
|
fun versionSum(): Int = when (type) {
|
||||||
|
PacketType.LiteralValue -> version.toInt()
|
||||||
|
else -> version.toInt() + packets.sumOf(Packet::versionSum)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eval(): Long = when (type) {
|
||||||
|
PacketType.Sum -> packets.sumOf(Packet::eval)
|
||||||
|
PacketType.Product -> packets.fold(1) { result, packet -> result * packet.eval() }
|
||||||
|
PacketType.Minimum -> packets.minOf(Packet::eval)
|
||||||
|
PacketType.LiteralValue -> bytes.fold(0) { result, byte -> result.shl(4).or(byte.toLong()) }
|
||||||
|
PacketType.Maximum -> packets.maxOf(Packet::eval)
|
||||||
|
PacketType.GreaterThan -> if (packets[0].eval() > packets[1].eval()) 1 else 0
|
||||||
|
PacketType.LessThan -> if (packets[0].eval() < packets[1].eval()) 1 else 0
|
||||||
|
PacketType.EqualTo -> if (packets[0].eval() == packets[1].eval()) 1 else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Int>.chunk(from: Int, to: Int): UInt {
|
||||||
|
check(to - from <= 32)
|
||||||
|
|
||||||
|
var result = 0u
|
||||||
|
var usedBits = 0
|
||||||
|
var i = from.div(4)
|
||||||
|
|
||||||
|
// prefix
|
||||||
|
if (from.mod(4) != 0) {
|
||||||
|
val offset = 4 - from.mod(4)
|
||||||
|
val offsetMask = 1.shl(offset) - 1
|
||||||
|
|
||||||
|
result = this[i].and(offsetMask).toUInt()
|
||||||
|
|
||||||
|
usedBits += offset
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
while (4 * i < to) {
|
||||||
|
val fromFirst = this[i].toUInt()
|
||||||
|
result = result.shl(4).or(fromFirst)
|
||||||
|
|
||||||
|
i++
|
||||||
|
usedBits += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.shr(usedBits - (to - from))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseGroups(bytes: List<Int>, groups: MutableList<Byte>, from: Int): Int {
|
||||||
|
val mask = (1.shl(4) - 1).toUInt()
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
var lastByte = false
|
||||||
|
while (!lastByte) {
|
||||||
|
val byte = bytes.chunk(from + 5 * i, from + 5 * (i + 1))
|
||||||
|
groups.add(byte.and(mask).toByte())
|
||||||
|
|
||||||
|
i++
|
||||||
|
lastByte = byte.shr(4) == 0u
|
||||||
|
}
|
||||||
|
|
||||||
|
return from + 5 * i
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parsePackets(bytes: List<Int>, packets: MutableList<Packet>, from: Int): Int {
|
||||||
|
var index = from + 1
|
||||||
|
when (bytes.chunk(from, from + 1).toByte().toLengthType()) {
|
||||||
|
LengthType.TotalLength -> {
|
||||||
|
val packetsSize = bytes.chunk(index, index + 15).toInt()
|
||||||
|
index += 15
|
||||||
|
|
||||||
|
var read = 0
|
||||||
|
while (read < packetsSize) {
|
||||||
|
val (packet, newIndex) = parsePacket(bytes, index)
|
||||||
|
|
||||||
|
packets.add(packet)
|
||||||
|
read += newIndex - index
|
||||||
|
index = newIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LengthType.Count -> {
|
||||||
|
val packetCount = bytes.chunk(index, index + 11).toInt()
|
||||||
|
index += 11
|
||||||
|
|
||||||
|
repeat(packetCount) {
|
||||||
|
val (packet, newIndex) = parsePacket(bytes, index)
|
||||||
|
|
||||||
|
packets.add(packet)
|
||||||
|
index = newIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parsePacket(bytes: List<Int>, from: Int): Pair<Packet, Int> {
|
||||||
|
val version = bytes.chunk(from, from + 3).toByte()
|
||||||
|
val typeId = bytes.chunk(from + 3, from + 6).toByte().toPacketType()
|
||||||
|
|
||||||
|
val byteGroups = mutableListOf<Byte>()
|
||||||
|
val packets = mutableListOf<Packet>()
|
||||||
|
|
||||||
|
val offset = when (typeId) {
|
||||||
|
PacketType.LiteralValue -> parseGroups(bytes, byteGroups, from + 6)
|
||||||
|
else -> parsePackets(bytes, packets, from + 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair(Packet(version, typeId, byteGroups, packets), offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.toPacket(): Packet =
|
||||||
|
parsePacket(this.map { it.digitToInt(16) }, 0).first
|
||||||
|
|
||||||
|
fun part1(input: String): Int = input.toPacket().versionSum()
|
||||||
|
fun part2(input: String): Long = input.toPacket().eval()
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val input = readInput(16, "input").first()
|
||||||
|
|
||||||
|
println(part1(input))
|
||||||
|
println(part2(input))
|
||||||
|
}
|
100
src/year2021/day16/Day16KtTest.kt
Normal file
100
src/year2021/day16/Day16KtTest.kt
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package year2021.day16
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
internal class Day16KtTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun chunkWithOneElement() {
|
||||||
|
val list = listOf(15)
|
||||||
|
assertEquals(1u, list.chunk(0, 1))
|
||||||
|
assertEquals(3u, list.chunk(0, 2))
|
||||||
|
assertEquals(7u, list.chunk(0, 3))
|
||||||
|
assertEquals(15u, list.chunk(0, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun chunk() {
|
||||||
|
val list = listOf(15, 12, 10, 3)
|
||||||
|
assertEquals(15u.shl(4).or(12u).shl(4).or(10u).shl(4).or(3u), list.chunk(0, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun chunkWholeInt() {
|
||||||
|
val list = listOf(15, 12, 10, 3, 6)
|
||||||
|
assertEquals(15u.shl(4).or(12u).shl(4).or(10u).shl(4).or(3u).shl(4).or(6u), list.chunk(0, 20))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parsing() {
|
||||||
|
assertEquals(
|
||||||
|
Packet(
|
||||||
|
6.toByte(),
|
||||||
|
PacketType.LiteralValue,
|
||||||
|
bytes = listOf(7, 14, 5).map(Int::toByte),
|
||||||
|
),
|
||||||
|
"D2FE28".toPacket()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part1a() {
|
||||||
|
assertEquals(16, part1("8A004A801A8002F478"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part1b() {
|
||||||
|
assertEquals(12, part1("620080001611562C8802118E34"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part1c() {
|
||||||
|
assertEquals(23, part1("C0015000016115A2E0802F182340"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part1d() {
|
||||||
|
assertEquals(31, part1("A0016C880162017C3686B18A3D4780"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2Sum() {
|
||||||
|
assertEquals(3, part2("C200B40A82"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2Product() {
|
||||||
|
assertEquals(54, part2("04005AC33890"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2Minimum() {
|
||||||
|
assertEquals(7, part2("880086C3E88112"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2Maximum() {
|
||||||
|
assertEquals(9, part2("CE00C43D881120"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2LessThan() {
|
||||||
|
assertEquals(1, part2("D8005AC2A8F0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2GreaterThan() {
|
||||||
|
assertEquals(0, part2("F600BC2D8F"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2EqualTo() {
|
||||||
|
assertEquals(0, part2("9C005AC2F8F0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun part2Complex() {
|
||||||
|
assertEquals(1, part2("9C0141080250320F1802104A08"))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue