From 6dae7502db449d7dab4e386c3c33e861ede2851f Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sun, 17 Dec 2023 15:31:11 +0000 Subject: [PATCH] day-16 --- day-16/debug.go | 30 +++++++++++ day-16/go.mod | 3 ++ day-16/makefile | 10 ++++ day-16/mover.go | 64 +++++++++++++++++++++++ day-16/one.go | 127 +++++++++++++++++++++++++++++++++++++++++++++ day-16/solution.go | 21 ++++++++ day-16/two.go | 53 +++++++++++++++++++ day-16/util.go | 45 ++++++++++++++++ 8 files changed, 353 insertions(+) create mode 100644 day-16/debug.go create mode 100644 day-16/go.mod create mode 100644 day-16/makefile create mode 100644 day-16/mover.go create mode 100644 day-16/one.go create mode 100644 day-16/solution.go create mode 100644 day-16/two.go create mode 100644 day-16/util.go diff --git a/day-16/debug.go b/day-16/debug.go new file mode 100644 index 0000000..ee85211 --- /dev/null +++ b/day-16/debug.go @@ -0,0 +1,30 @@ +package main + +import "fmt" + +func printDebug(move *mover, lines []string) int { + num := 0 + for i, line := range lines { + for j, r := range line { + inNodes := func(c coords) bool { + for _, node := range move.nodes { + if c.X == node.X && c.Y == node.Y { + return true + } + } + return false + }(newCoords(j, i)) + + if inNodes { + fmt.Printf("#") + num++ + //} else if r == '|' || r == '\\' || r == '/' || r == '-' { + //fmt.Printf(".") + } else { + fmt.Printf("%c", r) + } + } + fmt.Println() + } + return num +} diff --git a/day-16/go.mod b/day-16/go.mod new file mode 100644 index 0000000..6de2904 --- /dev/null +++ b/day-16/go.mod @@ -0,0 +1,3 @@ +module github.com/onyx-and-iris/aoc2023/day-16 + +go 1.21.5 diff --git a/day-16/makefile b/day-16/makefile new file mode 100644 index 0000000..5a0aa8d --- /dev/null +++ b/day-16/makefile @@ -0,0 +1,10 @@ +TEST="test.txt" +INPUT="input.txt" + +test: + cat $(TEST) | go run . + +run: + cat $(INPUT) | go run . + +all: test diff --git a/day-16/mover.go b/day-16/mover.go new file mode 100644 index 0000000..6d5f21e --- /dev/null +++ b/day-16/mover.go @@ -0,0 +1,64 @@ +package main + +import ( + "fmt" +) + +type coords struct { + X int + Y int +} + +func newCoords(x, y int) coords { + return coords{X: x, Y: y} +} + +// node represents a single node with coordinates and direction +type node struct { + coords + direction int +} + +func newNode(x, y int, direction int) node { + return node{coords: newCoords(x, y), direction: direction} +} + +// String implements the fmt.Stringer interface +func (n node) String() string { + return fmt.Sprintf("%v%s", n.coords, dirs[n.direction]) +} + +type mover struct { + node + nodes []node +} + +// newMover sets the start coordinates and direction +// it returns a mover type +func newMover(n node) *mover { + return &mover{node: n, nodes: make([]node, 0)} +} + +// direction returns the current node direction +func (m *mover) direction() int { + return m.node.direction +} + +// setDirection sets the current node direction +func (m *mover) setDirection(val int) { + m.node.direction = val +} + +// move shifts the X,Y coordinate by one depending on direction +func (m *mover) move() { + switch m.node.direction { + case N: + m.Y-- + case S: + m.Y++ + case W: + m.X-- + case E: + m.X++ + } +} diff --git a/day-16/one.go b/day-16/one.go new file mode 100644 index 0000000..36a3491 --- /dev/null +++ b/day-16/one.go @@ -0,0 +1,127 @@ +package main + +import ( + "math" + + log "github.com/sirupsen/logrus" +) + +const ( + SPACE = '.' + V_MIRROR = '|' + H_MIRROR = '-' + F_MIRROR = '/' + B_MIRROR = '\\' +) + +const ( + N = iota + S + W + E +) + +// only for debugging +var steps int +var dirs = []string{"N", "S", "W", "E"} + +func runner(move *mover, lines []string) { + for steps < math.MaxInt && move.Y >= 0 && move.Y < len(lines) && move.X >= 0 && move.X < len(lines[move.Y]) { + //log.Debug(move.X, ":", move.Y, " ", string(lines[move.Y][move.X]), " ", dirs[move.direction()]) + //log.Debug(move.nodes) + if nodeInNodes(move.node, move.nodes) { + log.Debug(move.node, " in nodes, breaking.") + break + } + + move.nodes = append(move.nodes, move.node) + + switch lines[move.Y][move.X] { + case SPACE: // '.' + //log.Debug("we have space and direction is ", dirs[move.direction()]) + move.move() + + case F_MIRROR: // '/' + //log.Debug("we have forward mirror and direction is ", dirs[move.direction()]) + switch move.direction() { + case N: + move.setDirection(E) + case S: + move.setDirection(W) + case W: + move.setDirection(S) + case E: + move.setDirection(N) + } + //log.Debug("step: ", steps, " ", string(F_MIRROR), " direction changed to ", dirs[move.direction()]) + move.move() + + case B_MIRROR: // '\' + //log.Debug("we have backwards mirror and direction is ", dirs[move.direction()]) + switch move.direction() { + case N: + move.setDirection(W) + case S: + move.setDirection(E) + case W: + move.setDirection(N) + case E: + move.setDirection(S) + } + //log.Debug("step: ", steps, " ", string(B_MIRROR), " direction changed to ", dirs[move.direction()]) + move.move() + + case V_MIRROR: // '|' + //log.Debug("we have vertical mirror and direction is ", dirs[move.direction()]) + if move.direction() == N || move.direction() == S { + move.move() + continue + } + if move.direction() == W || move.direction() == E { + c := move.coords + move.setDirection(N) + move.move() + runner(move, lines) + move.coords = c + move.setDirection(S) + move.move() + runner(move, lines) + } + + case H_MIRROR: // '-' + //log.Debug("we have horizontal mirror and direction is ", dirs[move.direction()]) + if move.direction() == W || move.direction() == E { + move.move() + continue + } + if move.direction() == N || move.direction() == S { + c := move.coords + move.setDirection(W) + move.move() + runner(move, lines) + move.coords = c + move.setDirection(E) + move.move() + runner(move, lines) + } + + default: + log.Fatal("unknown node") + } + } + steps++ +} + +// one +func one(lines []string) int { + move := newMover(newNode(0, 0, E)) + + runner(move, lines) + + if log.GetLevel() == log.DebugLevel { + n := printDebug(move, lines) + log.Debug("total: ", n) + } + + return uniqueNodes(move) +} diff --git a/day-16/solution.go b/day-16/solution.go new file mode 100644 index 0000000..926f1d4 --- /dev/null +++ b/day-16/solution.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + + log "github.com/sirupsen/logrus" +) + +func init() { + log.SetLevel(log.InfoLevel) +} + +func main() { + lines := readlines() + + ans := one(lines) + fmt.Printf("solution one: %d\n", ans) + + ans = two(lines) + fmt.Printf("solution two: %d\n", ans) +} diff --git a/day-16/two.go b/day-16/two.go new file mode 100644 index 0000000..72978c5 --- /dev/null +++ b/day-16/two.go @@ -0,0 +1,53 @@ +package main + +// returns the number of unique nodes (reducing multiple nodes with different directions to one) +func uniqueNodes(move *mover) int { + uniqueCoords := []coords{} + for _, node := range move.nodes { + if !coordInCoords(node.coords, uniqueCoords) { + uniqueCoords = append(uniqueCoords, node.coords) + } + } + return len(uniqueCoords) +} + +// spawn invoked a single runner with a single mover +func spawn(i, j, direction int, lines []string) int { + m := newMover(newNode(i, j, direction)) + runner(m, lines) + return uniqueNodes(m) +} + +// two returns the highest energized value for any beam spawn point/direction +func two(lines []string) int { + res := 0 + n := 0 + + var x = 0 + for x < len(lines[0]) { + n = spawn(x, 0, S, lines) + if n > res { + res = n + } + n = spawn(x, len(lines[0])-1, N, lines) + if n > res { + res = n + } + x++ + } + + var y = 0 + for y < len(lines) { + n = spawn(0, y, E, lines) + if n > res { + res = n + } + n = spawn(len(lines[0])-1, y, W, lines) + if n > res { + res = n + } + y++ + } + + return res +} diff --git a/day-16/util.go b/day-16/util.go new file mode 100644 index 0000000..ea20a35 --- /dev/null +++ b/day-16/util.go @@ -0,0 +1,45 @@ +package main + +import ( + "bufio" + "log" + "os" +) + +// readlines reads lines from stdin. +// it returns them as an array of strings +func readlines() []string { + lines := []string{} + + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return lines +} + +// nodeInNodes returns true if node n is in nodes +// X, Y coords and direction must match +func nodeInNodes(n node, nodes []node) bool { + for _, node := range nodes { + if n.X == node.X && n.Y == node.Y && n.direction == node.direction { + return true + } + } + return false +} + +// coordInCoords returns true if coords c is in coords +func coordInCoords(c coords, coords []coords) bool { + for _, coord := range coords { + if c.X == coord.X && c.Y == coord.Y { + return true + } + } + return false +}