From b98379ca2bf02817bfe779212c05d9a90551d5d5 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Thu, 14 Dec 2023 22:22:41 +0000 Subject: [PATCH] day-14 --- day-14/go.mod | 3 +++ day-14/image.go | 35 ++++++++++++++++++++++++++++ day-14/makefile | 10 ++++++++ day-14/one.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++ day-14/solution.go | 21 +++++++++++++++++ day-14/two.go | 45 ++++++++++++++++++++++++++++++++++++ day-14/util.go | 31 +++++++++++++++++++++++++ 7 files changed, 202 insertions(+) create mode 100644 day-14/go.mod create mode 100644 day-14/image.go create mode 100644 day-14/makefile create mode 100644 day-14/one.go create mode 100644 day-14/solution.go create mode 100644 day-14/two.go create mode 100644 day-14/util.go diff --git a/day-14/go.mod b/day-14/go.mod new file mode 100644 index 0000000..84a5740 --- /dev/null +++ b/day-14/go.mod @@ -0,0 +1,3 @@ +module github.com/onyx-and-iris/aoc2023/day-14 + +go 1.21.5 diff --git a/day-14/image.go b/day-14/image.go new file mode 100644 index 0000000..e4975ec --- /dev/null +++ b/day-14/image.go @@ -0,0 +1,35 @@ +package main + +import "fmt" + +type img struct { + raw []string +} + +func newImg(sz int) img { + return img{raw: make([]string, sz)} +} + +// transposed rotates an image rightwards ninety degrees +func (i img) transposed() []string { + transposed := []string{} + + for x := 0; x < len(i.raw[0]); x++ { + buf := "" + for j := len(i.raw) - 1; j >= 0; j-- { + buf += string(i.raw[j][x]) + } + transposed = append(transposed, buf) + } + + return transposed +} + +// String implements the Stringer interface +func (i img) String() string { + out := "" + for _, line := range i.raw { + out += fmt.Sprintf("%s\n", line) + } + return out +} diff --git a/day-14/makefile b/day-14/makefile new file mode 100644 index 0000000..5a0aa8d --- /dev/null +++ b/day-14/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-14/one.go b/day-14/one.go new file mode 100644 index 0000000..3c94200 --- /dev/null +++ b/day-14/one.go @@ -0,0 +1,57 @@ +package main + +const MAX = 10 + +func getload(raw []string) int { + load := 0 + for _, line := range raw { + for i, r := range line { + if r == 'O' { + load += (i + 1) + } + } + } + + return load +} + +func spacesToRight(i int, line string) int { + num := 0 +outer: + for i += 1; i < len(line); i++ { + switch line[i] { + case '.': + num++ + case '#', 'O': + break outer + } + } + return num +} + +func rollRight(line string) string { + for i := len(line) - 1; i >= 0; i-- { + if line[i] == 'O' { + n := spacesToRight(i, line) + if n == 0 { + continue + } + line = replaceAtIndex(line, '.', i) + line = replaceAtIndex(line, 'O', i+n) + } + } + + return line +} + +// one returns the load of all boulders after a single image cycle +func one(lines []string) int { + image := newImg(len(lines)) + copy(image.raw, lines) + + for i, line := range image.transposed() { + image.raw[i] = rollRight(line) + } + + return getload(image.raw) +} diff --git a/day-14/solution.go b/day-14/solution.go new file mode 100644 index 0000000..926f1d4 --- /dev/null +++ b/day-14/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-14/two.go b/day-14/two.go new file mode 100644 index 0000000..fc90696 --- /dev/null +++ b/day-14/two.go @@ -0,0 +1,45 @@ +package main + +const CYCLES = 1000000000 + +// cycleOnce transposes and rolls a single image once +func cycleOnce(image img) { + for i := 0; i < 4; i++ { + for i, line := range image.transposed() { + image.raw[i] = rollRight(line) + } + } +} + +// cycleMany cycles a single image for a given number of iterations +// it also caches each image and measures the period of repetition +func cycleMany(image img, iterations int) { + cache := make(map[string]int) + i, start := 0, 0 + for ; i < iterations; i++ { + cycleOnce(image) + + if idx, ok := cache[image.String()]; ok { // we found first repeated image + start = idx + i++ + break + } + + cache[image.String()] = i + 1 + } + period := i - start // length of a full period + remaining := (iterations - i - 1) % period // number of cycles left in current period + for j := 0; j < remaining; j++ { + cycleOnce(image) // rotate to the final image in the period + } +} + +// two returns the load of all boulders after 1000000000 cycles +func two(lines []string) int { + image := newImg(len(lines)) + copy(image.raw, lines) + + cycleMany(image, CYCLES) + + return getload(image.transposed()) +} diff --git a/day-14/util.go b/day-14/util.go new file mode 100644 index 0000000..ad6da51 --- /dev/null +++ b/day-14/util.go @@ -0,0 +1,31 @@ +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 +} + +// replaceAtIndex replaces a character in a string at index i +func replaceAtIndex(in string, r rune, i int) string { + out := []rune(in) + out[i] = r + return string(out) +}