233 lines
3.9 KiB
Go
233 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/jdkaplan/advent-of-code/aoc"
|
|
)
|
|
|
|
func main() {
|
|
text := aoc.Input().ReadFile("day16.txt")
|
|
b := parse(text)
|
|
fmt.Println(part1(b))
|
|
fmt.Println(part2(b))
|
|
}
|
|
|
|
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 part2(b bits) uint {
|
|
p, _ := packet(b)
|
|
return p.Value()
|
|
}
|
|
|
|
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
|
|
Value() uint
|
|
}
|
|
|
|
type Literal struct {
|
|
V, T uint
|
|
Val uint
|
|
}
|
|
|
|
func (l Literal) Version() uint { return l.V }
|
|
func (l Literal) Type() uint { return l.T }
|
|
func (l Literal) Value() uint { return l.Val }
|
|
|
|
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 (o Operator) Value() uint {
|
|
switch o.T {
|
|
case 0: // sum
|
|
var sum uint
|
|
for _, p := range o.Packets {
|
|
sum += p.Value()
|
|
}
|
|
return sum
|
|
case 1: // product
|
|
prod := uint(1)
|
|
for _, p := range o.Packets {
|
|
prod *= p.Value()
|
|
}
|
|
return prod
|
|
case 2: // minimum
|
|
min := o.Packets[0].Value()
|
|
for _, p := range o.Packets[1:] {
|
|
if v := p.Value(); v < min {
|
|
min = v
|
|
}
|
|
}
|
|
return min
|
|
case 3: // maximum
|
|
max := uint(0)
|
|
for _, p := range o.Packets {
|
|
if v := p.Value(); v > max {
|
|
max = v
|
|
}
|
|
}
|
|
return max
|
|
case 4: // literal
|
|
panic("Unexpected literal")
|
|
case 5: // greater than
|
|
if l := len(o.Packets); l != 2 {
|
|
panic(fmt.Sprintf("Expected 2 packets, got %d", l))
|
|
}
|
|
v1 := o.Packets[0].Value()
|
|
v2 := o.Packets[1].Value()
|
|
if v1 > v2 {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
case 6: // less than
|
|
if l := len(o.Packets); l != 2 {
|
|
panic(fmt.Sprintf("Expected 2 packets, got %d", l))
|
|
}
|
|
v1 := o.Packets[0].Value()
|
|
v2 := o.Packets[1].Value()
|
|
if v1 < v2 {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
case 7: // equal to
|
|
if l := len(o.Packets); l != 2 {
|
|
panic(fmt.Sprintf("Expected 2 packets, got %d", l))
|
|
}
|
|
v1 := o.Packets[0].Value()
|
|
v2 := o.Packets[1].Value()
|
|
if v1 == v2 {
|
|
return 1
|
|
} else {
|
|
return 0
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("Unexpected type: %d", 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
|
|
}
|