@@ -2,10 +2,136 @@ import Solver from "../../../../../website/src/components/Solver.js"
2
2
3
3
# Day 18: Lavaduct Lagoon
4
4
5
+ by [ @EugeneFlesselle ] ( https://github.com/EugeneFlesselle )
6
+
5
7
## Puzzle description
6
8
7
9
https://adventofcode.com/2023/day/18
8
10
11
+ ## Solution Summary
12
+
13
+ Assume we have a given ` digPlan: Seq[Trench] ` for which to compute the area,
14
+ and let the following classes:
15
+ ``` scala 3
16
+ enum Direction :
17
+ case Up , Down , Left , Right
18
+
19
+ case class Trench (dir : Direction , length : Int )
20
+ ```
21
+
22
+ We can go through the dig plan keeping track of the current position,
23
+ by starting from ` (x = 0, y = 0) ` , increasing ` x ` when going right, increasing ` y ` when going down, and so on.
24
+
25
+ Provided our current position, we can then keep track of the lagoon area as follows:
26
+ - When going ` Right ` : we count all the points in the line we cover, i.e the ` length ` of the trench.
27
+ - When going ` Down ` : we count all the points which we leave on the left (or pass over),
28
+ i.e. the ` length ` of the downwards trench times our current ` x ` coordinate,
29
+ ` +1 ` to count the current vertical trench as in the area.
30
+ Of course, we may be including too much at this point,
31
+ since we do not yet know what part of the left is actually in the lagoon,
32
+ but we will account for it later.
33
+ - When going ` Left ` : there is nothing to add,
34
+ the position could only have been reached from a downwards trench,
35
+ hence the area has already been counted.
36
+ - When going ` Up ` : we now know by how much we had over increased the area when going down,
37
+ and can remove everything strictly to the left of the current ` x ` position.
38
+
39
+ In summary, we assume we cover everything to the left when going down
40
+ and remove the uncovered part when coming back up.
41
+ Finally, we must start from an area of ` 1 ` as the starting position ` (0, 0) ` is naturally covered,
42
+ but isn't counted by the first trench whichever it may be.
43
+ All of which translates to the following foldLeft in scala 😉:
44
+
45
+ ``` scala 3
46
+ val (_, area) = digPlan.foldLeft((0 , 0 ), 1L ):
47
+ case (((x, y), area), Trench (dir, len)) => dir match
48
+ case Right => ((x + len, y), area + len)
49
+ case Down => ((x, y + len), area + (x + 1 ) * len.toLong)
50
+ case Left => ((x - len, y), area)
51
+ case Up => ((x, y - len), area - x * len.toLong)
52
+ ```
53
+
54
+ Also note we have to use ` Long ` s to avoid the computations from overflowing.
55
+
56
+
57
+ ### Part 1
58
+
59
+ We can get the ` Trench ` of each line in the ` input: String ` ,
60
+ by parsing the direction from the corresponding character
61
+ and ignoring the color of the trench.
62
+ And then proceed as above with the obtained ` digPlan ` .
63
+
64
+ ``` scala 3
65
+ object Direction :
66
+ def fromChar (c : Char ): Direction = c match
67
+ case 'U' => Up case 'D' => Down case 'L' => Left case 'R' => Right
68
+
69
+ val digPlan = for
70
+ case s " $dirC $len (# $_) " <- input.linesIterator
71
+ dir = Direction .fromChar(dirC.head)
72
+ yield Trench (dir, len.toInt)
73
+ ```
74
+
75
+ ### Part 2
76
+
77
+ We do the same for part 2, except we use the color to
78
+ get the trench fields:
79
+ its direction from the last digit,
80
+ and its length by converting from the hexadecimal encoding of the remaining digits.
81
+
82
+ ``` scala 3
83
+ object Direction :
84
+ def fromInt (i : Char ): Direction = i match
85
+ case '0' => Right case '1' => Down case '2' => Left case '3' => Up
86
+
87
+ val digPlan = for
88
+ case s " $_ $_ (# $color) " <- input.linesIterator
89
+ dir = Direction .fromInt(color.last)
90
+ len = BigInt (x = color.init, radix = 16 )
91
+ yield Trench (dir, len.toInt)
92
+ ```
93
+
94
+ ## Final code
95
+
96
+ ``` scala 3
97
+ enum Direction :
98
+ case Up , Down , Left , Right
99
+ object Direction :
100
+ def fromChar (c : Char ): Direction = c match
101
+ case 'U' => Up case 'D' => Down case 'L' => Left case 'R' => Right
102
+ def fromInt (i : Char ): Direction = i match
103
+ case '0' => Right case '1' => Down case '2' => Left case '3' => Up
104
+ import Direction .*
105
+
106
+ case class Trench (dir : Direction , length : Int )
107
+
108
+ def area (digPlan : Seq [Trench ]): Long =
109
+ val (_, area) = digPlan.foldLeft((0 , 0 ), 1L ):
110
+ case (((x, y), area), Trench (dir, len)) => dir match
111
+ case Right => ((x + len, y), area + len)
112
+ case Down => ((x, y + len), area + (x + 1 ) * len.toLong)
113
+ case Left => ((x - len, y), area)
114
+ case Up => ((x, y - len), area - x * len.toLong)
115
+ area
116
+
117
+ def part1 (input : String ): String =
118
+ val digPlan = for
119
+ case s " $dirC $len (# $_) " <- input.linesIterator
120
+ dir = Direction .fromChar(dirC.head)
121
+ yield Trench (dir, len.toInt)
122
+
123
+ area(digPlan.toSeq).toString
124
+
125
+ def part2 (input : String ): String =
126
+ val digPlan = for
127
+ case s " $_ $_ (# $color) " <- input.linesIterator
128
+ dir = Direction .fromInt(color.last)
129
+ len = BigInt (x = color.init, radix = 16 )
130
+ yield Trench (dir, len.toInt)
131
+
132
+ area(digPlan.toSeq).toString
133
+ ```
134
+
9
135
## Solutions from the community
10
136
11
137
- [ Solution] ( https://github.com/merlinorg/aoc2023/blob/main/src/main/scala/Day18.scala ) by [ merlin] ( https://github.com/merlinorg/ )
0 commit comments