aoc2021/day16/p2.nim
2021-12-18 15:08:15 -07:00

164 lines
3.4 KiB
Nim

import os, bitops, sugar
import std/[strutils, sequtils]
type
ReadResult =
tuple[value: int, bitsRead: int]
ParseResult = int
PacketType = enum
Sum
Product
Minimum
Maximum
Literal
GreaterThan
LessThan
Equal
proc hexToBin(hex: char): int {.inline.} =
case hex:
of '0': 0b0000
of '1': 0b0001
of '2': 0b0010
of '3': 0b0011
of '4': 0b0100
of '5': 0b0101
of '6': 0b0110
of '7': 0b0111
of '8': 0b1000
of '9': 0b1001
of 'A', 'a': 0b1010
of 'B', 'b': 0b1011
of 'C', 'c': 0b1100
of 'D', 'd': 0b1101
of 'E', 'e': 0b1110
of 'F', 'f': 0b1111
else: 0
template readBits(packet: string, numBits: int): ReadResult =
let bytesToRead = (((bitPos mod 4) + numBits) div 4) + 1
var skipBits = bitPos mod 4
var numBitsToRead = numBits
var res: ReadResult
var i = 0
while numBitsToRead > 0 and i < bytesToRead and ((bitPos div 4) + i) < packet.len:
var bin = packet[(bitPos div 4) + i].hexToBin
var currentBit = 4
case skipBits:
of 1: currentBit = 3
of 2: currentBit = 2
of 3: currentBit = 1
else: discard
skipBits = 0
while currentBit > 0 and numBitsToRead > 0:
let currentBVal = bin.testBit(currentBit - 1).int
res.value += currentBVal shl (numBitsToRead - 1)
res.bitsRead += 1
currentBit -= 1
numBitsToRead -= 1
i.inc
bitPos += res.bitsRead
res
template readLiteral(packet: string): ReadResult =
var
indicator = packet.readBits(1)
nextSection: ReadResult
res: ReadResult
res.bitsRead += indicator.bitsRead
while indicator.value == 1:
nextSection = packet.readBits(4)
res.value = res.value shl 4
res.value += nextSection.value
res.bitsRead += nextSection.bitsRead
indicator = packet.readBits(1)
res.bitsRead += indicator.bitsRead
# read the last line
nextSection = packet.readBits(4)
res.value = res.value shl 4
res.value += nextSection.value
res.bitsRead += nextSection.bitsRead
res
proc parsePacket(packet: string, bitPos: var int, verSum: var int): ParseResult =
let
version = packet.readBits(3)
packetType = packet.readBits(3)
verSum += version.value
var operation: (int, int) -> int
case packetType.value.PacketType:
of Literal:
# Read Literal value
return packet.readLiteral().value
of Sum:
operation = (x, y: int) => x + y
of Product:
operation = (x, y: int) => x * y
of Minimum:
operation = (x, y: int) => min(x, y)
of Maximum:
operation = (x, y: int) => max(x, y)
of GreaterThan:
operation = (x, y: int) => (x > y).int
of LessThan:
operation = (x, y: int) => (x < y).int
of Equal:
operation = (x, y: int) => (x == y).int
let lengthTypeId = packet.readBits(1)
var acc: seq[int]
if lengthTypeId.value == 0:
# 15 bit mode
let subPacketLen = packet.readBits(15).value
var
bitsRead = 0
initialBitPos = bitPos
while bitsRead < subPacketLen:
acc.add packet.parsePacket(bitPos, verSum)
bitsRead = bitPos - initialBitPos
else:
# 11 bit mode
let numSubPackets = packet.readBits(11).value
for i in 0 ..< numSubPackets:
acc.add packet.parsePacket(bitPos, verSum)
return acc.foldl(operation(a, b))
proc main() =
let
fileName = paramStr(1)
var packet: string
try:
packet = fileName.readFile().strip()
except IOError:
packet = paramStr(1)
var bitPos = 0
var verSum = 0
echo packet.parsePacket(bitPos, verSum)
main()