~zacbrown/aoc-2019-go

67561ff8c0b7469a0038435dbc68b5a063c37fe8 — Zac Brown 4 years ago 8bf2c6d
Day 7 solution. Part 1 & 2 complete.

Signed-off-by: Zac Brown <git@zacbrown.org>
1 files changed, 456 insertions(+), 0 deletions(-)

A cmd/day7/main.go
A cmd/day7/main.go => cmd/day7/main.go +456 -0
@@ 0,0 1,456 @@
package main

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

var intCodeProgram = []int{
	3, 8, 1001, 8, 10, 8, 105, 1, 0, 0, 21, 38, 63, 72, 81, 106, 187, 268, 349, 430,
	99999, 3, 9, 101, 5, 9, 9, 1002, 9, 3, 9, 101, 3, 9, 9, 4, 9, 99, 3, 9, 102, 3, 9,
	9, 101, 4, 9, 9, 1002, 9, 2, 9, 1001, 9, 2, 9, 1002, 9, 4, 9, 4, 9, 99, 3, 9, 1001,
	9, 3, 9, 4, 9, 99, 3, 9, 102, 5, 9, 9, 4, 9, 99, 3, 9, 102, 4, 9, 9, 1001, 9, 2, 9,
	1002, 9, 5, 9, 1001, 9, 2, 9, 102, 3, 9, 9, 4, 9, 99, 3, 9, 1001, 9, 2, 9, 4, 9, 3,
	9, 1002, 9, 2, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9,
	102, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 101,
	1, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 99, 3, 9, 1001, 9,
	2, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9,
	4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4,
	9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 99,
	3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9,
	101, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101,
	2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9,
	4, 9, 99, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 1, 9,
	4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3,
	9, 101, 1, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9,
	1002, 9, 2, 9, 4, 9, 99, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 101,
	1, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9,
	4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9,
	3, 9, 101, 2, 9, 9, 4, 9, 99,
}

var reader *bufio.Reader

const (
	positionMode  = 0
	immediateMode = 1
)

type Program struct {
	inputs  []string
	program []int
	output  int
	pc      []int
}

func newProgram(program []int, inputs []string) *Program {
	return &Program{inputs, program, 0, program[:]}
}

const (
	PAUSE = iota
	HALT
)

func (p *Program) eval() int {
	for {
		// If the instruction is 1002, we want the least two digits - the tens
		// and the ones position. To get this, we modulo by 100: 1002 % 100 = 02
		instr := p.pc[0] % 100
		// Perform whatever operation we're going to.
		switch instr {
		case 1:
			// Add
			in1 := p.pc[1]
			in1Mode := (p.pc[0] / 100) % 10
			in2 := p.pc[2]
			in2Mode := (p.pc[0] / 1000) % 10
			out := p.pc[3]

			var in1Value int
			if in1Mode == positionMode {
				in1Value = p.program[in1]
			} else {
				in1Value = in1
			}

			var in2Value int
			if in2Mode == positionMode {
				in2Value = p.program[in2]
			} else {
				in2Value = in2
			}

			p.program[out] = in1Value + in2Value
			p.pc = p.pc[4:]
		case 2:
			// Multiply
			in1 := p.pc[1]
			in1Mode := (p.pc[0] / 100) % 10
			in2 := p.pc[2]
			in2Mode := (p.pc[0] / 1000) % 10
			out := p.pc[3]

			var in1Value int
			if in1Mode == positionMode {
				in1Value = p.program[in1]
			} else {
				in1Value = in1
			}

			var in2Value int
			if in2Mode == positionMode {
				in2Value = p.program[in2]
			} else {
				in2Value = in2
			}

			p.program[out] = in1Value * in2Value
			p.pc = p.pc[4:]
		case 3:
			// Input
			print("input requested: ")
			inPos := p.pc[1]
			input := p.inputs[0] // Use a sentinel instead
			println(input)
			p.inputs = p.inputs[1:] // pop the head off
			input = strings.Trim(input, " \n\t")

			inputAsInt, err := strconv.Atoi(input)
			if err != nil {
				panic(fmt.Sprintf("Non-integer input provided: %s. Err: %v", input, err))
			}

			p.program[inPos] = inputAsInt
			p.pc = p.pc[2:]
		case 4:
			// Output
			outPos := p.pc[1]
			outMode := (p.pc[0] / 100) % 10

			if outMode == positionMode {
				println(p.program[outPos])
				p.output = p.program[outPos]
			} else {
				println(outPos)
				p.output = outPos
			}

			outputVal := strconv.Itoa(p.output)
			p.inputs = append(p.inputs, outputVal)

			p.pc = p.pc[2:]
			return PAUSE
		case 5:
			// jump-if-true
			in1 := p.pc[1]
			in1Mode := (p.pc[0] / 100) % 10

			in2 := p.pc[2]
			in2Mode := (p.pc[0] / 1000) % 10

			var numToEval int
			if in1Mode == positionMode {
				numToEval = p.program[in1]
			} else {
				numToEval = in1
			}

			var targetPosition int
			if in2Mode == positionMode {
				targetPosition = p.program[in2]
			} else {
				targetPosition = in2
			}

			// Jump if it's non-zero.
			if numToEval != 0 {
				p.pc = p.program[targetPosition:]
			} else {
				p.pc = p.pc[3:]
			}
		case 6:
			// jump-if-false
			in1 := p.pc[1]
			in1Mode := (p.pc[0] / 100) % 10

			in2 := p.pc[2]
			in2Mode := (p.pc[0] / 1000) % 10

			var numToEval int
			if in1Mode == positionMode {
				numToEval = p.program[in1]
			} else {
				numToEval = in1
			}

			var targetPosition int
			if in2Mode == positionMode {
				targetPosition = p.program[in2]
			} else {
				targetPosition = in2
			}

			// Jump if it's zero.
			if numToEval == 0 {
				p.pc = p.program[targetPosition:]
			} else {
				p.pc = p.pc[3:]
			}
		case 7:
			// less than
			in1 := p.pc[1]
			in1Mode := (p.pc[0] / 100) % 10

			in2 := p.pc[2]
			in2Mode := (p.pc[0] / 1000) % 10

			outPos := p.pc[3]

			var left int
			if in1Mode == positionMode {
				left = p.program[in1]
			} else {
				left = in1
			}

			var right int
			if in2Mode == positionMode {
				right = p.program[in2]
			} else {
				right = in2
			}

			if left < right {
				p.program[outPos] = 1
			} else {
				p.program[outPos] = 0
			}
			p.pc = p.pc[4:]
		case 8:
			// less than
			in1 := p.pc[1]
			in1Mode := (p.pc[0] / 100) % 10

			in2 := p.pc[2]
			in2Mode := (p.pc[0] / 1000) % 10

			outPos := p.pc[3]

			var left int
			if in1Mode == positionMode {
				left = p.program[in1]
			} else {
				left = in1
			}

			var right int
			if in2Mode == positionMode {
				right = p.program[in2]
			} else {
				right = in2
			}

			if left == right {
				p.program[outPos] = 1
			} else {
				p.program[outPos] = 0
			}
			p.pc = p.pc[4:]
		case 99:
			return HALT
		}
	}
}

var phaseSettingValuePart1 = []int{0, 1, 2, 3, 4}
var phaseSettingValuePart2 = []int{5, 6, 7, 8, 9}
var phaseSettingPermutations [][]int

func generatePhaseSettingPermutations(input []int, size int) {
	if size == 1 {
		copiedInput := make([]int, len(input))
		copy(copiedInput, input)
		phaseSettingPermutations = append(phaseSettingPermutations, copiedInput)
	}

	for i := 0; i < size; i++ {
		generatePhaseSettingPermutations(input, size-1)

		if size%2 == 1 {
			input[0], input[size-1] = input[size-1], input[0]
		} else {
			input[i], input[size-1] = input[size-1], input[i]
		}
	}
}

func part1() int {
	phaseSettingPermutations = nil
	generatePhaseSettingPermutations(phaseSettingValuePart1, len(phaseSettingValuePart1))

	highSignal := -1
	for _, permutation := range phaseSettingPermutations {
		// A
		programCopyA := make([]int, len(intCodeProgram))
		copy(programCopyA, intCodeProgram)
		phaseSetting := strconv.Itoa(permutation[0])
		println("phase setting A: ", permutation[0])
		inputSignal := "0"
		pA := newProgram(programCopyA, []string{
			phaseSetting,
			inputSignal,
		})
		pA.eval()

		// B
		programCopyB := make([]int, len(intCodeProgram))
		copy(programCopyB, intCodeProgram)
		println("phase setting B: ", permutation[1])
		pB := newProgram(programCopyB, []string{
			strconv.Itoa(permutation[1]),
			strconv.Itoa(pA.output),
		})
		pB.eval()

		// C
		programCopyC := make([]int, len(intCodeProgram))
		copy(programCopyC, intCodeProgram)
		println("phase setting C: ", permutation[2])
		pC := newProgram(programCopyC, []string{
			strconv.Itoa(permutation[2]),
			strconv.Itoa(pB.output),
		})
		pC.eval()

		// D
		programCopyD := make([]int, len(intCodeProgram))
		copy(programCopyD, intCodeProgram)
		println("phase setting D: ", permutation[3])
		pD := newProgram(programCopyD, []string{
			strconv.Itoa(permutation[3]),
			strconv.Itoa(pC.output),
		})
		pD.eval()

		// E
		programCopyE := make([]int, len(intCodeProgram))
		copy(programCopyE, intCodeProgram)
		println("phase setting E: ", permutation[4])
		pE := newProgram(programCopyE, []string{
			strconv.Itoa(permutation[4]),
			strconv.Itoa(pD.output),
		})
		pE.eval()

		if pE.output > highSignal {
			highSignal = pE.output
		}
	}
	return highSignal
}

func part2() int {
	phaseSettingPermutations = nil
	generatePhaseSettingPermutations(phaseSettingValuePart2, len(phaseSettingValuePart2))

	highSignal := -1
	for _, permutation := range phaseSettingPermutations {
		fmt.Printf("Phase settings: %v\n", permutation)
		// A
		programCopyA := make([]int, len(intCodeProgram))
		copy(programCopyA, intCodeProgram)
		phaseSetting := strconv.Itoa(permutation[0])
		inputSignal := "0"
		pA := newProgram(programCopyA, []string{
			phaseSetting,
			inputSignal,
		})
		pA.eval()

		// B
		programCopyB := make([]int, len(intCodeProgram))
		copy(programCopyB, intCodeProgram)
		pB := newProgram(programCopyB, []string{
			strconv.Itoa(permutation[1]),
			strconv.Itoa(pA.output),
		})
		pB.eval()

		// C
		programCopyC := make([]int, len(intCodeProgram))
		copy(programCopyC, intCodeProgram)
		pC := newProgram(programCopyC, []string{
			strconv.Itoa(permutation[2]),
			strconv.Itoa(pB.output),
		})
		pC.eval()

		// D
		programCopyD := make([]int, len(intCodeProgram))
		copy(programCopyD, intCodeProgram)
		pD := newProgram(programCopyD, []string{
			strconv.Itoa(permutation[3]),
			strconv.Itoa(pC.output),
		})
		pD.eval()

		// E
		programCopyE := make([]int, len(intCodeProgram))
		copy(programCopyE, intCodeProgram)
		pE := newProgram(programCopyE, []string{
			strconv.Itoa(permutation[4]),
			strconv.Itoa(pD.output),
		})
		pE.eval()

		for {
			// Phase settings are set. Now grab the output from first run of E.
			pA.inputs = []string{strconv.Itoa(pE.output)}

			// Now run them in feedback mode.
			println("Run part A")
			if pA.eval() == HALT {
				break
			}

			pB.inputs = []string{strconv.Itoa(pA.output)}
			println("Run part B")
			if pB.eval() == HALT {
				panic("we halted at B?")
			}

			pC.inputs = []string{strconv.Itoa(pB.output)}
			println("Run part C")
			if pC.eval() == HALT {
				panic("we halted at C?")
			}

			pD.inputs = []string{strconv.Itoa(pC.output)}
			println("Run part D")
			if pD.eval() == HALT {
				panic("we halted at D?")
			}

			pE.inputs = []string{strconv.Itoa(pD.output)}
			println("Run part E")
			if pE.eval() == HALT {
				panic("we halted at E?")
			}
		}

		if pE.output > highSignal {
			highSignal = pE.output
		}
	}
	return highSignal
}

func main() {
	part1High := part1()
	part2High := part2()
	println("[Part 1] High Signal: ", part1High)
	println("[Part 2] High Signal: ", part2High)
}