@@ -18,10 +18,98 @@ parse = unzip . map (line . words) . lines
18
18
dir ' 3' = ' U'
19
19
hex s = fst $ (\ (h: _)-> h) (readHex s)
20
20
21
- -- Calculate the area of the polygon using:
22
- -- 1. Shoelace's formula to get the area of the interior points,
23
- -- 2. Adding the points on the circumference as we go around it,
24
- -- 3. And adding 1 to account for the origin.
21
+ -- We use two related but separate results here: Shoelace formula, and Pick's
22
+ -- theorem.
23
+ --
24
+ -- The Shoelace formula gives us an easy way of calculating the area† of a
25
+ -- polygon with integral coordinates by doing a signed sum of the various
26
+ -- trapezoids. There are multiple ways to express the formula, the one we use
27
+ -- here computes the signed value
28
+ --
29
+ -- 1/2 * (y1 + y2) * (x1 - x2)
30
+ --
31
+ -- for each pair of vertices as we go along the polygon. Summed together, they
32
+ -- give us the area†.
33
+ --
34
+ -- But I'm adding a disclaimer '†' to the word area, because just like with
35
+ -- Mario, the princess is in another castle, and this this isn't the area we
36
+ -- need. The area† given by the Shoelace formula does not account for the width
37
+ -- of the boundary cells. Thus, it is neither:
38
+ --
39
+ -- 1. The area of the entire polygon (which is what we want - the number of
40
+ -- integral points on or inside the boundary defined by the polygon's edges).
41
+ --
42
+ -- 2. Nor is it the internal area (the number of integral points strictly inside
43
+ -- the polygon).
44
+ --
45
+ -- Instead, it is something in between. This is best understood visually. Here
46
+ -- is a 3x3 grid, with the dots indicating the centers of the cells:
47
+ --
48
+ -- +---+---+---+
49
+ -- | · | · | · |
50
+ -- +---+---+---+
51
+ -- | · | · | · |
52
+ -- +---+---+---+
53
+ -- | · | · | · |
54
+ -- +---+---+---+
55
+ --
56
+ -- The area given by the Shoelace formula would be with respect to these
57
+ -- centers, i.e. it'll be the area shown in the enclosed polygon below:
58
+ --
59
+ -- +---+---+---+
60
+ -- | ·---·---· |
61
+ -- +-|-+---+-|-+
62
+ -- | · | · | · |
63
+ -- +-|-+---+-|-+
64
+ -- | ·---·---· |
65
+ -- +---+---+---+
66
+ --
67
+ -- Visually, we can see that the area we want is 9 - there are 9 cells on or
68
+ -- inside the polygon. But the enclosed polygon covers fractional cells, so we
69
+ -- end up with:
70
+ --
71
+ -- 1 (fully internal cell)
72
+ -- + 0.5 * 4 (boundary cells, of which only half is covered)
73
+ -- + 0.25 * 4 (corner boundary cells) => 1 + 2 + 1 => 4
74
+ --
75
+ -- Indeed, the value obtained by using Shoelace formula will give us the result
76
+ -- 4, and not the 9 that we want.
77
+ --
78
+ -- So how do we fix this? That's where Pick's theorem enters into the picture.
79
+ -- Pick's theorem states that the following quantities:
80
+ --
81
+ -- - A: The area given by Shoelace formula
82
+ -- - I: The number of cells internal to the polygon ("internal area")
83
+ -- - B: The number of boundary cells
84
+ --
85
+ -- Are related by the following equation:
86
+ --
87
+ -- A = I + B/2 - 1
88
+ --
89
+ -- Rearranging, we get,
90
+ --
91
+ -- I = A - B/2 + 1 -- Eq 1
92
+ --
93
+ -- In our case, what we want is the total area, which is the number of cells
94
+ -- inside the polygon (I) plus the number of cells on the boundary (B).
95
+ --
96
+ -- That is, we want I + B.
97
+ --
98
+ -- What we already have is A, from our Shoelace computation, and B, the number
99
+ -- of perimeter points. So the final value we want is
100
+ --
101
+ -- I + B
102
+ -- = (A - B/2 + 1) + B (Substituting Eq 1)
103
+ -- = A + B/2 + 1
104
+ --
105
+ -- And that's it. One minor detail is that instead of doing the 1/2 as mentioned
106
+ -- in the trapezoid formulation of the Shoelace formula, we just don't do it, so
107
+ -- the quantity we end up is actually 2A. Then, when computing the final result,
108
+ -- we can divide by 2 only once. In the formulation below, to make it even
109
+ -- simpler, we start with 2 instead of 1, so the final compution just becomes
110
+ --
111
+ -- (2A + B + 2) / 2
112
+ --
25
113
area :: [Step ] -> Int
26
114
area steps = abs (snd (foldl f ((0 ,0 ), 2 ) steps)) `div` 2
27
115
where
0 commit comments