This commit is contained in:
onyx-and-iris 2023-12-14 22:22:41 +00:00
parent 0c26ef875a
commit b98379ca2b
7 changed files with 202 additions and 0 deletions

3
day-14/go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/onyx-and-iris/aoc2023/day-14
go 1.21.5

35
day-14/image.go Normal file
View File

@ -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
}

10
day-14/makefile Normal file
View File

@ -0,0 +1,10 @@
TEST="test.txt"
INPUT="input.txt"
test:
cat $(TEST) | go run .
run:
cat $(INPUT) | go run .
all: test

57
day-14/one.go Normal file
View File

@ -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)
}

21
day-14/solution.go Normal file
View File

@ -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)
}

45
day-14/two.go Normal file
View File

@ -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())
}

31
day-14/util.go Normal file
View File

@ -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)
}