advent of code solutions aoc.oppi.li
haskell aoc

2025: add d9

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li c8f19f2e f25f0039

verified
+331 -16
+2 -1
book/build.sh
··· 10 10 pandoc "$INDIR"/**/*.lhs \ 11 11 -o "$OUTDIR" \ 12 12 -f markdown \ 13 - -t chunkedhtml \ 14 13 --toc=true \ 15 14 --split-level=2 \ 16 15 --toc-depth=2 \ 16 + -t chunkedhtml \ 17 + --metadata-file "$SCRIPTDIR"/meta.yml \ 17 18 --css style.css \ 18 19 --lua-filter "$SCRIPTDIR"/filter.lua \ 19 20 --highlight-style "$SCRIPTDIR"/highlight.theme \
+3
book/meta.yml
··· 1 + title: The Book of Solves 2 + subtitle: Advent of Code solutions in Haskell 3 + author: "[@oppi.li](https://tangled.org/oppi.li)"
+4
book/style.css
··· 79 79 code span.co { color: var(--syntax-comment); font-style: italic; } 80 80 code span.kw { color: var(--syntax-keyword); font-weight: bold; } 81 81 82 + .title, .subtitle, .author, .date { 83 + text-align: center !important; 84 + } 85 + 82 86 @media (max-width: 768px) { 83 87 body { 84 88 max-width: 100%;
+1 -1
book/template.html
··· 73 73 <p class="subtitle">$subtitle$</p> 74 74 $endif$ 75 75 $for(author)$ 76 - <p class="author">$author$</p> 76 + <p class="author">by $author$</p> 77 77 $endfor$ 78 78 $if(date)$ 79 79 <p class="date">$date$</p>
+4 -3
out/1-section.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>2016</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 31 32 <nav id="sitenav"> 32 33 <div class="sitenav"> 33 34 <span class="navlink"> 34 - <span class="navlink-label">Up:</span> <a href="index.html" accesskey="u" rel="up"></a> 35 + <span class="navlink-label">Up:</span> <a href="index.html" accesskey="u" rel="up">The Book of Solves</a> 35 36 </span> 36 37 <span class="navlink"> 37 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 38 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 38 39 </span> 39 40 </div> 40 41 <div class="sitenav"> 41 42 <span class="navlink"> 42 - <span class="navlink-label">Previous:</span> <a href="index.html" accesskey="p" rel="previous"></a> 43 + <span class="navlink-label">Previous:</span> <a href="index.html" accesskey="p" rel="previous">The Book of Solves</a> 43 44 </span> 44 45 <span class="navlink"> 45 46 <span class="navlink-label">Next:</span> <a href="1.1-day-1.html" accesskey="n" rel="next">Day 1</a>
+2 -1
out/1.1-day-1.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>Day 1</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 81 82 <span class="navlink-label">Up:</span> <a href="1-section.html" accesskey="u" rel="up">2016</a> 82 83 </span> 83 84 <span class="navlink"> 84 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 85 86 </span> 86 87 </div> 87 88 <div class="sitenav">
+2 -1
out/1.2-day-2.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>Day 2</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 81 82 <span class="navlink-label">Up:</span> <a href="1-section.html" accesskey="u" rel="up">2016</a> 82 83 </span> 83 84 <span class="navlink"> 84 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 85 86 </span> 86 87 </div> 87 88 <div class="sitenav">
+3 -2
out/2-section-1.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>2025</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 31 32 <nav id="sitenav"> 32 33 <div class="sitenav"> 33 34 <span class="navlink"> 34 - <span class="navlink-label">Up:</span> <a href="index.html" accesskey="u" rel="up"></a> 35 + <span class="navlink-label">Up:</span> <a href="index.html" accesskey="u" rel="up">The Book of Solves</a> 35 36 </span> 36 37 <span class="navlink"> 37 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 38 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 38 39 </span> 39 40 </div> 40 41 <div class="sitenav">
+2 -1
out/2.1-day-2-1.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>Day 2</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 81 82 <span class="navlink-label">Up:</span> <a href="2-section-1.html" accesskey="u" rel="up">2025</a> 82 83 </span> 83 84 <span class="navlink"> 84 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 85 86 </span> 86 87 </div> 87 88 <div class="sitenav">
+2 -1
out/2.2-day-4.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>Day 4</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 81 82 <span class="navlink-label">Up:</span> <a href="2-section-1.html" accesskey="u" rel="up">2025</a> 82 83 </span> 83 84 <span class="navlink"> 84 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 85 86 </span> 86 87 </div> 87 88 <div class="sitenav">
+2 -1
out/2.3-day-5.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>Day 5</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 81 82 <span class="navlink-label">Up:</span> <a href="2-section-1.html" accesskey="u" rel="up">2025</a> 82 83 </span> 83 84 <span class="navlink"> 84 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 85 86 </span> 86 87 </div> 87 88 <div class="sitenav">
+3 -1
out/2.4-day-6.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 7 8 <title>Day 6</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} ··· 81 82 <span class="navlink-label">Up:</span> <a href="2-section-1.html" accesskey="u" rel="up">2025</a> 82 83 </span> 83 84 <span class="navlink"> 84 - <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top"></a> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 85 86 </span> 86 87 </div> 87 88 <div class="sitenav"> ··· 89 90 <span class="navlink-label">Previous:</span> <a href="2.3-day-5.html" accesskey="p" rel="previous">Day 5</a> 90 91 </span> 91 92 <span class="navlink"> 93 + <span class="navlink-label">Next:</span> <a href="2.5-day-9.html" accesskey="n" rel="next">Day 9</a> 92 94 </span> 93 95 </div> 94 96 </nav>
+213
out/2.5-day-9.html
··· 1 + <!DOCTYPE html> 2 + <html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <meta name="generator" content="pandoc" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 + <meta name="author" content="@oppi.li" /> 8 + <title>Day 9</title> 9 + <style> 10 + code{white-space: pre-wrap;} 11 + span.smallcaps{font-variant: small-caps;} 12 + div.columns{display: flex; gap: min(4vw, 1.5em);} 13 + div.column{flex: auto; overflow-x: auto;} 14 + div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} 15 + /* The extra [class] is a hack that increases specificity enough to 16 + override a similar rule in reveal.js */ 17 + ul.task-list[class]{list-style: none;} 18 + ul.task-list li input[type="checkbox"] { 19 + font-size: inherit; 20 + width: 0.8em; 21 + margin: 0 0.8em 0.2em -1.6em; 22 + vertical-align: middle; 23 + } 24 + .display.math{display: block; text-align: center; margin: 0.5rem auto;} 25 + /* CSS for syntax highlighting */ 26 + html { -webkit-text-size-adjust: 100%; } 27 + pre > code.sourceCode { white-space: pre; position: relative; } 28 + pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } 29 + pre > code.sourceCode > span:empty { height: 1.2em; } 30 + .sourceCode { overflow: visible; } 31 + code.sourceCode > span { color: inherit; text-decoration: inherit; } 32 + div.sourceCode { margin: 1em 0; } 33 + pre.sourceCode { margin: 0; } 34 + @media screen { 35 + div.sourceCode { overflow: auto; } 36 + } 37 + @media print { 38 + pre > code.sourceCode { white-space: pre-wrap; } 39 + pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; } 40 + } 41 + pre.numberSource code 42 + { counter-reset: source-line 0; } 43 + pre.numberSource code > span 44 + { position: relative; left: -4em; counter-increment: source-line; } 45 + pre.numberSource code > span > a:first-child::before 46 + { content: counter(source-line); 47 + position: relative; left: -1em; text-align: right; vertical-align: baseline; 48 + border: none; display: inline-block; 49 + -webkit-touch-callout: none; -webkit-user-select: none; 50 + -khtml-user-select: none; -moz-user-select: none; 51 + -ms-user-select: none; user-select: none; 52 + padding: 0 4px; width: 4em; 53 + } 54 + pre.numberSource { margin-left: 3em; padding-left: 4px; } 55 + div.sourceCode 56 + { } 57 + @media screen { 58 + pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; } 59 + } 60 + code span.al { font-weight: bold; } /* Alert */ 61 + code span.an { font-style: italic; } /* Annotation */ 62 + code span.cf { font-weight: bold; } /* ControlFlow */ 63 + code span.co { font-style: italic; } /* Comment */ 64 + code span.cv { font-style: italic; } /* CommentVar */ 65 + code span.do { font-style: italic; } /* Documentation */ 66 + code span.dt { color: #8f4e8b; } /* DataType */ 67 + code span.er { font-weight: bold; } /* Error */ 68 + code span.in { font-style: italic; } /* Information */ 69 + code span.kw { font-weight: bold; } /* Keyword */ 70 + code span.pp { font-weight: bold; } /* Preprocessor */ 71 + code span.wa { font-style: italic; } /* Warning */ 72 + </style> 73 + <link rel="stylesheet" href="style.css" /> 74 + <link rel="preconnect" href="https://fonts.googleapis.com"> 75 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 76 + <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&family=Libre+Baskerville:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet"> 77 + </head> 78 + <body> 79 + <nav id="sitenav"> 80 + <div class="sitenav"> 81 + <span class="navlink"> 82 + <span class="navlink-label">Up:</span> <a href="2-section-1.html" accesskey="u" rel="up">2025</a> 83 + </span> 84 + <span class="navlink"> 85 + <span class="navlink-label">Top:</span> <a href="index.html" accesskey="t" rel="top">The Book of Solves</a> 86 + </span> 87 + </div> 88 + <div class="sitenav"> 89 + <span class="navlink"> 90 + <span class="navlink-label">Previous:</span> <a href="2.4-day-6.html" accesskey="p" rel="previous">Day 6</a> 91 + </span> 92 + <span class="navlink"> 93 + </span> 94 + </div> 95 + </nav> 96 + 97 + <h2 data-number="2.5" id="day-9">Day 9</h2> 98 + <div class="row"> 99 + <p>This day’s problem is based on simple coordinate geometry.</p> 100 + <div class="code"> 101 + 102 + </div> 103 + </div> 104 + <div class="row"> 105 + <p>Parse the input data first (pairs of numbers separated by 106 + commas, one pair per line):</p> 107 + <div class="code"> 108 + <div class="sourceCode" id="cb1"><pre 109 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List.Split</span> (splitOn)</span> 110 + <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> 111 + <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot">parse ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> [[<span class="dt">Integer</span>]]</span> 112 + <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>parse <span class="ot">=</span> <span class="fu">map</span> (<span class="fu">map</span> <span class="fu">read</span> <span class="op">.</span> splitOn <span class="st">&quot;,&quot;</span>) <span class="op">.</span> <span class="fu">lines</span></span></code></pre></div> 113 + </div> 114 + </div> 115 + <div class="row"> 116 + <p>Onto the problem itself.</p> 117 + <div class="code"> 118 + 119 + </div> 120 + </div> 121 + <h3 data-number="2.5.1" id="part-1-6">Part 1</h3> 122 + <div class="row"> 123 + <p>This part requires us to simply find a rectangle with the 124 + largest area among the points given to us, so first write a method 125 + to compute areas given a point. Remember that our representation 126 + of a point is <code>[x, y]</code>:</p> 127 + <div class="code"> 128 + <div class="sourceCode" id="cb2"><pre 129 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">area ::</span> [<span class="dt">Integer</span>] <span class="ot">-&gt;</span> [<span class="dt">Integer</span>] <span class="ot">-&gt;</span> <span class="dt">Integer</span></span> 130 + <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>area [x, y] [x&#39;, y&#39;] <span class="ot">=</span> </span> 131 + <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> (<span class="dv">1</span> <span class="op">+</span> <span class="fu">abs</span> (x <span class="op">-</span> x&#39;)) <span class="op">*</span> (<span class="dv">1</span> <span class="op">+</span> <span class="fu">abs</span> (y <span class="op">-</span> y&#39;))</span></code></pre></div> 132 + </div> 133 + </div> 134 + <div class="row"> 135 + <p>Thus, the solution to the first part is given by the following 136 + list comprehension:</p> 137 + <div class="code"> 138 + <div class="sourceCode" id="cb3"><pre 139 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">p1 ::</span> [[<span class="dt">Integer</span>]] <span class="ot">-&gt;</span> <span class="dt">Integer</span></span> 140 + <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>p1 poly <span class="ot">=</span> <span class="fu">maximum</span> [area p p&#39; <span class="op">|</span> p <span class="ot">&lt;-</span> poly, p&#39; <span class="ot">&lt;-</span> poly]</span></code></pre></div> 141 + </div> 142 + </div> 143 + <h3 data-number="2.5.2" id="part-2-5">Part 2</h3> 144 + <div class="row"> 145 + <p>This part is a whole lot trickier. We now require only 146 + rectangles that fall <em>within</em> the polygon. Rectangles that 147 + have areas outside the polygon should be ignored. The methodology 148 + here is to identify rectangles that do not intersect in any way 149 + with the polygon itself. Sharing an edge with the polygon is 150 + alright however.</p> 151 + <div class="code"> 152 + 153 + </div> 154 + </div> 155 + <div class="row"> 156 + <p>Luckily, the intersections are quite easily calculated, since 157 + all lines of the polygon are oriented along the axes, and so are 158 + the lines forming the rectangle. If a line of the polygon falls to 159 + the left of the leftmost line of the rectangle, or to the right of 160 + the rightmost line, or above the topmost line, or below the 161 + bottommost line, then the line does not intersect the 162 + rectangle:</p> 163 + <div class="code"> 164 + <div class="sourceCode" id="cb4"><pre 165 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">intersects ::</span> [<span class="dt">Integer</span>] <span class="ot">-&gt;</span> [<span class="dt">Integer</span>] <span class="ot">-&gt;</span> [[<span class="dt">Integer</span>]] <span class="ot">-&gt;</span> <span class="dt">Bool</span></span> 166 + <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>intersects [x, y] [x&#39;, y&#39;] <span class="ot">=</span> <span class="fu">not</span> <span class="op">.</span> <span class="fu">all</span> away <span class="op">.</span> pairs</span> 167 + <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 168 + <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> pairs (p <span class="op">:</span> ps) <span class="ot">=</span> <span class="fu">zip</span> (p <span class="op">:</span> ps) (ps <span class="op">++</span> [p])</span> 169 + <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> away ([lx, ly], [lx&#39;, ly&#39;]) <span class="ot">=</span></span> 170 + <span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> (<span class="fu">max</span> lx lx&#39; <span class="op">&lt;=</span> <span class="fu">min</span> x x&#39;)</span> 171 + <span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="op">||</span> (<span class="fu">min</span> lx lx&#39; <span class="op">&gt;=</span> <span class="fu">max</span> x x&#39;)</span> 172 + <span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="op">||</span> (<span class="fu">max</span> ly ly&#39; <span class="op">&lt;=</span> <span class="fu">min</span> y y&#39;)</span> 173 + <span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> <span class="op">||</span> (<span class="fu">min</span> ly ly&#39; <span class="op">&gt;=</span> <span class="fu">max</span> y y&#39;)</span></code></pre></div> 174 + </div> 175 + </div> 176 + <div class="row"> 177 + <p>To explain a bit further, we define <code>away</code> according 178 + the the rules above. Then the <code>intersects</code> method is 179 + given by simply ensuring that of all pairs of points in the 180 + polygon (we need pairs to compute line segments of the polygon), 181 + we have atleast one line segment that is not away from the 182 + rectangle.</p> 183 + <div class="code"> 184 + 185 + </div> 186 + </div> 187 + <div class="row"> 188 + <p>Thus, the solution to the second part is a similar list 189 + comprehension with an additional guard:</p> 190 + <div class="code"> 191 + <div class="sourceCode" id="cb5"><pre 192 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">p2 ::</span> [[<span class="dt">Integer</span>]] <span class="ot">-&gt;</span> <span class="dt">Integer</span></span> 193 + <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>p2 poly <span class="ot">=</span></span> 194 + <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">maximum</span></span> 195 + <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> [ area p p&#39;</span> 196 + <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> p <span class="ot">&lt;-</span> poly,</span> 197 + <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> p&#39; <span class="ot">&lt;-</span> poly,</span> 198 + <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">not</span> (intersects p p&#39; poly)</span> 199 + <span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> 200 + </div> 201 + </div> 202 + <div class="row"> 203 + <p>Finally, a main function to wrap it all up:</p> 204 + <div class="code"> 205 + <div class="sourceCode" id="cb6"><pre 206 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span> 207 + <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> n <span class="ot">&lt;-</span> parse <span class="op">&lt;$&gt;</span> <span class="fu">getContents</span></span> 208 + <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span> <span class="op">$</span> p1 n</span> 209 + <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span> <span class="op">$</span> p2 n</span></code></pre></div> 210 + </div> 211 + </div> 212 + </body> 213 + </html>
+10 -2
out/index.html
··· 4 4 <meta charset="utf-8" /> 5 5 <meta name="generator" content="pandoc" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> 7 - <title>01</title> 7 + <meta name="author" content="@oppi.li" /> 8 + <title>The Book of Solves</title> 8 9 <style> 9 10 code{white-space: pre-wrap;} 10 11 span.smallcaps{font-variant: small-caps;} ··· 43 44 </span> 44 45 </div> 45 46 </nav> 46 - </header> 47 + <header id="title-block-header"> 48 + <h1 class="title">The Book of Solves</h1> 49 + <p class="subtitle">Advent of Code solutions in Haskell</p> 50 + <p class="author">by <a 51 + href="https://tangled.org/oppi.li"><span class="citation" 52 + data-cites="oppi.li">@oppi.li</span></a></p> 53 + </header> 47 54 <nav id="TOC" role="doc-toc"> 48 55 <ul> 49 56 <li><a href="1-section.html#section" id="toc-section">2016</a> ··· 57 64 <li><a href="2.2-day-4.html#day-4" id="toc-day-4">Day 4</a></li> 58 65 <li><a href="2.3-day-5.html#day-5" id="toc-day-5">Day 5</a></li> 59 66 <li><a href="2.4-day-6.html#day-6" id="toc-day-6">Day 6</a></li> 67 + <li><a href="2.5-day-9.html#day-9" id="toc-day-9">Day 9</a></li> 60 68 </ul></li> 61 69 </ul> 62 70 </nav>
+1 -1
out/sitemap.json
··· 1 - {"section":{"id":"","level":"0","number":null,"path":"index.html","title":""},"subsections":[{"section":{"id":"section","level":"1","number":"1","path":"1-section.html#section","title":"2016"},"subsections":[{"section":{"id":"day-1","level":"2","number":"1.1","path":"1.1-day-1.html#day-1","title":"Day 1"},"subsections":[{"section":{"id":"part-1","level":"3","number":"1.1.1","path":"1.1-day-1.html#part-1","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2","level":"3","number":"1.1.2","path":"1.1-day-1.html#part-2","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-2","level":"2","number":"1.2","path":"1.2-day-2.html#day-2","title":"Day 2"},"subsections":[{"section":{"id":"part-1-1","level":"3","number":"1.2.1","path":"1.2-day-2.html#part-1-1","title":"Part 1"},"subsections":[]}]}]},{"section":{"id":"section-1","level":"1","number":"2","path":"2-section-1.html#section-1","title":"2025"},"subsections":[{"section":{"id":"day-2-1","level":"2","number":"2.1","path":"2.1-day-2-1.html#day-2-1","title":"Day 2"},"subsections":[{"section":{"id":"part-1-2","level":"3","number":"2.1.1","path":"2.1-day-2-1.html#part-1-2","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-1","level":"3","number":"2.1.2","path":"2.1-day-2-1.html#part-2-1","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-4","level":"2","number":"2.2","path":"2.2-day-4.html#day-4","title":"Day 4"},"subsections":[{"section":{"id":"part-1-3","level":"3","number":"2.2.1","path":"2.2-day-4.html#part-1-3","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-2","level":"3","number":"2.2.2","path":"2.2-day-4.html#part-2-2","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-5","level":"2","number":"2.3","path":"2.3-day-5.html#day-5","title":"Day 5"},"subsections":[{"section":{"id":"part-1-4","level":"3","number":"2.3.1","path":"2.3-day-5.html#part-1-4","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-3","level":"3","number":"2.3.2","path":"2.3-day-5.html#part-2-3","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-6","level":"2","number":"2.4","path":"2.4-day-6.html#day-6","title":"Day 6"},"subsections":[{"section":{"id":"part-1-5","level":"3","number":"2.4.1","path":"2.4-day-6.html#part-1-5","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-4","level":"3","number":"2.4.2","path":"2.4-day-6.html#part-2-4","title":"Part 2"},"subsections":[]}]}]}]} 1 + {"section":{"id":"","level":"0","number":null,"path":"index.html","title":"The Book of Solves"},"subsections":[{"section":{"id":"section","level":"1","number":"1","path":"1-section.html#section","title":"2016"},"subsections":[{"section":{"id":"day-1","level":"2","number":"1.1","path":"1.1-day-1.html#day-1","title":"Day 1"},"subsections":[{"section":{"id":"part-1","level":"3","number":"1.1.1","path":"1.1-day-1.html#part-1","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2","level":"3","number":"1.1.2","path":"1.1-day-1.html#part-2","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-2","level":"2","number":"1.2","path":"1.2-day-2.html#day-2","title":"Day 2"},"subsections":[{"section":{"id":"part-1-1","level":"3","number":"1.2.1","path":"1.2-day-2.html#part-1-1","title":"Part 1"},"subsections":[]}]}]},{"section":{"id":"section-1","level":"1","number":"2","path":"2-section-1.html#section-1","title":"2025"},"subsections":[{"section":{"id":"day-2-1","level":"2","number":"2.1","path":"2.1-day-2-1.html#day-2-1","title":"Day 2"},"subsections":[{"section":{"id":"part-1-2","level":"3","number":"2.1.1","path":"2.1-day-2-1.html#part-1-2","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-1","level":"3","number":"2.1.2","path":"2.1-day-2-1.html#part-2-1","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-4","level":"2","number":"2.2","path":"2.2-day-4.html#day-4","title":"Day 4"},"subsections":[{"section":{"id":"part-1-3","level":"3","number":"2.2.1","path":"2.2-day-4.html#part-1-3","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-2","level":"3","number":"2.2.2","path":"2.2-day-4.html#part-2-2","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-5","level":"2","number":"2.3","path":"2.3-day-5.html#day-5","title":"Day 5"},"subsections":[{"section":{"id":"part-1-4","level":"3","number":"2.3.1","path":"2.3-day-5.html#part-1-4","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-3","level":"3","number":"2.3.2","path":"2.3-day-5.html#part-2-3","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-6","level":"2","number":"2.4","path":"2.4-day-6.html#day-6","title":"Day 6"},"subsections":[{"section":{"id":"part-1-5","level":"3","number":"2.4.1","path":"2.4-day-6.html#part-1-5","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-4","level":"3","number":"2.4.2","path":"2.4-day-6.html#part-2-4","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-9","level":"2","number":"2.5","path":"2.5-day-9.html#day-9","title":"Day 9"},"subsections":[{"section":{"id":"part-1-6","level":"3","number":"2.5.1","path":"2.5-day-9.html#part-1-6","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-5","level":"3","number":"2.5.2","path":"2.5-day-9.html#part-2-5","title":"Part 2"},"subsections":[]}]}]}]}
+4
out/style.css
··· 79 79 code span.co { color: var(--syntax-comment); font-style: italic; } 80 80 code span.kw { color: var(--syntax-keyword); font-weight: bold; } 81 81 82 + .title, .subtitle, .author, .date { 83 + text-align: center !important; 84 + } 85 + 82 86 @media (max-width: 768px) { 83 87 body { 84 88 max-width: 100%;
+73
src/2025/09.lhs
··· 1 + ## Day 9 2 + 3 + This day's problem is based on simple coordinate geometry. 4 + 5 + Parse the input data first (pairs of numbers separated by commas, one pair per line): 6 + 7 + ```haskell 8 + import Data.List.Split (splitOn) 9 + 10 + parse :: String -> [[Integer]] 11 + parse = map (map read . splitOn ",") . lines 12 + ``` 13 + 14 + Onto the problem itself. 15 + 16 + ### Part 1 17 + 18 + This part requires us to simply find a rectangle with the largest area among the points given to us, so first write a method to compute areas given a point. Remember that our representation of a point is `[x, y]`: 19 + 20 + ```haskell 21 + area :: [Integer] -> [Integer] -> Integer 22 + area [x, y] [x', y'] = 23 + (1 + abs (x - x')) * (1 + abs (y - y')) 24 + ``` 25 + 26 + Thus, the solution to the first part is given by the following list comprehension: 27 + 28 + ```haskell 29 + p1 :: [[Integer]] -> Integer 30 + p1 poly = maximum [area p p' | p <- poly, p' <- poly] 31 + ``` 32 + 33 + ### Part 2 34 + 35 + This part is a whole lot trickier. We now require only rectangles that fall *within* the polygon. Rectangles that have areas outside the polygon should be ignored. The methodology here is to identify rectangles that do not intersect in any way with the polygon itself. Sharing an edge with the polygon is alright however. 36 + 37 + Luckily, the intersections are quite easily calculated, since all lines of the polygon are oriented along the axes, and so are the lines forming the rectangle. If a line of the polygon falls to the left of the leftmost line of the rectangle, or to the right of the rightmost line, or above the topmost line, or below the bottommost line, then the line does not intersect the rectangle: 38 + 39 + ```haskell 40 + intersects :: [Integer] -> [Integer] -> [[Integer]] -> Bool 41 + intersects [x, y] [x', y'] = not . all away . pairs 42 + where 43 + pairs (p : ps) = zip (p : ps) (ps ++ [p]) 44 + away ([lx, ly], [lx', ly']) = 45 + (max lx lx' <= min x x') 46 + || (min lx lx' >= max x x') 47 + || (max ly ly' <= min y y') 48 + || (min ly ly' >= max y y') 49 + ``` 50 + 51 + To explain a bit further, we define `away` according the the rules above. Then the `intersects` method is given by simply ensuring that of all pairs of points in the polygon (we need pairs to compute line segments of the polygon), we have atleast one line segment that is not away from the rectangle. 52 + 53 + Thus, the solution to the second part is a similar list comprehension with an additional guard: 54 + 55 + ```haskell 56 + p2 :: [[Integer]] -> Integer 57 + p2 poly = 58 + maximum 59 + [ area p p' 60 + | p <- poly, 61 + p' <- poly, 62 + not (intersects p p' poly) 63 + ] 64 + ``` 65 + 66 + Finally, a main function to wrap it all up: 67 + 68 + ```haskell 69 + main = do 70 + n <- parse <$> getContents 71 + print $ p1 n 72 + print $ p2 n 73 + ```