generated from devries/aoc_template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
finished day 20 part 1, will work on more after work
- Loading branch information
Showing
3 changed files
with
307 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
Submodule inputs
updated
from deca86 to 55ae37