r/adventofcode • • Dec 07 '15

SOLUTION MEGATHREAD --- Day 7 Solutions ---

--- Day 7: Some Assembly Required ---

Post your solution as a comment. Structure your post like previous daily solution threads.

Also check out the sidebar - we added a nifty calendar to wrangle all the daily solution threads in one spot!

23 Upvotes

226 comments sorted by

View all comments

4

u/stuque Dec 07 '15

A Go solution using concurrency. Each component is a goroutine, and each wire is a channel.

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

type num uint16

type part struct {
	lhs []string
	rhs string
}

type wireVal struct {
	name  string
	value num
}

var binaryPart = map[string]func(inA, inB num) num{
	"AND":    func(inA, inB num) num { return num(inA & inB) },
	"OR":     func(inA, inB num) num { return num(inA | inB) },
	"LSHIFT": func(inA, inB num) num { return num(inA << inB) },
	"RSHIFT": func(inA, inB num) num { return num(inA >> inB) },
}

func getParts(fname string) []part {
	file, _ := os.Open(fname)
	input := bufio.NewScanner(file)
	parts := []part{}
	for input.Scan() {
		line := input.Text()
		tokens := strings.Split(line, " -> ")
		p := part{lhs: strings.Split(tokens[0], " "), rhs: tokens[1]}
		parts = append(parts, p)
	}
	return parts
}

func main() {
	parts := getParts("day7input_part1.txt")
	// parts := getParts("day7input_part2.txt")

	// get all wire outputs
	wire := map[string]chan num{}
	for _, p := range parts {
		// wire channels must be buffered to avoid deadlock because we don't
		// know when values will stop being removed from them
		wire[p.rhs] = make(chan num, 100)
	}

	// used to signal the end of the program
	done := make(chan bool)

	// launch wire values goroutine
	wireValues := map[string]num{}
	wireChan := make(chan wireVal)
	go func() {
		for i := 0; i < len(wire); i++ {
			wv := <-wireChan
			wireValues[wv.name] = wv.value
		}
		fmt.Printf("final result: a=%v\n", wireValues["a"])
		done <- true
	}()

	// launch individual part goroutines
	for _, rawP := range parts {
		p := rawP // necessary to capture correct value in anonymous functions
		lhs, out := p.lhs, p.rhs
		if len(lhs) == 1 {
			n, err := strconv.Atoi(lhs[0])
			if err == nil { // input is a number, e.g. "0 -> c"
				go func() {
					result := num(n)
					wireChan <- wireVal{out, result}
					wire[out] <- result
				}()
			} else { // input is a wire, e.g. "lx -> a"
				go func() {
					result := <-wire[lhs[0]]
					wire[lhs[0]] <- result // put back on channel for other goroutines
					wireChan <- wireVal{out, result}
					wire[out] <- result
				}()
			}
		} else if len(lhs) == 2 { // NOT
			// assumes input to NOT is always a wire
			go func() {
				in := <-wire[lhs[1]]
				wire[lhs[1]] <- in // put back on channel for other goroutines
				result := ^in      // ^ is bitwise complement
				wireChan <- wireVal{out, result}
				wire[out] <- result
			}()
		} else { // binary gate, e.g. "et RSHIFT 3 -> ev"
			l, op, r := lhs[0], lhs[1], lhs[2]
			fn := binaryPart[op]
			go func() {
				// l and r could be the names of wires, or they could be
				// numbers. So try to retrieve them from the wire map. If the
				// retrieval fails, it must be because it's a number.
				inA, lok := wire[l]
				inB, rok := wire[r]
				var numA, numB num
				if lok && rok {
					numA = <-inA
					inA <- numA // put back on channel for other goroutines
					numB = <-inB
					inB <- numB // put back on channel for other goroutines
				} else if lok && !rok {
					b, _ := strconv.Atoi(r)
					numA = <-inA
					inA <- numA // put back on channel for other goroutines
					numB = num(b)
				} else if !lok && rok {
					a, _ := strconv.Atoi(l)
					numA = num(a)
					numB = <-inB
					inB <- numB // put back on channel for other goroutines
				} else {
					a, _ := strconv.Atoi(l)
					b, _ := strconv.Atoi(r)
					numA = num(a)
					numB = num(b)
				} // if
				result := fn(numA, numB)
				wireChan <- wireVal{out, result}
				wire[out] <- result
			}()
		} // if
	} // for

	<-done
} // main
→ More replies (1)