Skip to content

Commit

Permalink
finished day 20 part 1, will work on more after work
Browse files Browse the repository at this point in the history
  • Loading branch information
devries committed Dec 20, 2023
1 parent cbcd134 commit a9961c2
Show file tree
Hide file tree
Showing 3 changed files with 307 additions and 1 deletion.
262 changes: 262 additions & 0 deletions day20p1/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
package day20p1

import (
"fmt"
"io"
"strconv"
"strings"

"aoc/utils"
)

func Solve(r io.Reader) any {
lines := utils.ReadLines(r)

modules := make(map[string]*module)
for _, ln := range lines {
name, mod := parseLine(ln)
modules[name] = mod
}

// get all sources for modules
for name, mod := range modules {
for _, d := range mod.destinations {
if md, ok := modules[d]; ok {
md.sources = append(md.sources, name)
}
}
}

// Set memory for all modules
bitpos := 0
for name, mod := range modules {
switch mod.kind {
case flipflop:
mod.bitnumber = bitpos
bitpos++
case conjunction:
mod.bitnumber = bitpos
bitpos += len(mod.sources)
}

if utils.Verbose {
fmt.Printf("%s %#v\n", name, *mod)
}
}

bits := NewBitfield(bitpos)
var lowCount, highCount uint64

sb := bits.Serialize()
for i := 0; i < 1000; i++ {
res := pressButton(sb, modules)
sb = res.serializedBits
lowCount += res.lowPulses
highCount += res.highPulses
}

return lowCount * highCount
}

type pressResult struct {
serializedBits string
lowPulses uint64
highPulses uint64
}

type pulse struct {
source string
destination string
isHigh bool
}

func pressButton(serializedState string, modules map[string]*module) pressResult {
bits, err := Deserialize(serializedState)
utils.Check(err, "deserialization error for %s", serializedState)

var lowCount uint64
var highCount uint64

queue := []pulse{{"button", "broadcaster", false}}

for len(queue) > 0 {
p := queue[0]
queue = queue[1:]
if utils.Verbose {
fmt.Printf("Pulse: %s -> %s high: %t\n", p.source, p.destination, p.isHigh)
}

switch p.isHigh {
case true:
highCount++
case false:
lowCount++
}

md, ok := modules[p.destination]

if !ok {
continue
}

switch md.kind {
case broadcast:
for _, dName := range md.destinations {
queue = append(queue, pulse{p.destination, dName, p.isHigh})
}

case flipflop:
if p.isHigh == false {
currentState := bits.Get(md.bitnumber)
newState := !currentState
switch newState {
case true:
bits.Set(md.bitnumber)
case false:
bits.Unset(md.bitnumber)
}

for _, dName := range md.destinations {
queue = append(queue, pulse{p.destination, dName, newState})
}
}
case conjunction:
idx := -1
for i, v := range md.sources {
if v == p.source {
idx = i
break
}
}
if idx < 0 {
panic(fmt.Errorf("did not find %s in sources for module %s", p.source, p.destination))
}

switch p.isHigh {
case true:
bits.Set(md.bitnumber + idx)
case false:
bits.Unset(md.bitnumber + idx)
}

sendHigh := false
for i := md.bitnumber; i < md.bitnumber+len(md.sources); i++ {
if !bits.Get(i) {
sendHigh = true
break
}
}
for _, dName := range md.destinations {
queue = append(queue, pulse{p.destination, dName, sendHigh})
}
}
}

res := pressResult{bits.Serialize(), lowCount, highCount}
return res
}

type modtype int

const (
flipflop modtype = iota
conjunction
broadcast
)

type module struct {
kind modtype
destinations []string
sources []string
bitnumber int
}

func parseLine(ln string) (string, *module) {
var ret module
var name string

components := strings.Split(ln, " -> ")
ret.destinations = strings.Split(components[1], ", ")

switch components[0][0] {
case '%':
// flip flop
ret.kind = flipflop
name = components[0][1:]
case '&':
ret.kind = conjunction
name = components[0][1:]
default:
ret.kind = broadcast
name = components[0]
}

return name, &ret
}

// -- the code below was generated by bing chat -- //
// Bitfield represents an arbitrary-length bitfield.
type Bitfield struct {
bits []uint64
}

// NewBitfield creates a new Bitfield with the specified number of bits.
func NewBitfield(numBits int) *Bitfield {
numUint64s := (numBits + 63) / 64
return &Bitfield{
bits: make([]uint64, numUint64s),
}
}

// Set sets the specified bit to 1.
func (bf *Bitfield) Set(bitIndex int) {
wordIndex, offset := bitIndex/64, uint(bitIndex%64)
bf.bits[wordIndex] |= (1 << offset)
}

// Get checks if the specified bit is set (1).
func (bf *Bitfield) Get(bitIndex int) bool {
wordIndex, offset := bitIndex/64, uint(bitIndex%64)
return (bf.bits[wordIndex] & (1 << offset)) != 0
}

// Len returns the total number of bits in the bitfield.
func (bf *Bitfield) Len() int {
return len(bf.bits) * 64
}

// -- end of bing generated code -- //

// Unset sets the specified bit to 0 (why was this not included?)
func (bf *Bitfield) Unset(bitIndex int) {
wordIndex, offset := bitIndex/64, uint(bitIndex%64)
bf.bits[wordIndex] &^= (1 << offset)
}

// Serialize to string
func (bf *Bitfield) Serialize() string {
ret := make([]string, len(bf.bits))

for i, b := range bf.bits {
ret[i] = strconv.FormatUint(b, 16)
}

return strings.Join(ret, ",")
}

// Deserialize string to bitfield
func Deserialize(s string) (*Bitfield, error) {
components := strings.Split(s, ",")

bits := make([]uint64, len(components))

for i, c := range components {
v, err := strconv.ParseUint(c, 16, 64)
if err != nil {
return nil, err
}

bits[i] = v
}
return &Bitfield{bits}, nil
}
44 changes: 44 additions & 0 deletions day20p1/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package day20p1

import (
"strings"
"testing"

"aoc/utils"
)

var testInput = `broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a`

var testInput2 = `broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output`

func TestSolve(t *testing.T) {
tests := []struct {
input string
answer uint64
}{
{testInput, 32000000},
{testInput2, 11687500},
}

if testing.Verbose() {
utils.Verbose = true
}

for _, test := range tests {
r := strings.NewReader(test.input)

result := Solve(r).(uint64)

if result != test.answer {
t.Errorf("Expected %d, got %d", test.answer, result)
}
}
}
2 changes: 1 addition & 1 deletion inputs

0 comments on commit a9961c2

Please sign in to comment.