diff --git a/2021/aoc/aoc.go b/2021/aoc/aoc.go index 9ecfc27..69fa93a 100644 --- a/2021/aoc/aoc.go +++ b/2021/aoc/aoc.go @@ -39,6 +39,14 @@ func MustInt(s string) int { return i } +func MustHex(s string) int { + i, err := strconv.ParseInt(s, 16, 0) + if err != nil { + panic(err) + } + return int(i) +} + type Inputs struct { fs fs.FS } diff --git a/2021/day16/main.go b/2021/day16/main.go new file mode 100644 index 0000000..687d515 --- /dev/null +++ b/2021/day16/main.go @@ -0,0 +1,154 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/jdkaplan/advent-of-code/aoc" +) + +func main() { + text := aoc.Input().ReadFile("day16.txt") + fmt.Println(part1(parse(text))) +} + +type bits []int + +func (b bits) String() string { + var sb strings.Builder + for _, x := range b { + fmt.Fprintf(&sb, "%b", x) + } + return sb.String() +} + +func (b bits) Uint() uint { + var u uint + for _, i := range b { + u <<= 1 + u += uint(i) + } + return u +} + +func (b bits) Consume(n uint) (prefix uint, extra bits) { + return b[:n].Uint(), b[n:] +} + +func parse(text string) (b bits) { + for _, char := range strings.Split(text, "") { + x := aoc.MustHex(char) + for i := 3; i >= 0; i-- { + b = append(b, (x>>i)&1) + } + } + return +} + +func part1(b bits) uint { + p, _ := packet(b) + return sumVersions(p) +} + +func sumVersions(p Packet) uint { + switch p := p.(type) { + case Literal: // no-op + return p.Version() + case Operator: + sum := p.Version() + for _, inner := range p.Packets { + sum += sumVersions(inner) + } + return sum + default: + panic(fmt.Sprintf("Unknown data type: %T", p)) + } +} + +type Packet interface { + Version() uint + Type() uint +} + +type Literal struct { + V, T uint + Value uint +} + +func (l Literal) Version() uint { return l.V } +func (l Literal) Type() uint { return l.T } + +type Operator struct { + V, T uint + Packets []Packet +} + +func (o Operator) Version() uint { return o.V } +func (o Operator) Type() uint { return o.T } + +func packet(b bits) (Packet, bits) { + v, b := b.Consume(3) + t, b := b.Consume(3) + switch t { + case 4: + var val uint + val, b = literal(b) + return Literal{v, t, val}, b + default: + var ps []Packet + ps, b = operator(b) + return Operator{v, t, ps}, b + } +} + +func literal(b bits) (uint, bits) { + var val uint + for { + var n uint + n, b = b.Consume(5) + + val <<= 4 + val += n & 0b01111 + + if n&0b10000 == 0 { + break + } + } + return val, b +} + +func operator(b bits) ([]Packet, bits) { + lt, b := b.Consume(1) + var ps []Packet + if lt == 0 { + var l uint + l, b = b.Consume(15) + taken := uint(0) + for l-taken > 6 { // min length for a packet + var p Packet + oldlen := len(b) + p, b = packet(b) + ps = append(ps, p) + taken += uint(oldlen - len(b)) + } + b = padding(b, l-taken) + return ps, b + } else { + var n uint + n, b = b.Consume(11) + for i := uint(0); i < n; i++ { + var p Packet + p, b = packet(b) + ps = append(ps, p) + } + return ps, b + } +} + +func padding(b bits, n uint) bits { + u, b := b.Consume(n) + if u != 0 { + panic(fmt.Sprintf("Expected padding, got nonzero bits: %b", u)) + } + return b +}