diff --git a/challenge/day23/a.go b/challenge/day23/a.go index 0572513..0afda1b 100644 --- a/challenge/day23/a.go +++ b/challenge/day23/a.go @@ -23,6 +23,11 @@ const ( ) func a(challenge *challenge.Input) int { + inputs, outputs := bootNetwork(challenge) + return <-NewRouter(inputs, outputs).RouteTraffic() +} + +func bootNetwork(challenge *challenge.Input) ([]chan int, []<-chan int) { program := <-challenge.Lines() computers := make([]*intcode.CPU, networkSize) inputs := make([]chan int, networkSize) @@ -37,7 +42,7 @@ func a(challenge *challenge.Input) int { go func(i int) { // Boot the nic and assign the ID - fmt.Printf("[%2d] booting...\n", i) + fmt.Printf("[%3d] booting...\n", i) go computers[i].Run() inputs[i] <- i startup.Done() @@ -45,6 +50,5 @@ func a(challenge *challenge.Input) int { } startup.Wait() - - return <-NewRouter(inputs, outputs).RouteTraffic() + return inputs, outputs } diff --git a/challenge/day23/b.go b/challenge/day23/b.go new file mode 100644 index 0000000..d23776c --- /dev/null +++ b/challenge/day23/b.go @@ -0,0 +1,24 @@ +package day23 + +import ( + "fmt" + + "github.com/nlowe/aoc2019/challenge" + "github.com/spf13/cobra" +) + +var B = &cobra.Command{ + Use: "23b", + Short: "Day 23, Problem B", + Run: func(_ *cobra.Command, _ []string) { + fmt.Printf("Answer: %d\n", b(challenge.FromFile())) + }, +} + +func b(challenge *challenge.Input) int { + inputs, outputs := bootNetwork(challenge) + r := NewRouter(inputs, outputs) + r.trackNat = true + + return <-r.RouteTraffic() +} diff --git a/challenge/day23/router.go b/challenge/day23/router.go index 10569a9..32b6acf 100644 --- a/challenge/day23/router.go +++ b/challenge/day23/router.go @@ -1,12 +1,17 @@ package day23 import ( + "context" "fmt" "reflect" + "runtime" + "time" + + "golang.org/x/sync/semaphore" ) const ( - addressAnswer = 255 + addressNAT = 255 routerBufferSize = 16 ) @@ -21,7 +26,11 @@ type router struct { ins []chan int outs []<-chan int - bufs []chan packet + bufs []chan packet + idleTracker *semaphore.Weighted + + trackNat bool + lastNATPacket packet } func NewRouter(in []chan int, out []<-chan int) *router { @@ -34,6 +43,8 @@ func NewRouter(in []chan int, out []<-chan int) *router { ins: in, outs: out, bufs: bufs, + + idleTracker: semaphore.NewWeighted(int64(len(in))), } } @@ -41,6 +52,9 @@ func (r *router) RouteTraffic() <-chan int { answer := make(chan int) go r.runPacketAggregator(answer) go r.runTransmitter() + if r.trackNat { + go r.runNATHandler(answer) + } return answer } @@ -61,13 +75,22 @@ func (r *router) runPacketAggregator(answer chan int) { x := <-r.outs[sender] y := <-r.outs[sender] - if dst == addressAnswer { - answer <- y - return + parsed := packet{sender, dst, x, y} + + if dst == addressNAT { + if !r.trackNat { + fmt.Printf("[NAT] write answer (non-tracking): %d\n", y) + answer <- y + return + } + + r.lastNATPacket = parsed + fmt.Printf("[NAT] intercept {%d,%d} from %d\n", x, y, sender) + continue } - fmt.Printf("[%2d] send {%d,%d} to %d\n", sender, x, y, dst) - r.bufs[dst] <- packet{sender, dst, x, y} + fmt.Printf("[%3d] send {%d,%d} to %d\n", sender, x, y, dst) + r.bufs[dst] <- parsed } } @@ -86,9 +109,43 @@ func (r *router) runTransmitterFor(id int) { select { case in <- -1: case p := <-packets: - fmt.Printf("[%2d] got {%d,%d} from %d\n", id, p.x, p.y, p.from) + _ = r.idleTracker.Acquire(context.Background(), 1) + fmt.Printf("[%3d] got {%d,%d} from %d\n", id, p.x, p.y, p.from) in <- p.x in <- p.y + r.idleTracker.Release(1) + } + + // Give other goroutines some time to run + runtime.Gosched() + } +} + +func (r *router) runNATHandler(answer chan int) { + last := -1 + for { + // Give the adapters some time to send some traffic, especially if we just + // kick-started node 0 + time.Sleep(50 * time.Millisecond) + + if r.lastNATPacket.to != addressNAT || !r.idleTracker.TryAcquire(int64(len(r.ins))) { + // Yield back to the scheduler + runtime.Gosched() + continue } + + if r.lastNATPacket.y == last { + fmt.Printf("[NAT] write answer (tracking) %d\n", last) + answer <- r.lastNATPacket.y + return + } + + fmt.Printf("[NAT] network idle enough, sending {%d,%d} from %d to [ 0]\n", r.lastNATPacket.x, r.lastNATPacket.y, r.lastNATPacket.from) + r.bufs[0] <- r.lastNATPacket + last = r.lastNATPacket.y + + // Don't try to immediately steal the semaphore back + r.idleTracker.Release(int64(len(r.ins))) + runtime.Gosched() } } diff --git a/go.mod b/go.mod index 4a910b1..d926bc0 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/stretchr/testify v1.4.0 github.com/zalando/go-keyring v0.0.0-20191212171435-ac5f1d08068b // indirect github.com/zellyn/kooky v0.0.0-20190514172626-f2bb24889ec7 + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect golang.org/x/text v0.3.2 // indirect gopkg.in/yaml.v2 v2.2.7 // indirect diff --git a/go.sum b/go.sum index 8c782a2..0d6d30b 100644 --- a/go.sum +++ b/go.sum @@ -169,7 +169,10 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/intcode/cpu.go b/intcode/cpu.go index da52a8a..dbef7b7 100644 --- a/intcode/cpu.go +++ b/intcode/cpu.go @@ -106,7 +106,7 @@ func (c *CPU) write(mode, offset, value int) { func (c *CPU) debugState() string { view := strings.Builder{} - view.WriteString("...") + view.WriteString("... ") for i := c.pc - 10; i < c.pc+10; i++ { if i == c.pc { diff --git a/main.go b/main.go index d5cb33a..2239ea7 100644 --- a/main.go +++ b/main.go @@ -86,7 +86,7 @@ func init() { day20.A, day20.B, day21.A, day21.B, day22.A, day22.B, - day23.A, + day23.A, day23.B, ) flags := rootCmd.PersistentFlags()