Skip to content

Commit

Permalink
Add day 17
Browse files Browse the repository at this point in the history
  • Loading branch information
devries committed Dec 17, 2023
1 parent 81da6d5 commit ae807a2
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 2 deletions.
8 changes: 7 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: 32](https://img.shields.io/badge/⭐_Stars-32-yellow)](https://adventofcode.com/2023)
[![Stars: 34](https://img.shields.io/badge/⭐_Stars-34-yellow)](https://adventofcode.com/2023)

## Plan for This Year

Expand Down Expand Up @@ -266,3 +266,9 @@ the third run of my solution after compilation on my Raspberry Pi.
starting from each state in the system to reuse when I ran additional starting
directions, but that seems like it would have been tricky with the requirement
that energized tiles only be counted once.
- [Day 17: Clumsy Crucible](https://adventofcode.com/2023/day/17) - [⭐ part 1](day17p1/solution.go), [⭐ part 2](day17p2/solution.go)
First use of Dijkstra! It was fairly easy to adapt the conditions of the
problem to the Graph routines in my code. I think memory allocation may be
the bottleneck in my implementation, but it seems fast enough.
80 changes: 80 additions & 0 deletions day17p1/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package day17p1

import (
"io"

"aoc/utils"
)

func Solve(r io.Reader) any {
lines := utils.ReadLines(r)

m := NewMaze(lines)

dij := utils.NewDijkstra[State]()

finalState, err := dij.Run(m)
utils.Check(err, "error in search")

return dij.Distance[finalState]
}

type Maze struct {
HeatLoss map[utils.Point]uint64
BottomRight utils.Point
}

func NewMaze(lines []string) *Maze {
m := Maze{HeatLoss: make(map[utils.Point]uint64)}

for j, ln := range lines {
for i, r := range ln {
p := utils.Point{X: i, Y: -j}
v := uint64(r - '0')
m.HeatLoss[p] = v
m.BottomRight = p
}
}

return &m
}

type State struct {
Position utils.Point
Direction utils.Point
Steps int
}

func (m *Maze) GetInitial() State {
return State{utils.Point{X: 0, Y: 0}, utils.East, 0}
}

func (m *Maze) GetEdges(s State) []utils.Edge[State] {
ret := []utils.Edge[State]{}

// forward
np := s.Position.Add(s.Direction)
if hl, ok := m.HeatLoss[s.Position.Add(s.Direction)]; ok && s.Steps < 3 {
newState := State{np, s.Direction, s.Steps + 1}
ret = append(ret, utils.Edge[State]{Node: newState, Distance: hl})
}

// Left and Right
for _, dir := range []utils.Point{s.Direction.Right(), s.Direction.Left()} {
np := s.Position.Add(dir)
if hl, ok := m.HeatLoss[np]; ok {
newState := State{np, dir, 1}
ret = append(ret, utils.Edge[State]{Node: newState, Distance: hl})
}
}

return ret
}

func (m *Maze) IsFinal(s State) bool {
if s.Position == m.BottomRight {
return true
}

return false
}
45 changes: 45 additions & 0 deletions day17p1/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package day17p1

import (
"strings"
"testing"

"aoc/utils"
)

var testInput = `2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533`

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

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

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

result := Solve(r).(uint64)

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

import (
"io"

"aoc/utils"
)

func Solve(r io.Reader) any {
lines := utils.ReadLines(r)

m := NewMaze(lines)

dij := utils.NewDijkstra[State]()

finalState, err := dij.Run(m)
utils.Check(err, "error in search")

return dij.Distance[finalState]
}

type Maze struct {
HeatLoss map[utils.Point]uint64
BottomRight utils.Point
}

func NewMaze(lines []string) *Maze {
m := Maze{HeatLoss: make(map[utils.Point]uint64)}

for j, ln := range lines {
for i, r := range ln {
p := utils.Point{X: i, Y: -j}
v := uint64(r - '0')
m.HeatLoss[p] = v
m.BottomRight = p
}
}

return &m
}

type State struct {
Position utils.Point
Direction utils.Point
Steps int
}

func (m *Maze) GetInitial() State {
return State{utils.Point{X: 0, Y: 0}, utils.East, 0}
}

func (m *Maze) GetEdges(s State) []utils.Edge[State] {
ret := []utils.Edge[State]{}

// forward
np := s.Position.Add(s.Direction)
if hl, ok := m.HeatLoss[s.Position.Add(s.Direction)]; ok && s.Steps < 10 {
newState := State{np, s.Direction, s.Steps + 1}
ret = append(ret, utils.Edge[State]{Node: newState, Distance: hl})
}

// Left and Right
for _, dir := range []utils.Point{s.Direction.Right(), s.Direction.Left()} {
np := s.Position.Add(dir)
if hl, ok := m.HeatLoss[np]; ok && s.Steps >= 4 {
newState := State{np, dir, 1}
ret = append(ret, utils.Edge[State]{Node: newState, Distance: hl})
}
}

return ret
}

func (m *Maze) IsFinal(s State) bool {
if s.Position == m.BottomRight && s.Steps >= 4 {
return true
}

return false
}
52 changes: 52 additions & 0 deletions day17p2/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package day17p2

import (
"strings"
"testing"

"aoc/utils"
)

var testInput = `2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533`

var testInput2 = `111111111111
999999999991
999999999991
999999999991
999999999991`

func TestSolve(t *testing.T) {
tests := []struct {
input string
answer uint64
}{
{testInput, 94},
{testInput2, 71},
}

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

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

result := Solve(r).(uint64)

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

0 comments on commit ae807a2

Please sign in to comment.