Watch
1
0
Fork
You've already forked advent-of-code
0

2023 Day 17 Part 1

This commit is contained in:
Jeremy Kaplan 2023-12-17 13:56:50 -08:00
commit b414a9d349

147
2023/day17.nim Normal file
View file

@ -0,0 +1,147 @@
import std/algorithm
import std/deques
import std/enumerate
import std/heapqueue
import std/math
import std/options
import std/sequtils
import std/sets
import std/strscans
import std/strutils
import std/sugar
import std/tables
import std/terminal
import std/times
proc dbg[T](v: T): T = (echo(v); v)
func empty[T](v: seq[T]): bool = v.len == 0
proc split[T](v: seq[T], pred: proc(elt: T): bool {.noSideEffect.}): seq[seq[T]] =
var current: seq[T]
for elt in v:
if pred(elt):
result.add current
current = @[]
else:
current.add elt
result.add current
func join[T](parts: seq[seq[T]], sep: T): seq[T] =
if parts.len > 0:
result &= parts[0]
for part in parts[1..^1]:
result.add sep
result &= part
type Point = tuple
r: int
c: int
type Map = object
height: int
width: int
grid: Table[Point, int]
func parseMap(text: string): Map =
let lines = text.strip.splitLines()
result.height = lines.len
result.width = lines[0].len
for (r, line) in enumerate(lines):
for (c, sym) in enumerate(line):
result.grid[(r, c)] = sym.int - '0'.int
type Direction = enum
N
W
S
E
func cw(d: Direction): Direction =
case d:
of N: E
of E: S
of S: W
of W: N
func ccw(d: Direction): Direction =
case d:
of N: W
of W: S
of S: E
of E: N
func move(p: Point, d: Direction, n: int): Point =
case d:
of N: (p.r - n, p.c)
of S: (p.r + n, p.c)
of W: (p.r, p.c - n)
of E: (p.r, p.c + n)
func inBounds(m: Map, p: Point): bool =
p.r in 0 ..< m.height and p.c in 0 ..< m.width
func loss(m: Map, p: Point): int =
if m.inBounds(p):
return m.grid[p]
type State = tuple
pos: Point
dir: Direction
type Node = tuple
state: State
cost: int
func `<`(a, b: Node): bool = a.cost < b.cost
iterator successors(m: Map, state: State): (State, int) =
for steps in 1..3:
var pos = state.pos
var loss = 0
for _ in 1..steps:
pos = pos.move(state.dir, 1)
loss += m.loss(pos)
yield ((pos, state.dir.cw), loss)
yield ((pos, state.dir.ccw), loss)
proc heatLoss(m: Map, start, goal: Point): int =
var queue: HeapQueue[Node]
var seen: HashSet[State]
# Unknown starting direction, so try them all.
queue.push ((start, N), 0)
queue.push ((start, S), 0)
queue.push ((start, E), 0)
queue.push ((start, W), 0)
while queue.len > 0:
let (state, cost) = queue.pop
if not m.inBounds(state.pos): continue
if state in seen: continue
seen.incl state
if state.pos == goal:
return cost
for (next, extra) in m.successors(state):
queue.push (next, cost + extra)
proc part1(input: string): int =
let text = readFile(input)
let map = parseMap(text)
map.heatLoss((0, 0), (map.height - 1, map.width - 1))
proc timed[T](f: () -> T): T =
let start = cpuTime()
result = f()
echo "Time :", cpuTime() - start
echo timed(proc(): int = part1("input/test.txt"))
echo timed(proc(): int = part1("input/day17.txt"))