Skip to content
This repository has been archived by the owner on Dec 28, 2024. It is now read-only.

Commit

Permalink
Add solution day 20 part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
terminalnode committed Dec 20, 2024
1 parent 27179eb commit 49e4f16
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ automatically rebuilt and redeployed every time the `common` module or their own
| 04 | ⭐ ⭐ | 17 | ⭐ ⭐ |
| 05 | ⭐ ⭐ | 18 | ⭐ ⭐ |
| 06 | ⭐ ⭐ | 19 | ⭐ ⭐ |
| 07 | ⭐ ⭐ | 20 | |
| 07 | ⭐ ⭐ | 20 | |
| 08 | ⭐ ⭐ | 21 | |
| 09 | ⭐ ⭐ | 22 | |
| 10 | ⭐ ⭐ | 23 | |
Expand Down
17 changes: 17 additions & 0 deletions common/util/coordinate.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ func (c Coordinate) PositiveModulo(x int, y int) Coordinate {
}
}

func (c Coordinate) Adjacent4() []Coordinate {
return []Coordinate{c.North(), c.East(), c.South(), c.West()}
}

func (c Coordinate) Adjacent8() []Coordinate {
return []Coordinate{
c.North(),
c.NorthEast(),
c.NorthWest(),
c.South(),
c.SouthEast(),
c.SouthWest(),
c.East(),
c.West(),
}
}

func (c Coordinate) North() Coordinate {
return Coordinate{c.X, c.Y - 1}
}
Expand Down
97 changes: 96 additions & 1 deletion solutions/day20/main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,104 @@
package main

import (
"fmt"
"github.com/terminalnode/adventofcode2024/common"
"github.com/terminalnode/adventofcode2024/common/util"
"slices"
)

func main() {
common.Setup(20, nil, nil)
common.Setup(20, part1, nil)
}

func part1(
input string,
) string {
p, err := parse(input)
if err != nil {
return fmt.Sprintf("Failed to parse input: %v", err)
}

dm, path, err := p.createDistanceMap()
if err != nil {
return fmt.Sprintf("Failed to create distance map: %v", err)
}

cheatCounts := make(map[int]int)
for _, pos := range path {
distance := dm[pos.Y][pos.X]
next := []util.Coordinate{
pos.North().North(),
pos.South().South(),
pos.East().East(),
pos.West().West(),
}

for _, nPos := range next {
// Normally moving two steps should give us two less distance,
// so these 2 are subtracted from the saved amount.
nDistance := dm[nPos.Y][nPos.X]
saved := nDistance - distance - 2
if saved > 0 {
cheatCounts[saved] += 1
}
}
}

count := 0
for k, v := range cheatCounts {
if k >= 100 {
count += v
}
fmt.Printf("There are %d cheats that save %d picoseconds.\n", v, k)
}

return fmt.Sprintf("Number of cheats saving at least 100ps: %d", count)
}

func (p parsedInput) createDistanceMap() (distanceMap, []util.Coordinate, error) {
newM := make(distanceMap)
path := make([]util.Coordinate, 0, p.length)
for y, _ := range p.m {
newM[y] = make(map[intX]int)
}

distance := 0
curr := util.Coordinate{X: p.e.X, Y: p.e.Y}
newM[curr.Y][curr.X] = 0

visited := make(map[intY]map[intX]bool)
for y := range p.m {
visited[y] = make(map[intX]bool)
}
visited[curr.Y][curr.X] = true

for !curr.Equals(p.s) {
distance++
found := false
for _, newP := range curr.Adjacent4() {
isBlocked := !p.m[newP.Y][newP.X]
isVisited := visited[newP.Y][newP.X]
if isBlocked || isVisited {
continue
}

found = true
newM[newP.Y][newP.X] = distance
path = append(path, newP)
visited[newP.Y][newP.X] = true
curr = newP
break
}

if !found {
return newM, path, fmt.Errorf("missing path from %v", curr)
}
}

// Skip end position and position next to end position,
// because there's no point in cheating there.
slices.Reverse(path[1:])

return newM, path, nil
}
61 changes: 61 additions & 0 deletions solutions/day20/parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"errors"
"github.com/terminalnode/adventofcode2024/common/util"
"strings"
)

type intX = int
type intY = int
type raceMap map[intY]map[intX]bool
type distanceMap map[intY]map[intX]int

type parsedInput struct {
m raceMap
length int
s util.Coordinate
e util.Coordinate
}

func parse(
input string,
) (parsedInput, error) {
lines := strings.Split(input, "\n")
m := make(raceMap)
length := 0
s := util.Coordinate{}
e := util.Coordinate{}
sFound := false
eFound := false

for y, line := range lines {
m[y] = make(map[intX]bool)
for x, ch := range line {
if ch == '#' {
continue
}
length += 1

if ch == 'S' {
sFound = true
s = util.Coordinate{X: x, Y: y}
} else if ch == 'E' {
eFound = true
e = util.Coordinate{X: x, Y: y}
}
m[y][x] = true
}
}
out := parsedInput{m: m, s: s, e: e, length: length}

if !sFound && !eFound {
return out, errors.New("neither start nor end found")
} else if !sFound {
return out, errors.New("no start found")
} else if !eFound {
return out, errors.New("no end found")
}

return out, nil
}

0 comments on commit 49e4f16

Please sign in to comment.