From b867d91c4fc5d0f70f5cf3d59961ae764f8d739a Mon Sep 17 00:00:00 2001 From: merlin Date: Wed, 25 Dec 2024 01:03:42 -0500 Subject: [PATCH] day 25 article --- docs/2024/puzzles/day25.md | 74 +++++++++++++++++++ .../src/main/scala/adventofcode/Solver.scala | 1 + 2 files changed, 75 insertions(+) diff --git a/docs/2024/puzzles/day25.md b/docs/2024/puzzles/day25.md index 8b0ab3831..2ffd91ca9 100644 --- a/docs/2024/puzzles/day25.md +++ b/docs/2024/puzzles/day25.md @@ -2,10 +2,84 @@ import Solver from "../../../../../website/src/components/Solver.js" # Day 25: Code Chronicle +by [@merlinorg](https://github.com/merlinorg) + ## Puzzle description https://adventofcode.com/2024/day/25 +## Solution summary + +1. Parse and partition the locks and keys. +2. Find all lock and key combinations that could fit. +3. Return the count of potential matches. + +## Parsing + +It's the last day of Advent of Code so we'll keep it simple. The input consists +of a sequence of grids, separated by blank lines. Each grid is a sequence of lines +consisting of the `#` and `.` characters. Locks have `#` characters in the first +row, where keys have `.` characters. To parse this we'll just use simple string +splitting. + +First, we split the input into grids by matching on the blank lines. This gives us +an array of strings, each grid represented by a single string. Then we partition +this into two arrays; one the keys, and the other locks. For this, we use the +`partition` method that takes a predicate; every grid that matches this predicate +will be placed in the first array of the resulting tuple, the rest in the second. + +```scala 3 +val (locks, keys) = input.split("\n\n").partition(_.startsWith("#")) +``` + +In general, arrays are not the most ergonomic data structures to use, but for +this final puzzle they are more than sufficient. + +## Matching + +To find all potential matches we will use a for comprehension to loop through +all the locks, and then for each lock, through all the keys. For each pair of +a lock and key, we want to determine whether there is any overlap that would +prevent the key fitting the lock. +We can perform this test by simply zipping the key and the lock strings; this +gives us a collection of every corresponding character from each string. The +key can fit the lock if there is no location containing a `#` character in +both grids. + +```scala 3 +val matches = for + lock <- locks + key <- keys + if lock.zip(key).forall: (lockChar, keyChar) => + lockChar != '#' || keyChar != '#' +yield lock -> key +``` + +This returns all of the matching lock and key combinations; the solution to +the puzzle is the size of this array. + +## Final code + +```scala 3 +def part1(input: String): Int = + val (locks, keys) = input.split("\n\n").partition(_.startsWith("#")) + + val matches = for + lock <- locks + key <- keys + if lock.zip(key).forall: (lockChar, keyChar) => + lockChar != '#' || keyChar != '#' + yield lock -> key + + matches.length +``` + +### Run it in the browser + +#### Part 1 + + + ## Solutions from the community Share your solution to the Scala community by editing this page. diff --git a/solver/src/main/scala/adventofcode/Solver.scala b/solver/src/main/scala/adventofcode/Solver.scala index ff150c88a..2705c7a0e 100644 --- a/solver/src/main/scala/adventofcode/Solver.scala +++ b/solver/src/main/scala/adventofcode/Solver.scala @@ -15,6 +15,7 @@ object Solver: "day21-part2" -> day21.part2, "day22-part1" -> day22.part1, "day22-part2" -> day22.part2, + "day25-part1" -> day25.part1, ) private val solutions2023: Map[String, String => Any] =