advent of code solutions aoc.oppi.li
haskell aoc

2025: d10, d11, d12

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

oppi.li bbf0f188 13610586

verified
+1013 -16
+15 -5
book/filter.lua
··· 10 10 return false 11 11 end 12 12 13 + function is_wrappable(block) 14 + -- Block types that should be wrapped in the two-column layout 15 + local wrappable_types = { 16 + Para = true, 17 + BulletList = true 18 + } 19 + return wrappable_types[block.t] or false 20 + end 21 + 13 22 function Pandoc(doc) 14 23 local new_blocks = {} 15 24 local i = 1 16 - 25 + 17 26 while i <= #doc.blocks do 18 27 local current = doc.blocks[i] 19 28 20 - if current.t == "Para" then 21 - -- collect all consecutive haskell code blocks following this paragraph 29 + if is_wrappable(current) then 30 + -- collect all consecutive haskell code blocks following this element 22 31 local code_blocks = {} 23 32 local j = i + 1 33 + 24 34 while j <= #doc.blocks and is_haskell(doc.blocks[j]) do 25 35 local code_block = doc.blocks[j] 26 36 ··· 56 66 i = i + 1 57 67 end 58 68 else 59 - -- not a paragraph, just pass through 69 + -- not a wrappable element, just pass through 60 70 table.insert(new_blocks, current) 61 71 i = i + 1 62 72 end 63 73 end 64 - 74 + 65 75 return pandoc.Pandoc(new_blocks, doc.meta) 66 76 end
+1
out/2.6-day-9.html
··· 90 90 <span class="navlink-label">Previous:</span> <a href="2.5-day-6.html" accesskey="p" rel="previous">Day 6</a> 91 91 </span> 92 92 <span class="navlink"> 93 + <span class="navlink-label">Next:</span> <a href="2.7-day-10.html" accesskey="n" rel="next">Day 10</a> 93 94 </span> 94 95 </div> 95 96 </nav>
+219
out/2.7-day-10.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 10</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.6-day-9.html" accesskey="p" rel="previous">Day 9</a> 91 + </span> 92 + <span class="navlink"> 93 + <span class="navlink-label">Next:</span> <a href="2.8-day-11.html" accesskey="n" rel="next">Day 11</a> 94 + </span> 95 + </div> 96 + </nav> 97 + 98 + <h2 data-number="2.7" id="day-10">Day 10</h2> 99 + <div class="row"> 100 + <p>Start by parsing the input:</p> 101 + <div class="code"> 102 + <div class="sourceCode" id="cb1"><pre 103 + 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> 104 + <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> 105 + <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">Int</span>, [[<span class="dt">Int</span>]], [<span class="dt">Int</span>])]</span> 106 + <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>parse <span class="ot">=</span> <span class="fu">map</span> (line <span class="op">.</span> <span class="fu">words</span>) <span class="op">.</span> <span class="fu">lines</span></span> 107 + <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 108 + <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> line (t <span class="op">:</span> ws) <span class="ot">=</span> </span> 109 + <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> (target t, <span class="fu">map</span> list (<span class="fu">init</span> ws), list (<span class="fu">last</span> ws))</span> 110 + <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> target s <span class="ot">=</span> fromBits <span class="op">.</span> <span class="fu">init</span> <span class="op">$</span> <span class="fu">tail</span> s</span> 111 + <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> list <span class="ot">=</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">init</span> <span class="op">.</span> <span class="fu">tail</span></span></code></pre></div> 112 + </div> 113 + </div> 114 + <div class="row"> 115 + <p>Of interest is the lighting sequence, which is represented as 116 + dots (<code>.</code>) and hashes (<code>#</code>). You can think 117 + of this as a number expressed as zeroes and ones:</p> 118 + <div class="code"> 119 + <div class="sourceCode" id="cb2"><pre 120 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">fromBits ::</span> [<span class="dt">Char</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span> 121 + <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>fromBits <span class="ot">=</span> <span class="fu">foldl</span> f <span class="dv">0</span> <span class="op">.</span> <span class="fu">reverse</span></span> 122 + <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 123 + <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> f acc <span class="ch">&#39;.&#39;</span> <span class="ot">=</span> acc <span class="op">*</span> <span class="dv">2</span></span> 124 + <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> f acc <span class="ch">&#39;#&#39;</span> <span class="ot">=</span> acc <span class="op">*</span> <span class="dv">2</span> <span class="op">+</span> <span class="dv">1</span></span></code></pre></div> 125 + </div> 126 + </div> 127 + <div class="row"> 128 + <p>And so we move onto the first part.</p> 129 + <div class="code"> 130 + 131 + </div> 132 + </div> 133 + <h3 data-number="2.7.1" id="part-1-8">Part 1</h3> 134 + <div class="row"> 135 + <p>The first part requires us to find the combinations of buttons 136 + that can result in the lighting sequence, with repetitions 137 + allowed. Pressing once switches on the lights, and pressing a 138 + second time switches it off. This behavior is identical to XOR! 139 + Therefore, the first part is a combination-sum problem, but using 140 + XOR, so a combination-xor problem if you will.</p> 141 + <div class="code"> 142 + 143 + </div> 144 + </div> 145 + <div class="row"> 146 + <p>First, we’d need to convert a button into its bitwise form:</p> 147 + <div class="code"> 148 + <div class="sourceCode" id="cb3"><pre 149 + class="sourceCode haskell haskell-top"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Bits</span> (bit, xor, (.|.))</span> 150 + <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Control.Monad</span> (filterM)</span></code></pre></div> 151 + <div class="sourceCode" id="cb4"><pre 152 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">toBits ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span> 153 + <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>toBits <span class="ot">=</span> <span class="fu">foldl1</span> (<span class="op">.|.</span>) <span class="op">.</span> <span class="fu">map</span> bit</span></code></pre></div> 154 + </div> 155 + </div> 156 + <div class="row"> 157 + <p>Next, we require the powerset of all possible buttons:</p> 158 + <div class="code"> 159 + <div class="sourceCode" id="cb5"><pre 160 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>powerset [] <span class="ot">=</span> [[]]</span> 161 + <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>powerset (x <span class="op">:</span> xs) <span class="ot">=</span> <span class="fu">map</span> (x <span class="op">:</span>) subset <span class="op">++</span> subset</span> 162 + <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 163 + <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> subset <span class="ot">=</span> powerset xs</span></code></pre></div> 164 + </div> 165 + </div> 166 + <div class="row"> 167 + <p>Thus, our combination-xor is defined like so:</p> 168 + <div class="code"> 169 + <div class="sourceCode" id="cb6"><pre 170 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>comboXor target <span class="ot">=</span> </span> 171 + <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">filter</span> ((<span class="op">==</span> target) <span class="op">.</span> <span class="fu">foldl</span> xor <span class="dv">0</span>) <span class="op">.</span> powerset</span></code></pre></div> 172 + </div> 173 + </div> 174 + <div class="row"> 175 + <p>And the solution to the first part, is simply the application 176 + of <code>comboXor</code> to each line of the input, and finding 177 + the smallest possible sequence in each solution:</p> 178 + <div class="code"> 179 + <div class="sourceCode" id="cb7"><pre 180 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>p1 <span class="ot">=</span> <span class="fu">sum</span> <span class="op">.</span> <span class="fu">map</span> solveLine</span> 181 + <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 182 + <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> solveLine (target, btns, _) <span class="ot">=</span></span> 183 + <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">minimum</span> <span class="op">.</span> <span class="fu">map</span> <span class="fu">length</span> <span class="op">$</span> comboXor target (<span class="fu">map</span> toBits btns)</span></code></pre></div> 184 + </div> 185 + </div> 186 + <h3 data-number="2.7.2" id="part-2-7">Part 2</h3> 187 + <div class="row"> 188 + <p>This part requires us to minimize a collection of equations to 189 + identify N coefficients. The solution for this part is given quite 190 + easily by LP/SAT solvers, but not so easily by hand. Some 191 + resources:</p> 192 + <div class="code"> 193 + 194 + </div> 195 + </div> 196 + <div class="row"> 197 + <ul> 198 + <li><a 199 + href="https://en.wikipedia.org/wiki/Simplex_algorithm">Simplex 200 + Algorithm</a></li> 201 + <li><a 202 + href="https://en.wikipedia.org/wiki/Gaussian_elimination">Gaussian 203 + Eliminiation</a></li> 204 + </ul> 205 + <div class="code"> 206 + 207 + </div> 208 + </div> 209 + <div class="row"> 210 + <p>Finally, a main function to wrap it all up:</p> 211 + <div class="code"> 212 + <div class="sourceCode" id="cb8"><pre 213 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span> 214 + <span id="cb8-2"><a href="#cb8-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> 215 + <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span> <span class="op">$</span> p1 n</span></code></pre></div> 216 + </div> 217 + </div> 218 + </body> 219 + </html>
+281
out/2.8-day-11.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 11</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.7-day-10.html" accesskey="p" rel="previous">Day 10</a> 91 + </span> 92 + <span class="navlink"> 93 + <span class="navlink-label">Next:</span> <a href="2.9-day-12.html" accesskey="n" rel="next">Day 12</a> 94 + </span> 95 + </div> 96 + </nav> 97 + 98 + <h2 data-number="2.8" id="day-11">Day 11</h2> 99 + <div class="row"> 100 + <p>Start by munging the input:</p> 101 + <div class="code"> 102 + <div class="sourceCode" id="cb1"><pre 103 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>parse <span class="ot">=</span> graph <span class="op">.</span> <span class="fu">map</span> (l <span class="op">.</span> <span class="fu">words</span> <span class="op">.</span> <span class="fu">filter</span> (<span class="op">/=</span> <span class="ch">&#39;:&#39;</span>)) <span class="op">.</span> <span class="fu">lines</span></span> 104 + <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 105 + <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> l (node <span class="op">:</span> edges) <span class="ot">=</span> (node, edges)</span></code></pre></div> 106 + </div> 107 + </div> 108 + <div class="row"> 109 + <p>Our graph representation is a map of nodes to the list of 110 + outgoing edges:</p> 111 + <div class="code"> 112 + <div class="sourceCode" id="cb2"><pre 113 + class="sourceCode haskell haskell-top"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Map</span> <span class="kw">as</span> <span class="dt">M</span></span></code></pre></div> 114 + <div class="sourceCode" id="cb3"><pre 115 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>graph ls <span class="ot">=</span></span> 116 + <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> M.union</span> 117 + <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> (M.fromList ls)</span> 118 + <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> (M.fromList [(e, []) <span class="op">|</span> (_, es) <span class="ot">&lt;-</span> ls, e <span class="ot">&lt;-</span> es])</span></code></pre></div> 119 + </div> 120 + </div> 121 + <div class="row"> 122 + <p>This, the edge list (a list of <code>(node, edge)</code>) is 123 + given by:</p> 124 + <div class="code"> 125 + <div class="sourceCode" id="cb4"><pre 126 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>edges <span class="ot">=</span> <span class="fu">concatMap</span> (\(n, e) <span class="ot">-&gt;</span> <span class="fu">map</span> (n,) e) <span class="op">.</span> M.toList</span></code></pre></div> 127 + </div> 128 + </div> 129 + <div class="row"> 130 + <p>And the list of nodes is simply:</p> 131 + <div class="code"> 132 + <div class="sourceCode" id="cb5"><pre 133 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>nodes <span class="ot">=</span> M.keys</span></code></pre></div> 134 + </div> 135 + </div> 136 + <div class="row"> 137 + <p>The list of neighbours of a node:</p> 138 + <div class="code"> 139 + <div class="sourceCode" id="cb6"><pre 140 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>neighbours <span class="ot">=</span> (<span class="op">M.!</span>)</span></code></pre></div> 141 + </div> 142 + </div> 143 + <div class="row"> 144 + <p>That forms our primitive graph library, onto the solution 145 + itself!</p> 146 + <div class="code"> 147 + 148 + </div> 149 + </div> 150 + <h3 data-number="2.8.1" id="part-1-9">Part 1</h3> 151 + <div class="row"> 152 + <p>The first part requires us to find all paths from one node in 153 + the digraph to another node. An algorithm for this is to traverse 154 + the graph in topological order, and the number of “ways” into each 155 + node would be them sum of ways into all neighbours.</p> 156 + <div class="code"> 157 + 158 + </div> 159 + </div> 160 + <div class="row"> 161 + <p>To first sort the nodes in topo-order however, we need to know 162 + the indegrees of all nodes. Every destination node in the 163 + edge-list has an indegree of 1, so sum those up, and then combine 164 + with all nodes. The second step is necessary to include those 165 + nodes aren’t present in the edge-list already. Recall that 166 + <code>M.union</code> is left-biased, so we won’t be overwriting 167 + calculations.</p> 168 + <div class="code"> 169 + <div class="sourceCode" id="cb7"><pre 170 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">indeg ::</span> (<span class="dt">Ord</span> a) <span class="ot">=&gt;</span> <span class="dt">M.Map</span> a [a] <span class="ot">-&gt;</span> <span class="dt">M.Map</span> a <span class="dt">Int</span></span> 171 + <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>indeg <span class="ot">=</span></span> 172 + <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> M.union</span> 173 + <span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;$&gt;</span> M.fromListWith (<span class="op">+</span>) <span class="op">.</span> <span class="fu">map</span> ((,<span class="dv">1</span>) <span class="op">.</span> <span class="fu">snd</span>) <span class="op">.</span> edges</span> 174 + <span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="op">&lt;*&gt;</span> M.fromList <span class="op">.</span> <span class="fu">map</span> (,<span class="dv">0</span>) <span class="op">.</span> nodes</span></code></pre></div> 175 + </div> 176 + </div> 177 + <div class="row"> 178 + <p>Thus, our toposort algorithm is given by:</p> 179 + <div class="code"> 180 + <div class="sourceCode" id="cb8"><pre 181 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>toposort m</span> 182 + <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> M.size m <span class="op">==</span> <span class="dv">0</span> <span class="ot">=</span> []</span> 183 + <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> start <span class="op">++</span> toposort rest</span> 184 + <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 185 + <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> start <span class="ot">=</span> nodes <span class="op">.</span> M.filter (<span class="op">==</span> <span class="dv">0</span>) <span class="op">$</span> indeg m</span> 186 + <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> rest <span class="ot">=</span> <span class="fu">foldr</span> M.delete m start</span></code></pre></div> 187 + </div> 188 + </div> 189 + <div class="row"> 190 + <p>Now, to calculate the number of paths, given a graph, a start 191 + node and an end node, we define <code>paths</code> like so:</p> 192 + <div class="code"> 193 + <div class="sourceCode" id="cb9"><pre 194 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>paths g src dst <span class="ot">=</span> foldl&#39; go start (toposort g) <span class="op">M.!</span> dst</span> 195 + <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 196 + <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> start <span class="ot">=</span></span> 197 + <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> M.insert src <span class="dv">1</span></span> 198 + <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> M.fromList</span> 199 + <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> <span class="fu">map</span> (,<span class="dv">0</span>)</span> 200 + <span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> nodes</span> 201 + <span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="op">$</span> g</span> 202 + <span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a></span> 203 + <span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> go ways node <span class="ot">=</span></span> 204 + <span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> M.unionWith (<span class="op">+</span>) ways (upd ways node)</span> 205 + <span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a></span> 206 + <span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a> upd ways node <span class="ot">=</span></span> 207 + <span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a> M.fromList</span> 208 + <span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> <span class="fu">map</span> (,ways <span class="op">M.!</span> node)</span> 209 + <span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> <span class="op">$</span> neighbours g node</span></code></pre></div> 210 + </div> 211 + </div> 212 + <div class="row"> 213 + <p>To explain a bit further</p> 214 + <div class="code"> 215 + 216 + </div> 217 + </div> 218 + <div class="row"> 219 + <ul> 220 + <li><code>start</code>: this is the initial <code>Map</code> 221 + containing the number of paths from <code>start</code> to all 222 + other nodes in the graph. The number of paths from 223 + <code>start</code> to itself is 1</li> 224 + <li><code>go</code>: this is the fold function that runs on every 225 + node in topo-order. We simply update the <code>ways</code> map and 226 + combine with the existing map</li> 227 + <li><code>upd</code>: find the neighbours of this node, and update 228 + their entry in the <code>ways</code> Map to be equal to the number 229 + of ways to get to <code>node</code></li> 230 + </ul> 231 + <div class="code"> 232 + 233 + </div> 234 + </div> 235 + <div class="row"> 236 + <p>Thus, the solution to the first part is given as:</p> 237 + <div class="code"> 238 + <div class="sourceCode" id="cb10"><pre 239 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>p1 n <span class="ot">=</span> paths n <span class="st">&quot;you&quot;</span> <span class="st">&quot;out&quot;</span></span></code></pre></div> 240 + </div> 241 + </div> 242 + <h3 data-number="2.8.2" id="part-2-8">Part 2</h3> 243 + <div class="row"> 244 + <p>And the solution to the second part is given as:</p> 245 + <div class="code"> 246 + <div class="sourceCode" id="cb11"><pre 247 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>p2 n <span class="ot">=</span></span> 248 + <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">product</span></span> 249 + <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> [ paths n <span class="st">&quot;svr&quot;</span> <span class="st">&quot;fft&quot;</span>,</span> 250 + <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> paths n <span class="st">&quot;fft&quot;</span> <span class="st">&quot;dac&quot;</span>,</span> 251 + <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> paths n <span class="st">&quot;dac&quot;</span> <span class="st">&quot;out&quot;</span></span> 252 + <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> ]</span> 253 + <span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span></span> 254 + <span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">product</span></span> 255 + <span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> [ paths n <span class="st">&quot;svr&quot;</span> <span class="st">&quot;dac&quot;</span>,</span> 256 + <span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> paths n <span class="st">&quot;dac&quot;</span> <span class="st">&quot;fft&quot;</span>,</span> 257 + <span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> paths n <span class="st">&quot;fft&quot;</span> <span class="st">&quot;out&quot;</span></span> 258 + <span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> 259 + </div> 260 + </div> 261 + <div class="row"> 262 + <p>Because in a digraph, the number of paths from 263 + <code>A -&gt; B</code> via <code>C</code>, is given by the product 264 + of paths from <code>A -&gt; B</code> and 265 + <code>B -&gt; C</code>.</p> 266 + <div class="code"> 267 + 268 + </div> 269 + </div> 270 + <div class="row"> 271 + <p>Finally, a main function to wrap it all up:</p> 272 + <div class="code"> 273 + <div class="sourceCode" id="cb12"><pre 274 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span> 275 + <span id="cb12-2"><a href="#cb12-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> 276 + <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span> <span class="op">$</span> p1 n</span> 277 + <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span> <span class="op">$</span> p2 n</span></code></pre></div> 278 + </div> 279 + </div> 280 + </body> 281 + </html>
+201
out/2.9-day-12.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 12</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.8-day-11.html" accesskey="p" rel="previous">Day 11</a> 91 + </span> 92 + <span class="navlink"> 93 + </span> 94 + </div> 95 + </nav> 96 + 97 + <h2 data-number="2.9" id="day-12">Day 12</h2> 98 + <div class="row"> 99 + <p>This input looks quite complex, so lets start by defining some 100 + types to fit the input into:</p> 101 + <div class="code"> 102 + <div class="sourceCode" id="cb1"><pre 103 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Region</span> <span class="ot">=</span> (<span class="dt">Int</span>, <span class="dt">Int</span>, [<span class="dt">Int</span>])</span> 104 + <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span> 105 + <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Shape</span> <span class="ot">=</span> <span class="dt">M.Map</span> (<span class="dt">Int</span>, <span class="dt">Int</span>) <span class="dt">Char</span></span> 106 + <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>area <span class="ot">=</span> M.size <span class="op">.</span> M.filter (<span class="op">==</span> <span class="ch">&#39;#&#39;</span>)</span></code></pre></div> 107 + </div> 108 + </div> 109 + <div class="row"> 110 + <p>Onto parsing:</p> 111 + <div class="code"> 112 + <div class="sourceCode" id="cb2"><pre 113 + class="sourceCode haskell haskell-top"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.List.Split</span> (splitOn)</span> 114 + <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Map</span> <span class="kw">as</span> <span class="dt">M</span></span></code></pre></div> 115 + </div> 116 + </div> 117 + <div class="row"> 118 + <p>We are using <code>&lt;*&gt;</code> here, which stands for 119 + sequential application. We are applying two functions onto the 120 + input, one to parse regions and one to parse shapes:</p> 121 + <div class="code"> 122 + <div class="sourceCode" id="cb3"><pre 123 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">parse ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> ([<span class="dt">Shape</span>], [<span class="dt">Region</span>])</span> 124 + <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>parse <span class="ot">=</span> ((,) <span class="op">&lt;$&gt;</span> sps <span class="op">&lt;*&gt;</span> regs) <span class="op">.</span> splitOn <span class="st">&quot;\n\n&quot;</span></span> 125 + <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 126 + <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> regs <span class="ot">=</span> <span class="fu">map</span> parseReg <span class="op">.</span> <span class="fu">lines</span> <span class="op">.</span> <span class="fu">last</span></span> 127 + <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> sps <span class="ot">=</span> <span class="fu">map</span> parseShape <span class="op">.</span> <span class="fu">init</span></span></code></pre></div> 128 + </div> 129 + </div> 130 + <div class="row"> 131 + <p>Parsing a region is then simply:</p> 132 + <div class="code"> 133 + <div class="sourceCode" id="cb4"><pre 134 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">parseReg ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Region</span></span> 135 + <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>parseReg l <span class="ot">=</span> (x, y, <span class="fu">map</span> <span class="fu">read</span> idxs)</span> 136 + <span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 137 + <span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> (dims <span class="op">:</span> idxs) <span class="ot">=</span> <span class="fu">words</span> l</span> 138 + <span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> [x, y] <span class="ot">=</span> <span class="fu">map</span> <span class="fu">read</span> <span class="op">.</span> splitOn <span class="st">&quot;x&quot;</span> <span class="op">$</span> <span class="fu">init</span> dims</span></code></pre></div> 139 + </div> 140 + </div> 141 + <div class="row"> 142 + <p>And to parse a shape (we are using a map to represent a 143 + shape):</p> 144 + <div class="code"> 145 + <div class="sourceCode" id="cb5"><pre 146 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">parseShape ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Shape</span></span> 147 + <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>parseShape n <span class="ot">=</span></span> 148 + <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> M.fromList</span> 149 + <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> [ ((x, y), c)</span> 150 + <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> (x, r) <span class="ot">&lt;-</span> <span class="fu">zip</span> [<span class="dv">0</span> <span class="op">..</span>] (<span class="fu">tail</span> (<span class="fu">lines</span> n)),</span> 151 + <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> (y, c) <span class="ot">&lt;-</span> <span class="fu">zip</span> [<span class="dv">0</span> <span class="op">..</span>] r</span> 152 + <span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div> 153 + </div> 154 + </div> 155 + <div class="row"> 156 + <p>Moving onto the solution itself.</p> 157 + <div class="code"> 158 + 159 + </div> 160 + </div> 161 + <h3 data-number="2.9.1" id="part-1-10">Part 1</h3> 162 + <div class="row"> 163 + <p>At first glance, this problem seems quite difficult. There is 164 + not an optimal algorithm that I know of, and backtracking with 165 + rotations could take quite a while. So it would make sense to 166 + reduce the problem space as much as possible right away. One way 167 + to do that is to simply check if all shapes can fit within their 168 + respective regions. And turns out that is sufficient to solve this 169 + day’s problem!</p> 170 + <div class="code"> 171 + <div class="sourceCode" id="cb6"><pre 172 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>p1 (sps, regs) <span class="ot">=</span> <span class="fu">length</span> <span class="op">$</span> <span class="fu">filter</span> fits regs</span> 173 + <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span> 174 + <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> fits (x, y, cs) <span class="ot">=</span></span> 175 + <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> (x <span class="op">*</span> y) <span class="op">&gt;=</span> <span class="fu">sum</span> (<span class="fu">zipWith</span> (<span class="op">*</span>) (<span class="fu">map</span> area sps) cs)</span></code></pre></div> 176 + </div> 177 + </div> 178 + <div class="row"> 179 + <p>Historically, the last day of AOC has always been quite 180 + simple.</p> 181 + <div class="code"> 182 + 183 + </div> 184 + </div> 185 + <div class="row"> 186 + <p>Finally, a main function to wrap it all up:</p> 187 + <div class="code"> 188 + <div class="sourceCode" id="cb7"><pre 189 + class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span> 190 + <span id="cb7-2"><a href="#cb7-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> 191 + <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">print</span> <span class="op">$</span> p1 n</span></code></pre></div> 192 + </div> 193 + </div> 194 + <div class="row"> 195 + <p>See you next year!</p> 196 + <div class="code"> 197 + 198 + </div> 199 + </div> 200 + </body> 201 + </html>
+3
out/index.html
··· 66 66 <li><a href="2.4-day-5.html#day-5" id="toc-day-5">Day 5</a></li> 67 67 <li><a href="2.5-day-6.html#day-6" id="toc-day-6">Day 6</a></li> 68 68 <li><a href="2.6-day-9.html#day-9" id="toc-day-9">Day 9</a></li> 69 + <li><a href="2.7-day-10.html#day-10" id="toc-day-10">Day 10</a></li> 70 + <li><a href="2.8-day-11.html#day-11" id="toc-day-11">Day 11</a></li> 71 + <li><a href="2.9-day-12.html#day-12" id="toc-day-12">Day 12</a></li> 69 72 </ul></li> 70 73 </ul> 71 74 </nav>
+1 -1
out/sitemap.json
··· 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-1-1","level":"2","number":"2.1","path":"2.1-day-1-1.html#day-1-1","title":"Day 1"},"subsections":[{"section":{"id":"part-1-2","level":"3","number":"2.1.1","path":"2.1-day-1-1.html#part-1-2","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-1","level":"3","number":"2.1.2","path":"2.1-day-1-1.html#part-2-1","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-2-1","level":"2","number":"2.2","path":"2.2-day-2-1.html#day-2-1","title":"Day 2"},"subsections":[{"section":{"id":"part-1-3","level":"3","number":"2.2.1","path":"2.2-day-2-1.html#part-1-3","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-2","level":"3","number":"2.2.2","path":"2.2-day-2-1.html#part-2-2","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-4","level":"2","number":"2.3","path":"2.3-day-4.html#day-4","title":"Day 4"},"subsections":[{"section":{"id":"part-1-4","level":"3","number":"2.3.1","path":"2.3-day-4.html#part-1-4","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-3","level":"3","number":"2.3.2","path":"2.3-day-4.html#part-2-3","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-5","level":"2","number":"2.4","path":"2.4-day-5.html#day-5","title":"Day 5"},"subsections":[{"section":{"id":"part-1-5","level":"3","number":"2.4.1","path":"2.4-day-5.html#part-1-5","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-4","level":"3","number":"2.4.2","path":"2.4-day-5.html#part-2-4","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-6","level":"2","number":"2.5","path":"2.5-day-6.html#day-6","title":"Day 6"},"subsections":[{"section":{"id":"part-1-6","level":"3","number":"2.5.1","path":"2.5-day-6.html#part-1-6","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-5","level":"3","number":"2.5.2","path":"2.5-day-6.html#part-2-5","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-9","level":"2","number":"2.6","path":"2.6-day-9.html#day-9","title":"Day 9"},"subsections":[{"section":{"id":"part-1-7","level":"3","number":"2.6.1","path":"2.6-day-9.html#part-1-7","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-6","level":"3","number":"2.6.2","path":"2.6-day-9.html#part-2-6","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-1-1","level":"2","number":"2.1","path":"2.1-day-1-1.html#day-1-1","title":"Day 1"},"subsections":[{"section":{"id":"part-1-2","level":"3","number":"2.1.1","path":"2.1-day-1-1.html#part-1-2","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-1","level":"3","number":"2.1.2","path":"2.1-day-1-1.html#part-2-1","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-2-1","level":"2","number":"2.2","path":"2.2-day-2-1.html#day-2-1","title":"Day 2"},"subsections":[{"section":{"id":"part-1-3","level":"3","number":"2.2.1","path":"2.2-day-2-1.html#part-1-3","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-2","level":"3","number":"2.2.2","path":"2.2-day-2-1.html#part-2-2","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-4","level":"2","number":"2.3","path":"2.3-day-4.html#day-4","title":"Day 4"},"subsections":[{"section":{"id":"part-1-4","level":"3","number":"2.3.1","path":"2.3-day-4.html#part-1-4","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-3","level":"3","number":"2.3.2","path":"2.3-day-4.html#part-2-3","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-5","level":"2","number":"2.4","path":"2.4-day-5.html#day-5","title":"Day 5"},"subsections":[{"section":{"id":"part-1-5","level":"3","number":"2.4.1","path":"2.4-day-5.html#part-1-5","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-4","level":"3","number":"2.4.2","path":"2.4-day-5.html#part-2-4","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-6","level":"2","number":"2.5","path":"2.5-day-6.html#day-6","title":"Day 6"},"subsections":[{"section":{"id":"part-1-6","level":"3","number":"2.5.1","path":"2.5-day-6.html#part-1-6","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-5","level":"3","number":"2.5.2","path":"2.5-day-6.html#part-2-5","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-9","level":"2","number":"2.6","path":"2.6-day-9.html#day-9","title":"Day 9"},"subsections":[{"section":{"id":"part-1-7","level":"3","number":"2.6.1","path":"2.6-day-9.html#part-1-7","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-6","level":"3","number":"2.6.2","path":"2.6-day-9.html#part-2-6","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-10","level":"2","number":"2.7","path":"2.7-day-10.html#day-10","title":"Day 10"},"subsections":[{"section":{"id":"part-1-8","level":"3","number":"2.7.1","path":"2.7-day-10.html#part-1-8","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-7","level":"3","number":"2.7.2","path":"2.7-day-10.html#part-2-7","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-11","level":"2","number":"2.8","path":"2.8-day-11.html#day-11","title":"Day 11"},"subsections":[{"section":{"id":"part-1-9","level":"3","number":"2.8.1","path":"2.8-day-11.html#part-1-9","title":"Part 1"},"subsections":[]},{"section":{"id":"part-2-8","level":"3","number":"2.8.2","path":"2.8-day-11.html#part-2-8","title":"Part 2"},"subsections":[]}]},{"section":{"id":"day-12","level":"2","number":"2.9","path":"2.9-day-12.html#day-12","title":"Day 12"},"subsections":[{"section":{"id":"part-1-10","level":"3","number":"2.9.1","path":"2.9-day-12.html#part-1-10","title":"Part 1"},"subsections":[]}]}]}]}
+3 -8
src/2016/02.lhs
··· 6 6 {-# LANGUAGE LambdaCase #-} 7 7 8 8 import qualified Data.Map as M 9 - import System.Environment (getArgs) 10 9 11 10 grid = M.fromList $ zip [(x, y) | y <- [1, 0, -1], x <- [-1, 0, 1]] [1 .. 9] 12 11 ··· 36 35 p2 ns = tail $ map (grid2 M.!) $ scanl (foldl' (mmove grid2)) (-2, 0) ns 37 36 38 37 main = do 39 - args <- getArgs 40 - n <- case args of 41 - ["-"] -> getContents 42 - [file] -> readFile file 43 - let f = lines n 44 - print $ p1 f 45 - print $ p2 f 38 + n <- lines <$> getContents 39 + print $ p1 n 40 + print $ p2 n 46 41 ```
-2
src/2025/02.lhs
··· 1 - # 2025 2 - 3 1 ## Day 2 4 2 5 3 Starting with the parse step, the input is of the form `11-22,15-17,...`.
+84
src/2025/10.lhs
··· 1 + ## Day 10 2 + 3 + Start by parsing the input: 4 + 5 + ```haskell 6 + import Data.List.Split (splitOn) 7 + 8 + parse :: String -> [(Int, [[Int]], [Int])] 9 + parse = map (line . words) . lines 10 + where 11 + line (t : ws) = 12 + (target t, map list (init ws), list (last ws)) 13 + target s = fromBits . init $ tail s 14 + list = map read . splitOn "," . init . tail 15 + ``` 16 + 17 + Of interest is the lighting sequence, which is represented as dots (`.`) and hashes (`#`). You can think of this as a number expressed as zeroes and ones: 18 + 19 + ```haskell 20 + fromBits :: [Char] -> Int 21 + fromBits = foldl f 0 . reverse 22 + where 23 + f acc '.' = acc * 2 24 + f acc '#' = acc * 2 + 1 25 + ``` 26 + 27 + And so we move onto the first part. 28 + 29 + ### Part 1 30 + 31 + The first part requires us to find the combinations of buttons that can result in the lighting sequence, with repetitions allowed. Pressing once switches on the lights, and pressing a second time switches it off. This behavior is identical to XOR! Therefore, the first part is a combination-sum problem, but using XOR, so a combination-xor problem if you will. 32 + 33 + First, we'd need to convert a button into its bitwise form: 34 + 35 + ```haskell-top 36 + import Data.Bits (bit, xor, (.|.)) 37 + import Control.Monad (filterM) 38 + ``` 39 + 40 + ```haskell 41 + toBits :: [Int] -> Int 42 + toBits = foldl1 (.|.) . map bit 43 + ``` 44 + 45 + Next, we require the powerset of all possible buttons: 46 + 47 + ```haskell 48 + powerset [] = [[]] 49 + powerset (x : xs) = map (x :) subset ++ subset 50 + where 51 + subset = powerset xs 52 + ``` 53 + 54 + Thus, our combination-xor is defined like so: 55 + 56 + ```haskell 57 + comboXor target = 58 + filter ((== target) . foldl xor 0) . powerset 59 + ``` 60 + 61 + And the solution to the first part, is simply the application of `comboXor` to each line of the input, and finding the smallest possible sequence in each solution: 62 + 63 + ```haskell 64 + p1 = sum . map solveLine 65 + where 66 + solveLine (target, btns, _) = 67 + minimum . map length $ comboXor target (map toBits btns) 68 + ``` 69 + 70 + ### Part 2 71 + 72 + This part requires us to minimize a collection of equations to identify N coefficients. The solution for this part is given quite easily by LP/SAT solvers, but not so easily by hand. Some resources: 73 + 74 + - [Simplex Algorithm](https://en.wikipedia.org/wiki/Simplex_algorithm) 75 + - [Gaussian Eliminiation](https://en.wikipedia.org/wiki/Gaussian_elimination) 76 + 77 + Finally, a main function to wrap it all up: 78 + 79 + ```haskell 80 + main = do 81 + n <- parse <$> getContents 82 + print $ p1 n 83 + ``` 84 +
+130
src/2025/11.lhs
··· 1 + ## Day 11 2 + 3 + Start by munging the input: 4 + 5 + ```haskell 6 + parse = graph . map (l . words . filter (/= ':')) . lines 7 + where 8 + l (node : edges) = (node, edges) 9 + ``` 10 + 11 + Our graph representation is a map of nodes to the list of outgoing edges: 12 + 13 + ```haskell-top 14 + import qualified Data.Map as M 15 + ``` 16 + 17 + ```haskell 18 + graph ls = 19 + M.union 20 + (M.fromList ls) 21 + (M.fromList [(e, []) | (_, es) <- ls, e <- es]) 22 + ``` 23 + 24 + This, the edge list (a list of `(node, edge)`) is given by: 25 + 26 + ```haskell 27 + edges = concatMap (\(n, e) -> map (n,) e) . M.toList 28 + ``` 29 + 30 + And the list of nodes is simply: 31 + 32 + ```haskell 33 + nodes = M.keys 34 + ``` 35 + 36 + The list of neighbours of a node: 37 + 38 + ```haskell 39 + neighbours = (M.!) 40 + ``` 41 + 42 + That forms our primitive graph library, onto the solution itself! 43 + 44 + ### Part 1 45 + 46 + The first part requires us to find all paths from one node in the digraph to another node. An algorithm for this is to traverse the graph in topological order, and the number of "ways" into each node would be them sum of ways into all neighbours. 47 + 48 + To first sort the nodes in topo-order however, we need to know the indegrees of all nodes. Every destination node in the edge-list has an indegree of 1, so sum those up, and then combine with all nodes. The second step is necessary to include those nodes aren't present in the edge-list already. Recall that `M.union` is left-biased, so we won't be overwriting calculations. 49 + 50 + ```haskell 51 + indeg :: (Ord a) => M.Map a [a] -> M.Map a Int 52 + indeg = 53 + M.union 54 + <$> M.fromListWith (+) . map ((,1) . snd) . edges 55 + <*> M.fromList . map (,0) . nodes 56 + ``` 57 + 58 + Thus, our toposort algorithm is given by: 59 + 60 + ```haskell 61 + toposort m 62 + | M.size m == 0 = [] 63 + | otherwise = start ++ toposort rest 64 + where 65 + start = nodes . M.filter (== 0) $ indeg m 66 + rest = foldr M.delete m start 67 + ``` 68 + 69 + Now, to calculate the number of paths, given a graph, a start node and an end node, we define `paths` like so: 70 + 71 + ```haskell 72 + paths g src dst = foldl' go start (toposort g) M.! dst 73 + where 74 + start = 75 + M.insert src 1 76 + . M.fromList 77 + . map (,0) 78 + . nodes 79 + $ g 80 + 81 + go ways node = 82 + M.unionWith (+) ways (upd ways node) 83 + 84 + upd ways node = 85 + M.fromList 86 + . map (,ways M.! node) 87 + $ neighbours g node 88 + ``` 89 + 90 + To explain a bit further 91 + 92 + - `start`: this is the initial `Map` containing the number of paths from `start` to all other nodes in the graph. The number of paths from `start` to itself is 1 93 + - `go`: this is the fold function that runs on every node in topo-order. We simply update the `ways` map and combine with the existing map 94 + - `upd`: find the neighbours of this node, and update their entry in the `ways` Map to be equal to the number of ways to get to `node` 95 + 96 + Thus, the solution to the first part is given as: 97 + 98 + ```haskell 99 + p1 n = paths n "you" "out" 100 + ``` 101 + 102 + ### Part 2 103 + 104 + And the solution to the second part is given as: 105 + 106 + ```haskell 107 + p2 n = 108 + product 109 + [ paths n "svr" "fft", 110 + paths n "fft" "dac", 111 + paths n "dac" "out" 112 + ] 113 + + 114 + product 115 + [ paths n "svr" "dac", 116 + paths n "dac" "fft", 117 + paths n "fft" "out" 118 + ] 119 + ``` 120 + 121 + Because in a digraph, the number of paths from `A -> B` via `C`, is given by the product of paths from `A -> B` and `B -> C`. 122 + 123 + Finally, a main function to wrap it all up: 124 + 125 + ```haskell 126 + main = do 127 + n <- parse <$> getContents 128 + print $ p1 n 129 + print $ p2 n 130 + ```
+75
src/2025/12.lhs
··· 1 + ## Day 12 2 + 3 + This input looks quite complex, so lets start by defining some types to fit the input into: 4 + 5 + ```haskell 6 + type Region = (Int, Int, [Int]) 7 + 8 + type Shape = M.Map (Int, Int) Char 9 + area = M.size . M.filter (== '#') 10 + ``` 11 + 12 + Onto parsing: 13 + 14 + ```haskell-top 15 + import Data.List.Split (splitOn) 16 + import qualified Data.Map as M 17 + ``` 18 + 19 + We are using `<*>` here, which stands for sequential application. We are applying two functions onto the input, one to parse regions and one to parse shapes: 20 + 21 + ```haskell 22 + parse :: String -> ([Shape], [Region]) 23 + parse = ((,) <$> sps <*> regs) . splitOn "\n\n" 24 + where 25 + regs = map parseReg . lines . last 26 + sps = map parseShape . init 27 + ``` 28 + 29 + Parsing a region is then simply: 30 + 31 + ```haskell 32 + parseReg :: String -> Region 33 + parseReg l = (x, y, map read idxs) 34 + where 35 + (dims : idxs) = words l 36 + [x, y] = map read . splitOn "x" $ init dims 37 + ``` 38 + 39 + And to parse a shape (we are using a map to represent a shape): 40 + 41 + ```haskell 42 + parseShape :: String -> Shape 43 + parseShape n = 44 + M.fromList 45 + [ ((x, y), c) 46 + | (x, r) <- zip [0 ..] (tail (lines n)), 47 + (y, c) <- zip [0 ..] r 48 + ] 49 + ``` 50 + 51 + Moving onto the solution itself. 52 + 53 + ### Part 1 54 + 55 + At first glance, this problem seems quite difficult. There is not an optimal algorithm that I know of, and backtracking with rotations could take quite a while. So it would make sense to reduce the problem space as much as possible right away. One way to do that is to simply check if all shapes can fit within their respective regions. And turns out that is sufficient to solve this day's problem! 56 + 57 + ```haskell 58 + p1 (sps, regs) = length $ filter fits regs 59 + where 60 + fits (x, y, cs) = 61 + (x * y) >= sum (zipWith (*) (map area sps) cs) 62 + ``` 63 + 64 + Historically, the last day of AOC has always been quite simple. 65 + 66 + 67 + Finally, a main function to wrap it all up: 68 + 69 + ```haskell 70 + main = do 71 + n <- parse <$> getContents 72 + print $ p1 n 73 + ``` 74 + 75 + See you next year!