@@ -6,6 +6,85 @@ import Solver from "../../../../../website/src/components/Solver.js"
6
6
7
7
https://adventofcode.com/2024/day/2
8
8
9
+ ## Solution summary
10
+
11
+ - First we parse each line of the input into a ` Report ` case class: ` case class Report(levels: Seq[Long]) `
12
+ - In each ` Report ` , we construct the sequence of consecutive pairs.
13
+ - For part 1, we check if the pairs are all increasing or all decreasing, and if the difference is within the given limits (such a report is "safe").
14
+ - For part 2, we construct new ` Report ` s obtained by dropping one entry in the original report, and check if there exists a safe ` Report ` among these.
15
+ - In both parts, we simply count the number of ` Report ` s that are considered safe.
16
+
17
+ ### Parsing
18
+
19
+ Each line of input is a string of numbers separated by a single space.
20
+ Therefore parsing looks like this:
21
+
22
+ ``` scala
23
+ case class Report (levels : Seq [Long ])
24
+
25
+ def parseLine (line : String ): Report = Report (line.split(" " ).map(_.toLong).toSeq)
26
+
27
+ def parse (input : String ): Seq [Report ] = input
28
+ .linesIterator
29
+ .map(parseLine)
30
+ .toSeq
31
+ ```
32
+
33
+ ### Part 1: methods of the ` Report ` case class
34
+
35
+ We need to check consecutive pairs of numbers in each report in 3 ways:
36
+
37
+ - to see if they are all increasing,
38
+ - to see if they are all decreasing,
39
+ - to see if their differences are within given bounds.
40
+
41
+ So let's construct them only once, save it as a ` val ` , then reuse this value 3 times.
42
+ It's not the most efficient way (like traversing only once and keeping track of everything),
43
+ but it's very clean and simple:
44
+
45
+ ``` scala
46
+ case class Report (levels : Seq [Long ]):
47
+ val pairs = levels.init.zip(levels.tail) // consecutive pairs
48
+ def allIncr : Boolean = pairs.forall(_ < _)
49
+ def allDecr : Boolean = pairs.forall(_ > _)
50
+ def within (lower : Long , upper : Long ): Boolean = pairs.forall: pair =>
51
+ val diff = math.abs(pair._1 - pair._2)
52
+ lower <= diff && diff <= upper
53
+ def isSafe : Boolean = (allIncr || allDecr) && within(1L , 3L )
54
+ ```
55
+
56
+ Part 1 solver simply counts safe reports, so it looks like this:
57
+
58
+ ``` scala
59
+ def part1 (input : String ): Int = parse(input).count(_.isSafe)
60
+ ```
61
+
62
+ ### Part 2
63
+
64
+ Now we add new methods to ` Report ` .
65
+ We check if there exists a ` Report ` obtained by dropping one number, such that it's safe.
66
+ We do this by iterating over the index of each ` Report ` .
67
+ Then, a ` Report ` is safe, if it's safe as in Part 1, or one of the dampened reports is safe:
68
+
69
+ ``` scala
70
+ case class Report (levels : Seq [Long ]):
71
+ // ... as before
72
+ def checkDampenedReports : Boolean = (0 until levels.size).exists: index =>
73
+ val newLevels = levels.take(index) ++ levels.drop(index + 1 )
74
+ Report (newLevels).isSafe
75
+ def isDampenedSafe : Boolean = isSafe || checkDampenedReports
76
+ ```
77
+
78
+ Again this is not the most efficient way (we are creating many new ` Report ` instances),
79
+ but our puzzle inputs are fairly short (there are at most 8 levels in each ` Report ` ),
80
+ so it's a simple approach that reuses the ` isSafe ` method from Part 1.
81
+
82
+ Part 2 solver now counts the dampened safe reports:
83
+
84
+ ``` scala
85
+ def part2 (input : String ): Int = parse(input).count(_.isDampenedSafe)
86
+ ```
87
+
9
88
## Solutions from the community
10
89
11
90
- [ Solution] ( https://github.com/rmarbeck/advent2024/tree/main/day2 ) by [ Raphaël Marbeck] ( https://github.com/rmarbeck )
0 commit comments