From f24a9085b3ccecd79fa35e788f2b39b88a8b9c4f Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Mon, 11 Dec 2023 03:55:09 +0000 Subject: [PATCH] day-10 --- day-10/check.go | 53 +++++++++++++++++ day-10/go.mod | 3 + day-10/one.go | 142 +++++++++++++++++++++++++++++++++++++++++++++ day-10/run.sh | 5 ++ day-10/solution.go | 21 +++++++ day-10/two.go | 40 +++++++++++++ day-10/util.go | 50 ++++++++++++++++ 7 files changed, 314 insertions(+) create mode 100644 day-10/check.go create mode 100644 day-10/go.mod create mode 100644 day-10/one.go create mode 100644 day-10/run.sh create mode 100644 day-10/solution.go create mode 100644 day-10/two.go create mode 100644 day-10/util.go diff --git a/day-10/check.go b/day-10/check.go new file mode 100644 index 0000000..c064018 --- /dev/null +++ b/day-10/check.go @@ -0,0 +1,53 @@ +package main + +/* + functions that check connections between pipes in each direction +*/ + +func checkSouth(point point) point { + if point.Y == len(pointsArray)-1 { + return point + } + + target := pointsArray[point.Y+1].points[point.X] + if point.S && target.N { + return target + } + return point +} + +func checkEast(point point) point { + if point.X == len(pointsArray[point.Y].points)-1 { + return point + } + + target := pointsArray[point.Y].points[point.X+1] + if point.E && target.W { + return target + } + return point +} + +func checkNorth(point point) point { + if point.Y == 0 { + return point + } + + target := pointsArray[point.Y-1].points[point.X] + if point.N && target.S { + return target + } + return point +} + +func checkWest(point point) point { + if point.X == 0 { + return point + } + + target := pointsArray[point.Y].points[point.X-1] + if point.W && target.E { + return target + } + return point +} diff --git a/day-10/go.mod b/day-10/go.mod new file mode 100644 index 0000000..f003ce8 --- /dev/null +++ b/day-10/go.mod @@ -0,0 +1,3 @@ +module github.com/onyx-and-iris/aoc2023/day-10 + +go 1.21.5 diff --git a/day-10/one.go b/day-10/one.go new file mode 100644 index 0000000..9223d6c --- /dev/null +++ b/day-10/one.go @@ -0,0 +1,142 @@ +package main + +import ( + log "github.com/sirupsen/logrus" +) + +type coords struct { + X int + Y int +} + +type points struct { + points []point +} + +type point struct { + N bool + S bool + E bool + W bool + coords + identifier rune +} + +var pointsArray = []points{} + +type tracker struct { + init point + point point + last []coords +} + +func (t tracker) X() int { + return t.point.X +} + +func (t tracker) Y() int { + return t.point.Y +} + +// mapPoints builds the points array mapping connection types for each coordinate +func mapPoints(lines []string) { + pointsArray = make([]points, 0) + + for i, line := range lines { + var ps = make([]point, 0) + for j, r := range line { + switch r { + case '|': + ps = append(ps, point{N: true, S: true, E: false, W: false}) // N,S + case '-': + ps = append(ps, point{N: false, S: false, E: true, W: true}) // E, W + case 'L': + ps = append(ps, point{N: true, S: false, E: true, W: false}) // N, E + case 'J': + ps = append(ps, point{N: true, S: false, E: false, W: true}) // N, W + case '7': + ps = append(ps, point{N: false, S: true, E: false, W: true}) // S, W + case 'F': + ps = append(ps, point{N: false, S: true, E: true, W: false}) // S, E + case '.': + ps = append(ps, point{N: false, S: false, E: false, W: false}) // ground + case 'S': + ps = append(ps, point{N: true, S: true, E: true, W: true}) // start + default: + log.Debug("ground point") + } + ps[j].identifier = r + ps[j].coords.Y = i + ps[j].coords.X = j + } + pointsArray = append(pointsArray, points{points: ps}) + } +} + +// initializeTracker stores the starting point, creates coords array for tracking last moves +func initializeTracker() tracker { + tracker := tracker{last: make([]coords, 0)} + for _, each := range pointsArray { + for _, point := range each.points { + if point.identifier == 'S' { + tracker.init = point + tracker.point = tracker.init + tracker.last = make([]coords, 1) + tracker.last = append(tracker.last, tracker.point.coords) + } + } + } + return tracker +} + +// walk moves along the pipes storing poinst that mark loop locations +// it also keeps a track of last two moves +// returns the number of steps to traverse all pipes +func walk(tracker tracker) int { + var steps int + for steps := 0; steps == 0 || !comparePoints(tracker.init, tracker.point); steps++ { + next := checkSouth(tracker.point) + if !inLastMoves(tracker.last, next.coords) { + log.Debug("moving south from ", string(tracker.point.identifier), " to ", string(next.identifier)) + tracker.point = next + loop = append(loop, pointsArray[tracker.Y()].points[tracker.X()].coords) + tracker.last = append(tracker.last[1:], next.coords) + continue + } + next = checkEast(tracker.point) + if !inLastMoves(tracker.last, next.coords) { + log.Debug("moving east from ", string(tracker.point.identifier), " to ", string(next.identifier)) + tracker.point = next + loop = append(loop, pointsArray[tracker.Y()].points[tracker.X()].coords) + tracker.last = append(tracker.last[1:], next.coords) + continue + } + next = checkNorth(tracker.point) + if !inLastMoves(tracker.last, next.coords) { + log.Debug("moving north from ", string(tracker.point.identifier), " to ", string(next.identifier)) + tracker.point = next + loop = append(loop, pointsArray[tracker.Y()].points[tracker.X()].coords) + tracker.last = append(tracker.last[1:], next.coords) + continue + } + next = checkWest(tracker.point) + if !inLastMoves(tracker.last, next.coords) { + log.Debug("moving west from ", string(tracker.point.identifier), " to ", string(next.identifier)) + tracker.point = next + loop = append(loop, pointsArray[tracker.Y()].points[tracker.X()].coords) + tracker.last = append(tracker.last[1:], next.coords) + continue + } + steps++ + } + return steps +} + +// one returns the number of steps to reach the furthest point from the start +func one(lines []string) int { + mapPoints(lines) + + tracker := initializeTracker() + + return walk(tracker) / 2 +} diff --git a/day-10/run.sh b/day-10/run.sh new file mode 100644 index 0000000..3f6776a --- /dev/null +++ b/day-10/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +INPUT="input.txt" + +cat $INPUT | go run . \ No newline at end of file diff --git a/day-10/solution.go b/day-10/solution.go new file mode 100644 index 0000000..926f1d4 --- /dev/null +++ b/day-10/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-10/two.go b/day-10/two.go new file mode 100644 index 0000000..66f4402 --- /dev/null +++ b/day-10/two.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "math" + + log "github.com/sirupsen/logrus" +) + +func debugPrint() { + for _, points := range pointsArray { + for _, point := range points.points { + if contains(loop, point.coords) { + fmt.Printf("%c ", point.identifier) + } else { + fmt.Printf(". ") + } + } + fmt.Printf("\n") + } +} + +var loop []coords + +// two +func two(lines []string) int { + if log.GetLevel() == log.DebugLevel { + debugPrint() + } + + area := 0 + for i := 0; i < len(loop); i++ { + next := loop[(i+1)%len(loop)] + area += loop[i].X*next.Y - loop[i].Y*next.X + } + + area = int(math.Abs(float64(area))) / 2 + + return area - len(loop)/2 + 1 +} diff --git a/day-10/util.go b/day-10/util.go new file mode 100644 index 0000000..d2df244 --- /dev/null +++ b/day-10/util.go @@ -0,0 +1,50 @@ +package main + +import ( + "bufio" + "os" + + log "github.com/sirupsen/logrus" +) + +// readlines reads lines from stdin. +// returns input 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 +} + +// inLastMoves checks if next coords exists in past moves +func inLastMoves(last []coords, b coords) bool { + for _, co := range last { + if co.X == b.X && co.Y == b.Y { + return true + } + } + return false +} + +// comparePoints returns true if coordinates for two points match +func comparePoints(a, b point) bool { + return a.X == b.X && a.Y == b.Y +} + +// contains returns true if a slice of elements contains a given element +func contains[T comparable](elems []T, v T) bool { + for _, s := range elems { + if v == s { + return true + } + } + return false +}