diff --git a/2021/nim/day15.nim b/2021/nim/day15.nim index 954a72b..8bee055 100644 --- a/2021/nim/day15.nim +++ b/2021/nim/day15.nim @@ -1,8 +1,80 @@ import ./common, std/[sequtils, algorithm, sugar, sets, strformat, strutils, tables] -proc p1(input: Lines): uint64 = - return 0 +type + Coordinate = uint + Cost = uint + Node = (Coordinate, Coordinate) + NodeSet = HashSet[Node] + Grid = Table[Node, Cost] +proc digitToUint(c: char): Cost = c.uint - '0'.uint +proc toGrid(input: Lines): (Grid, Coordinate, Coordinate) = + for y, line in input.pairs: + for x, c in line: + result[1] = max(x.uint + 1, result[1]) + result[2] = max(y.uint + 1, result[2]) + result[0][(x.uint, y.uint)] = c.digitToUint + +proc lowest(nodes: NodeSet, scores: Grid): Node = + var lowest = uint.high + for n in nodes: + let score = scores.getOrDefault(n, uint.high) + if score < lowest: + result = n + lowest = score + +proc astarCost(grid: Grid, start: Node, goal: Node, w: Coordinate, h: Coordinate, heuristic: (Node) -> Cost): Cost = + var open: NodeSet = [start].toHashSet + var cameFrom = initTable[Node, Node]() + var bestScores: Grid = {start: start.heuristic}.toTable + var guessScores: Grid = {start: start.heuristic}.toTable + + while open.len() > 0: + var (x, y) = open.lowest guessScores + if (x, y) == goal: + var walker = goal + while cameFrom.contains(walker): + result += walker.heuristic + walker = cameFrom[walker] + return + + open.excl (x, y) + for neighbor in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]: + if uint.high == neighbor[0] or uint.high == neighbor[1] or w == neighbor[0] or h == neighbor[1]: continue + # TODO: could this overflow? + var maybeBestScore = bestScores[(x, y)] + neighbor.heuristic + if maybeBestScore < bestScores.getOrDefault(neighbor, uint.high): + cameFrom[neighbor] = (x, y) + bestScores[neighbor] = maybeBestScore + guessScores[neighbor] = maybeBestScore + neighbor.heuristic + if not open.contains(neighbor): + open.incl(neighbor) + +proc p1(input: Lines): uint64 = + let (grid, w, h) = input.toGrid + let heuristic = (n: Node) => grid[n] + let start: Node = (0'u, 0'u) + let goal: Node = (w - 1, h - 1) + astarCost(grid, start, goal, w, h, heuristic) + +proc p2(input: Lines): uint64 = + let (mainTile, mtw, mth) = input.toGrid + var grid: Grid = initTable[Node, Cost]() + for tx in 0'u..<5'u: + for ty in 0'u..<5'u: + for x in 0'u.. 9: c -= 9 + grid[(ax, ay)] = c + let (w, h) = (mtw * 5'u, mth * 5'u) + let heuristic = (n: Node) => grid[n] + let start: Node = (0'u, 0'u) + let goal: Node = (w - 1, h - 1) + astarCost(grid, start, goal, w, h, heuristic) + + # astar const input = """ 1163751742 1381373672 @@ -15,4 +87,4 @@ const input = """ 1293138521 2311944581 """.strip().split('\n').mapIt(it.strip) -doDayX 15, n => n.loadInput, p1, p1, (input, 0'u64, 0'u64) +doDayX 15, (n: int) => n.loadInput, p1, p2, (input, 40'u64, 315'u64)