Day 16: Reindeer Maze
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Dart
I liked the flexibility of the
path
operator in the Uiua solution so much that I built a similar search function in Dart. Not quite as compact, but still an interesting piece of code that I will keep on hand for other path-finding puzzles.About 80 lines of code, about half of which is the super-flexible search function.
import 'dart:math'; import 'package:collection/collection.dart'; import 'package:more/more.dart'; List<Point<num>> d4 = [Point(1, 0), Point(-1, 0), Point(0, 1), Point(0, -1)]; /// Returns cost to destination, plus list of routes to destination. /// Does Dijkstra/A* search depending on whether heuristic returns 1 or /// something better. (num, List<List<T>>) aStarSearch<T>(T start, Map<T, num> Function(T) fNext, int Function(T) fHeur, bool Function(T) fAtEnd) { var cameFrom = SetMultimap<T, T>.fromEntries([MapEntry(start, start)]); var ends = <T>{}; var front = PriorityQueue<T>((a, b) => fHeur(a).compareTo(fHeur(b))) ..add(start); var cost = <T, num>{start: 0}; while (front.isNotEmpty) { var here = front.removeFirst(); if (fAtEnd(here)) { ends.add(here); continue; } var ns = fNext(here); for (var n in ns.keys) { var nCost = cost[here]! + ns[n]!; if (!cost.containsKey(n) || nCost < cost[n]!) { cost[n] = nCost; front.add(n); cameFrom.removeAll(n); } if (cost[n] == nCost) cameFrom[n].add(here); } } Iterable<List<T>> routes(T h) sync* { if (h == start) { yield [h]; return; } for (var p in cameFrom[h]) { yield* routes(p).map((e) => e + [h]); } } var minCost = ends.map((e) => cost[e]!).min; ends = ends.where((e) => cost[e]! == minCost).toSet(); return (minCost, ends.fold([], (s, t) => s..addAll(routes(t).toList()))); } typedef PP = (Point, Point); (num, List<List<PP>>) solve(List<String> lines) { var grid = { for (var r in lines.indexed()) for (var c in r.value.split('').indexed().where((e) => e.value != '#')) Point<num>(c.index, r.index): c.value }; var start = grid.entries.firstWhere((e) => e.value == 'S').key; var end = grid.entries.firstWhere((e) => e.value == 'E').key; var dir = Point<num>(1, 0); fHeur(PP pd) => 1; // faster than euclidean distance. fNextAndCost(PP pd) => <PP, int>{ for (var n in d4 .where((n) => n != pd.last * -1 && grid.containsKey(pd.first + n))) (pd.first + n, n): ((n == pd.last) ? 1 : 1001) // (Point, Dir) : Cost }; fAtEnd(PP pd) => pd.first == end; return aStarSearch<PP>((start, dir), fNextAndCost, fHeur, fAtEnd); } part1(List<String> lines) => solve(lines).first; part2(List<String> lines) => solve(lines) .last .map((l) => l.map((e) => e.first).toSet()) .flattenedToSet .length;