Skip to content

Commit

Permalink
finished day 14
Browse files Browse the repository at this point in the history
  • Loading branch information
devries committed Dec 14, 2023
1 parent 13942fa commit 5ebb35b
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 2 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Advent of Code 2023

[![Tests](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml/badge.svg)](https://github.com/devries/advent_of_code_2023/actions/workflows/main.yml)
[![Stars: 26](https://img.shields.io/badge/⭐_Stars-26-yellow)](https://adventofcode.com/2023)
[![Stars: 28](https://img.shields.io/badge/⭐_Stars-28-yellow)](https://adventofcode.com/2023)

## Plan for This Year

Expand Down Expand Up @@ -199,3 +199,18 @@ the third run of my solution after compilation on my Raspberry Pi.
than the XOR function, which of course caused a few errors. My extra test
cases were made to track down those errors. I used Kernighan's bit counting
algorithm to find the number of bits in the XOR difference.

- [Day 14: Parabolic Reflector Dish](https://adventofcode.com/2023/day/14) - [⭐ part 1](day14p1/solution.go), [⭐ part 2](day14p2/solution.go)

The tilting mechanic was fairly straighforward, though for the spin cycle I
decided to write four separate loops rather than a more generic loop which
would work for each direction. Obviously iterating 1,000,000,000 times is not
practical, however the arrangement of rocks should begin cycling through a
sequence of repeated positions, so we just need to find where that cycle
starts and ends, and then jump ahead N cycles to just before the iterations
are complete. The only problem is storing the map state. This tripped me up a
bit as I tried to find a unique integer that summarized the map, but that's
the job of a hash function. I took the sorted list of positions of rolling
rocks, turned the coordinate into an integer, *sorted them*, and then wrote
the binary encoding of those integers to a fnv-1a hash function. I then was
able to look for when the hashes started to repeat.
64 changes: 64 additions & 0 deletions day14p1/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package day14p1

import (
"io"

"aoc/utils"
)

func Solve(r io.Reader) any {
lines := utils.ReadLines(r)
ymax := len(lines)
xmax := len(lines[0])

pos := make(map[utils.Point]rune)

for j, ln := range lines {
for i, r := range ln {
if r != '.' {
p := utils.Point{X: i, Y: ymax - j - 1}
pos[p] = r
}
}
}

platform := Platform{xmax, ymax, pos}

tilt(&platform)

// measure load
sum := 0
for k, v := range platform.Positions {
if v == 'O' {
sum += k.Y + 1
}
}
return sum
}

type Platform struct {
XMax int
YMax int
Positions map[utils.Point]rune
}

func tilt(platform *Platform) {
// iterate over each row

for i := 0; i < platform.XMax; i++ {
blocker := platform.YMax // what blocks the stones
for j := platform.YMax - 1; j >= 0; j-- {
pt := utils.Point{X: i, Y: j}
r := platform.Positions[pt]
switch r {
case 'O':
newPt := utils.Point{X: i, Y: blocker - 1}
platform.Positions[pt] = 0
platform.Positions[newPt] = 'O'
blocker = blocker - 1
case '#':
blocker = j
}
}
}
}
42 changes: 42 additions & 0 deletions day14p1/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package day14p1

import (
"strings"
"testing"

"aoc/utils"
)

var testInput = `O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....`

func TestSolve(t *testing.T) {
tests := []struct {
input string
answer int
}{
{testInput, 136},
}

if testing.Verbose() {
utils.Verbose = true
}

for _, test := range tests {
r := strings.NewReader(test.input)

result := Solve(r).(int)

if result != test.answer {
t.Errorf("Expected %d, got %d", test.answer, result)
}
}
}
180 changes: 180 additions & 0 deletions day14p2/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package day14p2

import (
"encoding/binary"
"fmt"
"hash/fnv"
"io"
"sort"

"aoc/utils"
)

var maxiter = 1000000000

func Solve(r io.Reader) any {
lines := utils.ReadLines(r)
ymax := len(lines)
xmax := len(lines[0])

pos := make(map[utils.Point]rune)

for j, ln := range lines {
for i, r := range ln {
if r != '.' {
p := utils.Point{X: i, Y: ymax - j - 1}
pos[p] = r
}
}
}

platform := Platform{xmax, ymax, pos}

checksums := make(map[uint64]int)
looking := true
for i := 0; i < maxiter; i++ {
tiltCycle(&platform)
cs := platform.checkSum()
if v, ok := checksums[cs]; ok && looking {
interval := v - i
start := v
span := maxiter - start
ncycles := span / interval
i = start + ncycles*interval
looking = false
}
checksums[cs] = i
}

if utils.Verbose {
fmt.Println(platform.checkSum())
platform.print()
}
// measure load
sum := 0
for k, v := range platform.Positions {
if v == 'O' {
sum += k.Y + 1
}
}
return sum
}

type Platform struct {
XMax int
YMax int
Positions map[utils.Point]rune
}

func tiltCycle(platform *Platform) {
// Tilt north
for i := 0; i < platform.XMax; i++ {
blocker := platform.YMax // what blocks the stones
for j := platform.YMax - 1; j >= 0; j-- {
pt := utils.Point{X: i, Y: j}
r := platform.Positions[pt]
switch r {
case 'O':
newPt := utils.Point{X: i, Y: blocker - 1}
delete(platform.Positions, pt)
platform.Positions[newPt] = 'O'
blocker = blocker - 1
case '#':
blocker = j
}
}
}

// Tilt west
for j := 0; j < platform.YMax; j++ {
blocker := -1 // what blocks the stones
for i := 0; i < platform.XMax; i++ {
pt := utils.Point{X: i, Y: j}
r := platform.Positions[pt]
switch r {
case 'O':
newPt := utils.Point{X: blocker + 1, Y: j}
delete(platform.Positions, pt)
platform.Positions[newPt] = 'O'
blocker = blocker + 1
case '#':
blocker = i
}
}
}

// Tilt south
for i := 0; i < platform.XMax; i++ {
blocker := -1 // what blocks the stones
for j := 0; j < platform.YMax; j++ {
pt := utils.Point{X: i, Y: j}
r := platform.Positions[pt]
switch r {
case 'O':
newPt := utils.Point{X: i, Y: blocker + 1}
delete(platform.Positions, pt)
platform.Positions[newPt] = 'O'
blocker = blocker + 1
case '#':
blocker = j
}
}
}

// Tilt east
for j := 0; j < platform.YMax; j++ {
blocker := platform.XMax // what blocks the stones
for i := platform.XMax - 1; i >= 0; i-- {
pt := utils.Point{X: i, Y: j}
r := platform.Positions[pt]
switch r {
case 'O':
newPt := utils.Point{X: blocker - 1, Y: j}
delete(platform.Positions, pt)
platform.Positions[newPt] = 'O'
blocker = blocker - 1
case '#':
blocker = i
}
}
}
}

func (p *Platform) print() {
for j := p.YMax - 1; j >= 0; j-- {
for i := 0; i < p.XMax; i++ {
pt := utils.Point{X: i, Y: j}
r := p.Positions[pt]
if r == 0 {
fmt.Printf(".")
} else {
fmt.Printf("%c", r)
}
}
fmt.Printf("\n")
}
fmt.Printf("\n")
}

func (p *Platform) checkSum() uint64 {
parray := []int{}
for j := p.YMax - 1; j >= 0; j-- {
for i := 0; i < p.XMax; i++ {
pt := utils.Point{X: i, Y: j}
r := p.Positions[pt]
if r == 'O' {
parray = append(parray, p.XMax*j+i)
}
}
}

sort.Ints(parray)
hf := fnv.New64a()

for _, v := range parray {
err := binary.Write(hf, binary.LittleEndian, int32(v))
utils.Check(err, "Error writing binary")
}

return hf.Sum64()
}
42 changes: 42 additions & 0 deletions day14p2/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package day14p2

import (
"strings"
"testing"

"aoc/utils"
)

var testInput = `O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....`

func TestSolve(t *testing.T) {
tests := []struct {
input string
answer int
}{
{testInput, 64},
}

if testing.Verbose() {
utils.Verbose = true
}

for _, test := range tests {
r := strings.NewReader(test.input)

result := Solve(r).(int)

if result != test.answer {
t.Errorf("Expected %d, got %d", test.answer, result)
}
}
}
2 changes: 1 addition & 1 deletion inputs

0 comments on commit 5ebb35b

Please sign in to comment.