Skip to content

Commit 8ccba09

Browse files
committed
Figure out and add the actual explanation of what's going on
Thanks to many reddit comment, esp these two: https://www.reddit.com/r/adventofcode/comments/18l2tap/comment/kdw7rqk https://www.reddit.com/r/adventofcode/comments/18lg2we/comment/kdyomrm
1 parent 936c040 commit 8ccba09

File tree

2 files changed

+99
-13
lines changed

2 files changed

+99
-13
lines changed

18.hs

+92-4
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,98 @@ parse = unzip . map (line . words) . lines
1818
dir '3' = 'U'
1919
hex s = fst $ (\(h:_)->h) (readHex s)
2020

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+
--
25113
area :: [Step] -> Int
26114
area steps = abs (snd (foldl f ((0,0), 2) steps)) `div` 2
27115
where

18.swift

+7-9
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,15 @@ func readInput() -> ([Step], [Step]) {
2727
return (s1, s2)
2828
}
2929

30-
/// Explanation: Shoelace formula + Circumference + 1
30+
/// Explanation: Shoelace formula + Circumference / 2 + 1
3131
///
32-
/// The interior area of a polygon with integral cartesian coordinates can be
33-
/// computed by summing up the signed areas of the trapezoids formed by
34-
/// consecutive pairs of vertices (**Shoelace formula**).
32+
/// The area of a polygon with integral cartesian coordinates can be computed by
33+
/// summing up the signed areas of the trapezoids formed by consecutive pairs of
34+
/// vertices (**Shoelace formula**). However, this gives us an area where the
35+
/// boundary cells are not fully accounted for, so to correct that, we need to
36+
/// use **Pick's theorem** and add the (Circumference / 2 + 1) correction term.
3537
///
36-
/// To get the total area (i.e. the interior area plus the edge cells), we can
37-
/// just add the circumference as we circumnavigate it.
38-
///
39-
/// Finally, since the circumference didn't include the starting square, we add
40-
/// 1 to account for it.
38+
/// See `18.hs`` for a much more detailed explanation.
4139
func area(steps: [Step]) -> Int {
4240
var px = 0, py = 0, s = 0
4341
for step in steps {

0 commit comments

Comments
 (0)