1
0
Fork 0

day(16): add solution

Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
Matej Focko 2021-12-16 10:37:53 +01:00
parent 6b17fef240
commit 1f8d32f78c
3 changed files with 253 additions and 0 deletions

View file

@ -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
View 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))
}

View 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"))
}
}