Day 12: Garden Groups
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
Filling to find regions was easy. Counting areas was easy. Counting fences was okay. Counting sides caused me a lot of frustration as I tried and rejected a number of approaches, eventually arriving at a reasonably simple corner-counting approach. None of this was helped by all the examples lacking at least two important layouts, causing today to be the first day that I ran out of hints for wrong answers :-(.
(
corners
is where the magic happens)70 or so lines, half a second to run, so that's fine for today.
import 'dart:math'; import 'package:collection/collection.dart'; import 'package:more/more.dart'; const List<Point> n4 = [Point(0, 1), Point(0, -1), Point(1, 0), Point(-1, 0)]; List<Point> n8 = n4 + [Point(1, 1), Point(1, -1), Point(-1, 1), Point(-1, -1)]; const List<Point> c4 = [Point(0, 0), Point(0, 1), Point(1, 0), Point(1, 1)]; (Map<Point, String>, Map<Point, List<Point>>) parse(ls) { var nodes = { for (var y in 0.to(ls.length)) for (var x in 0.to(ls.first.length)) Point<num>(x, y): ls[y][x] as String }; var nexts = Map.fromEntries(nodes.keys.map((n) => MapEntry( n, n4 .map((d) => n + d) .where((d) => (nodes[d] ?? '') == nodes[n]!) .toList()))); return (nodes, nexts); } (int, Set<Point>) survey( Point here, String target, Map<Point<num>, List<Point>> nexts, [Set sofar = const {}]) { seen.add(here); var fences = 4 - nexts[here]!.length; var area = {here}; for (var f in nexts[here]!.where((e) => !seen.contains(e))) { var (fs, a) = survey(f, target, nexts, sofar.toSet()..add(f)); fences += fs; area.addAll(a); } return (fences, area); } late Set<Point> seen; List<(int, Set<Point<num>>)> costs(List<String> lines) { seen = {}; var ret = <(int, Set<Point<num>>)>[]; var (nodes, nexts) = parse(lines); var toVisit = nodes.keys.toSet(); while (toVisit.isNotEmpty) { var here = toVisit.first; toVisit.remove(here); ret.add(survey(here, nodes[here]!, nexts)); toVisit.removeAll(seen); } return ret; } Function eq = const ListEquality().equals; int corners(Set<Point> points) { var border = points.map((e) => n8.map((n) => n + e)).flattenedToSet ..addAll(points); // A corner is where a 2x2 grid contains one/three in-shape points, or // two diagonally-opposite cells var corners = 0; for (var cell in border) { var count = c4.map((e) => points.contains(e + cell)).toList(); if (count.count((e) => e) % 2 == 1) { corners += 1; } else { if (eq(count, [true, false, false, true]) || eq(count, [false, true, true, false])) { corners += 2; } } } return corners; } part1(lines) => costs(lines).map((e) => e.first * e.last.length).sum; part2(lines) => costs(lines).map((e) => corners(e.last) * e.last.length).sum;