2023 Day 17 Part 1
This commit is contained in:
parent
6f07fd5c27
commit
b414a9d349
1 changed files with 147 additions and 0 deletions
147
2023/day17.nim
Normal file
147
2023/day17.nim
Normal 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"))
|
||||
Loading…
Reference in a new issue