Completed both parts of Day 20 puzzle with help from reddit thread for the second part (detect that there are 4 subgraphs and use LCM)

main
oabrivard 2 years ago
parent d8eddf80e3
commit c431e8741b

@ -0,0 +1,184 @@
package day20
import (
"fmt"
"strings"
)
const (
UNTYPED = "?"
FLIP_FLOP = "%"
BROADCASDTER = "b"
CONJUNCTION = "&"
LOW = 0
HIGH = 1
)
type Module struct {
kind string
name string
status bool
ouputs []string
inputs []string
inputMem map[string]int
}
type Pulse struct {
kind int
src string
dst string
}
func parseModules(lines []string) map[string]*Module {
modules := map[string]*Module{}
modulesByOrderOfAppearance := []*Module{}
for _, line := range lines {
kind := string(line[0])
parts := strings.Split(line, " -> ")
name := parts[0][1:]
outputs := strings.Split(parts[1], ", ")
if kind == "b" {
name = "b" + name
}
module := &Module{kind, name, false, outputs, []string{}, map[string]int{}}
modules[name] = module
modulesByOrderOfAppearance = append(modulesByOrderOfAppearance, module)
}
// setup inputs
for _, module := range modulesByOrderOfAppearance {
for _, outputName := range module.ouputs {
m, ok := modules[outputName]
if !ok {
// create ad'hoc untyped module
m = &Module{UNTYPED, outputName, false, []string{}, []string{}, map[string]int{}}
modules[outputName] = m
modulesByOrderOfAppearance = append(modulesByOrderOfAppearance, m)
}
m.inputs = append(m.inputs, module.name)
m.inputMem[module.name] = LOW
}
}
return modules
}
func runSim(modules map[string]*Module, pulseQueue []*Pulse, count int) (int, int) {
countLow := 0
countHigh := 0
for len(pulseQueue) > 0 {
pulse := pulseQueue[0]
pulseQueue[0] = nil
pulseQueue = pulseQueue[1:]
module := modules[pulse.dst]
switch module.kind {
case BROADCASDTER:
for _, dst := range module.ouputs {
newPulse := &Pulse{pulse.kind, module.name, dst}
pulseQueue = append(pulseQueue, newPulse)
if newPulse.kind == LOW {
countLow++
} else {
countHigh++
}
}
case FLIP_FLOP:
if pulse.kind == LOW {
var pulseKind int
if module.status {
pulseKind = LOW
} else {
pulseKind = HIGH
}
module.status = !module.status // flip status
for _, dst := range module.ouputs {
newPulse := &Pulse{pulseKind, module.name, dst}
pulseQueue = append(pulseQueue, newPulse)
if newPulse.kind == LOW {
countLow++
} else {
countHigh++
}
}
}
case CONJUNCTION:
if pulse.kind == HIGH && module.name == "qn" {
fmt.Println("qn from ", pulse.src, " : ", count)
}
module.inputMem[pulse.src] = pulse.kind
allHigh := true
for _, v := range module.inputMem {
if v == LOW {
allHigh = false
break
}
}
var pulseKind int
if allHigh {
pulseKind = LOW
} else {
pulseKind = HIGH
}
for _, dst := range module.ouputs {
newPulse := &Pulse{pulseKind, module.name, dst}
pulseQueue = append(pulseQueue, newPulse)
if newPulse.kind == LOW {
countLow++
} else {
countHigh++
}
}
case UNTYPED:
module.status = pulse.kind == HIGH
}
}
return countLow, countHigh
}
func Part1(lines []string, pressCount int) (map[string]*Module, int) {
modules := parseModules(lines)
sumLow := 0
sumHigh := 0
for i := 0; i < pressCount; i++ {
queue := []*Pulse{}
queue = append(queue, &Pulse{LOW, "button", "broadcaster"})
countLow, countHigh := runSim(modules, queue, i+1)
countLow++
sumLow += countLow
sumHigh += countHigh
}
return modules, sumLow * sumHigh
}
func Part2(lines []string, pressCount int) (map[string]*Module, int) {
modules := parseModules(lines)
for i := 0; i < pressCount; i++ {
queue := []*Pulse{}
queue = append(queue, &Pulse{LOW, "button", "broadcaster"})
runSim(modules, queue, i+1)
}
return modules, 0
}

@ -0,0 +1,125 @@
package day20
import (
"testing"
"gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils"
)
func TestParseModule(t *testing.T) {
lines := []string{
`broadcaster -> a, b, c`,
`%a -> b`,
`%b -> c`,
`%c -> inv`,
`&inv -> a`,
}
result := parseModules(lines)
if len(result) != 5 {
t.Fatalf("expected 5, got %d", len(result))
}
}
func Test1Part1(t *testing.T) {
lines := []string{
`broadcaster -> a, b, c`,
`%a -> b`,
`%b -> c`,
`%c -> inv`,
`&inv -> a`,
}
result, count := Part1(lines, 1)
if count != 32 {
t.Fatalf("expected 32, got %d", count)
}
if result["a"].status || result["b"].status || result["c"].status {
t.Fatalf("expected flip-flop a, b and c to be off, got %v %v %v", result["a"].status, result["b"].status, result["c"].status)
}
}
func Test2Part1(t *testing.T) {
lines := []string{
`broadcaster -> a, b, c`,
`%a -> b`,
`%b -> c`,
`%c -> inv`,
`&inv -> a`,
}
_, count := Part1(lines, 1000)
if count != 32000000 {
t.Fatalf("expected 32000000, got %d", count)
}
}
func Test3Part1(t *testing.T) {
lines := []string{
"broadcaster -> a",
"%a -> inv, con",
"&inv -> b",
"%b -> con",
"&con -> output",
}
result, _ := Part1(lines, 3)
if !result["output"].status || !result["a"].status || result["b"].status {
t.Fatalf("expected flip-flop a to be on, b to be off and output to be on, got %v %v %v", result["a"].status, result["b"].status, result["output"].status)
}
result, _ = Part1(lines, 4)
if !result["output"].status || result["a"].status || result["b"].status {
t.Fatalf("expected flip-flop a and b to be off and output to be on, got %v %v %v", result["a"].status, result["b"].status, result["output"].status)
}
}
func Test4Part1(t *testing.T) {
lines := []string{
"broadcaster -> a",
"%a -> inv, con",
"&inv -> b",
"%b -> con",
"&con -> output",
}
_, count := Part1(lines, 1000)
if count != 11687500 {
t.Fatalf("expected 11687500, got %d", count)
}
}
func TestPart1WithInput(t *testing.T) {
lines := utils.ReadLines("input.txt")
_, count := Part1(lines, 1000)
if count != 925955316 {
t.Fatalf("expected 925955316, got %d", count)
}
}
func TestPart2WithInput(t *testing.T) {
lines := utils.ReadLines("input.txt")
Part2(lines, 5000)
// use firt four different values printed during simulation that starts with "qn from"
// then find the LCM of the values. The LCM is the solution to this problem.
// Found it thanks to explanation from jwezorek and JuniorBirdman1115 on
// https://www.reddit.com/r/adventofcode/comments/18mmfxb/2023_day_20_solutions/
/*
qn from jx : 3907
qn from qz : 3911
qn from tt : 3931
qn from cq : 4021
LCM is 241528477694627 (computed it with https://www.calculatorsoup.com/calculators/math/lcm.php)
*/
}

@ -0,0 +1,58 @@
%cg -> mt, hb
%sp -> xm
%nr -> hf, mt
broadcaster -> tl, gd, zb, gc
&qz -> qn
%df -> hd
%vg -> rm, kx
%gm -> mt, md
%ls -> hc
%lq -> zq, fx
&zd -> bz, kg, zb, lf, sq, zk, jx
%lz -> mt
%sq -> zk
%zn -> kx, tc
&zq -> mb, hc, qz, ql, tl, ls
&mt -> zm, tt, mh, gd, md
%lm -> mb, zq
%hf -> mt, sm
%hb -> mh, mt
%rm -> kx
%gc -> kx, sp
&cq -> qn
%mh -> jt
%zm -> nr
%xm -> kx, ld
&jx -> qn
&qn -> rx
%mp -> qt, kx
%zk -> vj
%hd -> mp, kx
%tl -> zq, hl
%zb -> zd, ph
%cl -> zd
&tt -> qn
%ld -> zn
%js -> lq, zq
%sm -> mt, lz
%qt -> vg, kx
%md -> cg
%vj -> bz, zd
%qs -> zd, fs
%mb -> ps
&kx -> cq, gc, sp, df, ld
%hc -> lm
%tc -> df, kx
%ps -> js, zq
%fs -> qc, zd
%hl -> jj, zq
%bz -> qs
%jj -> zq, ql
%ql -> ls
%ph -> kg, zd
%qc -> cl, zd
%lf -> sq
%kg -> lf
%fx -> zq
%jt -> zm, mt
%gd -> gm, mt
Loading…
Cancel
Save