<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://codingindex.xyz/feed.xml" rel="self" type="application/atom+xml" /><link href="https://codingindex.xyz/" rel="alternate" type="text/html" /><updated>2024-05-13T11:22:32+00:00</updated><id>https://codingindex.xyz/feed.xml</id><title type="html">CodingIndex’s Random Shenanigans</title><subtitle>A (human) index that likes to code. Also drinks way too much coffee.</subtitle><author><name>James</name></author><entry><title type="html">Cheap man’s Linux Multi-Monitor Setup</title><link href="https://codingindex.xyz/2024/04/27/cheap-man-linux-multi-monitor/" rel="alternate" type="text/html" title="Cheap man’s Linux Multi-Monitor Setup" /><published>2024-04-27T23:33:00+00:00</published><updated>2024-04-27T23:33:00+00:00</updated><id>https://codingindex.xyz/2024/04/27/cheap-man-linux-multi-monitor</id><content type="html" xml:base="https://codingindex.xyz/2024/04/27/cheap-man-linux-multi-monitor/"><![CDATA[<p>EDIT (13/5/2024): A quick note for the various mentions of Immersed in this post. The company has
recently removed support for physical multi-monitor setups, <em>requiring</em> you to turn off all external
monitors to use Immersed. While the company reserves all rights to do whatever they want with their
software, they have begun banning and moderating away posts that criticised this change. There are
speculations that this is done to keep their records clean for the upcoming IPO. Hence, I cannot, in
good faith, endorse Immersed anymore. There is currently no good alternative for a physical +
virtual multi-monitor setup.</p>

<p>Good morning! :coffee:</p>

<p>Recently, I’ve been playing around with some ideas that’ll eventually get me 24
monitors. Why the impractical and unholy number of monitors?</p>

<p>If I’m being completely realistic, that’s way too many monitors to be
practical. It’s just way too many monitors to even be useful, except
for some extenuating circumstances like stocks trading.</p>

<p>So, instead of coming up with a good excuse, I present you the best argument
I’ve had since time immemorial: why not, sounds fun!</p>

<p>There have been some interesting ideas to accomplish this surrounding the use
of Virtual Reality (VR) headsets, using apps like
<a href="https://immersed.com">Immersed</a> to achieve both a multi-monitor setup, and a
distraction-free environment. When COVID-19 was still a prominent part of life,
I could see this being used fairly frequently.</p>

<p>On <a href="https://immersed.com/faq">Immersed’s FAQ</a>, under “What devices can run immersed?”, it is stated
that “Currently, Linux only supports plugged-in external monitors” with “virtual displays coming
soon!” (accurate at the time of writing).</p>

<p>Borrowing a mate’s Quest 2, I was able to verify that Immersed wasn’t able to
spawn new displays on Linux.</p>

<p>So that got me wondering; what if I could implement this killer feature? Apart from Immersed, I
could turn old devices into high-speed, low latency external displays.</p>

<p>Apps like these already exist; notable examples include GNOME’s virtual displays and
<a href="https://deskreen.com/lang-en">deskreen</a>.</p>

<p>“But!” I exclaim to myself.</p>

<p>“I want a potentially unlimited number of virtual displays!” I bemoaned.</p>

<p>“And I want to do it by <em>myself</em>!” I lamented.</p>

<p>Lo and behold, of course I’d find a way.</p>

<hr />

<h1 id="discovery">Discovery</h1>

<p>Ideally, I want whatever solution I come up with to be GPU-agnostic; i.e. there shouldn’t be
something that <em>only</em> works for Intel iGPUs (as in the case with <code class="language-plaintext highlighter-rouge">VirtualHeads</code> in XOrg
configurations).</p>

<p>When searching online, I came across <a href="https://unix.stackexchange.com/a/585078">this wonderful person’s
post</a>, who suggested that we can use <code class="language-plaintext highlighter-rouge">DisplayLink</code>’s <code class="language-plaintext highlighter-rouge">evdi</code>
kernel module, which allows us to set an initial number of devices.</p>

<p>Running the right <code class="language-plaintext highlighter-rouge">xrandr</code> commands will then get us virtual monitors that we can’t
directly observe on physical monitors, but can instead be accessed via something like VNC or an alternate method
which I will later propose.</p>

<h1 id="base-setting-up-multiple-monitors">Base: Setting up multiple monitors</h1>

<p>The first step is to install the <code class="language-plaintext highlighter-rouge">evdi</code> kernel module. On Ubuntu, it is as simple as running <code class="language-plaintext highlighter-rouge">sudo
apt install evdi-dkms</code> and then restarting the system.</p>

<p>On other systems, either look for <code class="language-plaintext highlighter-rouge">evdi</code> in your package manager, or compile it from the
<a href="https://github.com/DisplayLink/evdi">source</a>.</p>

<p>Now, run <code class="language-plaintext highlighter-rouge">modprobe evdi initial_device_count=2</code> (or however many you want). 
After which, restart your X session; this can typically be done by signing out and then logging back
in, although I’ve only ever tested it by using the “restart X session” functionality on my i3
config. (i.e. killing the X session and restarting it).</p>

<blockquote>
  <p>You’d have to do this every restart. If you already have a good idea on how
many additional virtual monitors you want, you can choose to add this to
<code class="language-plaintext highlighter-rouge">/etc/modprobe.d/local-evdi.conf</code>: <code class="language-plaintext highlighter-rouge">options evdi initial_device_count=X</code>, where <code class="language-plaintext highlighter-rouge">X</code> is the number
of monitors you intend to boot with.</p>
</blockquote>

<p>Now, perform <code class="language-plaintext highlighter-rouge">xrandr --query</code>. You should see a bunch of disconnected monitors, which can look like
this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DVI-I-3-2 disconnected (normal left inverted right x axis y axis)
DVI-I-2-1 disconnected (normal left inverted right x axis y axis)
eDP-1-1 connected primary 1920x1080+0+0 (normal left invertest right x axis y axis) ...
</code></pre></div></div>

<p>At this point, add the resolution you want your virtual monitors to be. There are plenty of guides
online on how you can add custom resolutions, but if you’re adding well-known resolutions (such as
“1920x1080”, “1920x1200”), you can do so by running these commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xrandr --addmode DVI-I-2-1 1920x1200
xrandr --addmode DVI-I-3-2 1920x1080
</code></pre></div></div>

<blockquote>
  <p>Note: If you have other interfaces that are free, you can use those instead. The <code class="language-plaintext highlighter-rouge">DVI-I</code> ones are
generates by <code class="language-plaintext highlighter-rouge">EVDI</code>.</p>
</blockquote>

<p>Figure out how you want to lay your monitors. In my setup, I want <code class="language-plaintext highlighter-rouge">DVI-I-2-1</code> to be on the right of
<code class="language-plaintext highlighter-rouge">eDP-1-1</code>, and <code class="language-plaintext highlighter-rouge">DVI-I-3-2</code> to be on the right of <code class="language-plaintext highlighter-rouge">DVI-I-2-1</code>. Here’s the <code class="language-plaintext highlighter-rouge">xrandr</code> magic to achieve
that:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xrandr --output DVI-I-2-1 --mode 1920x1200 --right-of eDP-1-1
xrandr --output DVI-I-3-2 --mode 1920x1080 --right-of DVI-I-2-1
</code></pre></div></div>

<p>Congratulations! You’ve managed to set up virtual desktops. Way to go :beers:</p>

<p>Now, how do you see content on those monitors?</p>

<hr />

<h1 id="viewing">Viewing</h1>

<p>Suppose you have two other devices to display the two new virtual monitors you’ve set up; then there
are actually a fairly abundant number of ways you can go about this. The easiest way is probably
with a VNC server and client, where there are plenty of guides for.</p>

<p>One way I tried that didn’t work was with NoMachine (notoriously known to be incredibly fast); it
wasn’t happy about the virtual monitors and drew a large black box over where they were supposed to
positioned.</p>

<p>If you have a VR workspace emulator like Immersed, the virtual monitors you’ve created should just
work straight away (tried and tested).</p>

<p>The rest of this blog post will outline a less conventional way - using
<code class="language-plaintext highlighter-rouge">ffmpeg</code> and <code class="language-plaintext highlighter-rouge">ffplay</code>. This allows me to take advantage of a host’s NVIDIA card
to display my virtual monitors on other devices.</p>

<h2 id="method">Method</h2>

<p>First, run <code class="language-plaintext highlighter-rouge">xrandr --query</code> to figure out the offsets of your outputs. For instance, here’s what
mine looks like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; xrandr --query

DVI-I-3-2 disconnected 1920x1200+3360+1000 (normal left inverted right x axis y axis) 0mm x 0mm
DVI-I-2-1 disconnected 1920x1080+5280+1000 (normal left inverted right x axis y axis) 0mm x 0mm
</code></pre></div></div>

<p>This means that <code class="language-plaintext highlighter-rouge">DVI-I-3-2</code> has an x-offset of <code class="language-plaintext highlighter-rouge">3360</code>, and a y-offset of <code class="language-plaintext highlighter-rouge">1000</code>, while <code class="language-plaintext highlighter-rouge">DVI-I-2-1</code>
has an x-offset of <code class="language-plaintext highlighter-rouge">5280</code> and a y-offset of <code class="language-plaintext highlighter-rouge">1000</code> (I have a weird setup).</p>

<p>As you may have guessed, the devices I am planning to project the virtual monitors to are
<code class="language-plaintext highlighter-rouge">1920x1200</code> and <code class="language-plaintext highlighter-rouge">1920x1080</code> in resolutions respectively.</p>

<p>Hence, on the host, I run the following command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffmpeg -video_size 3840x1200 -f x11grab -framerate 60 -i :0.0+3360,1000 \
-c:v h264_nvenc -zerolatency 1 -profile:v main -preset llhq -maxrate 500k \
-bufsize 1m -qp 0 -f mpegts udp://&lt;client 1 IP&gt;:&lt;some port you choose&gt; -c:v \
h264_nvenc -zerolatency 1 -profile:v main -preset llhq -maxrate 500k -bufsize 1m \
-qp 0 -f mpegts udp://&lt;client 2 IP&gt;:&lt;some port you choose&gt;
</code></pre></div></div>

<blockquote>
  <p>Quick Disclaimer: I am not a <code class="language-plaintext highlighter-rouge">ffmpeg</code> pro. I’m fairly certain this can be optimized to smithereens,
but for the purposes of this blog post (and my usage), this is more than good enough.</p>
</blockquote>

<p>The command above screen grabs the regions defined above (essentially the two virtual monitors), and
sends the stream to both devices. The other flags are there to decrease the latency as much as
possible. Note that this setup is <em>still</em> not sub-one latency, but is much faster than achievable
with VNC (<code class="language-plaintext highlighter-rouge">ffmpeg</code> pros could probably get it to sub-one latency).</p>

<p>Ensure that the client allows ingress into both ports on their firewall, then, on the respective
clients, run the following <code class="language-plaintext highlighter-rouge">ffplay</code> commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Client 1 (the 1920x1200 one)
ffplay -vf "crop=1920:1200:0:0,setpts=0" -fflags nobuffer -flags low_delay \
-framedrop -strict experimental -probesize 32 -fast -an udp://127.0.0.1:&lt;port&gt;

# Client 2 (the 1920x1080 one)
ffplay -vf "crop=1920:1080:1920:0,setpts=0" -fflags nobuffer -flags low_delay \
-framedrop -strict experimental -probesize 32 -fast -an udp://127.0.0.1:&lt;port&gt;
</code></pre></div></div>

<p>The two clients should connect, after which you can press “F” to fullscreen the window.
Congratulations, both devices should now be displaying your virtual screens!
You can now interact with them as if they were external monitors to your host machine.</p>

<hr />

<h1 id="conclusions">Conclusions</h1>

<p>The solution above can be combined into a single script to suit your needs. It shows that even
without dedicated software, it is possible to have a virtual monitor setup that (basically) supports
unlimited monitors.</p>

<p>If you have any spare devices laying around, and they can run VNC clients / <code class="language-plaintext highlighter-rouge">ffplay</code>, give this a
shot! You may be able to give it a new lease of life as a secondary monitor.</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="linux" /><category term="linux" /><category term="multi" /><category term="monitor" /><summary type="html"><![CDATA[EDIT (13/5/2024): A quick note for the various mentions of Immersed in this post. The company has recently removed support for physical multi-monitor setups, requiring you to turn off all external monitors to use Immersed. While the company reserves all rights to do whatever they want with their software, they have begun banning and moderating away posts that criticised this change. There are speculations that this is done to keep their records clean for the upcoming IPO. Hence, I cannot, in good faith, endorse Immersed anymore. There is currently no good alternative for a physical + virtual multi-monitor setup.]]></summary></entry><entry><title type="html">I tried to make another game…</title><link href="https://codingindex.xyz/2024/01/05/passable-game/" rel="alternate" type="text/html" title="I tried to make another game…" /><published>2024-01-05T15:49:00+00:00</published><updated>2024-01-05T15:49:00+00:00</updated><id>https://codingindex.xyz/2024/01/05/passable-game</id><content type="html" xml:base="https://codingindex.xyz/2024/01/05/passable-game/"><![CDATA[<p>Happy New Year! :fireworks: How has everyone been?</p>

<p>Ah yes, I can already hear the scorn and disdain of some of you wondering where I’ve been all this time. Short answer: I
got lazy. Long answer: I have <em>a lot</em> to do, but I’m also procrastinating. Man, I swear, it’s probably a comedic routine
at this point to start my blog posts with some kind of excuse.</p>

<p>Anyways, you’ve read the title right; I tried to make another game!</p>

<p><img src="/images/20240105_1.gif" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="untitled game" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">untitled game | Source: Me</p>

<blockquote>
  <p>Note: Skip to <a href="#day-1">Day 1</a> for the actual blog content.</p>
</blockquote>

<p>Table of contents:</p>

<ol>
  <li><a href="#prelude---the-story-thus-far">Prelude - The story thus far</a></li>
  <li><a href="#day-1">Day 1</a></li>
  <li><a href="#day-2">Day 2</a></li>
  <li><a href="#day-3">Day 3</a></li>
  <li><a href="#epilogue">Epilogue</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
</ol>

<h1 id="prelude---the-story-thus-far">Prelude - The story thus far</h1>

<p>Now, if you’ve ever read any of my blog posts, you’d know that I’ve <em>tried</em> making a game before, with a deadline to
boot. It was called <a href="/2020/10/06/failed-game/">Failed Game</a>, and was made for my buddy
<a href="https://modelconverge.xyz/">ModelConverge</a>. It was a massive failure, with me spending eons trying to get the exact
movement I wanted, fixing size mismatches, writing a “manager”, etc. Absolutely horrendous time management.</p>

<p>Surely, I have been polishing my skills to create a game that can captivate players for a gamejam, right? I definitely
should have improved since 2020!</p>

<p>Nope, I’ve not touched game development since then because I was traumatized by how little I got done.</p>

<p>Fast forward a few years (2023, around March), I started watching <a href="https://www.twitch.tv/vedal987">Neuro-sama</a>, and got
hooked the moment an Alternate Reality Game (ARG) was released. This somehow warmed my cold introverted heart, leading
me to create my first Twitch account, revive my old Discord account, and chat with random internet strangers about how
we hold this cute little AI and its creator in eternal reverence.</p>

<p>Here are some clips I hold dear to my hear:</p>
<ul>
  <li><a href="https://www.youtube.com/watch?v=zkLU3-I0leU">Neuro and Vedal defuse bombs, but she progressively gets less helpful</a></li>
  <li><a href="https://www.youtube.com/watch?v=mhuQ_-UCbsg">Evil x Neuro V3 sings In Hell We Live, Lament</a></li>
  <li><a href="https://www.youtube.com/watch?v=-sr8m1L3HZ0">AI Evolved: Neuro-Sama Got A Lot Smarter After Vedal’s Latest Upgrades</a></li>
</ul>

<p>The ARG is found <a href="https://www.youtube.com/@_neurosama">here</a>.</p>

<p>The Neuro-sama community is one of the most “at-home” I have felt for a while. Even during my lurking phase, I felt
nothing but awe; people were kind, talented, and helpful. To me, the fact that this community exists at all is nothing
short of a miracle.</p>

<p>So, out of love for the little AI and her creator, I began contributing by attempting the ARG (badly, I’ve been nothing
much but dead-weight). I had no other talents to contribute, can’t do art, music, no bright ideas, and worst of all, I’m
not exactly a superstar programmer, especially compared to the AI’s creator and most people in the
<a href="https://discord.com/channels/574720535888396288/1071784467036913664">#programming</a> channel. Talk about a failure of
someone who literally runs a technology-related blog!</p>

<p>When the <a href="https://itch.io/jam/neurosama-birthday-game-jam">Neuro-sama Birthday Game Jam</a> (28/12/2023 - 31/12/2023, 72
hours) rolled around during the subathon, I knew this was my only chance to get involved and actually do something. A
recap:</p>

<ol>
  <li>I have not made a full game before</li>
  <li>I do not art</li>
  <li>I do not music</li>
  <li>I am at most a software engineer</li>
  <li>Holding a conversation with a cucumber takes 90% of my energy for the day</li>
</ol>

<p>I was hesitating - on one hand, I knew for a fact that I couldn’t have created anything remotely playable even with
infinite time, much less in 72 hours. Plus, I actually have real-life work to complete, which I took up because I was
sure I wouldn’t had any other commitments. On the other, I literally have no other chances to contribute to the
community. Furthermore, I’ve found myself working amazingly during tight deadlines, which is the case during my serial
hackathon days with <a href="https://modelconverge.xyz">ModelConverge</a> and <a href="https://nikhilr.io/">nikhilr</a>.</p>

<p>Amidst tormenting myself with hesitation, the theme was announced on-stream by Neuro-sama to be “Lost &amp; Found”.
Pondering what I could create, I realized that I actually had ideas; more than anything, I had a <strong>story</strong> I wanted to
tell.</p>

<blockquote>
  <p>While you’re here, have a look at <a href="/2023/05/31/short-stories/">my short stories</a>. They’re a <del>horrible</del>
collection of short stories I wrote expressing what I feel about our current world.</p>
</blockquote>

<p>And so, I joined the Game Jam. Alone (I think I have crippling social anxiety).</p>

<hr />

<h1 id="day-1">Day 1</h1>

<h2 id="concept">Concept</h2>

<p>When I heard the theme “Lost &amp; Found”, my mind wandered to “oh, digging”! The player could dig up for lost items. Sound
good to me!</p>

<p>What else can you dig?</p>

<p>…</p>

<p>Graves.</p>

<h2 id="story">Story</h2>

<p>I love games that can invoke emotions, because I don’t normally feel them. Happiness is an overrated emotion, so I
decided to go for the sad route.</p>

<p>Tragedies are pretty difficult to convey; over-exploiting elements will cause the story to become stale, and become a
case of “oh, the author’s at it again”. For example, if I kept killing important characters left and right, the players
would become numb to that sensation, and the story may become predictable.</p>

<p>A tragedy is good when it is unexpected from the onset, but makes sense after the fact.</p>

<blockquote>
  <p>Note: I have no idea what I’m talking about, this is all just personal takes on what makes a tragedy.</p>
</blockquote>

<p>Digging for items? Digging for graves?</p>

<p>Let’s add a plot twist. Let’s make them dig their own grave.</p>

<p><img src="/images/20240105_2.png" style="image-rendering: pixelated; max-width: 200px; width: 100%; margin: 0 auto; display: block;" alt="grave sprite" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Grave Sprite | Source: me</p>

<h2 id="core-mechanics">Core mechanics</h2>

<p>After spying on some streams of people building their games, I set out to code the core mechanics first (this was a good
idea). The ideas are as such:</p>

<ol>
  <li>Levels randomly generate, with “landmarks” (things like small mushrooms, etc) scattered around the map</li>
  <li>When a level begins, an overview map will be displayed with the locations of all the items and enemies.</li>
  <li>Once the overview map is dismissed, the items will hide themselves.</li>
  <li>Player goes around defeating enemies and collect items.</li>
</ol>

<p>At this point, I have not figured out how to progress the story yet.</p>

<p>These set of core mechanics took a good total of 2 days to fully implement, including actually sleeping. Most of the difficulty
stemmed from me trying to figure out how Godot worked (never used a Game Engine in my life), what the heck nodes are,
and how they interact.</p>

<blockquote>
  <p>I had the most trouble figuring out the difference between <code class="language-plaintext highlighter-rouge">Area2D</code>, <code class="language-plaintext highlighter-rouge">CharacterBody2D</code>, and <code class="language-plaintext highlighter-rouge">RigidBody2D</code>, which all
have different callbacks and different uses. Figuring out the difference between area collision and body collision was
a huge time sink :sweat_smile:</p>
</blockquote>

<p>I used a walker for the level generation, which basically uses DFS and some parameters to randomly generate a walkable
path. This effectively means we have infinite level generation!</p>

<p>I also implemented enemies, with plans to implement different types of enemies (didn’t end up doing it because of time).
To attack, the player would also use the shovel; hence, you couldn’t dig and attack at the same time. The idea was to
challenge the player to knock the enemies back far enough before digging for items. Speaking of items, I implemented
various levels of items to add some variance to the game.</p>

<p><img src="/images/20240105_3.png" style="max-width: 300px; width: 100%; margin: 0 auto; display: block;" alt="Low rarity item" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Amazing green ball with quality indicator | Source: me</p>

<p>The most challenging part of this day was figuring out (for the life of me) how collisions with tilemaps worked;
because I had no idea. Even after setting the right tiles for collision on a <code class="language-plaintext highlighter-rouge">TileSet</code>, the player character couldn’t
collide with the <code class="language-plaintext highlighter-rouge">TileMap</code> properly. It took a few hours, but I eventually figured it out and used a <code class="language-plaintext highlighter-rouge">CharacterBody2D</code>
on <code class="language-plaintext highlighter-rouge">floating</code> mode to introduce tile map collision physics.</p>

<p>HUD was also introduced on this day on another scene, adding HP and Stamina stats. I also eventually added other elements
to the HUD, like the score, and the timer indicating the amount of time left before the end of the level.</p>

<p>The second most challenging part of the day was navigation; it turns out, navigation only works on one layer (at the
time of writing) <a href="https://github.com/godotengine/godot/pull/73018">based on this PR</a>, and so I tore out my hair for no
reason trying to figure what in the world is going on. In the end, I resolved the navigation layer issue by using
background.</p>

<blockquote>
  <p>At first, I wrote linear path-finding using the <code class="language-plaintext highlighter-rouge">Behaviour</code> design pattern. But, like, who has time for design
patterns in a Game Jam?</p>
</blockquote>

<p>To end the day, I used the path generated by the walker to also randomly place items.</p>

<hr />

<h1 id="day-2">Day 2</h1>

<h2 id="core-mechanics-1">Core Mechanics</h2>

<p>On this day, I implemented real bars to represent HP:</p>

<p><img src="/images/20240105_4.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="bars" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Bars | Source: me</p>

<p>And finally added stamina. When attacking, I figured the player should have some visual feedback that <em>something</em> is
happening, so I decided to add slashes:</p>

<p><img src="/images/20240105_5.gif" style="max-width: 200px; width: 100%; margin: 0 auto; display: block;" alt="slashes" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Slashes | Source: me</p>

<p>I realized that not many people will understand the digging mechanic upon spawning on a random level, so I decided to
make a tutorial level:</p>

<p><img src="/images/20240105_6.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="tutorial level" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Tutorial Level | Source: me</p>

<p>Then, I put healthbars on enemies:</p>

<p><img src="/images/20240105_7.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="enemy healthbar" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Enemy HealthBar | Source: me</p>
<!-- enemy healthbars -->

<p>I also added some more quality-of-life mechanics, such as pressing a button to start a map, restarting a level, 
and a “level over” screen.</p>

<hr />

<h1 id="day-3">Day 3</h1>

<p>Honestly, at this point, I wasn’t sure if I could complete the game. I saw some people in the community becoming
disheartened that they may not finish their game and dropping out; but I figured I continued anyway.</p>

<p>So, I sat my butt down on my chair and began working harder.</p>

<h2 id="core-mechanics-2">Core Mechanics</h2>

<p>I implemented “landmarks” - kinda like random small terrain objects that spawn on the foreground layer as memorization
helpers for the player.</p>

<p><img src="/images/20240105_8.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="landmarks" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Landmarks | Source: me</p>

<p>And… well, I think the core mechanics were done!</p>

<h2 id="art">Art</h2>

<p>I suck at art. Nevertheless, I sat down and drew some sprites:</p>

<p><img src="/images/20240105_9.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="sprites" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Sprites | Source: me</p>

<p>And implemented them into the game.</p>

<h2 id="story--special-levels">Story + Special Levels</h2>

<p>Finally, I decided to actually flesh out the story. In my mind, I wanted to create something that <em>will</em> invoke some
sort of emotion within the player. The rough idea of the story was “dig to recover memory fragments”, and end off with
“here’s the whole reason why you’re in this mess”. The rough storyboard was as follows:</p>

<ol>
  <li>The introduction will be at the tutorial. Make it as vague as possible, but a hint of “this isn’t normal”</li>
  <li>Players recover fragments of the story as they progress through the game</li>
  <li>After <code class="language-plaintext highlighter-rouge">x</code> number of fragments, play a special level</li>
  <li>After <code class="language-plaintext highlighter-rouge">y</code> number of fragments in total, play the ending special level</li>
</ol>

<p>I ended up with 3 different special levels; I don’t really want to spoil the story, so here is the overview of two
of the levels (the 3rd one is a story spoiler, so I won’t show it):</p>

<p><img src="/images/20240105_11.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="evil level" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Special Level 1 | Source: me</p>

<p><img src="/images/20240105_10.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="neuro level" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Special Level 2 | Source: me</p>

<p>I would say I did pretty okay with the story. I’m not a professional writer, but I reckon it got the job done.</p>

<h2 id="sound">Sound</h2>

<p>At this point, I only had one hour left. So, I definitely couldn’t be learning how to compose my own music in time;
instead, I searched online for a suitable track. I wanted a “lost in the forest” kinda vibe, but in the depressing tone,
which led me to find <a href="https://chillmindscapes.itch.io/free-chiptune-music-pack-4-chillmindscapes">this</a> page, which has
a very fitting tune called “Goodbye Tales”.</p>

<p>Adding an audio player, whipping out some quick code to play it on a loop, and I shipped it and called it a day.</p>

<hr />

<h1 id="epilogue">Epilogue</h1>

<p>It was not a good game. The core mechanics were “complete”, but definitely not polished. The art-style was absolute
garbage, and the music wasn’t even mine.</p>

<p>The scoring mechanism to obtain fragments and reach special levels was completely broken, so I had to add a small note
to guide players towards obtaining them.</p>

<p>Many community members were able to notice the novice attempt at creating something resembling a game and gave me some
encouragement:</p>

<p><img src="/images/20240105_12.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="encouragement" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Community Encouragement | Source: rating page</p>

<p>Once again, my heart is warmed by the thoughtfulness of the community members.</p>

<h2 id="what-could-i-have-done-better">What could I have done better?</h2>

<p>I could have done better on the following aspects:</p>

<ul>
  <li>The code was horrendous; I could have written everything in the Model-View-Controller pattern; then maybe I wouldn’t
have spent so much time trying to add functionality</li>
  <li>Bugfixes (especially scoring)</li>
  <li>Getting people to play test</li>
  <li>Getting collaborators</li>
  <li>Using my own music</li>
</ul>

<p>If I had collaborators, I would not need to spend so much time creating assets and focus on actually building a fun game. However,
I am also socially awkward, and have no idea how to properly do game development. Furthermore, I realize that if I
didn’t work alone, I’d likely never have the chance to write the story I wanted to convey. After all, not everyone wishes
death upon someone/something they hold dear.</p>

<h2 id="what-i-found-fun">What I found fun?</h2>

<p>To be honest, the reviewing stage. I was given chance to rate other people’s games, and found many gems. Take a look at
the <a href="https://itch.io/jam/neurosama-birthday-game-jam/entries">submissions page</a> and try some yourself!</p>

<p>I’m a numbers-oriented person, and so I obsess over my analytics. This is partly the reason
why this website doesn’t have Google Analytics - I’ll probably compulsively obsess over it and get no work done.</p>

<p>However, that wasn’t it. I also enjoyed watching people play my game (when they try their best to avoid the bugs, of
course). The fact that someone out there is experiencing the thing I’ve crafted, feeling the things I intended for them
to feel, and generally thinking it wasn’t an abysmally horrible creation is something that keeps me ticking.</p>

<p>It reminded me of why I wanted to do technology in the first place; to create things used by others, creating as large
of an impact as possible. To this end, I’ve explored being a content creator (if you remember this you’re a real one),
hosting large services, writing stories, joined companies to work on large stuff, and basically making many of my
impactful projects open-source. Most of them were misses, but I can at least exclaim that I tried at one point.</p>

<h2 id="will-i-do-it-again">Will I do it again?</h2>

<p>Just like hackathons, the adrenaline really helped me realize I had skills I never thought I possessed. From rapid
learning via experimentation on Godot, to drawing sprites even though I failed art, to writing stories even though no
one ever reads mine. All of these (albeit almost non-existent) skills even helped me complete something as complicated
and scary as a game.</p>

<p>However, unlike hackathons, where I enjoyed working with like-minded individuals to get a product out that could
potentially solve industry-level problems, Game Jams are an expression of the team/individual’s creativity.</p>

<p>I don’t think I’ll join just any game jam in the future. I <em>do</em> need to care about it. The fact that the game jam was
centered around Neuro-sama helped a lot, because I already had a desire to give back.</p>

<p>However, I think I’ll participate in the next Neuro-sama game jam, whenever that happens. I have many more stories I
want to tell with the Neuro-sama-verse characters. They’ll probably not be very happy stories, though!</p>

<hr />

<h1 id="conclusion">Conclusion</h1>

<p>You can play the game on <a href="https://vanorsigma.itch.io/neuro-game-jam-untitled-game">itch.io</a>, and the source code can 
be found <a href="https://github.com/jameshi16/neuro-game-jam">here</a>.</p>

<p>Again, it’s not a very interesting game, so I hope you’ll forgive me for not providing a better experience.</p>

<p>Nevertheless, it was a fun 72-hour game jam! Hopefully you had fun reading about my experience as much as I did
reminiscing about it.</p>

<p>Happy Coding</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="game" /><category term="game" /><category term="godot" /><summary type="html"><![CDATA[Happy New Year! :fireworks: How has everyone been?]]></summary></entry><entry><title type="html">Running an embedded executable in Python</title><link href="https://codingindex.xyz/2023/10/08/running-an-embedded-program-in-python/" rel="alternate" type="text/html" title="Running an embedded executable in Python" /><published>2023-10-08T21:51:00+00:00</published><updated>2023-10-08T21:51:00+00:00</updated><id>https://codingindex.xyz/2023/10/08/running-an-embedded-program-in-python</id><content type="html" xml:base="https://codingindex.xyz/2023/10/08/running-an-embedded-program-in-python/"><![CDATA[<p>Good morning! :coffee:</p>

<p>Suppose a hypothetical situation where you gained access to a Python REPL on
some server, somewhere. The REPL is artificially limited such that you have no
access to any file or networking.</p>

<p>Given that you are in a REPL, you can theoretically write any program you want;
however, you are lazy to write such a program, and instead wish to run an
arbitrary executable. After running some REPL commands, like:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="n">sys</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="p">.</span><span class="n">version</span>
<span class="sh">'</span><span class="s">3.10.12 (main, Jun 11 2023, 00:00:00) [GCC 11.4.0]</span><span class="sh">'</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="p">.</span><span class="n">platform</span>
<span class="sh">'</span><span class="s">linux</span><span class="sh">'</span>
</code></pre></div></div>

<p>You realize the following:</p>

<ul>
  <li>The system is running &gt; Python 3.3 (more importantly, &gt; Python 3.8); and</li>
  <li>It’s running on Linux</li>
</ul>

<p>As someone who knows how to code, surely you can whip up a script that would
can execute any arbitrary binary file even under these conditions, right?</p>

<hr />

<h1 id="executing-an-arbitrary-executable">Executing an arbitrary executable</h1>

<p>On Unix and Windows, Python supports an executable, as long as a file path is
specified. This is typically done with the following recipe:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">os</span>
<span class="n">os</span><span class="p">.</span><span class="nf">execv</span><span class="p">(</span><span class="sh">"</span><span class="s">/bin/echo</span><span class="sh">"</span><span class="p">,</span> <span class="p">[</span><span class="sh">"</span><span class="s">-e</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">hello world</span><span class="sh">"</span><span class="p">])</span>
</code></pre></div></div>

<p>The code above causes the <code class="language-plaintext highlighter-rouge">/bin/echo</code> to replace the current process
immediately and prints “hello world”. After <code class="language-plaintext highlighter-rouge">/bin/echo</code> quits, so does Python.</p>

<p>Great, problem solved, right? Unfortunately, the oddly specific constraints
stated above has explicitly denied access to files, which includes the
<code class="language-plaintext highlighter-rouge">/bin/echo</code> executable.</p>

<p>Okay, so maybe we include the executable as part of the script instead. Since
we know that the REPL runs on Linux, we spin up a Docker container, and begin
experimenting.</p>

<p>First, we get the <code class="language-plaintext highlighter-rouge">/bin/echo</code> program as bytes:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="n">data</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">/bin/echo</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">).</span><span class="nf">read</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">data</span>
<span class="sa">b</span><span class="sh">'</span><span class="se">\x7f</span><span class="s">ELF</span><span class="se">\x02\x01\x01\x00</span><span class="s">...</span><span class="sh">'</span>
</code></pre></div></div>

<p>Backslashes looks really scary, so lets convert it to a Base64 encoded string:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="n">base64</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">data_str</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="nf">b64encode</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">data_str</span>
<span class="sa">b</span><span class="sh">'</span><span class="s">f0VMRgIBAQAAAAAAAAAAAAMA...</span><span class="sh">'</span>
</code></pre></div></div>

<p>Great, let’s copy the whole string and keep it in our clipboard for now.</p>

<p>Next, we whip up a script, and write:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">base64</span>

<span class="n">bin_file</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="nf">b64decode</span><span class="p">(</span><span class="sa">b</span><span class="sh">'</span><span class="s">f0VMRgIBAQAAAAAAAAAAAAMA...</span><span class="sh">'</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="nf">execv</span><span class="p">(</span><span class="n">bin_file</span><span class="p">,</span> <span class="p">[</span><span class="sh">'</span><span class="s">-e</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">hello world</span><span class="sh">'</span><span class="p">])</span>
</code></pre></div></div>

<p>And the run the program, and oh…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Traceback (most recent call last):
  File "your_file_here.py", line 1, in &lt;module&gt;
ValueError: execv: embedded null character in path
</code></pre></div></div>

<h1 id="execv-and-paths">execv and paths</h1>

<p>Turns out, even with all the bytes of the executable, you can’t just run it;
Python’s <code class="language-plaintext highlighter-rouge">os.exec*</code> series of functions only support executables specified as
paths.</p>

<p><em>Well…</em></p>

<p>That statement is only half-true. As of Python version 3.3, the <code class="language-plaintext highlighter-rouge">os.execve</code>
<a href="https://docs.python.org/3/library/os.html#os.execl">official Python
documentation</a> supports a
<em>file descriptor</em>.</p>

<blockquote>
  <p>According to <a href="https://stackoverflow.com/a/5256705">this StackOverflow
answer</a>, a <em>file descriptor</em> is an entry
created by the OS when a resource (e.g. file or sockets) is opened. This
entry stores information about the resource, which includes how to access it.
On Windows, this is also known as a <em>handle</em>.</p>
</blockquote>

<p>The file descriptors on Unix can be found in <code class="language-plaintext highlighter-rouge">/proc/&lt;pid&gt;/fd</code>, where <code class="language-plaintext highlighter-rouge">&lt;pid&gt;</code> is
the process ID of the current process. Each file descriptor is represented by
an integer.</p>

<p>Okay, but why is this important? Because the standard streams, i.e. <em>standard
input</em>, <em>standard output</em> and <em>standard error</em> all have their own file
descriptors, which are 0, 1, and 2 respectively.</p>

<p>Notably, those standard streams <em>definitely</em> don’t occupy disk space; the file
descriptor to these standard streams simply represent the concept of those
streams (<a href="https://stackoverflow.com/a/3511816">StackOverflow</a>). Even though the
files <code class="language-plaintext highlighter-rouge">/dev/stdout</code>, <code class="language-plaintext highlighter-rouge">/dev/stdin</code>, and <code class="language-plaintext highlighter-rouge">/dev/stderr</code> exist, they actually point
to <code class="language-plaintext highlighter-rouge">/proc/self/fd/&lt;0/1/2&gt;</code>, which is basically <code class="language-plaintext highlighter-rouge">/proc/&lt;pid&gt;/fd/&lt;0/1/2&gt;</code>, the
file descriptors in question.</p>

<p>In some sense, you can say that these streams exist in-memory (they’re
technically buffered there, according to <a href="https://www.quora.com/What-does-it-mean-to-buffer-or-stdin-stdout-and-stderr">this Quora
post</a>).</p>

<p>Now, answer me this: what happens if I pass <code class="language-plaintext highlighter-rouge">os.execve</code> a <em>file descriptor</em>
pointing to a resource that has executable content?</p>

<p>The theoretical answer: we can execute things.</p>

<h1 id="exploring-the-theoretical-answer">Exploring the theoretical answer</h1>

<p>Let’s run an experiment on a computer we have full access to.</p>

<p>We create two files; <code class="language-plaintext highlighter-rouge">redirect.py</code>, which basically redirects the standard
input to standard output, and <code class="language-plaintext highlighter-rouge">execute.py</code>, which spawns the <code class="language-plaintext highlighter-rouge">redirect.py</code>
subprocess, then attaches pipes to the standard output of <code class="language-plaintext highlighter-rouge">redirect.py</code>.</p>

<p><code class="language-plaintext highlighter-rouge">execute.py</code> will write the Base64 string to <code class="language-plaintext highlighter-rouge">redirect.py</code>, and <code class="language-plaintext highlighter-rouge">redirect.py</code>
will respond with the raw bytes.</p>

<blockquote>
  <p>We have to do it this way, because <code class="language-plaintext highlighter-rouge">sys.stdin.read()</code> reads <em>strings</em> instead
of bytes, which causes issues when trying to pass an entire executable. With
<code class="language-plaintext highlighter-rouge">sys.stdout.buffer.write()</code>, we can write raw bytes into the standard
output. Since we hijack <code class="language-plaintext highlighter-rouge">execute.py</code> with pipes, we can also receive raw
bytes from <code class="language-plaintext highlighter-rouge">redirect.py</code>.</p>
</blockquote>

<p><code class="language-plaintext highlighter-rouge">redirect.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">base64</span>
<span class="kn">import</span> <span class="n">sys</span>

<span class="n">r</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="nf">b64decode</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">stdin</span><span class="p">.</span><span class="nf">read</span><span class="p">())</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="nb">buffer</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="nf">flush</span><span class="p">()</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>
</code></pre></div></div>

<p>In <code class="language-plaintext highlighter-rouge">execute.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">subprocess</span>

<span class="n">bin_file</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">'</span><span class="s">f0VMRgIBAQAAAAAAAAAAAAMA...</span><span class="sh">'</span>
<span class="n">process</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="nc">Popen</span><span class="p">([</span><span class="sh">'</span><span class="s">python</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">redirect.py</span><span class="sh">'</span><span class="p">],</span> <span class="n">stdin</span><span class="o">=</span><span class="n">subprocess</span><span class="p">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="p">.</span><span class="n">PIPE</span><span class="p">)</span>

<span class="n">process</span><span class="p">.</span><span class="n">stdin</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">bin_file</span><span class="p">)</span>
<span class="n">process</span><span class="p">.</span><span class="n">stdin</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

<span class="n">os</span><span class="p">.</span><span class="nf">execve</span><span class="p">(</span><span class="n">process</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="nf">fileno</span><span class="p">(),</span> <span class="p">[</span><span class="sh">'</span><span class="s">-e</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">hello world</span><span class="sh">'</span><span class="p">],</span> <span class="p">{})</span>
</code></pre></div></div>

<p>Giving a quick whirl, we see… oh…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Traceback (most recent call last):
  File "execute.py", line 10, in &lt;module&gt;
    os.execve(process.stdout.fileno(), ['-e', 'hello world'], {})
PermissionError: [Errno 13] Permission denied: 5
</code></pre></div></div>

<p>Looking at this
<a href="https://www.askpython.com/python/examples/handling-error-13-permission-denied">AskPython</a>
article, it seems like this error happens when:</p>
<ul>
  <li>File doesn’t exist;</li>
  <li>Concurrent reads by another program;</li>
  <li>Permissions error</li>
</ul>

<p>Given that we’re using one of the standard streams, surely the file descriptor
points to something that actually exists; and given standard streams are
exclusive to processes, we couldn’t have concurrent reads.</p>

<p>Hence, the only logical explanation stems from us receiving a permissions
error. <em>However</em>, that conclusion is relatively ill-conceived - how do we
assign permissions to a pipe?</p>

<p>After calling <code class="language-plaintext highlighter-rouge">os.stat</code> on both the <code class="language-plaintext highlighter-rouge">process.stdout.fileno()</code> file descriptor
and a normal executable file descriptor, we discover that there are indeed
indicators on the file mode that differentiates a stream to an actual file on
the system.</p>

<p>In fact, it is possible to use <code class="language-plaintext highlighter-rouge">os.chmod</code> to change <code class="language-plaintext highlighter-rouge">process.stdout.fileno()</code>’s
file mode, but that will <em>still</em> not yield a working result.</p>

<p>So, end of the road? Can’t be done? Not quite.</p>

<h1 id="in-memory-files">In-memory files</h1>

<p>We have just established that we <em>need</em> files; the operating system has to
understand that the file descriptor points to a resource that is <em>meant</em> to be
a file.</p>

<p>This would mean that creating a temporary file would work; however, since we
don’t have write access to the filesystem, as constrained by above, we can’t do
that. Instead, we simply create a file in memory.</p>

<p>But how?</p>

<p>If we look carefully under the Linux kernel manual, under the <code class="language-plaintext highlighter-rouge">sys/mman.h</code>
header file, we see that there is an interesting function by the name of
<code class="language-plaintext highlighter-rouge">memfd_create</code>. Here is a <a href="https://man7.org/linux/man-pages/man2/memfd_create.2.html">link to that
manpage</a>. The
manpage describes that:</p>

<ul>
  <li>An <em>anonymous file</em> is created; this function returns a file descriptor that points to it</li>
  <li>It <em>behaves</em> like a normal file</li>
  <li>This file lives on the RAM</li>
</ul>

<p>And wouldn’t you know it, Python’s <code class="language-plaintext highlighter-rouge">os</code> module has a <code class="language-plaintext highlighter-rouge">memfd_create</code> function!</p>

<p>Here’s the plan:</p>

<ol>
  <li>We create an in-memory file and obtain a file descriptor</li>
  <li>We write the bytes of the Base64 string into the in-memory file</li>
  <li>We seek to the start of the file</li>
  <li>We send the file descriptor over to <code class="language-plaintext highlighter-rouge">os.execve</code> and we’re off the races!</li>
</ol>

<p>Here is the final script:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># script.py
</span><span class="kn">import</span> <span class="n">base64</span>
<span class="kn">import</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">sys</span>

<span class="n">bin_file</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="nf">b64decode</span><span class="p">(</span><span class="sa">b</span><span class="sh">'</span><span class="s">f0VMRgIBAQAAAAAAAAAAAAMA...</span><span class="sh">'</span><span class="p">)</span>

<span class="n">in_mem_fd</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">memfd_create</span><span class="p">(</span><span class="sh">"</span><span class="s">bin_name</span><span class="sh">"</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">MFD_CLOEXEC</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">in_mem_fd</span><span class="p">,</span> <span class="n">bin_file</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="nf">lseek</span><span class="p">(</span><span class="n">in_mem_fd</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">os</span><span class="p">.</span><span class="n">SEEK_SET</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="nf">execve</span><span class="p">(</span><span class="n">in_mem_fd</span><span class="p">,</span> <span class="p">[</span><span class="sh">'</span><span class="s">-e</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">hello world</span><span class="sh">'</span><span class="p">],</span> <span class="p">{})</span>
</code></pre></div></div>

<p>Finally, running the script will net us the result we were expecting:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 script.py
hello world
</code></pre></div></div>

<hr />

<h1 id="conclusion">Conclusion</h1>

<p>What are the implications of this? For starters, you can embed any kind of
executable into a Python script. In the case of a malware, the script can
download any random executable from the internet, and run it without leaving a
file trace on your computer.</p>

<p>With enough trickery, the script can also hijack standard input and standard
output of the embedded executable, with the UI being indistinguishable from
just running the executable directly.</p>

<p>On a lighter note, you can, in theory, package your entire suite of
applications into a single Python script. It isn’t feasible in production,
sure, but you can rest well knowing that it is indeed, <em>possible</em>.</p>

<p>Nevertheless, I hope this little fun adventure was entertaining to read. Until
next time!</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="python" /><category term="fun" /><category term="python" /><category term="fun" /><summary type="html"><![CDATA[Good morning! :coffee:]]></summary></entry><entry><title type="html">Developing GitLab CI pipelines locally</title><link href="https://codingindex.xyz/2023/08/29/gitlab-ci-local/" rel="alternate" type="text/html" title="Developing GitLab CI pipelines locally" /><published>2023-08-29T06:55:00+00:00</published><updated>2023-08-29T06:55:00+00:00</updated><id>https://codingindex.xyz/2023/08/29/gitlab-ci-local</id><content type="html" xml:base="https://codingindex.xyz/2023/08/29/gitlab-ci-local/"><![CDATA[<p>Hey there, good morning. Sit yourself down, and enjoy some :coffee:.</p>

<p>Recently, I’ve worked heavily on GitLab CI/CD pipelines. In my line of work, these pipelines must
incorporate security requirements, such as Static Application Security Testing (SAST), Dynamic
Application Security Testing (DAST), Code Scanning, Dependency Scanning, and so on. Furthermore, the
pipelines themselves should be templated to support several deployment variants, e.g. managed cloud
services, and Kubernetes.</p>

<p>As with all things, if you’ve dedicated 60% of your time on something for 3 months, you’re sure to
develop a love-hate relationship with that particular thing. For example, here is one gripe I have
for GitLab CI/CD:</p>

<blockquote>
  <p>According to <a href="https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence">Variable
Precedence</a>, Project Variables
have higher precedence compared to <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> variables. Hence, <em>why in the world</em> are
<code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> variables passed down to child pipelines spawned via <code class="language-plaintext highlighter-rouge">trigger</code>? That overrides
the settings I’ve set in Project Variables, and it just doesn’t make any sense.</p>
</blockquote>

<p>Moreover, there are so many issues open on <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/?label_name%5B%5D=section%3A%3Aci">GitLab’s own
repository</a>
regarding CI that I sometimes find myself wondering if the tool is actually production-ready.
Luckily (for you), this blog post is not about <strong>all</strong> the 789 problems I have with GitLab CI;
instead, it is about the biggest painpoint I had when developing pipelines: not being able to
develop them locally.</p>

<h1 id="why-is-this-a-problem">Why is this a problem?</h1>

<p>Typically, if you were to develop a pipeline, you’d really only know if it worked when you push the
commit to a branch somewhere. On a good day, the pipeline would fail because of some
misconfigured variables; for example, wrong SonarQube credentials. In that scenario, you’d just have to
modify the CI/CD variable from your settings, and invoke the reincarnation of our lord and savior:
the retry button.</p>

<p><img src="/images/20230829_1.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="the retry button" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">The Retry Button</p>

<p>What if the problem arises from your script? Unfortunately, this would mean you’d have to <code class="language-plaintext highlighter-rouge">vim</code> into
your YAML config, change the offending script, create a commit, push, and wait for the entire
pipeline to go through before you’d get feedback on whether your job is successful.</p>

<p>As a pipeline author, my job is to architect pipelines that will fail quickly so that developers get
feedback as soon as something is wrong. Why, as the pipeline author, do I have to wait for an
entire pipeline to figure out if I’ve fixed a job 4 stages later?</p>

<p>Being unable to test a pipeline locally would also pollute the commit logs with unnecessary commits; of course, I can simply
squash them prior to merging the <code class="language-plaintext highlighter-rouge">gitlab-ci.yml</code> file into the default branch, but I still find it
clunky and inelegant. The worst I’ve done is pushing 70 CI-related commits in a single afternoon,
debugging GitLab CI services. For some reason, services networking wasn’t functioning properly for
an in-runner DAST scan.</p>

<blockquote>
  <p>By the way, <code class="language-plaintext highlighter-rouge">$CI_DEBUG_SERVICES</code> is not an omnipotent flag that forces applications to produce logs; in some
Kubernetes configurations, services simply won’t output logs.</p>
</blockquote>

<p>In an ideal world, I’d be able to run the entire pipeline locally. Hence, I looked online and found <a href="https://github.com/firecow/gitlab-ci-local">firecow/gitlab-ci-local</a>.</p>

<p>This tool makes use of <code class="language-plaintext highlighter-rouge">docker</code> to emulate the functionality of GitLab CI/CD, and even has
services support. The feature parity is the most accurate I’ve seen; in fact, I’ve contributed <a href="https://github.com/firecow/gitlab-ci-local/pull/905">PR #905</a>
which pushes the tool closer to feature parity with actual CI/CD pipelines on GitLab.</p>

<p>The remainder of this blog post will walk through the typical workflow I follow when developing
pipelines, which is <strong>not</strong> a comprehensive look into the full features provided by
<code class="language-plaintext highlighter-rouge">gitlab-ci-local</code>. The focus here is <em>feature parity</em>, meaning to change as little as possible in
<code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> to get a pipeline working on both the runner, and locally on your computer.</p>

<h1 id="typical-usage">Typical Usage</h1>

<p>There are instructions for setting up the tool on various platforms in the tool’s
<a href="https://github.com/firecow/gitlab-ci-local#installation">README.md</a>, so get Docker and the tool
installed before continuing.</p>

<p>Let’s suppose we have a simple <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> file, like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">image</span><span class="pi">:</span> <span class="s">debian:latest</span>

<span class="na">stages</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">some-stage</span>

<span class="na">some-job</span><span class="pi">:</span>
  <span class="na">stage</span><span class="pi">:</span> <span class="s">some-stage</span>
  <span class="na">script</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">echo "Hello, world"</span>
</code></pre></div></div>

<p>If you run this with <code class="language-plaintext highlighter-rouge">gitlab-ci-local --list</code>, you should see <code class="language-plaintext highlighter-rouge">some-job</code>:</p>

<p><img src="/images/20230829_2.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="some-job is shown in the output" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">some-job is listed</p>

<p>Let’s quickly run it: <code class="language-plaintext highlighter-rouge">gitlab-ci-local some-job</code>:</p>

<p><img src="/images/20230829_3.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="some-job output" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">some-job output works locally</p>

<p>This allows us to run fairly simple jobs that takes in no variables. What if we want some variables?
Let’s update the <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> file:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="na">image</span><span class="pi">:</span> <span class="s">debian:latest</span>

<span class="na">stages</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">some-stage</span>

<span class="na">some-job</span><span class="pi">:</span>
  <span class="na">stage</span><span class="pi">:</span> <span class="s">some-stage</span>
  <span class="na">script</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">echo "$SOME_TEXT"</span>
</code></pre></div></div>

<p>If we suppose the variable will be set within GitLab’s CI/CD settings, then surely we need to have our
“local” version of those settings; this is achieved via the <code class="language-plaintext highlighter-rouge">.gitlab-ci-local-variables.yml</code> file.
Let’s create that file, and define <code class="language-plaintext highlighter-rouge">SOME_TEXT</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">SOME_TEXT</span><span class="pi">:</span> <span class="s2">"</span><span class="s">hello</span><span class="nv"> </span><span class="s">from</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">other</span><span class="nv"> </span><span class="s">side!"</span>
</code></pre></div></div>

<p>Great, let’s make it so that our job creates some sort of artifact. This pattern is commonly found
in build jobs:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ... just change some-job</span>
<span class="na">some-job</span><span class="pi">:</span>
  <span class="na">stage</span><span class="pi">:</span> <span class="s">some-stage</span>
  <span class="na">script</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">echo $SOME_TEXT &gt; some_output</span>
  <span class="na">artifacts</span><span class="pi">:</span>
    <span class="na">paths</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">some_output</span>
</code></pre></div></div>

<p>If you were to execute <code class="language-plaintext highlighter-rouge">gitlab-ci-local some-job</code> now, you should observe that <code class="language-plaintext highlighter-rouge">some_output</code> appears
within your directory. By default, the tool will copy the artifacts to the root of the repository,
for your inspection. Of course, you can turn this off by running: <code class="language-plaintext highlighter-rouge">gitlab-ci-local
--artifacts-to-source=false some-job</code>.</p>

<p>Let’s suppose we write another job in the same stage, that depends on that above dependency:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># append this job into your .gitlab-ci.yml file</span>
<span class="na">another-job</span><span class="pi">:</span>
  <span class="na">stage</span><span class="pi">:</span> <span class="s">some-stage</span>
  <span class="na">script</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">echo "The file outputs"</span>
    <span class="pi">-</span> <span class="s">cat some_output</span>
  <span class="na">needs</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">some-job</span>
  <span class="na">dependencies</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">some-job</span>
</code></pre></div></div>

<p>If we now run <code class="language-plaintext highlighter-rouge">gitlab-ci-local another-job</code>, we should see that this job is able to get the
artifacts from the dependent job. Caches also work the same way.</p>

<p>You’d have noticed that I specified the job to run: <code class="language-plaintext highlighter-rouge">gitlab-ci-local another-job</code>, and 
noted that artifacts and caches are propagated correctly. This saves lots of development time - you don’t have to run
all of the stages prior to the current job to check if your job works. This, to me, is a massive
improvement from the original cycle of iteration, which required me to commit every change, waiting
for all pre-requisites stages to run, only to be met with yet another error message to fix.</p>

<p>The whole pipeline can now be run with just simply <code class="language-plaintext highlighter-rouge">gitlab-ci-local</code>. If you just want to run a
single stage, then run <code class="language-plaintext highlighter-rouge">gitlab-ci-local --stage some-stage</code>.</p>

<h1 id="registries">Registries</h1>

<p>Typically, upon a successful build, we would want to upload the artifacts to some registry. For
example, if I were to build a container, it is likely that I want to push to some sort of
Docker registry.</p>

<p>GitLab offers a bunch of registries, including a Container Registry; you can read more about the
supported registry <a href="https://docs.gitlab.com/ee/user/packages/">here</a>.</p>

<blockquote>
  <p>Note: As a GitLab user, you can authenticate to the Container Registry using your username, and a
<a href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token">Personal Access Token</a> via <code class="language-plaintext highlighter-rouge">docker login</code>. The
registry URL will typically be: <code class="language-plaintext highlighter-rouge">registry.&lt;gitlab url&gt;.com</code>, where <code class="language-plaintext highlighter-rouge">&lt;gitlab url&gt;</code> is the
instance URL. You can then use images from the Container Registry, like so:</p>
  <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="na">images</span><span class="pi">:</span> <span class="s">registry.&lt;gitlab url&gt;.com/some/project/path/image:latest</span>
</code></pre></div>  </div>
  <p>By default, GitLab runners will already be authenticated to the registry, so there is no
additional step to authenticate your jobs.</p>
</blockquote>

<blockquote>
  <p>Another Note: To <em>push</em> to the container registry, you need to define the following variables
within your <code class="language-plaintext highlighter-rouge">.gitlab-ci-local-variables.yml</code>:</p>
  <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="na">CI_REGISTRY_USER</span><span class="pi">:</span> <span class="s">some-username</span>
  <span class="na">CI_REGISTRY_PASSWORD</span><span class="pi">:</span> <span class="s">some-password</span>
  <span class="na">CI_REGISTRY_IMAGE</span><span class="pi">:</span> <span class="s">&lt;registry URL&gt;/&lt;namespace&gt;/&lt;project&gt;</span>
</code></pre></div>  </div>
</blockquote>

<p>Let’s say we’re on an esoteric project that doesn’t really use any of the above registries; so
we’d choose to use the Generic Package Registry.</p>

<p>On GitLab runners, a token known as <code class="language-plaintext highlighter-rouge">$CI_JOB_TOKEN</code> will be populated automatically, allowing the CI
job to authenticate to most GitLab services without any additional configuration from the job
runner. This also bypasses issues related to secrets rotation, which is a huge boon overall for
everyone involved.</p>

<p>However, <code class="language-plaintext highlighter-rouge">$CI_JOB_TOKEN</code> will not be populated automatically when running <code class="language-plaintext highlighter-rouge">gitlab-ci-local</code>, because
obviously, there just isn’t a valid job token to use. Hence, the obvious solution is to use a
<a href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token">Project Access
Token</a>,
and then change our <code class="language-plaintext highlighter-rouge">.gitlab-ci-local-variables.yml</code> to reflect the token:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ...whatever variables before</span>
<span class="na">CI_JOB_TOKEN</span><span class="pi">:</span> <span class="s">&lt;project access token here&gt;</span>
</code></pre></div></div>

<p>However, upon closer inspection from the <a href="https://docs.gitlab.com/ee/user/packages/generic_packages/#publish-a-package-file">GitLab
documentation</a>,
we observe that the <code class="language-plaintext highlighter-rouge">curl</code> command has an issue:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">--header</span> <span class="s2">"PRIVATE-TOKEN: &lt;project_access_token&gt;"</span> <span class="se">\</span>
     <span class="nt">--upload-file</span> path/to/file.txt <span class="se">\</span>
     <span class="s2">"https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt?select=package_file"</span>
</code></pre></div></div>

<p>Here’s the catch: <code class="language-plaintext highlighter-rouge">$CI_JOB_TOKEN</code> that is populated by the runner has the type of <code class="language-plaintext highlighter-rouge">BOT-TOKEN</code>, which
means that the correct flag to use in the job would be <code class="language-plaintext highlighter-rouge">--header "JOB-TOKEN: $CI_JOB_TOKEN"</code>.
However, the Project Access Token we’ve generated earlier requires the flag to be: <code class="language-plaintext highlighter-rouge">--header
"PRIVATE-TOKEN: $CI_JOB_TOKEN</code> to run locally.</p>

<p>Remember the motto we’ve established earlier: feature parity with GitLab runner. With the motto in
mind, we simply change the flag to be: <code class="language-plaintext highlighter-rouge">--header "$TOKEN_TYPE: $CI_JOB_TOKEN"</code>.  According to
variable precedence, since our <code class="language-plaintext highlighter-rouge">.gitlab-ci-variables.yml</code> is considered to be a part of “Project
Variables”, it has a higher precedence compared to job variables. So, all we need to do now is to set the job
variable to <code class="language-plaintext highlighter-rouge">TOKEN_TYPE: JOB-TOKEN</code>, and set <code class="language-plaintext highlighter-rouge">TOKEN_TYPE: PRIVATE-TOKEN</code> within
<code class="language-plaintext highlighter-rouge">gitlab-ci-variables.yml</code>.</p>

<p>Hence, the final <code class="language-plaintext highlighter-rouge">curl</code> command that should be used is:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">--header</span> <span class="s2">"</span><span class="nv">$TOKEN_TYPE</span><span class="s2">: </span><span class="nv">$CI_JOB_TOKEN</span><span class="s2">"</span> <span class="nt">--upload-file</span> some_output <span class="s2">"https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/some_output?select=package_file"</span>
</code></pre></div></div>

<p>So, we create a job within our <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code>, like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># append</span>
<span class="na">upload-job</span><span class="pi">:</span>
  <span class="na">image</span><span class="pi">:</span> <span class="s">curlimages/curl</span>
  <span class="na">stage</span><span class="pi">:</span> <span class="s">some-stage</span>
  <span class="na">needs</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">some-job</span>
  <span class="na">dependencies</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">some-job</span>
  <span class="na">variables</span><span class="pi">:</span>
    <span class="na">TOKEN_TYPE</span><span class="pi">:</span> <span class="s">JOB-TOKEN</span>
  <span class="na">script</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">curl --header "$TOKEN_TYPE</span><span class="err">:</span> <span class="s">$CI_JOB_TOKEN" --upload-file some_output "https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/some_output?select=package_file"</span>
</code></pre></div></div>

<p>And then amend our <code class="language-plaintext highlighter-rouge">.gitlab-ci-local-variables.yml</code> like so:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ...whatever variables before</span>
<span class="na">CI_JOB_TOKEN</span><span class="pi">:</span> <span class="s">&lt;project access token here&gt;</span>
<span class="na">TOKEN_TYPE</span><span class="pi">:</span> <span class="s">PRIVATE-TOKEN</span>
</code></pre></div></div>

<p>Running <code class="language-plaintext highlighter-rouge">gitlab-ci-local upload-file</code> should then yield a successful result:</p>

<p><img src="/images/20230829_4.png" style="max-width: 800px; width: 100%; margin: 0 auto; display: block;" alt="Successful publish" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">A successful publish</p>

<p><img src="/images/20230829_5.png" style="max-width: 600px; width: 100%; margin: 0 auto; display: block;" alt="A file output" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">A file output</p>

<p>Needless to say, this <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> also works when pushed to GitLab.</p>

<h1 id="including">Including</h1>

<p>In large enough enterprises, you may encounter the need to <code class="language-plaintext highlighter-rouge">include</code> other templates, something like
this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># appears at the top of the gitlab-ci.yml file</span>
<span class="na">include</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">project</span><span class="pi">:</span> <span class="s2">"</span><span class="s">some-template-project"</span>
    <span class="na">ref</span><span class="pi">:</span> <span class="s2">"</span><span class="s">some-tag"</span>
    <span class="na">file</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">BUILD.gitlab-ci.yml</span>
      <span class="pi">-</span> <span class="s">COPY.gitlab-ci.yml</span>
      <span class="pi">-</span> <span class="s">TEST.gitlab-ci.yml</span>
</code></pre></div></div>

<p>As long as you have access to <code class="language-plaintext highlighter-rouge">git clone</code> the current repository, the include will work
transparently with the local tool.</p>

<blockquote>
  <p>The <code class="language-plaintext highlighter-rouge">gitlab-ci-local</code> tool looks through your Git remote list, picks the first one, and attempts
to fetch the referenced files.</p>
</blockquote>

<p>This is useful because you can now do <code class="language-plaintext highlighter-rouge">gitlab-ci-local --preview | less</code>, which will render <em>all</em> of
the included files into one gigantic file. If you have multiple layers of <code class="language-plaintext highlighter-rouge">include</code>, i.e. the
included references also includes other references, they will all be flattened and displayed.</p>

<p>This makes debugging templates much easier.</p>

<h2 id="another-file">Another file</h2>

<p>In some pipeline architectures, child pipelines are heavily relied upon. In
such configurations, you may have two pipeline files, maybe something like:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> for common CI work,</li>
  <li><code class="language-plaintext highlighter-rouge">DEPLOYMENT.gitlab-ci.yml</code> for project-specific deployment</li>
</ul>

<p>Where you have a <code class="language-plaintext highlighter-rouge">.gitlab-ci.yml</code> job that looks something like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">spawn-child-pipeline</span><span class="pi">:</span>
  <span class="na">stage</span><span class="pi">:</span> <span class="s">some-stage</span>
  <span class="na">trigger</span><span class="pi">:</span>
    <span class="na">include</span><span class="pi">:</span> <span class="s">DEPLOYMENT.gitlab-ci.yml</span>
</code></pre></div></div>

<p>GitLab’s own pipeline editor doesn’t support multiple files; hence, you won’t have the nice features
to validate rules, checking conditions, etc.</p>

<p>However, that isn’t an issue with <code class="language-plaintext highlighter-rouge">gitlab-ci-local</code>; simply add the <code class="language-plaintext highlighter-rouge">--file
DEPLOYMENT.gitlab-ci.yml</code> file during development. All the suffixes used thus far, such as <code class="language-plaintext highlighter-rouge">--list</code>
and <code class="language-plaintext highlighter-rouge">--preview</code> work as expected.</p>

<p>Unfortunately, it seems like <code class="language-plaintext highlighter-rouge">trigger</code> is currently not supported by the local tool; you can only
perform a “close” imitation by running this command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gitlab-ci-local <span class="nt">--file</span> <span class="s2">"DEPLOYMENT.gitlab-ci.yml"</span> <span class="nt">--variable</span> <span class="nv">CI_PIPELINE_SOURCE</span><span class="o">=</span>parent_pipeline
</code></pre></div></div>

<h1 id="pushing-to-registries-without-gitlab">Pushing to registries (without GitLab)</h1>

<p>Sometimes, when testing locally, you may not want to pollute the GitLab registry with unnecessary
container images. In scenarios like this, it might be useful to create your own local registry for
testing. Here’s a useful script to create 3 registries at once:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">""</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
    </span>docker run <span class="nt">-d</span> <span class="nt">-p</span> 5000:5000 <span class="nt">--name</span> registry <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/auth:/auth <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/certs:/certs <span class="nt">-e</span> <span class="s2">"REGISTRY_AUTH=htpasswd"</span> <span class="nt">-e</span> <span class="s2">"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"</span> <span class="nt">-e</span> <span class="nv">REGISTRY_AUTH_HTPASSWD_PATH</span><span class="o">=</span>/auth/htpasswd registry:2
    docker run <span class="nt">-d</span> <span class="nt">-p</span> 5001:5000 <span class="nt">--name</span> registry2 <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/auth:/auth <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/certs:/certs <span class="nt">-e</span> <span class="s2">"REGISTRY_AUTH=htpasswd"</span> <span class="nt">-e</span> <span class="s2">"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"</span> <span class="nt">-e</span> <span class="nv">REGISTRY_AUTH_HTPASSWD_PATH</span><span class="o">=</span>/auth/htpasswd registry:2
    docker run <span class="nt">-d</span> <span class="nt">-p</span> 5002:5000 <span class="nt">--name</span> registry3 <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/auth:/auth <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">"</span>/certs:/certs <span class="nt">-e</span> <span class="s2">"REGISTRY_AUTH=htpasswd"</span> <span class="nt">-e</span> <span class="s2">"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"</span> <span class="nt">-e</span> <span class="nv">REGISTRY_AUTH_HTPASSWD_PATH</span><span class="o">=</span>/auth/htpasswd registry:2
<span class="k">fi

if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"stop"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
    </span>docker stop registry
    docker stop registry2
    docker stop registry3
    docker <span class="nb">rm </span>registry
    docker <span class="nb">rm </span>registry2
    docker <span class="nb">rm </span>registry3
<span class="k">fi</span>
</code></pre></div></div>

<p>You can then hijack the <code class="language-plaintext highlighter-rouge">$CI_REGISTRY_*</code> variables via <code class="language-plaintext highlighter-rouge">.gitlab-ci-local-variables.yml</code> to point to your local registry:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">CI_REGISTRY_USER</span><span class="pi">:</span> <span class="s">some-username</span>
<span class="na">CI_REGISTRY_PASSWORD</span><span class="pi">:</span> <span class="s">some-password</span>
<span class="na">CI_REGISTRY_IMAGE</span><span class="pi">:</span> <span class="s">172.17.0.2:5000</span> <span class="c1"># use your docker IP here, using docker inspect</span>
</code></pre></div></div>

<p>To create a user, do the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> <span class="nt">-p</span> auth <span class="se">\</span>
docker run <span class="nt">--entrypoint</span> htpasswd httpd:2 <span class="nt">-Bbn</span> testuser testpassword <span class="o">&gt;&gt;</span> auth/htpasswd <span class="se">\</span>
docker run <span class="nt">--entrypoint</span> htpasswd httpd:2 <span class="nt">-Bbn</span> AWS testpassword <span class="o">&gt;&gt;</span> auth/htpasswd
</code></pre></div></div>

<p>To list the images in the registry:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> GET <span class="nt">-u</span> testuser:testpassword http://localhost:5000/v2/_catalog
</code></pre></div></div>

<p>The above spawns registries without TLS verification. If you’re using <code class="language-plaintext highlighter-rouge">skopeo</code>, you may have to set
<code class="language-plaintext highlighter-rouge">--src-tls-verify=false</code> and <code class="language-plaintext highlighter-rouge">--dest-tls-verify=false</code> in your job scripts, via something akin to <code class="language-plaintext highlighter-rouge">$ADDITIONAL_OPTS</code>.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Generally speaking, developing pipelines can be fairly painful; however, towards DevOps and
automating the pain of deploying applications, it is a necessary sacrifice. By using supporting
tools such as <code class="language-plaintext highlighter-rouge">gitlab-ci-local</code>, engineers can quickly iterate through pipeline development.</p>

<p>Hopefully, both the tool and this blog post has proved useful for any pipeline work you’ll be doing.</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="ci" /><category term="ci" /><category term="gitlab" /><summary type="html"><![CDATA[Hey there, good morning. Sit yourself down, and enjoy some :coffee:.]]></summary></entry><entry><title type="html">Short Stories</title><link href="https://codingindex.xyz/2023/05/31/short-stories/" rel="alternate" type="text/html" title="Short Stories" /><published>2023-05-31T21:43:00+00:00</published><updated>2023-05-31T21:43:00+00:00</updated><id>https://codingindex.xyz/2023/05/31/short-stories</id><content type="html" xml:base="https://codingindex.xyz/2023/05/31/short-stories/"><![CDATA[<p>Good morning! :coffee:</p>

<p>Ever since my last blog post, which was the <a href="/2022/12/25/advent-of-code-22/">Advent of Code 22</a>, it seems like lots of things has happened, and the world is devolving into a discourse.</p>

<p>My life has also experienced a drastic change as I finally went back to academia and touched a textbook for the first time in 3 years. I want to share some opinions I have. However, since no one really reads opinions fully anymore, I figured I’ll spin them into short stories that you can enjoy!</p>

<blockquote>
  <p>Note: Whatever views I express here are not related to my employer, college, alma mater, my parents, my hypothetical pets, my imaginary friends, or anyone/anything else. Hence, don’t go around claiming that “CodingIndex says this, imagine what other people from &lt;insert institution here&gt; think!”. Chances are, nine times out of ten, I hold the pessimistic and anti-societal opinion amongst my peers.</p>
</blockquote>

<p>Table of contents:</p>

<ol>
  <li><a href="#steve-and-his-hobby">Steve and his hobby</a></li>
  <li><a href="#wood-carpenter">Wood Carpenter</a></li>
  <li><a href="#news-agency">News Agency</a></li>
</ol>

<hr />

<h1 id="steve-and-his-hobby">Steve and his hobby</h1>

<p>Steve is a great builder in his favourite video game, Minecraft. Back in 2011, he started off with little dirt shacks, and waited the night to pass while he watched online walkthroughs of the game.</p>

<p>He discovered many content creators in the same situation as he was. Being impatient, he furiously clicked through the episodes to see where he could have ended up.</p>

<p>What started off as a simple dirt shack grew to become a cosy wooden cabin, decorated with flowers, filled to the brim with utility on the interior. It wasn’t too shabby of a home for a game of blocks.</p>

<p>He clicked through another 10 episodes, and saw the content creator putting hours of work on their house, adding gardens, farms, lakes, building paths, creating stables, and becoming something magnificent. Houses slowly became castles, utilitarian buildings became aesthetic pleasing constructions with modern architecture.</p>

<p>Steve was inspired. He started to watch tutorials on how to build better, and began refining his craft.</p>

<p>A few months later, he got to the point where he could will mansions and entire environments into existence. He knew the intricacies of how every block added color, variant and personality to the builds. He was genuinely enjoying the artform.</p>

<p>He was building a giant city when he got a ping from his friend on IRC. “Hey”, they said. “Check this out”.</p>

<p>It was a link that pointed him to a game mod that could procedurally generate modern architectures in the game. It used state-of-the-art technology that, with reference to all the architectural designs up till 2021, generates structures that looked realistic. It was even able to generate interiors using countless interior design plans!</p>

<p>“This is so cool!”, replied Steve. He downloaded the program and started to watch tutorials on how to use the mod. In his peripheral vision, however, he started noticing videos with the titles like “This mod will <strong>ruin</strong> Minecraft creativity!” and “Why building is dead” in his suggestions feed.</p>

<p>“Have you seen videos on it? Seems like you’ll be replaced soon.” the friend commented.</p>

<p>Steve pondered what this meant to himself; will people no longer appreciate the builders in this community? Will he become irrelevant? Has all the skills he has learnt till this point been for naught?</p>

<p>Steve went out for a walk. He observed that the real world was in a state of turmoil, with people fighting against the worsening economy, aging population, and social media over-exposure. He was part of the gloomiest generation alive, and he was worried he wouldn’t be able to land a job with the ever-advancing technology, or find a place to live. Is it even affordable to eat 3 meals a day anymore?</p>

<p>His mind wandered, thoughts after thoughts tangling in his mind, creating a large sense of unease for the future. He then reminiscences the past, about the good old days where he just built structures in Minecraft, worrying about nothing in particular.</p>

<p>An epiphany struck. Why did it matter if he was going to be replaced? Or if his skills are no longer appreciated? Or if he becomes irrelevant? Why did it matter to <em>himself</em>?</p>

<p>Steve sat in front of the computer, booted it, launched Minecraft, and began building. To him, building is what he does to escape reality. It’s his hobby, his passion, and what he wants to continue doing. Even if the entire world forgets such trivialities used to be done by humans, he wants to continue doing it; not because it is the “hip” thing to do, but because he finds it fun.</p>

<p>“I think the mod’s pretty cool, I’ve basically been doing the same thing. By the way, look at what I’ve built!” Steve excitedly sends a screenshot on IRC.</p>

<p>Steve is perfectly happy.</p>

<hr />

<h1 id="wood-carpenter">Wood Carpenter</h1>

<p>I am a wood carpenter.</p>

<p>When I was younger, I followed my dad around as he fulfilled odd-jobs. He was multi-faceted in his expertise, being well-known for repairing electrical installations, plumbing, pest control, and even motorbike maintenance. However, the one job that has always fascinated me was wood carpentry.</p>

<p>Looking back at it now, he definitely wasn’t very skilled at it; he knew enough to get by repairing furniture, but definitely not enough to build furniture from logs. That level of skill would require machines and hand power tools, something outside the capability of our family’s finances.</p>

<p>Regardless, the thing that captured my interest was the first time he showed me how to join two pieces of wood; turns out, there were many creative ways to do so. The best kind of joints would hide the fact that there were two pieces of wood involved in the first place.</p>

<p>From there, my obsession to wood spiralled out of control; from the types of wood, their strengths, what they signify in superstition, woodworking techniques, and so on. Eventually, I was the guy in town who could not only repair furniture, but also build them. In that sense, I have surpassed my father.</p>

<p>While half-drunk, my father sent me for my first competition as a trial for my carpentry skills. There, I caught the attention of some executive from the Ministry of Labour, who decided to grant me a full-ride scholarship to a trade school. Overjoyed, I went on to best trade school in the world and specialized in wood carpentry.</p>

<p>I’ve built over a hundred furniture at this point. While they have not seen use outside of my town, townsfolk would always comment on how my wooden furniture have withstood their harsh daily use for years and required minimal maintenance. Needless to say, I am proud of what I’ve created, but I definitely have ways to go before I become a master.</p>

<p>First year of the wood carpentry curriculum was everything I’ve already known, digested and used extensively in actual carpentry. However, being conceit goes against the values of an aspiring master craftsman; hence, I used the opportunity to etch the concepts onto my soul.</p>

<p>Of the hundred furnitures I have created in my lifetime, some were catastrophic failures. Unbalanced stool supports, wood not covered properly in resin causing rot, etc. Nevertheless, I’ve learnt from those mistakes to create even better furniture. Craftsmanship is practical art - creative endeavours turned utility. I loved it as a hobby and a career; surely, I must love learning about it.</p>

<p>One of the assignments for a critical skill officially named “Blueprints” was to theorize about the strengths and weaknesses of different types of leg stands for a table. We were given four types of wood to think about; all of varying brittleness, weight, and resistance to bugs.</p>

<p>Sounds straight forward enough.</p>

<p>And so I submitted a report detailing a table, and theorized about each leg stand, how much force they can each support, and which wood to pick for a long-lasting piece of furniture. I also submitted a blueprint, and an additional analysis guide for the derivation of that blueprint. I took some photographs of a mock table I built with that blueprint to prove my point.</p>

<p>After a month or so, I received devastating feedback. Turns out, the instructor expected a rectangular table, while I’ve analyzed table stands for a triangular table. I was also expected to report on each leg of the table, even though the results would have been identical to just reporting one. For the blueprint, I was chided about providing additional materials. When I wanted to refute the feedback, I was told that this was professional judgement. Maybe I was too narrow-minded to realize what the master wanted.</p>

<p>Regardless, I now have an official record of being weak at “Blueprints”, a skill so fundamental to being a craftsman that bad = incompetency, no matter the other skills. Perhaps I’m thinking too much about it, maybe I’ll have the chance to explain it when I look for an apprenticeship programme. Surely I wouldn’t be rejected before even stepping foot into the real working world, right?</p>

<p>The Ministry of Labour dropped my scholarship for my poor performance in trade school, where “Blueprints” had a great influence in the decision-making process. It can’t be helped; trade school performance is baked into the contract after all.</p>

<p>During examinations, all I could think about was “Blueprints”. Distracted, I flunked all the examinations. Seems like dropping my scholarship was a good move for the Ministry of Labour; somehow I’ve lost the “spark” and “interest” to remain in the woodworking business.</p>

<p>In my second year, I tried to get an apprenticeship to further my woodworking. I would always get questions about “Blueprints”, and why it received such an undesirable record. Whenever I was given the chance to, I would explain; but what would an apprentice know anyway? Compared to the well-esteemed expert that is the master craftsman, my words may as well be the whispering wind. Of course, I wouldn’t be accepted to any apprenticeship programmes, because I supposedly can’t do blueprints. Meanwhile, my peers were seen as budding talents of the craft - up to this point, they’ve built a single table.</p>

<p>I couldn’t find a job, and had to drop out for my third year. Trade school is expensive, after all. Back at home, I was ridiculed by the same people who encouraged me in the past for being a quitter, and being lucky. Or maybe I was just imagining it.</p>

<p>Somehow, my entire life now revolves being weak at “Blueprints”. Maybe I’m just imagining it.</p>

<p>During a family reunion, I saw cousins who had no interest in wood working becoming successful after attending the trade school. They seem to have their life put together. Strange that the dynamic was the other way just a few years ago. They’re looking at me. Their faces seem to be in disdain. I could see hatred from the years of being compared to me, all directed towards me at once. They seemed satisfied. Surely, it must be my imagination.</p>

<p>I went home and tried to build a table. I didn’t have enough tools to do so; I’ve gotten into an argument with my family and they’ve tossed away most of them, stating that “it has ruined my life”. Surely, they still support me, and I’m just imagining things.</p>

<p>It is now 4 years since I last left the trade school. I’m jobless; except for the occasional odd-job. I’ve not been asked to perform carpentry, since we now had several experts (my cousins) in town. Oh, how I envy them. While I experience financial drought, they can comfortably get by creating masterpieces after masterpieces.</p>

<p>Today is the day my last remaining family died. They said it was of old age. I no longer have a reason to work. I no longer had a reason to live. Maybe I’ll go back to wood carpentry?</p>

<p>No. Of the hundred and one furnitures I have created, all of them were catastrophic failures. If the world thinks so, then it must be true. I shouldn’t soil this world with my horrendous work.</p>

<p>They’d be sad if I just stopped living. So I’ll continue, but I’ll lay rest what I am within. This is the best way.</p>

<p>I wish I was a wood carpenter, I thought, as I quell my anxiety after waking up from my own nightmare.</p>

<hr />

<h1 id="news-agency">News Agency</h1>

<p>“Hey, you’re still working on that draft?”</p>

<p>After nearly falling over from the friendly strong pat from David, Jack regained his balance and composed himself.</p>

<p>“Yeah, it really is taking a while. I’ve only gotten reliable first-hand accounts from the Core, but the Anti-Core? Hearsay at most.” Jack replied, taking a huge swig from his glass of ale.</p>

<p>David followed suit. Ever since the war started, the entire news agency went into chaos-mode covering the events. Jack and David were specially tasked to gather information for an exclusive news column on the war.</p>

<p>“I’m surprised you’ve even gotten a hold of a contact from the Core countries at all; who’s free enough to answer you?” David inquired. A reasonable question, seeing how their country was neither Core nor Anti-Core.</p>

<p>Jack put his drink down, and spent some time staring at the table. It looked as if he was too drunk to hear anything properly, or he was deep in thought - it wasn’t exactly clear given the question.</p>

<p>“I… made a promise to one of them.” Jack said, chugging the rest of his ale, and stood up in one swift motion. Before David could respond, Jack walked out of the door, but not before shouting across the bar, “sorry, see you soon, I’ve gotta meet someone!”</p>

<p><em>It was 01:31AM; who would he even be meeting? It’s probably none of my business…</em></p>

<p>David found himself tailing Jack. Jack looked around him at every intersection he took - he seemed to be more wary as streets became alleyways. Eventually, Jack stopped in front of a metallic backdoor of an otherwise unsuspecting noodle shop.</p>

<p><em>Knock knock knock</em></p>

<p>David pressed himself against a nearby wall. What was Jack doing in this suspicious alleyway?</p>

<p>A husky accent replied softly in broken English through the door. David couldn’t quite hear it, but it didn’t stop his imagination from going wild. Was Jack involved in some illegal business? Money-lending? A drug deal perhaps? Maybe even…</p>

<p>The metal door creaked open, and Jack slipped inside. When the door closed, David edged closer to the door, and leaned his ear against the cold metal. While muffled, he could just about make up the conversation:</p>

<p>“… sellout … you and your family … citizenship.” this was a native voice. This must be Jack.</p>

<p>A long silence loomed. He could hear a disgruntled husky sigh. This must have been the other person through the door.</p>

<p>“… corruption … attack … no basis … lose. locations… do not reveal my identity. my family … anti-core”</p>

<p>The rest of the conversation was inaudible, except: “I’ll bring you 28 thousand tomorrow”, which got progressively louder. David took the cue and fled the scene.</p>

<p>The next day, David went over to Jack’s desk, where he found Jack furiously typing away. David kept silent, having inferred how Jack was getting his information. At night, during their drinking session, as David was mentally anticipating Jack to fulfil his promise, the television was broadcasting about the current state of the war.</p>

<p>“That’s your work, right? Good job!” David happily exclaimed, as he raised his ale to clink glasses in celebration. Jack happily obliged, while watching the television intently.</p>

<p>“… Anti-Core Citizen <strong>Garn Nova</strong> said that the country is full of corrupted officials. In this exclusive report, we reveal key Anti-Core military installations never discovered till today. Stay tuned.”</p>

<p>David’s eyes widened in horror; at the corner of his eyes, he observed Jack still maintaining his perfect smile.</p>

<p>All of a sudden, the door to the bar slammed open. All eyes were on the perpetrator. He then unholstered his rifle, letting out a battle-cry. This was followed by uncontrolled shots to the ceiling and anything else in his path. In the ensuing panic, full of shrieks, the man shouted in a rather familiar voice: “Where are you, Jack!”</p>

<p>Among the shrieks and a general sense of fear in the air, David’s mind remained calm; he recognized this voice. The tone and signature were identical to the husky voice he heard yesterday. This was Jack’s correspondent from the Anti-Core.</p>

<p>“How dare you, Jack! You have doomed my entire family!” the man was livid. He shot at random things in the room, hoping one of his bullets would slot itself cleanly through Jack’s temples by sheer luck - he didn’t care about the innocent crossfire; to him, his life has ended the moment his name was published on television.</p>

<p>David’s eyes darted around the room, searching for the antagonist of their current predicament. What he found was not his meek co-worker who stood for writing excellence, but a monster who kept his smile, talking to his phone under cover from the uncontrolled bullet spray.</p>

<p>In seconds, men donning blue tactical gear emerged from the back of the bar. The resulting chaos between the police force and a man who lost everything was too gruesome to watch. To an outsider, it would seem to be a case where “the police force saves the day from a madman”, but to David, he was witnessing the tale of a man who trusted a monster.</p>

<p>The event made national news - politicians used it to talk about national defence, and there were even talks to ally themselves with the Core. Under the leadership of the newly-promoted chief investigator, who authored both the exclusive insight to the war, and the bar event that shook the nation, the news agency prospered.</p>

<p>David resigned a few months after, supposedly to “look for new opportunities elsewhere”.</p>

<p>David sighed as he carries his bag of canned beer, on his way home. He has since moved away from the district where he used to work; to stay away from the stuff that sometimes fuels his nightmares. He never thought that someone he was close to could kill a man, let alone their entire family.</p>

<p>Sometimes, the sad tale of a poor man who sought refuge in another country, alone and away from his family in the Anti-Core states replayed itself, rent-free in the mind of David. What would he have done if he was the man? What should he have done when he found Jack performing shady dealings? In the first place, how could he have misjudged Jack?</p>

<p>As David reached his street of residence, he noticed a familiar figure and stopped in his tracks. The figure noticed this and turned towards his direction.</p>

<p>Somehow, without even confirming, the figure said, “David! It has been a while, I’ve been trying to reach you forever!”.</p>

<p>“How did you know-“</p>

<p>“It’s not nice, ignoring your friend, ya know.”</p>

<p>“I didn’t tell any-“</p>

<p>“Especially since I let you follow me to my secret spot the other day.”</p>

<p>David froze. Jack knew that he was followed all along.</p>

<p>“You know, trust is a funny thing. They say it’s difficult to build, but easy to break. Have those people ever been desperate?”</p>

<p>“You are a monster.”</p>

<p>Jack paused his over-exaggerated movements, and stared at David for a while. Disgust filled David as Jack expressed a puzzled look.</p>

<p>“I just wanted to be promoted. The economy’s hard, you know?”</p>

<p>Repulsive. Abhorrent. Absolute filth. Trash. How is this parasite still alive?</p>

<p>“This is basically what everyone does, ya know? The guy wasn’t even one of our own!”</p>

<p>David snapped. He couldn’t remember exactly what he said, but it was something about exposing Jack to the entire world.</p>

<p>“That’s troublesome. It’ll hinder my progress to become a minister.” Jack stated apathetically, as if he practiced for this exact scenario.</p>

<p>“Hence, it’ll be nice if you don’t do that.”</p>

<p>In David’s anger, he failed to notice the black-suited people surrounding him. Before he could react, his entire world went black.</p>

<p>In his final moments, he thought about the first time he met Jack. Jack was serious about his work, but he absolutely hated how the world worked - it was a worldview that resonated with David. When did this Jack change? Or, could it be, that there were no changes in the first place?</p>

<p>Then, he thought about the man. Even in David’s final moments, David couldn’t help but feel pity for the man who was misled to accept the poisoned kindness of a monster. Just how many more monsters roam this earth?</p>

<hr />

<h1 id="conclusion">Conclusion</h1>

<p>Hope you enjoyed the short stories! I’m not much of a writer, but these were quite fun to assemble.</p>

<p>If you like programming, please subscribe to my blog, either on RSS (link your reader to <a href="https://codingindex.xyz/feed.xml">https://codingindex.xyz/feed.xml</a>), or via <a href="http://esub.codingindex.xyz/">email</a>).</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="stories" /><category term="short" /><category term="stories" /><summary type="html"><![CDATA[Good morning! :coffee:]]></summary></entry><entry><title type="html">Advent of Code 22</title><link href="https://codingindex.xyz/2022/12/25/advent-of-code-22/" rel="alternate" type="text/html" title="Advent of Code 22" /><published>2022-12-25T12:05:00+00:00</published><updated>2022-12-25T12:05:00+00:00</updated><id>https://codingindex.xyz/2022/12/25/advent-of-code-22</id><content type="html" xml:base="https://codingindex.xyz/2022/12/25/advent-of-code-22/"><![CDATA[<p>:coffee: Hi!</p>

<p>After having absolutely <em>zero</em> blog posts for the past 11 months, including on my treasured <a href="/anime">anime</a> page, here I am declaring that I will be participating in the <a href="https://adventofcode.com/">Advent of Code</a> (AOC).</p>

<p>I’ve never completed an AOC before, so it’ll be a nice challenge to breathe vitality into this blog before the New Years. To motivate me, I have invited my buddies over at <a href="https://modelconverge.xyz">modelconverge</a> and <a href="https://nikhilr.io">nikhilr</a> to join me.</p>

<p>Throughout AOC, I will update this blog post in a rolling manner to discuss my thought processes from ideation to solution. Do check back every day!</p>

<h1 id="day-1">Day 1</h1>

<p>Thanks to deadlines being a thing, I ended up doing Day 1 24 hours late. Anyways, it seems like we need to make a simple program to figure out who is carrying the most amount of calories among the elves.</p>

<h2 id="part-1">Part 1</h2>

<p>I broke down the problem into processing chunks of numbers at once:</p>

<ol>
  <li>Each block is delimited by <code class="language-plaintext highlighter-rouge">\n\n</code> (two newlines), and</li>
  <li>Each calorie-qualifying item is delimited by <code class="language-plaintext highlighter-rouge">\n</code>.</li>
</ol>

<p>So, the steps to solve this problem will be:</p>

<ol>
  <li>Define a list, <code class="language-plaintext highlighter-rouge">l</code>;</li>
  <li>Read input line by line;</li>
  <li>For each line, check if the string is just space;</li>
  <li>If it is just space, we add an integer, <code class="language-plaintext highlighter-rouge">0</code> into the list, <code class="language-plaintext highlighter-rouge">l</code>;</li>
  <li>Otherwise, we parse the input as an integer and add it to the last integer in <code class="language-plaintext highlighter-rouge">l</code>;</li>
  <li>Repeat step 2 until EOF;</li>
  <li>We take the maximum of the list <code class="language-plaintext highlighter-rouge">l</code>, completing our algorithm.</li>
</ol>

<p>Framing the problem another way, <code class="language-plaintext highlighter-rouge">l</code> is the accumulator of integers, and we are processing a list of strings with a function that:</p>

<ol>
  <li>Adds a new number to the accumulator if string is empty;</li>
  <li>Otherwise, adds the integer representation of the string into the last element of the accumulator.</li>
</ol>

<p>Then, we take the maximum of the list. Naturally, this means that the problem can be solved with two lines of Python:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="nb">reduce</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">max</span><span class="p">((</span><span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">accum</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">accum</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">y</span> <span class="o">==</span> <span class="sh">""</span> <span class="k">else</span> <span class="n">accum</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">accum</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">)],</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">).</span><span class="nf">read</span><span class="p">().</span><span class="nf">splitlines</span><span class="p">(),</span> <span class="p">[</span><span class="mi">0</span><span class="p">]))))</span>
</code></pre></div></div>

<p>Where the contents of <code class="language-plaintext highlighter-rouge">input.txt</code> are given by the puzzle input.</p>

<h2 id="part-2">Part 2</h2>

<p>The second part wants us to get the three highest elements in the list. So, just a small tweak to part 1:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="nb">reduce</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">sorted</span><span class="p">((</span><span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">accum</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">accum</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">y</span> <span class="o">==</span> <span class="sh">""</span> <span class="k">else</span> <span class="n">accum</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">accum</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">)],</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">).</span><span class="nf">read</span><span class="p">().</span><span class="nf">splitlines</span><span class="p">(),</span> <span class="p">[</span><span class="mi">0</span><span class="p">])),</span> <span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">)[:</span><span class="mi">3</span><span class="p">]))</span>
</code></pre></div></div>

<p>All I did here was to replace <code class="language-plaintext highlighter-rouge">max</code> with a composition of <code class="language-plaintext highlighter-rouge">sum</code> and <code class="language-plaintext highlighter-rouge">sorted</code>.</p>

<hr />

<h1 id="day-2">Day 2</h1>

<h2 id="part-1-1">Part 1</h2>

<p>Parsing the problem into programmer monkey brain language, the question is essentially:</p>

<ul>
  <li>Given an input:
    <ul>
      <li>Each line is a combination of two characters from different source ranges delimited by space, i.e.: <code class="language-plaintext highlighter-rouge">A X</code> where <code class="language-plaintext highlighter-rouge">A = ['A','B','C']</code> and <code class="language-plaintext highlighter-rouge">X = ['X','Y','Z']</code>.</li>
      <li>Lines delimited by <code class="language-plaintext highlighter-rouge">\n</code>.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">X</code> are enumeration representations of the possible moves in rock, paper and scissors. The truth table is as follows:</li>
</ul>

<table>
  <thead>
    <tr>
      <th><strong>Left</strong></th>
      <th><strong>Right</strong></th>
      <th><strong>State</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>A</td>
      <td>X</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>B</td>
      <td>Y</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>C</td>
      <td>Z</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>A</td>
      <td>Y</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>B</td>
      <td>Z</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>C</td>
      <td>X</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>A</td>
      <td>Z</td>
      <td>Lose</td>
    </tr>
    <tr>
      <td>B</td>
      <td>X</td>
      <td>Lose</td>
    </tr>
    <tr>
      <td>C</td>
      <td>Y</td>
      <td>Lose</td>
    </tr>
  </tbody>
</table>

<ul>
  <li><code class="language-plaintext highlighter-rouge">X</code>, <code class="language-plaintext highlighter-rouge">Y</code>, <code class="language-plaintext highlighter-rouge">Z</code> have a partial score of 1, 2, 3 respectively</li>
  <li>Winning will grant a partial score of 6, Ties will grant 3, and losing will grant 0.</li>
</ul>

<p>The first thing I did was to “normalize” and simplify the truth table by taking the difference between <code class="language-plaintext highlighter-rouge">X</code> and <code class="language-plaintext highlighter-rouge">A</code>. So, before simplification, the table looked like this:</p>

<table>
  <thead>
    <tr>
      <th><strong>Left</strong></th>
      <th><strong>Right</strong></th>
      <th><strong>Diff</strong></th>
      <th><strong>State</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>0</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>2</td>
      <td>2</td>
      <td>0</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>3</td>
      <td>3</td>
      <td>0</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>1</td>
      <td>2</td>
      <td>1</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>2</td>
      <td>3</td>
      <td>1</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>3</td>
      <td>1</td>
      <td>-2</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>1</td>
      <td>3</td>
      <td>2</td>
      <td>Lose</td>
    </tr>
    <tr>
      <td>2</td>
      <td>1</td>
      <td>-1</td>
      <td>Lose</td>
    </tr>
    <tr>
      <td>3</td>
      <td>2</td>
      <td>-1</td>
      <td>Lose</td>
    </tr>
  </tbody>
</table>

<p>I then simplify the table with the following thoughts:</p>
<ul>
  <li>Consider only the difference and states;</li>
  <li>Losing will grant zero points, which makes it inconsequential in our score calculation, so it can be completely removed.</li>
</ul>

<p>So, the table looks like this:</p>

<table>
  <thead>
    <tr>
      <th><strong>Diff</strong></th>
      <th><strong>State</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Tie</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Win</td>
    </tr>
    <tr>
      <td>-2</td>
      <td>Win</td>
    </tr>
  </tbody>
</table>

<p>Now, the problem of obtaining the win/tie/loss partial score has been simplified to check for these 3 cases. So, I could now write something like:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// a is normalized left, x is normalized right</span>
<span class="kt">int</span> <span class="n">partial_score</span> <span class="o">=</span> <span class="p">(</span><span class="n">a</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">a</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">x</span> <span class="o">-</span> <span class="n">a</span> <span class="o">==</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">6</span><span class="p">;</span>
</code></pre></div></div>

<p>The next sub-problem to tackle will be to normalize our inputs. All ASCII characters can be expressed as integers, and hence can be normalized by the lowest value of each range. In other words:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// a is left, x is right</span>
<span class="kt">int</span> <span class="n">normalised_a</span> <span class="o">=</span> <span class="n">a</span> <span class="o">-</span> <span class="sc">'A'</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">normalised_x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="sc">'X'</span><span class="p">;</span>
</code></pre></div></div>

<p>Performing this normalization almost conforms to the partial sum where <code class="language-plaintext highlighter-rouge">'X', 'Y', 'Z' -&gt; 1, 2, 3</code>. Right now, the map looks like <code class="language-plaintext highlighter-rouge">'X', 'Y', 'Z' -&gt; 0, 1, 2</code>. To fix this, just add 1:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// normalised_x as above</span>
<span class="kt">int</span> <span class="n">partial_score</span> <span class="o">=</span> <span class="n">normalised_x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>

<p>So, the total score can now be expressed as:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// a is normalised left, x is normalised right</span>
<span class="kt">int</span> <span class="n">score</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">a</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">a</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">x</span> <span class="o">-</span> <span class="n">a</span> <span class="o">==</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">6</span><span class="p">;</span>
</code></pre></div></div>

<p>All we need to do now is to do the preprocessing and required code to actually obtain <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">a</code>. I first wrote it in C, which looks like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">eval_score</span><span class="p">(</span><span class="kt">char</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">char</span> <span class="n">opp_a</span> <span class="o">=</span> <span class="n">a</span> <span class="o">-</span> <span class="sc">'A'</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">opp_b</span> <span class="o">=</span> <span class="n">b</span> <span class="o">-</span> <span class="sc">'X'</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">opp_b</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">opp_b</span> <span class="o">-</span> <span class="n">opp_a</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">opp_b</span> <span class="o">-</span> <span class="n">opp_a</span> <span class="o">==</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">6</span> <span class="o">+</span> <span class="p">(</span><span class="n">opp_a</span> <span class="o">==</span> <span class="n">opp_b</span><span class="p">)</span> <span class="o">*</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">FILE</span><span class="o">*</span> <span class="n">file</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"input.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="kt">long</span> <span class="n">accum_score</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">do</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">;</span>
    <span class="n">fscanf</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="s">"%c %c</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">first</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">second</span><span class="p">);</span>
    <span class="n">accum_score</span> <span class="o">+=</span> <span class="n">eval_score</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">feof</span><span class="p">(</span><span class="n">file</span><span class="p">));</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"%ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">accum_score</span><span class="p">);</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This was too long, so I decided to re-write the same thing in JavaScript:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">inputStr</span> <span class="o">=</span> <span class="s2">``</span> <span class="c1">// puzzle input</span>

<span class="nx">inputStr</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">).</span><span class="nf">reduce</span><span class="p">((</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">curr</span><span class="p">)</span> <span class="o">=&gt;</span>
        <span class="nx">acc</span><span class="p">.</span><span class="nf">concat</span><span class="p">(</span>
                <span class="p">((</span><span class="nx">codes</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span>
                        <span class="p">(</span><span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">6</span> <span class="o">+</span>
                        <span class="p">(</span><span class="nx">codes</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span>
                <span class="p">(((</span><span class="nx">raw</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="nx">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">charCodeAt</span><span class="p">()</span> <span class="o">-</span> <span class="mi">65</span><span class="p">,</span> <span class="nx">raw</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">charCodeAt</span><span class="p">()</span> <span class="o">-</span> <span class="mi">88</span><span class="p">])(</span><span class="nx">curr</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">)))),</span> <span class="p">[])</span>
        <span class="p">.</span><span class="nf">reduce</span><span class="p">((</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">curr</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">acc</span> <span class="o">+</span> <span class="nx">curr</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Which is shorter but kinda unreadable.</p>

<h2 id="part-2-1">Part 2</h2>

<p>Part 2 changes the interpretation of <code class="language-plaintext highlighter-rouge">X</code>. <code class="language-plaintext highlighter-rouge">"X"</code>, <code class="language-plaintext highlighter-rouge">"Y"</code>, and <code class="language-plaintext highlighter-rouge">"Z"</code> now represents <code class="language-plaintext highlighter-rouge">lose</code>, <code class="language-plaintext highlighter-rouge">tie</code>, and <code class="language-plaintext highlighter-rouge">win</code>. Upon closer inspection, this really only affects the partial sum used to calculate the score based on state; if anything, it made calculating the win/loss/tie partial score simple.</p>

<p>It can be easily realised that associating tie to <code class="language-plaintext highlighter-rouge">0</code>, win to <code class="language-plaintext highlighter-rouge">1</code> and loss to <code class="language-plaintext highlighter-rouge">-1</code> will make deriving the rock/paper/scissors move simple.</p>

<table>
  <thead>
    <tr>
      <th><strong>Left</strong></th>
      <th><strong>State</strong></th>
      <th><strong>Right</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>x</td>
      <td>Tie (0)</td>
      <td>x</td>
    </tr>
    <tr>
      <td>x</td>
      <td>Win (1)</td>
      <td>0 if x + 1 == 3 else x + 1</td>
    </tr>
    <tr>
      <td>x</td>
      <td>Lose (-1)</td>
      <td>2 if x - 1 == -1 else x - 1</td>
    </tr>
  </tbody>
</table>

<p>Remember that the normalised <code class="language-plaintext highlighter-rouge">"A", "B", "C" -&gt; 0, 1, 2</code>, so ties would imply <code class="language-plaintext highlighter-rouge">"A", "B", "C" -&gt; Scissors, Paper, Rock</code>, wins would imply <code class="language-plaintext highlighter-rouge">"A", "B", "C" -&gt; Paper, Rock, Scissors</code>, and losses will be <code class="language-plaintext highlighter-rouge">"A", "B", "C" -&gt; Scissors, Rock, Paper</code>.</p>

<p>Hence, the code would be changed to:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">inputStr</span> <span class="o">=</span> <span class="s2">``</span>

<span class="nx">inputStr</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">).</span><span class="nf">reduce</span><span class="p">((</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">curr</span><span class="p">)</span> <span class="o">=&gt;</span>
        <span class="nx">acc</span><span class="p">.</span><span class="nf">concat</span><span class="p">(</span>
                <span class="p">((</span><span class="nx">codes</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">((</span><span class="nx">codes</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">?</span> <span class="mi">2</span> <span class="p">:</span> <span class="p">(</span><span class="nx">codes</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">%</span> <span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span>
                        <span class="p">(</span><span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">6</span> <span class="o">+</span>
                        <span class="p">(</span><span class="nx">codes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span>
                <span class="p">(((</span><span class="nx">raw</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="nx">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">charCodeAt</span><span class="p">()</span> <span class="o">-</span> <span class="mi">65</span><span class="p">,</span> <span class="nx">raw</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">charCodeAt</span><span class="p">()</span> <span class="o">-</span> <span class="mi">89</span><span class="p">])(</span><span class="nx">curr</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">)))),</span> <span class="p">[])</span>
        <span class="p">.</span><span class="nf">reduce</span><span class="p">((</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">curr</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">acc</span> <span class="o">+</span> <span class="nx">curr</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Notice the change at <code class="language-plaintext highlighter-rouge">raw[1].charCodeAt() - 89</code>, which essentially absorbed an offset of <code class="language-plaintext highlighter-rouge">-1</code>.</p>

<hr />

<h1 id="day-3">Day 3</h1>

<h2 id="part-1-2">Part 1</h2>

<p>Today’s part 1 problem can be broken down into the following sub-problems:</p>

<ul>
  <li>Go through the input line by line;</li>
  <li>For each line, split the line by half, and find the intersect between the two lines;</li>
  <li>Due to the nature of the problem, it is guaranteed that the intersection is one and unique;</li>
  <li>For each of the intersections, calculate the respective priorities.</li>
</ul>

<p>I decided to use Haskell, because :shrug:. Inputs in Haskell is notoriously complex, so I decided to bypass that by utilizing my browser’s JavaScript engine to convert multi-line strings to normal strings delimited by <code class="language-plaintext highlighter-rouge">\n</code>, like this:</p>

<p><img src="/images/20221225_1.png" style="max-width: 800px; width: 100%; margin: 0 auto; display: block;" alt="Interpreting multi-string with JavaScript" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Converting to a single-line string with JavaScript</p>

<p>Doing so, I will be able to bypass all input-related processing in Haskell by assigning the string to the variable.</p>

<p>Let’s solve each sub-problem in Haskell:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- input string</span>
<span class="n">input</span> <span class="o">=</span> <span class="s">""</span>

<span class="c1">-- going through line by line</span>
<span class="n">lines</span> <span class="n">input</span>

<span class="c1">-- split line by half</span>
<span class="n">splitAt</span> <span class="p">(</span><span class="n">round</span> <span class="o">$</span> <span class="p">(</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> <span class="o">$</span> <span class="n">fromIntegral</span> <span class="o">$</span> <span class="n">length</span> <span class="n">line</span><span class="p">)</span> <span class="n">line</span>

<span class="c1">-- find intersection between the two halfs</span>
<span class="n">intersect</span> <span class="n">splitted_xs</span> <span class="n">splitted_ys</span>

<span class="c1">-- calculate priority</span>
<span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="o">-&gt;</span> <span class="kr">if</span> <span class="n">x</span> <span class="p">`</span><span class="n">elem</span><span class="p">`</span> <span class="p">[</span><span class="sc">'a'</span><span class="o">..</span><span class="sc">'z'</span><span class="p">]</span> <span class="kr">then</span> <span class="n">ord</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">96</span> <span class="kr">else</span> <span class="n">ord</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">65</span> <span class="o">+</span> <span class="mi">27</span><span class="p">)</span> <span class="o">$</span> <span class="p">(</span><span class="o">!!</span> <span class="mi">0</span><span class="p">)</span> <span class="n">intersected_list</span>
</code></pre></div></div>

<p>Some notes:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">length line</code> strictly returns an integer, which needs to be converted for division in Haskell;</li>
  <li>In the priority calculation, we subtract 96, which is 1 less than the ASCII value for ‘a’, so we introduce an offset of <code class="language-plaintext highlighter-rouge">+1</code>;</li>
  <li>The range <code class="language-plaintext highlighter-rouge">['A'..'Z']</code> has an offset of 26 + 1 after getting it’s sequence number from the ASCII value for ‘A’.</li>
</ul>

<p>Combining these together, we have:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="nn">Data.Char</span>
<span class="kr">import</span> <span class="nn">Data.List</span>

<span class="n">input</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">solution</span> <span class="n">input</span> <span class="o">=</span> <span class="n">sum</span> <span class="p">[(</span><span class="nf">\</span><span class="n">x</span> <span class="o">-&gt;</span> <span class="kr">if</span> <span class="n">x</span> <span class="p">`</span><span class="n">elem</span><span class="p">`</span> <span class="p">[</span><span class="sc">'a'</span><span class="o">..</span><span class="sc">'z'</span><span class="p">]</span> <span class="kr">then</span> <span class="n">ord</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">96</span> <span class="kr">else</span> <span class="n">ord</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">65</span> <span class="o">+</span> <span class="mi">27</span><span class="p">)</span> <span class="o">$</span> <span class="p">(</span><span class="o">!!</span> <span class="mi">0</span><span class="p">)</span> <span class="o">$</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">intersect</span> <span class="n">xs</span> <span class="n">ys</span><span class="p">)</span> <span class="o">$</span> <span class="n">splitAt</span> <span class="p">(</span><span class="n">round</span> <span class="o">$</span> <span class="p">(</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> <span class="o">$</span> <span class="n">fromIntegral</span> <span class="o">$</span> <span class="n">length</span> <span class="n">line</span><span class="p">)</span> <span class="n">line</span> <span class="o">|</span> <span class="n">line</span> <span class="o">&lt;-</span> <span class="n">lines</span> <span class="n">input</span><span class="p">]</span>
</code></pre></div></div>

<h2 id="part-2-2">Part 2</h2>

<p>The slight twist introduced here require us to do the following:</p>

<ul>
  <li>Group the lines by 3;</li>
  <li>Instead of getting the intersect between the two halves of a string, get the intersect between all elements in the groups of 3.</li>
</ul>

<p>It is guaranteed by the nature of the problem that our input’s number of lines will be divisible by 3.</p>

<p>There are many ways to group the lines by 3, and the way I chose is to maintain an accumulated list of lists, where each element list will contain 3 elements.</p>

<p>With that, we solve the sub-problems:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- grouping the lines by 3</span>
<span class="n">foldr</span> <span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="n">acc</span><span class="o">@</span><span class="p">(</span><span class="n">y</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kr">if</span> <span class="n">length</span> <span class="n">y</span> <span class="o">==</span> <span class="mi">3</span> <span class="kr">then</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="o">:</span><span class="n">acc</span> <span class="kr">else</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">y</span><span class="p">)</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="p">[</span><span class="kt">[]</span><span class="p">]</span> <span class="o">$</span> <span class="n">lines</span> <span class="n">input</span>

<span class="c1">-- intersecting 3 lines</span>
<span class="n">map</span> <span class="p">(</span><span class="n">foldr1</span> <span class="n">intersect</span><span class="p">)</span> <span class="n">output_of_above</span>
</code></pre></div></div>

<p>Then, reassembling the final solution:</p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="nn">Data.Char</span>
<span class="kr">import</span> <span class="nn">Data.List</span>

<span class="n">solution'</span> <span class="n">input</span> <span class="o">=</span> <span class="n">sum</span> <span class="o">$</span> <span class="n">map</span> <span class="p">((</span><span class="nf">\</span><span class="n">x</span> <span class="o">-&gt;</span> <span class="kr">if</span> <span class="n">x</span> <span class="p">`</span><span class="n">elem</span><span class="p">`</span> <span class="p">[</span><span class="sc">'a'</span><span class="o">..</span><span class="sc">'z'</span><span class="p">]</span> <span class="kr">then</span> <span class="n">ord</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">96</span> <span class="kr">else</span> <span class="n">ord</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">65</span> <span class="o">+</span> <span class="mi">27</span><span class="p">)</span> <span class="o">.</span> <span class="p">(</span><span class="o">!!</span> <span class="mi">0</span><span class="p">))</span> <span class="o">$</span> <span class="n">map</span> <span class="p">(</span><span class="n">foldr1</span> <span class="n">intersect</span><span class="p">)</span> <span class="o">$</span> <span class="n">foldr</span> <span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="n">acc</span><span class="o">@</span><span class="p">(</span><span class="n">y</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kr">if</span> <span class="n">length</span> <span class="n">y</span> <span class="o">==</span> <span class="mi">3</span> <span class="kr">then</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="o">:</span><span class="n">acc</span> <span class="kr">else</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">y</span><span class="p">)</span><span class="o">:</span><span class="n">ys</span><span class="p">)</span> <span class="p">[</span><span class="kt">[]</span><span class="p">]</span> <span class="o">$</span> <span class="n">lines</span> <span class="n">input</span>
</code></pre></div></div>

<hr />

<h1 id="day-4">Day 4</h1>

<h2 id="part-1-3">Part 1</h2>

<p>Feeling a little lazy today, I decided to work in Python. Today’s problem is broken down into the following, familiar sub-problems:</p>

<ol>
  <li>Read input line by line;</li>
  <li>Split the line by <code class="language-plaintext highlighter-rouge">,</code>, which we will call segments;</li>
  <li>Split the segments by <code class="language-plaintext highlighter-rouge">-</code>, which we will call fragments;</li>
  <li>Convert resulting fragments to integers;</li>
  <li>Figure out if one of the two segments are fully contained in one or another;</li>
  <li>Count the number of fully contained lines.</li>
</ol>

<p>Let’s talk about step 5. In set theory, if we wanted to know if <code class="language-plaintext highlighter-rouge">A</code> is fully contained in <code class="language-plaintext highlighter-rouge">B</code>, then <code class="language-plaintext highlighter-rouge">A⊂B</code>; however, this can be simplified if <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code> are sorted lists, which is the case for ranges defined solely by their boundaries. So, if I had an input line of <code class="language-plaintext highlighter-rouge">6-6,4-6</code> we can verify quite quickly that the left range is fully contained in the right range, not because we imagined if all elements of the left range is in the right range, but because of the lower bounds: <code class="language-plaintext highlighter-rouge">6 &gt; 4</code>, and the upper bounds: <code class="language-plaintext highlighter-rouge">6 == 6</code>, so therefore <code class="language-plaintext highlighter-rouge">6-6</code> is in <code class="language-plaintext highlighter-rouge">4-6</code>.</p>

<p>Similarly, for <code class="language-plaintext highlighter-rouge">2-8,3-7</code>, we see that <code class="language-plaintext highlighter-rouge">3 &gt; 2</code> and <code class="language-plaintext highlighter-rouge">7 &lt; 8</code>, so this means <code class="language-plaintext highlighter-rouge">3-7</code> must be in <code class="language-plaintext highlighter-rouge">2-8</code>.</p>

<p>With that context, the sub-problems can be solve like so in Python:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># read input line by line e.g. "2-8,3-7"
</span><span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()</span>

<span class="c1"># split line by ',', so we get ["2-8", "3-7"]
</span><span class="n">segments</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># split a single segment by '-' so we get fragment = ["2", "8"]
</span><span class="n">fragment</span> <span class="o">=</span> <span class="n">segment</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">)</span>
<span class="c1"># note that all fragments = [["2", "8"], ["3", "7"]]
</span>
<span class="c1"># convert to int [2, 8]
</span><span class="n">fragment_prime</span> <span class="o">=</span> <span class="nf">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">fragment</span><span class="p">)</span>

<span class="c1"># compare the ranges
</span><span class="n">possibility_1</span> <span class="o">=</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">possibility_2</span> <span class="o">=</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">possibility_1</span> <span class="ow">or</span> <span class="n">possibility_2</span>
</code></pre></div></div>

<p>The way I used to combine all of the sub-problems together is to use an unholy concoction of maps:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">xys</span><span class="p">:</span> <span class="p">(</span><span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">])</span> <span class="ow">or</span> <span class="p">(</span><span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]),</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">segments</span><span class="p">:</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">segment</span><span class="p">:</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">segment</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">))),</span> <span class="n">segments</span><span class="p">)),</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">line</span><span class="p">:</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">),</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()))))))))</span>
</code></pre></div></div>

<h2 id="part-2-3">Part 2</h2>

<p>Part 2 changes the so-called “set operation” we are performing. Instead of “fully contains”, we are looking for overlaps, or in set terms we are looking for, “A∩B≠Ø”.</p>

<p>Let’s consider the few possible cases, if we have a string in the format <code class="language-plaintext highlighter-rouge">a-b,x-y</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>case 1
......a###########b...
.x#y..................

case 2
..a######b...
.x###y....

case 3
..a###b....
....x###y..

case 4
.a####b.......
.........x##y.

case 5
....a####b....
......x#y.....
</code></pre></div></div>

<p>The cases imply the following:</p>

<ol>
  <li>No intersect: <code class="language-plaintext highlighter-rouge">a &gt; x</code>, <code class="language-plaintext highlighter-rouge">b &gt; x</code>, <code class="language-plaintext highlighter-rouge">x &lt; a</code>, <code class="language-plaintext highlighter-rouge">y &lt; a</code>;</li>
  <li>Intersect: <code class="language-plaintext highlighter-rouge">a &gt; x</code>, <code class="language-plaintext highlighter-rouge">b &gt; x</code>, <strong><code class="language-plaintext highlighter-rouge">x &lt; a</code>, <code class="language-plaintext highlighter-rouge">y &gt; a</code></strong>;</li>
  <li>Intersect: <strong><code class="language-plaintext highlighter-rouge">a &lt; x</code>, <code class="language-plaintext highlighter-rouge">b &gt; x</code></strong>, <code class="language-plaintext highlighter-rouge">x &gt; a</code>, <code class="language-plaintext highlighter-rouge">y &gt; a</code>;</li>
  <li>No intersect: <code class="language-plaintext highlighter-rouge">a &lt; x</code>, <code class="language-plaintext highlighter-rouge">b &lt; x</code>, <code class="language-plaintext highlighter-rouge">x &gt; a</code>, <code class="language-plaintext highlighter-rouge">y &gt; a</code>;</li>
  <li>Intersect: <strong><code class="language-plaintext highlighter-rouge">a &lt; x</code>, <code class="language-plaintext highlighter-rouge">b &gt; x</code></strong>, <code class="language-plaintext highlighter-rouge">x &gt; a</code>, <code class="language-plaintext highlighter-rouge">y &gt; a</code>.</li>
</ol>

<p>The relations in bold matter the most; we see that for any two ranges to intersect, the lower bound of the first range must be less than the lower bound of the second range, and the upper bound of the first range must be greater than the lower bound of the second range, <em>or</em> vice-versa.</p>

<p>Writing that in code, the testing statement becomes:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">possibility_1</span> <span class="o">=</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">possibility_2</span> <span class="o">=</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">fragment_2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">fragment_1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">possibility_1</span> <span class="ow">or</span> <span class="n">possibility_2</span>
</code></pre></div></div>

<p>So, our resulting code looks very similar to part 1, with a minor change of index in our comparison lambda:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">xys</span><span class="p">:</span> <span class="p">(</span><span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span> <span class="ow">or</span> <span class="p">(</span><span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">xys</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">xys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]),</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">segments</span><span class="p">:</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">segment</span><span class="p">:</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">segment</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">))),</span> <span class="n">segments</span><span class="p">)),</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">line</span><span class="p">:</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">),</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()))))))))</span>
</code></pre></div></div>

<hr />

<h1 id="day-5">Day 5</h1>

<p>Deadlines are looming, so I’ve haven’t got the time to compact this. However, a streak is a streak!</p>

<h2 id="part-1-4">Part 1</h2>

<p>Immediately after reading the question, I immediately thought of stacks. The sub-problems are as follows:</p>

<ol>
  <li>Split the input into two, the visual representation and the instructions;</li>
  <li>Break down the visual representation into stacks;</li>
  <li>Break down the instructions into something we can use;</li>
  <li>Use the instructions to identify:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">from</code> queue;</li>
      <li><code class="language-plaintext highlighter-rouge">to</code> queue;</li>
      <li>how many items to move.</li>
    </ul>
  </li>
</ol>

<p>Not being in the headspace to do function composition, I left the code separated in their respective chunks:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">functools</span>                                                                              
                                                                                              
<span class="n">data</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()</span>                                                     
                                                                                              
<span class="c1"># \n here is the divider                                                                      
</span><span class="n">segments</span> <span class="o">=</span> <span class="n">functools</span><span class="p">.</span><span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">accum</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="n">accum</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">accum</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">x</span><span class="p">]]</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span> <span class="k">else</span> <span class="n">accum</span> <span class="o">+</span> <span class="p">[[]],</span> <span class="n">data</span><span class="p">,</span> <span class="p">[[]])</span>

<span class="c1"># all characters are +4 away from one another, first one at pos 1. reparse accordingly        
</span><span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="mi">4</span><span class="p">)],</span> <span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>

<span class="c1"># flatten segments[0] into a queue-like structure                                             
</span><span class="n">stacks</span> <span class="o">=</span> <span class="p">[[]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]))]</span>                                             
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">][:</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
  <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">col</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">row</span><span class="p">):</span>                                                               
    <span class="k">if</span> <span class="n">col</span> <span class="o">!=</span> <span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">:</span>
      <span class="n">stacks</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="nf">append</span><span class="p">(</span><span class="n">col</span><span class="p">)</span>                                                                   
<span class="n">stacks</span> <span class="o">=</span> <span class="p">[</span><span class="nf">list</span><span class="p">(</span><span class="nf">reversed</span><span class="p">(</span><span class="n">stack</span><span class="p">))</span> <span class="k">for</span> <span class="n">stack</span> <span class="ow">in</span> <span class="n">stacks</span><span class="p">]</span>                                          

<span class="c1"># flatten segments[1] into a list of tuple instructions                                       
</span><span class="n">digit_fn</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="p">[</span><span class="nf">int</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">s</span><span class="p">.</span><span class="nf">split</span><span class="p">()</span> <span class="k">if</span> <span class="n">x</span><span class="p">.</span><span class="nf">isdigit</span><span class="p">()]</span>                               
<span class="n">instructions</span> <span class="o">=</span> <span class="p">[</span><span class="nf">digit_fn</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">segments</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>

<span class="c1"># do the movements                                                                            
</span><span class="k">for</span> <span class="n">instruction</span> <span class="ow">in</span> <span class="n">instructions</span><span class="p">:</span>                                                              
  <span class="n">stack_from</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span>                                                             
  <span class="n">stack_to</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span> 
  <span class="n">number</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  
  <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">number</span><span class="p">):</span>                                                                     
    <span class="n">stacks</span><span class="p">[</span><span class="n">stack_to</span><span class="p">].</span><span class="nf">append</span><span class="p">(</span><span class="n">stacks</span><span class="p">[</span><span class="n">stack_from</span><span class="p">].</span><span class="nf">pop</span><span class="p">())</span> 
  
<span class="c1"># get the top of all                                                                          
</span><span class="nf">print</span><span class="p">(</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">s</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">stacks</span><span class="p">]))</span>
</code></pre></div></div>

<h2 id="part-2-4">Part 2</h2>

<p>Part 2 essentially changes the data structure we are working with. Now, we’re breaking off lists at any arbitrary point, and appending it to another list (is there a name for this type of data structure)?</p>

<p>However, since this is a small change, I decided to change two lines and reuse the rest of the code, meaning that the main data structure in use is misnamed. Regardless, here it is:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">functools</span>                                                                              
                                                                                              
<span class="n">data</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()</span>                                                     
                                                                                              
<span class="c1"># \n here is the divider                                                                      
</span><span class="n">segments</span> <span class="o">=</span> <span class="n">functools</span><span class="p">.</span><span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">accum</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="n">accum</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">accum</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">x</span><span class="p">]]</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span> <span class="k">else</span> <span class="n">accum</span> <span class="o">+</span> <span class="p">[[]],</span> <span class="n">data</span><span class="p">,</span> <span class="p">[[]])</span>

<span class="c1"># all characters are +4 away from one another, first one at pos 1. reparse accordingly        
</span><span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="mi">4</span><span class="p">)],</span> <span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>

<span class="c1"># flatten segments[0] into a queue-like structure                                             
</span><span class="n">stacks</span> <span class="o">=</span> <span class="p">[[]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]))]</span>                                             
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">segments</span><span class="p">[</span><span class="mi">0</span><span class="p">][:</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
  <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">col</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">row</span><span class="p">):</span>                                                               
    <span class="k">if</span> <span class="n">col</span> <span class="o">!=</span> <span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">:</span>
      <span class="n">stacks</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="nf">append</span><span class="p">(</span><span class="n">col</span><span class="p">)</span>                                                                   
<span class="n">stacks</span> <span class="o">=</span> <span class="p">[</span><span class="nf">list</span><span class="p">(</span><span class="nf">reversed</span><span class="p">(</span><span class="n">stack</span><span class="p">))</span> <span class="k">for</span> <span class="n">stack</span> <span class="ow">in</span> <span class="n">stacks</span><span class="p">]</span>                                          

<span class="c1"># flatten segments[1] into a list of tuple instructions                                       
</span><span class="n">digit_fn</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">s</span><span class="p">:</span> <span class="p">[</span><span class="nf">int</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">s</span><span class="p">.</span><span class="nf">split</span><span class="p">()</span> <span class="k">if</span> <span class="n">x</span><span class="p">.</span><span class="nf">isdigit</span><span class="p">()]</span>                               
<span class="n">instructions</span> <span class="o">=</span> <span class="p">[</span><span class="nf">digit_fn</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">segments</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>

<span class="c1"># do the movements                                                                            
</span><span class="k">for</span> <span class="n">instruction</span> <span class="ow">in</span> <span class="n">instructions</span><span class="p">:</span>                                                              
  <span class="n">stack_from</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span>                                                             
  <span class="n">stack_to</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span> 
  <span class="n">number</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  
  <span class="n">stacks</span><span class="p">[</span><span class="n">stack_to</span><span class="p">].</span><span class="nf">extend</span><span class="p">(</span><span class="n">stacks</span><span class="p">[</span><span class="n">stack_from</span><span class="p">][</span><span class="o">-</span><span class="n">number</span><span class="p">:])</span>                                       
  <span class="n">stacks</span><span class="p">[</span><span class="n">stack_from</span><span class="p">]</span> <span class="o">=</span> <span class="n">stacks</span><span class="p">[</span><span class="n">stack_from</span><span class="p">][:</span><span class="o">-</span><span class="n">number</span><span class="p">]</span>
  
<span class="c1"># get the top of all                                                                          
</span><span class="nf">print</span><span class="p">(</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">s</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">stacks</span><span class="p">]))</span>
</code></pre></div></div>

<hr />

<h1 id="day-6">Day 6</h1>

<p>Oh no I can feel the deadlines! I’ve decided to take a crack at implementing another thing in C. Since I was also feeling lazy, I decided to use C.</p>

<h2 id="part-1-5">Part 1</h2>

<p>Today’s puzzle involves us picking out the position of the first unique character in a sliding frame of 4. The most obvious algorithm is generally as follows:</p>

<ol>
  <li>Load the first 4 characters into a set</li>
  <li>If the set has a length of 4, then you are done, position 4 is the answer</li>
  <li>Otherwise, go on to the next position, load the previous 3 characters and itself into a set, and check length of set</li>
  <li>If length is 4, current position is the answer, otherwise, repeat step 3</li>
</ol>

<p>The above algorithm is probably also the fastest I know, since the set operations involved is <code class="language-plaintext highlighter-rouge">O(4)</code>. Iterating through the string, that’s <code class="language-plaintext highlighter-rouge">O(n)</code>, so the total runtime of this solution would be <code class="language-plaintext highlighter-rouge">O(4n)</code>.</p>

<p>In C, however, we don’t have sets, and I don’t really feel like implementing one. Instead, I employed a technique known as dynamic programming to implement something like a queue, which memorizes 4 values at once. Whenever a new character is read from the input stream, the head of the queue is popped, and the new character is pushed into the queue.</p>

<p>To speed up figuring out if there are any duplicate elements, I created a map of 26 characters and maintain a reference count of each alphabet in the queue. In theory, the function will simply need to iterate through the queue, lookup the alphabet in the map, look at the reference count, and if it’s all 1, we’ve found our character.</p>

<p>This method has a rough time complexity of: <code class="language-plaintext highlighter-rouge">O(n)</code> for going through the string, <code class="language-plaintext highlighter-rouge">O(4)</code> for the dynamic programming implementation, <code class="language-plaintext highlighter-rouge">O(4)</code> for checking the queue. If 4 is an unknown, this’ll be <code class="language-plaintext highlighter-rouge">O(k^2 * n)</code>. Damn.</p>

<p>So:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"input.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="kt">char</span> <span class="n">exist_map</span><span class="p">[</span><span class="mi">26</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">a</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">*</span><span class="n">d</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="kt">size_t</span> <span class="n">n_processed</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">buf</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">while</span> <span class="p">((</span><span class="n">buf</span> <span class="o">=</span> <span class="n">fgetc</span><span class="p">(</span><span class="n">f</span><span class="p">))</span> <span class="o">!=</span> <span class="n">EOF</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">++</span><span class="n">n_processed</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">exist_map</span><span class="p">[</span><span class="n">buf</span> <span class="o">-</span> <span class="sc">'a'</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">a</span> <span class="o">!=</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">c</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"delimiter found at %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">n_processed</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">*</span><span class="n">a</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">d</span> <span class="o">=</span> <span class="n">exist_map</span> <span class="o">+</span> <span class="p">(</span><span class="n">buf</span> <span class="o">-</span> <span class="sc">'a'</span><span class="p">);</span>
    <span class="o">*</span><span class="n">d</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">a</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span> <span class="n">b</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">d</span><span class="p">;</span> <span class="n">d</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

</code></pre></div></div>

<p>The dynamic programming implementation can be improved, but oh well.</p>

<h2 id="part-2-5">Part 2</h2>

<p>Increasing the required unique characters from 4 to 14 would have been much easier on Python, but in C, this means I had to abstract my functions, and use an array of <code class="language-plaintext highlighter-rouge">char*</code> instead of defining each position in the queue on my own.</p>

<p>The two functions to abstract are:</p>

<ul>
  <li>the one that figures out if all the reference counts relevant to the queue is 1</li>
  <li>the one that shifts the queue to the left by 1, and adding the new value into the queue</li>
</ul>

<p>Improving the “queue” can be easily seen in this example, which involves introducing variables to keep a pointer of where the head and tail is. However, I was lazy. So:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">char</span> <span class="nf">areOnes</span><span class="p">(</span><span class="kt">char</span><span class="o">**</span> <span class="n">pointers</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">pointers</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> 
<span class="p">}</span> 
  
<span class="kt">void</span> <span class="nf">leftShiftExistMap</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">map</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">pointers</span><span class="p">,</span> <span class="kt">char</span> <span class="n">newVal</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">pointers</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">*</span><span class="p">(</span><span class="n">pointers</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">pointers</span><span class="p">[</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">map</span> <span class="o">+</span> <span class="p">(</span><span class="n">newVal</span> <span class="o">-</span> <span class="sc">'a'</span><span class="p">);</span>
  <span class="o">*</span><span class="p">(</span><span class="n">pointers</span><span class="p">[</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
    <span class="n">pointers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">pointers</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
  <span class="n">pointers</span><span class="p">[</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>   
    
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"input.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="kt">char</span> <span class="n">exist_map</span><span class="p">[</span><span class="mi">26</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">pointers</span><span class="p">[</span><span class="mi">14</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">};</span>
  <span class="kt">size_t</span> <span class="n">n_processed</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">buf</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="k">while</span> <span class="p">((</span><span class="n">buf</span> <span class="o">=</span> <span class="n">fgetc</span><span class="p">(</span><span class="n">f</span><span class="p">))</span> <span class="o">!=</span> <span class="n">EOF</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">++</span><span class="n">n_processed</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">exist_map</span><span class="p">[</span><span class="n">buf</span> <span class="o">-</span> <span class="sc">'a'</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">pointers</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> <span class="n">areOnes</span><span class="p">(</span><span class="n">pointers</span><span class="p">,</span> <span class="mi">14</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"delimiter found at %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">n_processed</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">leftShiftExistMap</span><span class="p">(</span><span class="n">exist_map</span><span class="p">,</span> <span class="n">pointers</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mi">14</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The time complexity is still the same, which is <code class="language-plaintext highlighter-rouge">O(k^2*n)</code> where <code class="language-plaintext highlighter-rouge">k = 14</code>. Use the right tools (i.e. Python) for the right job!</p>

<hr />

<h1 id="day-7">Day 7</h1>

<p>After a mere 4 hours of sleep, I continued to rush deadlines fueled by nothing but coffee in my stomach. Suffice to say, I’m not entirely satisfied with the work I’ve turned in, but what’s done is done, am I right?</p>

<p>Day 7 was done together with Day 8, because time was just simply not on my side. But hey, I’ve done both, cut me some slack!</p>

<h2 id="part-1-6">Part 1</h2>

<p>An interesting use case is presented in day 7, where we essentially had to rebuild the folder structure based on the output of a few commands, and figure out the sum of the set of folders (including subdirectories) that exceeds 100000.</p>

<p>My very tired and uncaffeinated (half-life of coffee was out) brain immediately thought “trees” and jumped straight into the code. We also have to write a simple parser to figure out what each line in the output did / displayed, so that we can use the information meaningfully.</p>

<p>So the sub-problems were:</p>

<ul>
  <li>Figure out what each line said (parsing);</li>
  <li>Create a new node if the line enters a directory.</li>
</ul>

<p>Parsing each line is simple, by using spaces as delimiters and tokenizing each word:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tokens</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>  <span class="c1"># x is a line
</span><span class="k">if</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">$</span><span class="sh">"</span><span class="p">:</span>
  <span class="k">if</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">ls</span><span class="sh">'</span><span class="p">:</span>
    <span class="c1"># do something
</span>  <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">..</span><span class="sh">'</span><span class="p">:</span>
    <span class="c1"># do something
</span>  <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span>
    <span class="c1"># do something
</span>  <span class="k">else</span><span class="p">:</span>
    <span class="c1"># do something, is a directory
</span><span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">isdigit</span><span class="p">():</span>
    <span class="c1"># is size of file
</span><span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">dir</span><span class="sh">'</span><span class="p">:</span>
    <span class="c1"># is telling us directory exist
</span></code></pre></div></div>

<p>All we need to do now is to create a <code class="language-plaintext highlighter-rouge">Node</code> class that represents our tree:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Node</span><span class="p">:</span>
  <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">parent</span> <span class="o">=</span> <span class="bp">None</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">dirname</span>
    <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">self</span><span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">parent</span>
    <span class="n">self</span><span class="p">.</span><span class="n">nodes</span> <span class="o">=</span> <span class="p">[]</span>

  <span class="k">def</span> <span class="nf">__eq__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">dirname</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">dirname</span>

  <span class="k">def</span> <span class="nf">__hash__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">return</span> <span class="nf">hash</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">)</span>

  <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">return</span> <span class="sh">"</span><span class="s">{} {}</span><span class="sh">"</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="p">[</span><span class="nf">str</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>

  <span class="k">def</span> <span class="nf">getSize</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
      <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="nf">sum</span><span class="p">([</span><span class="n">x</span><span class="p">.</span><span class="nf">getSize</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>
</code></pre></div></div>

<p>And then combine all the code together. I also add a <code class="language-plaintext highlighter-rouge">getSolutionSize</code> function in <code class="language-plaintext highlighter-rouge">Node</code>, which traverses the tree depth-first, gets the space occupied on the diskif it’s larger than <code class="language-plaintext highlighter-rouge">100000</code> (specified in the problem), and accumulates the size.:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">functools</span>
<span class="kn">import</span> <span class="n">sys</span>

<span class="k">class</span> <span class="nc">Node</span><span class="p">:</span>
  <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">parent</span> <span class="o">=</span> <span class="bp">None</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">dirname</span>
    <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">self</span><span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">parent</span>
    <span class="n">self</span><span class="p">.</span><span class="n">nodes</span> <span class="o">=</span> <span class="p">[]</span>

  <span class="k">def</span> <span class="nf">__eq__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">dirname</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">dirname</span>

  <span class="k">def</span> <span class="nf">__hash__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">return</span> <span class="nf">hash</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">)</span>

  <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">return</span> <span class="sh">"</span><span class="s">{} {}</span><span class="sh">"</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="p">[</span><span class="nf">str</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>

  <span class="k">def</span> <span class="nf">getSolutionSize</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
      <span class="k">return</span> <span class="mi">0</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">size</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">getSize</span><span class="p">()</span>
      <span class="nf">return </span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">size</span> <span class="o">&gt;</span> <span class="mi">100000</span> <span class="k">else</span> <span class="n">size</span><span class="p">)</span> <span class="o">+</span> <span class="nf">sum</span><span class="p">([</span><span class="n">x</span><span class="p">.</span><span class="nf">getSolutionSize</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>

  <span class="k">def</span> <span class="nf">getSize</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
      <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="nf">sum</span><span class="p">([</span><span class="n">x</span><span class="p">.</span><span class="nf">getSize</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span>
  <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> <span class="k">return</span>

  <span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">tokens</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">$</span><span class="sh">"</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">ls</span><span class="sh">'</span><span class="p">:</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">..</span><span class="sh">'</span><span class="p">:</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">.</span><span class="n">parent</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">rootNode</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">node</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">[</span><span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
  <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">isdigit</span><span class="p">():</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">node</span><span class="p">)</span>
    <span class="n">n</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>
  <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">dir</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">node</span><span class="p">)</span>
    <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>

<span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()[</span><span class="mi">1</span><span class="p">:]</span>
<span class="n">sys</span><span class="p">.</span><span class="nf">setrecursionlimit</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span>
<span class="nf">parselines</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">n</span><span class="p">.</span><span class="nf">getSolutionSize</span><span class="p">())</span>
</code></pre></div></div>

<p>Because we use recursion extensively, we have to increase our recursion limit to something we can work with.</p>

<h2 id="part-2-6">Part 2</h2>

<p>In Part 2, we find the folder with lowest value that is greater than the free space we need. Luckily, this is a small change (I use tuples, but actually we can just omit the <code class="language-plaintext highlighter-rouge">dirname</code> to remove that information, as we don’t need it for our solution):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">functools</span>
<span class="kn">import</span> <span class="n">sys</span>

<span class="k">class</span> <span class="nc">Node</span><span class="p">:</span>
  <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">dirname</span><span class="p">,</span> <span class="n">parent</span> <span class="o">=</span> <span class="bp">None</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">dirname</span>
    <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">self</span><span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">parent</span>
    <span class="n">self</span><span class="p">.</span><span class="n">nodes</span> <span class="o">=</span> <span class="p">[]</span>

  <span class="k">def</span> <span class="nf">__eq__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">dirname</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">dirname</span>

  <span class="k">def</span> <span class="nf">__hash__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">return</span> <span class="nf">hash</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">)</span>

  <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
    <span class="k">return</span> <span class="sh">"</span><span class="s">{} {}</span><span class="sh">"</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="p">[</span><span class="nf">str</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>

  <span class="k">def</span> <span class="nf">getSolution</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
      <span class="nf">return </span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">bestTuple</span> <span class="o">=</span> <span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="nf">getSize</span><span class="p">())</span>
      <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">:</span>
        <span class="n">childTuple</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">getSolution</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">childTuple</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">childTuple</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">bestTuple</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
          <span class="n">bestTuple</span> <span class="o">=</span> <span class="n">childTuple</span>
      <span class="k">return</span> <span class="n">bestTuple</span>

  <span class="k">def</span> <span class="nf">getSize</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
      <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">else</span> <span class="nf">sum</span><span class="p">([</span><span class="n">x</span><span class="p">.</span><span class="nf">getSize</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span>
  <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> <span class="k">return</span>

  <span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">tokens</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s">$</span><span class="sh">"</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">ls</span><span class="sh">'</span><span class="p">:</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">..</span><span class="sh">'</span><span class="p">:</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">.</span><span class="n">parent</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">rootNode</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">node</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">[</span><span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>
      <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
  <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">isdigit</span><span class="p">():</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">node</span><span class="p">)</span>
    <span class="n">n</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>
  <span class="k">elif</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">dir</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">node</span><span class="p">)</span>
    <span class="n">node</span><span class="p">.</span><span class="n">nodes</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="nf">parselines</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="n">rootNode</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>

<span class="n">n</span> <span class="o">=</span> <span class="nc">Node</span><span class="p">(</span><span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">input.txt</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">r</span><span class="sh">"</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()[</span><span class="mi">1</span><span class="p">:]</span>
<span class="n">sys</span><span class="p">.</span><span class="nf">setrecursionlimit</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span>
<span class="nf">parselines</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">n</span><span class="p">.</span><span class="nf">getSolution</span><span class="p">(</span><span class="mi">30000000</span> <span class="o">-</span> <span class="mi">70000000</span> <span class="o">+</span> <span class="n">n</span><span class="p">.</span><span class="nf">getSize</span><span class="p">()))</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">70000000</code> is the total disk space and <code class="language-plaintext highlighter-rouge">30000000</code> is the free space we need. The only change was to <code class="language-plaintext highlighter-rouge">getSolutionSize()</code>, which was changed to <code class="language-plaintext highlighter-rouge">getSolution()</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">def</span> <span class="nf">getSolution</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">target</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">value</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
      <span class="nf">return </span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">bestTuple</span> <span class="o">=</span> <span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">dirname</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="nf">getSize</span><span class="p">())</span>
      <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">nodes</span><span class="p">:</span>
        <span class="n">childTuple</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">getSolution</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">childTuple</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">target</span> <span class="ow">and</span> <span class="n">childTuple</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">bestTuple</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
          <span class="n">bestTuple</span> <span class="o">=</span> <span class="n">childTuple</span>
      <span class="k">return</span> <span class="n">bestTuple</span>
</code></pre></div></div>

<p>The code block figures out if a child is closer to the target value than itself, done recursively.</p>

<hr />

<h1 id="day-8">Day 8</h1>

<p>Are you tired of human-readable code yet?</p>

<h2 id="part-1-7">Part 1</h2>

<p>This is a classic problem, in the sense that many applications rely on figuring out if adjacent cells are blocking the view of a current cell. An example could be collision detection (blocking view distance = 1). The problem we are trying to solve, in programmer terms, is: given grid of numbers, find out if all the numbers to any of the edges of the grid are less than the value at the current (x,y).</p>

<p>Interestingly, this problem doesn’t have sub-problems, since it’s quite a well-contained problem. The algorithm to solve this would be:</p>

<ol>
  <li>Go through every x and y starting from <code class="language-plaintext highlighter-rouge">(1, 1)</code>, ending at <code class="language-plaintext highlighter-rouge">(max_x - 1, max_y - 1)</code></li>
  <li>Iterate from <code class="language-plaintext highlighter-rouge">0 to x - 1</code>, find out if there are any values that exceed the value at (x,y)</li>
  <li>Repeat step 2 for <code class="language-plaintext highlighter-rouge">x + 1</code> to <code class="language-plaintext highlighter-rouge">max_x - 1</code></li>
  <li>Repeat step 2 for <code class="language-plaintext highlighter-rouge">0</code> to <code class="language-plaintext highlighter-rouge">y - 1</code></li>
  <li>Repeat step 2 for <code class="language-plaintext highlighter-rouge">y + 1</code> to <code class="language-plaintext highlighter-rouge">max_y - 1</code></li>
  <li>If any of steps 2 to 5 reports that there are no values that exceed the value at (x,y), then the current (x,y) has met the target condition.</li>
  <li>Collect all the results, and count all (x,y)s that met the condition in step 6</li>
</ol>

<p>The code, is hence:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">itertools</span>
<span class="n">trees</span> <span class="o">=</span> <span class="p">[[</span><span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">x</span> <span class="k">if</span> <span class="n">y</span> <span class="o">!=</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">itertools</span><span class="p">.</span><span class="nf">starmap</span><span class="p">(</span><span class="k">lambda</span> <span class="n">row</span><span class="p">,</span> <span class="n">r_trees</span><span class="p">:</span> <span class="nf">list</span><span class="p">(</span><span class="n">itertools</span><span class="p">.</span><span class="nf">starmap</span><span class="p">(</span><span class="k">lambda</span> <span class="n">col</span><span class="p">,</span> <span class="n">tree</span><span class="p">:</span> <span class="nf">all</span><span class="p">([</span><span class="n">trees</span><span class="p">[</span><span class="n">c_u</span><span class="p">][</span><span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">c_u</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)])</span> <span class="ow">or</span> <span class="nf">all</span><span class="p">([</span><span class="n">trees</span><span class="p">[</span><span class="n">c_d</span><span class="p">][</span><span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">c_d</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">row</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">trees</span><span class="p">))])</span> <span class="ow">or</span> <span class="nf">all</span><span class="p">([</span><span class="n">trees</span><span class="p">[</span><span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">r_l</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">r_l</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)])</span> <span class="ow">or</span> <span class="nf">all</span><span class="p">([</span><span class="n">trees</span><span class="p">[</span><span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">r_r</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">r_r</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">col</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">r_trees</span><span class="p">))]),</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">r_trees</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]))),</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">trees</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">sum</span><span class="p">([</span><span class="nf">sum</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">result</span><span class="p">])</span> <span class="o">+</span> <span class="nf">len</span><span class="p">(</span><span class="n">trees</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="nf">len</span><span class="p">(</span><span class="n">trees</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span>
</code></pre></div></div>

<p>The most readable thing on the planet, I know.</p>

<h2 id="part-2-7">Part 2</h2>

<p>Instead of figuring out how many (x,y)s have larger values than all the values to any edges of the grid, we now compute a score for each (x,y) based on <em>how many</em> values there is until the current value <code class="language-plaintext highlighter-rouge">&lt;=</code> a value along the path to the edge of the grid, composited with multiplication.</p>

<p>It’s really changing the function <code class="language-plaintext highlighter-rouge">all</code> to <code class="language-plaintext highlighter-rouge">sum list itertools.takewhile</code>, which sums the list of True values, while current value is still more than the values it traverses to reach the edge. As the stopping number themselves is counted into the sum (+1), we need to handle the case where all of the numbers were lower than the value at (x,y), which shouldn’t have the +1 offset. A <code class="language-plaintext highlighter-rouge">min</code> function is applied to handle that case. So:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">itertools</span>
<span class="n">trees</span> <span class="o">=</span> <span class="p">[[</span><span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">x</span> <span class="k">if</span> <span class="n">y</span> <span class="o">!=</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">itertools</span><span class="p">.</span><span class="nf">starmap</span><span class="p">(</span><span class="k">lambda</span> <span class="n">row</span><span class="p">,</span> <span class="n">r_trees</span><span class="p">:</span> <span class="nf">list</span><span class="p">(</span><span class="n">itertools</span><span class="p">.</span><span class="nf">starmap</span><span class="p">(</span><span class="k">lambda</span> <span class="n">col</span><span class="p">,</span> <span class="n">tree</span><span class="p">:</span> <span class="nf">min</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">itertools</span><span class="p">.</span><span class="nf">takewhile</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">trees</span><span class="p">[</span><span class="n">c_u</span><span class="p">][</span><span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">c_u</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)])))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="nf">min</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">itertools</span><span class="p">.</span><span class="nf">takewhile</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">trees</span><span class="p">[</span><span class="n">c_d</span><span class="p">][</span><span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">c_d</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">row</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">trees</span><span class="p">))])))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">trees</span><span class="p">)</span> <span class="o">-</span> <span class="n">row</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="nf">min</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">itertools</span><span class="p">.</span><span class="nf">takewhile</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">trees</span><span class="p">[</span><span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">r_l</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">r_l</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)])))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="nf">min</span><span class="p">(</span><span class="nf">sum</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">itertools</span><span class="p">.</span><span class="nf">takewhile</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">trees</span><span class="p">[</span><span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">r_r</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">tree</span> <span class="k">for</span> <span class="n">r_r</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">col</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">r_trees</span><span class="p">))])))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">r_trees</span><span class="p">)</span> <span class="o">-</span> <span class="n">col</span> <span class="o">-</span> <span class="mi">2</span><span class="p">),</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">r_trees</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]))),</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">trees</span><span class="p">[</span><span class="mi">1</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]))</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">max</span><span class="p">([</span><span class="nf">max</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">result</span><span class="p">]))</span>
</code></pre></div></div>

<hr />

<h1 id="day-9">Day 9</h1>

<p>Ah yes, nothing like simulating ropes innit?</p>

<h2 id="part-1-8">Part 1</h2>

<p>Our adventures today bring us to simulating a head and tail, where tail has well-defined behaviour, which the prompt has kindly provided:</p>

<ul>
  <li>if the head and tail are on different rows and columns, move towards the head diagonally</li>
  <li>else, move towards the head laterally / vertically.</li>
</ul>

<p>The head is given a list of directions and number of squares to move. So, the sub-problems are:</p>

<ul>
  <li>parse instruction and number of squares to move</li>
  <li>every time the head moves, check if the tail needs to move
    <ul>
      <li>if the tail is within 1 square of the head, then it doesn’t need to move</li>
      <li>otherwise, move based on the behaviour given by the prompt</li>
    </ul>
  </li>
  <li>once the next position of the tail is decided, put it in the set</li>
  <li>at the end of the procedure, count the number of elements in the set</li>
</ul>

<p>My code today is a lot more readable, so it’s quite obvious how the sub-problems are defined:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">head_instructions</span> <span class="o">=</span> <span class="p">[(</span><span class="n">direction</span><span class="p">,</span> <span class="nf">int</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="nf">strip</span><span class="p">()))</span> <span class="k">for</span> <span class="n">direction</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="p">[</span><span class="n">x</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]]</span>

<span class="n">tail_positions</span> <span class="o">=</span> <span class="p">{(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)}</span>
<span class="n">last_head_pos</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">last_tail_pos</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">for</span> <span class="n">instruction</span> <span class="ow">in</span> <span class="n">head_instructions</span><span class="p">:</span>
  <span class="nb">dir</span><span class="p">,</span> <span class="n">val</span> <span class="o">=</span> <span class="n">instruction</span>
  <span class="n">h_x</span><span class="p">,</span><span class="n">h_y</span> <span class="o">=</span> <span class="n">last_head_pos</span>
  <span class="n">t_x</span><span class="p">,</span><span class="n">t_y</span> <span class="o">=</span> <span class="n">last_tail_pos</span>

  <span class="n">step</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">LD</span><span class="sh">'</span> <span class="k">else</span> <span class="mi">1</span>

  <span class="k">for</span> <span class="n">incr</span> <span class="ow">in</span> <span class="p">[</span><span class="n">step</span><span class="p">]</span> <span class="o">*</span> <span class="n">val</span><span class="p">:</span>
    <span class="n">h_y</span> <span class="o">+=</span> <span class="n">step</span> <span class="k">if</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">UD</span><span class="sh">'</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">h_x</span> <span class="o">+=</span> <span class="n">step</span> <span class="k">if</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">LR</span><span class="sh">'</span> <span class="k">else</span> <span class="mi">0</span>

    <span class="k">if</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_x</span> <span class="o">-</span> <span class="n">t_x</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span> <span class="ow">and</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_y</span> <span class="o">-</span> <span class="n">t_y</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
      <span class="k">continue</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">t_x</span> <span class="o">+=</span> <span class="nf">int</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">h_x</span> <span class="o">==</span> <span class="n">t_x</span> <span class="nf">else </span><span class="p">(</span><span class="n">h_x</span> <span class="o">-</span> <span class="n">t_x</span><span class="p">)</span> <span class="o">/</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_x</span> <span class="o">-</span> <span class="n">t_x</span><span class="p">))</span>
      <span class="n">t_y</span> <span class="o">+=</span> <span class="nf">int</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">h_y</span> <span class="o">==</span> <span class="n">t_y</span> <span class="nf">else </span><span class="p">(</span><span class="n">h_y</span> <span class="o">-</span> <span class="n">t_y</span><span class="p">)</span> <span class="o">/</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_y</span> <span class="o">-</span> <span class="n">t_y</span><span class="p">))</span>
      <span class="n">tail_positions</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">t_x</span><span class="p">,</span> <span class="n">t_y</span><span class="p">))</span>

  <span class="n">last_head_pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">h_x</span><span class="p">,</span> <span class="n">h_y</span><span class="p">)</span>
  <span class="n">last_tail_pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">t_x</span><span class="p">,</span> <span class="n">t_y</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">tail_positions</span><span class="p">))</span>
</code></pre></div></div>

<h2 id="part-2-8">Part 2</h2>

<p>Part 2 gives us more points to control (i.e. the tail follows a point which follows another point, etc until the head). This means we have to maintain the positions of all the points, and compare the positions pairwise. Luckily for us, the behaviour is the same. So, for each step in our instructions, we go through the positions pairwise and to update positions. Since we are interested in how the tail moves, we only store all the co-ordinates visited by the tail in our set.</p>

<p>So:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">head_instructions</span> <span class="o">=</span> <span class="p">[(</span><span class="n">direction</span><span class="p">,</span> <span class="nf">int</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="nf">strip</span><span class="p">()))</span> <span class="k">for</span> <span class="n">direction</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="p">[</span><span class="n">x</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]]</span>

<span class="n">tail_positions</span> <span class="o">=</span> <span class="p">{(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)}</span>
<span class="n">last_positions</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)]</span>
<span class="k">for</span> <span class="n">instruction</span> <span class="ow">in</span> <span class="n">head_instructions</span><span class="p">:</span>
  <span class="nb">dir</span><span class="p">,</span> <span class="n">val</span> <span class="o">=</span> <span class="n">instruction</span>
  <span class="n">step</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">LD</span><span class="sh">'</span> <span class="k">else</span> <span class="mi">1</span>

  <span class="k">for</span> <span class="n">incr</span> <span class="ow">in</span> <span class="p">[</span><span class="n">step</span><span class="p">]</span> <span class="o">*</span> <span class="n">val</span><span class="p">:</span>
    <span class="n">g_x</span><span class="p">,</span> <span class="n">g_y</span> <span class="o">=</span> <span class="n">last_positions</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">g_y</span> <span class="o">+=</span> <span class="n">step</span> <span class="k">if</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">UD</span><span class="sh">'</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">g_x</span> <span class="o">+=</span> <span class="n">step</span> <span class="k">if</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="sh">'</span><span class="s">LR</span><span class="sh">'</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">last_positions</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">g_x</span><span class="p">,</span> <span class="n">g_y</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">last_positions</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
      <span class="n">h_x</span><span class="p">,</span><span class="n">h_y</span> <span class="o">=</span> <span class="n">last_positions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
      <span class="n">t_x</span><span class="p">,</span><span class="n">t_y</span> <span class="o">=</span> <span class="n">last_positions</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>

      <span class="k">if</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_x</span> <span class="o">-</span> <span class="n">t_x</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span> <span class="ow">and</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_y</span> <span class="o">-</span> <span class="n">t_y</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">continue</span>
      <span class="k">else</span><span class="p">:</span>
        <span class="n">t_x</span> <span class="o">+=</span> <span class="nf">int</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">h_x</span> <span class="o">==</span> <span class="n">t_x</span> <span class="nf">else </span><span class="p">(</span><span class="n">h_x</span> <span class="o">-</span> <span class="n">t_x</span><span class="p">)</span> <span class="o">/</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_x</span> <span class="o">-</span> <span class="n">t_x</span><span class="p">))</span>
        <span class="n">t_y</span> <span class="o">+=</span> <span class="nf">int</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">h_y</span> <span class="o">==</span> <span class="n">t_y</span> <span class="nf">else </span><span class="p">(</span><span class="n">h_y</span> <span class="o">-</span> <span class="n">t_y</span><span class="p">)</span> <span class="o">/</span> <span class="nf">abs</span><span class="p">(</span><span class="n">h_y</span> <span class="o">-</span> <span class="n">t_y</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">9</span><span class="p">:</span>
          <span class="n">tail_positions</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">t_x</span><span class="p">,</span> <span class="n">t_y</span><span class="p">))</span>

      <span class="n">last_positions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">h_x</span><span class="p">,</span> <span class="n">h_y</span><span class="p">)</span>
      <span class="n">last_positions</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">t_x</span><span class="p">,</span> <span class="n">t_y</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">tail_positions</span><span class="p">))</span>
</code></pre></div></div>

<hr />

<h1 id="day-10">Day 10</h1>

<p>CPU instructions!</p>

<h2 id="part-1-9">Part 1</h2>

<p>This problem is what I would classify as a parser-type problem; it usually involves the programmer writing some sort of basic parser.</p>

<p>The sub-problems are:</p>

<ul>
  <li>For each line, split the line by the space character</li>
  <li>Based on the instruction:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">addx</code> increment cycles by two, figure out if within the two increments if we’ve crossed <code class="language-plaintext highlighter-rouge">20</code> or <code class="language-plaintext highlighter-rouge">- 20 mod 40</code>, and modify the signal strength accordingly</li>
      <li><code class="language-plaintext highlighter-rouge">noop</code> increment cycles by one, figure out if we’ve crossed <code class="language-plaintext highlighter-rouge">20</code> or <code class="language-plaintext highlighter-rouge">- 20 mod 40</code>, and modify the signal strength accordingly</li>
    </ul>
  </li>
</ul>

<p>Thinking that this would be easy to do in Haskell, I gave it a go:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">inputStr</span> <span class="o">=</span> <span class="s">""</span>

<span class="n">solution</span> <span class="o">::</span> <span class="kt">String</span> <span class="o">-&gt;</span> <span class="kt">Integer</span>
<span class="n">solution</span> <span class="n">input</span> <span class="o">=</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="kr">_</span><span class="p">,</span><span class="kr">_</span><span class="p">,</span><span class="n">z</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">z</span><span class="p">)</span> <span class="o">$</span> <span class="n">foldr</span> <span class="p">(</span><span class="nf">\</span><span class="n">accum</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">step</span> <span class="n">x</span> <span class="p">(</span><span class="kr">if</span> <span class="n">null</span> <span class="n">xs</span> <span class="kr">then</span> <span class="mi">0</span> <span class="kr">else</span> <span class="p">(</span><span class="n">read</span> <span class="o">$</span> <span class="n">head</span> <span class="n">xs</span><span class="p">))</span> <span class="n">accum</span><span class="p">)</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span> <span class="o">$</span> <span class="n">map</span> <span class="n">words</span> <span class="o">$</span> <span class="n">lines</span> <span class="n">input</span>
  <span class="kr">where</span>  
        <span class="n">stepAddX</span> <span class="n">x</span> <span class="n">accum</span><span class="o">@</span><span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="n">sums</span><span class="p">,</span><span class="n">sigstr</span><span class="p">)</span> <span class="n">y</span> <span class="o">=</span> <span class="kr">if</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="mi">20</span><span class="p">)</span> <span class="o">||</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">20</span><span class="p">)</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">40</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="kr">then</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">sums</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span> <span class="n">sigstr</span> <span class="o">+</span> <span class="kr">if</span> <span class="n">y</span> <span class="o">==</span> <span class="mi">1</span> <span class="kr">then</span> <span class="n">sums</span> <span class="o">*</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="kr">else</span> <span class="p">(</span><span class="n">sums</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span><span class="p">))</span> <span class="kr">else</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">sums</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span> <span class="n">sigstr</span><span class="p">)</span>
        <span class="n">step</span> <span class="s">"noop"</span> <span class="kr">_</span> <span class="n">accum</span><span class="o">@</span><span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="n">sums</span><span class="p">,</span><span class="n">sigstr</span><span class="p">)</span> <span class="o">=</span> <span class="kr">if</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">20</span><span class="p">)</span> <span class="o">||</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">-</span> <span class="mi">20</span><span class="p">)</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">40</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="kr">then</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">sums</span><span class="p">,</span> <span class="n">sigstr</span> <span class="o">+</span> <span class="n">sums</span> <span class="o">*</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="kr">else</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">sums</span><span class="p">,</span> <span class="n">sigstr</span><span class="p">)</span>
        <span class="n">step</span> <span class="s">"addx"</span> <span class="n">x</span> <span class="n">accum</span><span class="o">@</span><span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="kr">_</span><span class="p">,</span><span class="kr">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">stepAddX</span> <span class="n">x</span> <span class="n">accum</span> <span class="p">(</span><span class="kr">if</span> <span class="n">odd</span> <span class="n">cycles</span> <span class="kr">then</span> <span class="mi">1</span> <span class="kr">else</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<p>Compiles fine, but gives nonsensical values. I’ll give you some time, figure out what may have went wrong here.</p>

<p>Have you thought about it yet?</p>

<p>Right, the reason why this doesn’t work, is because we’re talking about <code class="language-plaintext highlighter-rouge">20</code> and <code class="language-plaintext highlighter-rouge">-20 mod 40</code>, which is a step function. The key to this error is <code class="language-plaintext highlighter-rouge">foldr</code>, which <strong>processes elements starting from the last element</strong>. This costed me 3 hours, no joke.</p>

<p>So, the final code works once I changed <code class="language-plaintext highlighter-rouge">foldr</code> to <code class="language-plaintext highlighter-rouge">foldl</code>, which processes lists starting from the first element.</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">inputStr</span> <span class="o">=</span> <span class="s">""</span>

<span class="n">solution</span> <span class="o">::</span> <span class="kt">String</span> <span class="o">-&gt;</span> <span class="kt">Integer</span>
<span class="n">solution</span> <span class="n">input</span> <span class="o">=</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="kr">_</span><span class="p">,</span><span class="kr">_</span><span class="p">,</span><span class="n">z</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">z</span><span class="p">)</span> <span class="o">$</span> <span class="n">foldl</span> <span class="p">(</span><span class="nf">\</span><span class="n">accum</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">step</span> <span class="n">x</span> <span class="p">(</span><span class="kr">if</span> <span class="n">null</span> <span class="n">xs</span> <span class="kr">then</span> <span class="mi">0</span> <span class="kr">else</span> <span class="p">(</span><span class="n">read</span> <span class="o">$</span> <span class="n">head</span> <span class="n">xs</span><span class="p">))</span> <span class="n">accum</span><span class="p">)</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span> <span class="o">$</span> <span class="n">map</span> <span class="n">words</span> <span class="o">$</span> <span class="n">lines</span> <span class="n">input</span>
  <span class="kr">where</span>
        <span class="n">stepAddX</span> <span class="n">x</span> <span class="n">accum</span><span class="o">@</span><span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="n">sums</span><span class="p">,</span><span class="n">sigstr</span><span class="p">)</span> <span class="n">y</span> <span class="o">=</span> <span class="kr">if</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="mi">20</span><span class="p">)</span> <span class="o">||</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">20</span><span class="p">)</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">40</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="kr">then</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">sums</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span> <span class="n">sigstr</span> <span class="o">+</span> <span class="kr">if</span> <span class="n">y</span> <span class="o">==</span> <span class="mi">1</span> <span class="kr">then</span> <span class="n">sums</span> <span class="o">*</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="kr">else</span> <span class="p">(</span><span class="n">sums</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="n">y</span><span class="p">))</span> <span class="kr">else</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">sums</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span> <span class="n">sigstr</span><span class="p">)</span>
        <span class="n">step</span> <span class="s">"noop"</span> <span class="kr">_</span> <span class="n">accum</span><span class="o">@</span><span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="n">sums</span><span class="p">,</span><span class="n">sigstr</span><span class="p">)</span> <span class="o">=</span> <span class="kr">if</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">20</span><span class="p">)</span> <span class="o">||</span> <span class="p">((</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">-</span> <span class="mi">20</span><span class="p">)</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">40</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="kr">then</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">sums</span><span class="p">,</span> <span class="n">sigstr</span> <span class="o">+</span> <span class="n">sums</span> <span class="o">*</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="kr">else</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">sums</span><span class="p">,</span> <span class="n">sigstr</span><span class="p">)</span>
        <span class="n">step</span> <span class="s">"addx"</span> <span class="n">x</span> <span class="n">accum</span><span class="o">@</span><span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="kr">_</span><span class="p">,</span><span class="kr">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">stepAddX</span> <span class="n">x</span> <span class="n">accum</span> <span class="p">(</span><span class="kr">if</span> <span class="n">odd</span> <span class="n">cycles</span> <span class="kr">then</span> <span class="mi">1</span> <span class="kr">else</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-9">Part 2</h2>

<p>Each day’s part 2 is typically a quick edit of each day’s part 1. However, not for this particular sub-problem. By changing the purpose of the CPU instructions, I had to pretty much change my entire function definition.</p>

<p>Luckily for me, for the most part, <code class="language-plaintext highlighter-rouge">cycles</code> and <code class="language-plaintext highlighter-rouge">sums</code> still have the same concepts. Hence, the only thing I really needed to modify was <code class="language-plaintext highlighter-rouge">sigstr</code>, and how I render the output:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="nn">Data.List.Split</span> <span class="p">(</span><span class="nf">chunksOf</span><span class="p">)</span>

<span class="n">inputStr</span> <span class="o">=</span> <span class="s">""</span>

<span class="n">solution</span> <span class="o">::</span> <span class="kt">String</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
<span class="n">solution</span> <span class="n">input</span> <span class="o">=</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="kr">_</span><span class="p">,</span><span class="kr">_</span><span class="p">,</span><span class="n">z</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">chunksOf</span> <span class="mi">40</span> <span class="o">$</span> <span class="n">reverse</span> <span class="n">z</span><span class="p">)</span> <span class="o">$</span> <span class="n">foldl</span> <span class="p">(</span><span class="nf">\</span><span class="n">accum</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">step</span> <span class="n">x</span> <span class="p">(</span><span class="kr">if</span> <span class="n">null</span> <span class="n">xs</span> <span class="kr">then</span> <span class="mi">0</span> <span class="kr">else</span> <span class="p">(</span><span class="n">read</span> <span class="o">$</span> <span class="n">head</span> <span class="n">xs</span><span class="p">))</span> <span class="n">accum</span><span class="p">)</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="s">"#"</span><span class="p">)</span> <span class="o">$</span> <span class="n">map</span> <span class="n">words</span> <span class="o">$</span> <span class="n">lines</span> <span class="n">input</span>
  <span class="kr">where</span>
        <span class="n">isWithin</span> <span class="n">cycles</span> <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">cycles</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">40</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">cycles</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">40</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">x</span>
        <span class="n">step</span> <span class="s">"noop"</span> <span class="kr">_</span> <span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="n">lastx</span><span class="p">,</span><span class="n">result</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">lastx</span><span class="p">,</span> <span class="p">(</span><span class="kr">if</span> <span class="p">(</span><span class="n">isWithin</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="n">lastx</span><span class="p">)</span> <span class="kr">then</span> <span class="sc">'#'</span> <span class="kr">else</span> <span class="sc">'.'</span><span class="p">)</span> <span class="o">:</span> <span class="n">result</span><span class="p">)</span>
        <span class="n">step</span> <span class="s">"addx"</span> <span class="n">x</span> <span class="p">(</span><span class="n">cycles</span><span class="p">,</span><span class="n">lastx</span><span class="p">,</span><span class="n">result</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">lastx</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span> <span class="p">(</span><span class="kr">if</span> <span class="n">isWithin</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">2</span><span class="p">)</span> <span class="p">(</span><span class="n">lastx</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="kr">then</span> <span class="sc">'#'</span> <span class="kr">else</span> <span class="sc">'.'</span><span class="p">)</span> <span class="o">:</span> <span class="p">(</span><span class="kr">if</span> <span class="n">isWithin</span> <span class="p">(</span><span class="n">cycles</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="n">lastx</span> <span class="kr">then</span> <span class="sc">'#'</span> <span class="kr">else</span> <span class="sc">'.'</span><span class="p">)</span> <span class="o">:</span> <span class="n">result</span><span class="p">)</span>
</code></pre></div></div>

<p>The answer would be a list of Strings, which I then manually copy and paste into a text editor to reformat into text that had any meaning to me.</p>

<hr />

<h1 id="day-11">Day 11</h1>

<p>I’ll be honest; this is the hardest part 2 yet. I solved part 2 instinctual, but it took a long time for me to figure out <em>why</em> my solution worked.</p>

<h2 id="part-1-10">Part 1</h2>

<p>Part 1 is quite simple; in simple programmer terms, we have some queues of items, and move the items around based on conditions that have its parameters changed based on the input.</p>

<p>Let’s deconstruct the problem a little bit more:</p>

<ul>
  <li>The condition parameters are:
    <ul>
      <li>the operator, which is either <code class="language-plaintext highlighter-rouge">+</code> or <code class="language-plaintext highlighter-rouge">*</code></li>
      <li>the operand, which is either a fixed integer, or <code class="language-plaintext highlighter-rouge">old</code>, which refers to the value of the item</li>
    </ul>
  </li>
  <li>Based on the condition being true/false, the item is redirected to another queue also defined by the input. e.g. If condition is true, send to queue 2. Else, send to queue 3.</li>
</ul>

<p>So, the sub-problems are:</p>

<ul>
  <li>Parse the input into blocks</li>
  <li>Extract the necessary information from each block: starting items, the operation, the operand, the test parameter, and the queues to send the item to depending on the condition</li>
  <li>For each round, for each block, send items to their new queues based on the condition</li>
  <li>Get the top two queues that processed the most items</li>
</ul>

<p>I decided to write my code with some level of structure this time round, because the implementation is slightly complicated compared to the past days.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">itertools</span> <span class="kn">import</span> <span class="n">islice</span>
<span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="nb">reduce</span>

<span class="k">class</span> <span class="nc">Monkey</span><span class="p">:</span>
  <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="n">items_inspected</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">self</span><span class="p">.</span><span class="nf">parse_block</span><span class="p">(</span><span class="n">block</span><span class="p">)</span>
  
  <span class="k">def</span> <span class="nf">parse_block</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="nb">id</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">][:</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">self</span><span class="p">.</span><span class="n">items</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s"> ,</span><span class="sh">'</span><span class="p">)))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">block</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]]</span>
    <span class="n">self</span><span class="p">.</span><span class="n">operation</span> <span class="o">=</span> <span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="p">)</span> <span class="k">if</span> <span class="n">block</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">*</span><span class="sh">'</span> <span class="nf">else </span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">is_mult</span> <span class="o">=</span> <span class="n">block</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">*</span><span class="sh">'</span>
    <span class="n">self</span><span class="p">.</span><span class="n">operand</span> <span class="o">=</span> <span class="n">block</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">5</span><span class="p">]</span>
    <span class="n">self</span><span class="p">.</span><span class="n">test</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">3</span><span class="p">])</span>
    <span class="n">self</span><span class="p">.</span><span class="n">true_result</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">5</span><span class="p">])</span>
    <span class="n">self</span><span class="p">.</span><span class="n">false_result</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">5</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">5</span><span class="p">])</span>
  
  <span class="k">def</span> <span class="nf">throw_items</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">monkeys</span><span class="p">):</span>
    <span class="k">while</span> <span class="ow">not</span> <span class="n">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
      <span class="n">item</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>
      <span class="n">worry</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">operation</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">item</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">operand</span> <span class="o">==</span> <span class="sh">'</span><span class="s">old</span><span class="sh">'</span> <span class="k">else</span> <span class="nf">int</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">operand</span><span class="p">))</span> <span class="o">//</span> <span class="mi">3</span> 
      <span class="n">monkeys</span><span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">true_result</span> <span class="k">if</span> <span class="n">worry</span> <span class="o">%</span> <span class="n">self</span><span class="p">.</span><span class="n">test</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">self</span><span class="p">.</span><span class="n">false_result</span><span class="p">].</span><span class="n">items</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">worry</span><span class="p">)</span>
      <span class="n">self</span><span class="p">.</span><span class="n">items_inspected</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="k">def</span> <span class="nf">processor</span><span class="p">(</span><span class="n">monkeys</span><span class="p">,</span> <span class="n">target_rounds</span><span class="p">):</span>
  <span class="k">for</span> <span class="n">n_rounds</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">target_rounds</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">monkey</span> <span class="ow">in</span> <span class="n">monkeys</span><span class="p">:</span>
      <span class="n">monkey</span><span class="p">.</span><span class="nf">throw_items</span><span class="p">(</span><span class="n">monkeys</span><span class="p">)</span>
  
  <span class="n">best_two</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">islice</span><span class="p">(</span><span class="nf">sorted</span><span class="p">(</span><span class="n">monkeys</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">.</span><span class="n">items_inspected</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span> <span class="mi">2</span><span class="p">))</span>
  <span class="k">return</span> <span class="n">best_two</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">items_inspected</span> <span class="o">*</span> <span class="n">best_two</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">items_inspected</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">'</span><span class="s">__main__</span><span class="sh">'</span><span class="p">:</span>
  <span class="n">lines</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()</span>
  <span class="n">blocks</span> <span class="o">=</span> <span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">accum</span><span class="p">,</span> <span class="n">line</span><span class="p">:</span> <span class="n">accum</span> <span class="o">+</span> <span class="p">[[]]</span> <span class="k">if</span> <span class="n">line</span> <span class="o">==</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span> <span class="k">else</span> <span class="n">accum</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">accum</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">()]],</span> <span class="n">lines</span><span class="p">,</span> <span class="p">[[]])</span>
  <span class="n">monkeys</span> <span class="o">=</span> <span class="p">[</span><span class="nc">Monkey</span><span class="p">(</span><span class="n">block</span><span class="p">)</span> <span class="k">for</span> <span class="n">block</span> <span class="ow">in</span> <span class="n">blocks</span><span class="p">]</span>

  <span class="nf">print</span><span class="p">(</span><span class="nf">processor</span><span class="p">(</span><span class="n">monkeys</span><span class="p">,</span> <span class="mi">20</span><span class="p">))</span>
</code></pre></div></div>

<h2 id="part-2-10">Part 2</h2>

<p>In this part, the condition was changed to no longer include the <code class="language-plaintext highlighter-rouge">// 3</code>, meaning that the numbers grew out of proportion, especially when we want 10000 rounds. In Python, large integers, although take time to function, and hence, the program will take too long to complete.</p>

<p>Hence, part 2’s prompt suggested that we find a better way to represent the <code class="language-plaintext highlighter-rouge">worry</code> variable. I went to inspect the counts of the queue at the end of 10, 20 and 30 rounds; even though there is some correlation in the rate of change of counts, it is not strictly linear. This is because the operations are different; inspect the input:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Monkey 0:
  Starting items: 79, 98
  Operation: new = old * 19
  Test: divisible by 23
    If true: throw to monkey 2
    If false: throw to monkey 3

Monkey 1:
  Starting items: 54, 65, 75, 74
  Operation: new = old + 6
  Test: divisible by 19
    If true: throw to monkey 2
    If false: throw to monkey 0

Monkey 2:
  Starting items: 79, 60, 97
  Operation: new = old * old
  Test: divisible by 13
    If true: throw to monkey 1
    If false: throw to monkey 3

Monkey 3:
  Starting items: 74
  Operation: new = old + 3
  Test: divisible by 17
    If true: throw to monkey 0
    If false: throw to monkey 1
</code></pre></div></div>

<p>There is a high probability that a value will go through queues 0, 3, and 1, but a probability still exists that it will go through queue 2, which affects the final queue count. Hence, attempting to map the queue count linearly is not viable.</p>

<p>The next thing I looked at was the input. I tried to think about how the operations will affect the divisibility of the items and concluded (after 30 minutes of thinking) that there is no fixed pattern, due addition. If all operations were multiplications, then the story would be different; we would be able to definitively tell if a number will be divisible by the condition the first time we look at the item, or the operand.</p>

<p>The next observation I made was that each test was relatively constant; they are always in the format: <code class="language-plaintext highlighter-rouge">divisible by &lt;prime number&gt;</code>. For a moment, I thought of some math, like “how would I know if 2^x + 3^y = 7n, where x, y, n are natural numbers?” -&gt; the answer is I have no idea.</p>

<p>Then, my instincts took over and I just replaced <code class="language-plaintext highlighter-rouge">// 3</code> with <code class="language-plaintext highlighter-rouge">mod (sum of all test prime numbers in the input)</code> and ran the script on the input without blinking twice. To my surprise, it worked; it was one of those situations where my instincts completed its processes far ahead of the capabilities of my logical thinking.</p>

<p>The code change was one of those that looks insignificant (it literally replaces 4 characters with a modulo), but had a few hours of effort put into it.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">queue</span> <span class="kn">import</span> <span class="n">Queue</span>
<span class="kn">from</span> <span class="n">itertools</span> <span class="kn">import</span> <span class="n">islice</span>
<span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="nb">reduce</span>

<span class="k">class</span> <span class="nc">Monkey</span><span class="p">:</span>
  <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="n">items_inspected</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">self</span><span class="p">.</span><span class="nf">parse_block</span><span class="p">(</span><span class="n">block</span><span class="p">)</span>

  <span class="k">def</span> <span class="nf">parse_block</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
    <span class="n">self</span><span class="p">.</span><span class="nb">id</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">][:</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">self</span><span class="p">.</span><span class="n">items</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>
    <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s"> ,</span><span class="sh">'</span><span class="p">)))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">block</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]]</span>
    <span class="n">self</span><span class="p">.</span><span class="n">operation</span> <span class="o">=</span> <span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="p">)</span> <span class="k">if</span> <span class="n">block</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">*</span><span class="sh">'</span> <span class="nf">else </span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">:</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">is_mult</span> <span class="o">=</span> <span class="n">block</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">*</span><span class="sh">'</span>
    <span class="n">self</span><span class="p">.</span><span class="n">operand</span> <span class="o">=</span> <span class="n">block</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">5</span><span class="p">]</span>
    <span class="n">self</span><span class="p">.</span><span class="n">test</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">3</span><span class="p">])</span>
    <span class="n">self</span><span class="p">.</span><span class="n">true_result</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">5</span><span class="p">])</span>
    <span class="n">self</span><span class="p">.</span><span class="n">false_result</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">block</span><span class="p">[</span><span class="mi">5</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)[</span><span class="mi">5</span><span class="p">])</span>

  <span class="k">def</span> <span class="nf">throw_items</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">monkeys</span><span class="p">):</span>
    <span class="k">while</span> <span class="ow">not</span> <span class="n">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
      <span class="n">item</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>
      <span class="n">worry</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">operation</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">item</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">operand</span> <span class="o">==</span> <span class="sh">'</span><span class="s">old</span><span class="sh">'</span> <span class="k">else</span> <span class="nf">int</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">operand</span><span class="p">))</span> <span class="o">%</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="mi">17</span> <span class="o">*</span> <span class="mi">7</span> <span class="o">*</span> <span class="mi">11</span> <span class="o">*</span> <span class="mi">19</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">*</span> <span class="mi">13</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span>
      <span class="n">monkeys</span><span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">true_result</span> <span class="k">if</span> <span class="n">worry</span> <span class="o">%</span> <span class="n">self</span><span class="p">.</span><span class="n">test</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="n">self</span><span class="p">.</span><span class="n">false_result</span><span class="p">].</span><span class="n">items</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">worry</span><span class="p">)</span>
      <span class="n">self</span><span class="p">.</span><span class="n">items_inspected</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="k">def</span> <span class="nf">processor</span><span class="p">(</span><span class="n">monkeys</span><span class="p">,</span> <span class="n">target_rounds</span><span class="p">):</span>
  <span class="k">for</span> <span class="n">n_rounds</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">target_rounds</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">monkey</span> <span class="ow">in</span> <span class="n">monkeys</span><span class="p">:</span>
      <span class="n">monkey</span><span class="p">.</span><span class="nf">throw_items</span><span class="p">(</span><span class="n">monkeys</span><span class="p">)</span>

  <span class="n">best_two</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">islice</span><span class="p">(</span><span class="nf">sorted</span><span class="p">(</span><span class="n">monkeys</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">.</span><span class="n">items_inspected</span><span class="p">,</span> <span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span> <span class="mi">2</span><span class="p">))</span>
  <span class="k">return</span> <span class="n">best_two</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">items_inspected</span> <span class="o">*</span> <span class="n">best_two</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">items_inspected</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">'</span><span class="s">__main__</span><span class="sh">'</span><span class="p">:</span>
  <span class="n">lines</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()</span>
  <span class="n">blocks</span> <span class="o">=</span> <span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">accum</span><span class="p">,</span> <span class="n">line</span><span class="p">:</span> <span class="n">accum</span> <span class="o">+</span> <span class="p">[[]]</span> <span class="k">if</span> <span class="n">line</span> <span class="o">==</span> <span class="sh">'</span><span class="se">\n</span><span class="sh">'</span> <span class="k">else</span> <span class="n">accum</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">accum</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">()]],</span> <span class="n">lines</span><span class="p">,</span> <span class="p">[[]])</span>
  <span class="n">monkeys</span> <span class="o">=</span> <span class="p">[</span><span class="nc">Monkey</span><span class="p">(</span><span class="n">block</span><span class="p">)</span> <span class="k">for</span> <span class="n">block</span> <span class="ow">in</span> <span class="n">blocks</span><span class="p">]</span>

  <span class="nf">print</span><span class="p">(</span><span class="nf">processor</span><span class="p">(</span><span class="n">monkeys</span><span class="p">,</span> <span class="mi">1000</span><span class="p">))</span>
</code></pre></div></div>

<p>After taking a shower, my logical thinking finally reached a conclusion.</p>

<p>Let’s break this down into a much simpler problem. Let’s say we have two test prime numbers, 2 and 3. There are 4 things that could possibly happen after applying the operation to our item’s value:</p>

<ol>
  <li>It’s divisible by 2 and not divisible by 3;</li>
  <li>It’s not divisible by 2 and divisible by 3;</li>
  <li>It’s divisible by 2 and divisible by 3;</li>
  <li>It’s not divisible by 2 and not divisible by 3.</li>
</ol>

<p>So, if we were to talk about the possible values of each of the bullet points:</p>

<ol>
  <li>[2, 4, 8, 10, etc]</li>
  <li>[3, 6, 9, 15, etc]</li>
  <li>[6, 12, 18, 24, etc]</li>
  <li>[1, 5, 7, 11, etc]</li>
</ol>

<p>Let’s think about all the numbers in their prime factors:</p>

<ol>
  <li>[2, 4, 2 * 3 + 2, 2 * 3 + 4, etc]</li>
  <li>[3, 6 + 0, 2 * 3 + 3, 2^2 * 3 + 3, etc]</li>
  <li>[2 * 3, 2^2 * 3, 2 * 3^2, 2^3 * 3^2, etc]</li>
  <li>[1, 5, 2 * 3 + 1, 2 * 3 + 5, etc]</li>
</ol>

<p>If we link this to our question, we realise that our these numbers are a combination of multiplication and addition. A further observation suggests that all numbers more than 6 can be broken down into <code class="language-plaintext highlighter-rouge">n = q * 6 + r</code>, where <code class="language-plaintext highlighter-rouge">n</code> is the original number, <code class="language-plaintext highlighter-rouge">q</code> is some number, and <code class="language-plaintext highlighter-rouge">r</code> is a number less than 6. We then realize that <code class="language-plaintext highlighter-rouge">r</code> is the remainder, and we also know that <code class="language-plaintext highlighter-rouge">n % 6 == r</code>.</p>

<p>We then realize that if we add a number, <code class="language-plaintext highlighter-rouge">m</code>, such that <code class="language-plaintext highlighter-rouge">n</code> is still not divisible by 6, and <code class="language-plaintext highlighter-rouge">r + m &lt; 6</code> then: <code class="language-plaintext highlighter-rouge">n + m = q * 6 + r + m</code>. Since <code class="language-plaintext highlighter-rouge">n + m</code> is not divisible by 6, then surely <code class="language-plaintext highlighter-rouge">r + m</code> is not divisible by 6. Likewise, for 2: <code class="language-plaintext highlighter-rouge">r + m &lt; 6</code>, then: <code class="language-plaintext highlighter-rouge">n + m = q * 6 + r + m</code>, since <code class="language-plaintext highlighter-rouge">n + m</code> is not divisible by 2, then surely <code class="language-plaintext highlighter-rouge">r + m</code> is not divisible by 2, and so on. This wouldn’t work if we try to test for divisibility by 7: <code class="language-plaintext highlighter-rouge">r + m &lt; 6</code> then: <code class="language-plaintext highlighter-rouge">n + m =/= q * 6 + r + m</code>, <code class="language-plaintext highlighter-rouge">r + m</code> not divisible by 7 (which is the case for all possible values of <code class="language-plaintext highlighter-rouge">r + m</code>, since <code class="language-plaintext highlighter-rouge">r + m</code> is within 0 to 6) does not necessarily mean <code class="language-plaintext highlighter-rouge">n + m</code> is not divisible by 7.</p>

<p>So, what this means is that any addition that does not make the expression immediately divisible by <strong><code class="language-plaintext highlighter-rouge">6</code> is added to the remainder</strong>, and we know that the <strong>modulo of the remainder is equal to the modulo of the original number</strong>. Since <code class="language-plaintext highlighter-rouge">6</code> can be broken down into the primes <code class="language-plaintext highlighter-rouge">2</code> and <code class="language-plaintext highlighter-rouge">3</code>, which are our test prime numbers, therefore, by performing modulo on all the test prime numbers within our input, we can fully express the divisibility of our number with any one of the primes just by maintaining the remainder.</p>

<p>Hence,</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="n">worry</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">operation</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">item</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">operand</span> <span class="o">==</span> <span class="sh">'</span><span class="s">old</span><span class="sh">'</span> <span class="k">else</span> <span class="nf">int</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">operand</span><span class="p">))</span> <span class="o">%</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="mi">17</span> <span class="o">*</span> <span class="mi">7</span> <span class="o">*</span> <span class="mi">11</span> <span class="o">*</span> <span class="mi">19</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">*</span> <span class="mi">13</span> <span class="o">*</span> <span class="mi">3</span><span class="p">)</span>
</code></pre></div></div>

<p>must work (the prime numbers are the terms I’m too lazy to evaluate).</p>

<hr />

<h1 id="day-12">Day 12</h1>

<p>Today is quite obviously a path-finding challenge.</p>

<h2 id="part-1-11">Part 1</h2>

<p>Admittedly, I spend an embarrassing amount of time figuring out that while I can only go up by one altitude unit at a time, I can actually descend more than 1 level at a time. I decided to use Breadth First Search to perform path-finding, since it’s good enough for the use case.</p>

<p>For every node I’ve visited, I replace it’s position with <code class="language-plaintext highlighter-rouge">#</code>, which denotes a visited node. So:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">grid</span> <span class="o">=</span> <span class="p">[[</span><span class="n">y</span> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">x</span><span class="p">.</span><span class="nf">strip</span><span class="p">()]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]</span>   
<span class="n">grid</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">a</span><span class="sh">'</span>
                                                                              
<span class="k">def</span> <span class="nf">bfs</span><span class="p">(</span><span class="n">pos</span><span class="p">):</span>
  <span class="n">q</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>                                                                 
  <span class="n">p</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>                                                                 
  <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span>
  
  <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>                                                                 
    <span class="k">while</span> <span class="ow">not</span> <span class="n">q</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>                                                      
      <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>                                                          
      <span class="n">elevation</span> <span class="o">=</span> <span class="sh">'</span><span class="s">a</span><span class="sh">'</span> <span class="k">if</span> <span class="n">grid</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">S</span><span class="sh">'</span> <span class="k">else</span> <span class="n">grid</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span>                    
      <span class="n">grid</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>                                                        
      <span class="n">moves</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]</span>                
      
      <span class="k">if</span> <span class="n">elevation</span> <span class="o">==</span> <span class="sh">'</span><span class="s">E</span><span class="sh">'</span><span class="p">:</span> 
        <span class="k">return</span> <span class="n">count</span>                                                          
      
      <span class="k">for</span> <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span> <span class="ow">in</span> <span class="n">moves</span><span class="p">:</span>                                              
        <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">new_x</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">new_y</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span> \             
          <span class="ow">and</span> <span class="n">grid</span><span class="p">[</span><span class="n">new_y</span><span class="p">][</span><span class="n">new_x</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span> \                                     
          <span class="ow">and</span> <span class="p">(</span><span class="o">-</span><span class="mi">999</span> <span class="o">&lt;=</span> <span class="nf">ord</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">new_y</span><span class="p">][</span><span class="n">new_x</span><span class="p">])</span> <span class="o">-</span> <span class="nf">ord</span><span class="p">(</span><span class="n">elevation</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span> \        
          <span class="ow">or</span> <span class="p">(</span><span class="n">elevation</span> <span class="o">==</span> <span class="sh">'</span><span class="s">z</span><span class="sh">'</span> <span class="ow">and</span> <span class="n">grid</span><span class="p">[</span><span class="n">new_y</span><span class="p">][</span><span class="n">new_x</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">E</span><span class="sh">'</span><span class="p">)):</span>               
            <span class="n">p</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">))</span>
          
    <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="n">q</span> <span class="o">=</span> <span class="n">p</span> 
    <span class="n">p</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>                                                               
    
<span class="nf">print</span><span class="p">(</span><span class="nf">bfs</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="mi">20</span><span class="p">)))</span>
</code></pre></div></div>

<p>It might be worth it to mention that <code class="language-plaintext highlighter-rouge">-999</code> is too large of a magnitude. <code class="language-plaintext highlighter-rouge">-2</code> would have been good enough; this means that I would be able to descend a maximum of <code class="language-plaintext highlighter-rouge">-2</code>. Experimental results for the win.</p>

<p>Also, if you think hard-coding the starting position is hacky, then you can look away.</p>

<h2 id="part-2-11">Part 2</h2>

<p>Part 2 requires us to find a better starting position, so that we minimize the amount of steps it takes to reach the peak, denoted by <code class="language-plaintext highlighter-rouge">E</code>. So, I first approached the problem the dumb way, which was to iterate through all positions of <code class="language-plaintext highlighter-rouge">a</code>, the lowest altitude, and accumulate the minimum.</p>

<p>Obviously, that was slow, so I thought about using another algorithm, like Dijkstra’s Shortest Path algorithm; however, there would be no benefit whatsoever over BFS since the weights of each nodes are the same.</p>

<p>Hence, I decided to perform a reverse BFS; instead of checking for <code class="language-plaintext highlighter-rouge">E</code>, I check for the closest <code class="language-plaintext highlighter-rouge">a</code>, given that we can instead ascend 2 levels and descend only 1 level (inverse of our ascending constraints).</p>

<p>So:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">queue</span> <span class="kn">import</span> <span class="n">Queue</span>                                                       

<span class="n">grid</span> <span class="o">=</span> <span class="p">[[</span><span class="n">y</span> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">x</span><span class="p">.</span><span class="nf">strip</span><span class="p">()]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]</span>

<span class="k">def</span> <span class="nf">bfs</span><span class="p">(</span><span class="n">pos</span><span class="p">):</span>
  <span class="n">q</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>
  <span class="n">p</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span> 
  <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span> 
      
  <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">while</span> <span class="bp">True</span><span class="p">:</span> 
    <span class="k">while</span> <span class="ow">not</span> <span class="n">q</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
      <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>
      <span class="n">elevation</span> <span class="o">=</span> <span class="sh">'</span><span class="s">z</span><span class="sh">'</span> <span class="k">if</span> <span class="n">grid</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">E</span><span class="sh">'</span> <span class="k">else</span> <span class="n">grid</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span>
      <span class="n">grid</span><span class="p">[</span><span class="n">y</span><span class="p">][</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
      <span class="n">moves</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]</span>
        
      <span class="k">if</span> <span class="n">elevation</span> <span class="o">==</span> <span class="sh">'</span><span class="s">a</span><span class="sh">'</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">count</span>
          
      <span class="k">for</span> <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span> <span class="ow">in</span> <span class="n">moves</span><span class="p">:</span>
        <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">new_x</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">new_y</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">grid</span><span class="p">)</span> \
          <span class="ow">and</span> <span class="n">grid</span><span class="p">[</span><span class="n">new_y</span><span class="p">][</span><span class="n">new_x</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span> \
          <span class="ow">and</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">&lt;=</span> <span class="nf">ord</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">new_y</span><span class="p">][</span><span class="n">new_x</span><span class="p">])</span> <span class="o">-</span> <span class="nf">ord</span><span class="p">(</span><span class="n">elevation</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">2</span> \
          <span class="ow">or</span> <span class="p">(</span><span class="n">elevation</span> <span class="o">==</span> <span class="sh">'</span><span class="s">a</span><span class="sh">'</span> <span class="ow">and</span> <span class="n">grid</span><span class="p">[</span><span class="n">new_y</span><span class="p">][</span><span class="n">new_x</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">S</span><span class="sh">'</span><span class="p">)):</span>
            <span class="n">p</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">))</span>
    <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="n">q</span> <span class="o">=</span> <span class="n">p</span>
    <span class="n">p</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">bfs</span><span class="p">((</span><span class="nf">len</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">-</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">20</span><span class="p">)))</span>
</code></pre></div></div>

<hr />

<h1 id="day-13">Day 13</h1>

<p>Nothing like spending 5 hours on Advent of Code, eh?</p>

<p>Felt a little down, so I decided to use good old C to do this. Little did I know, that was going to be a huge ordeal.</p>

<h2 id="part-1-12">Part 1</h2>

<p>This part was essentially about parsing. I may be able to summarize what I’ve essentially did here, but the process to get there is error-prone; I had to painstakingly debug the corner cases that occurred during my parsing.</p>

<p>In hindsight, it might have been a better idea to list all the possible corner cases before attempting the problem.</p>

<p>The input we are to parse can come in the following format:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[[[[3],[]],5],[[],[7,[3,3,3],2,[1],[6,7,9]],[],8,1],[9,[0,0,[5,3,5,1],[2],2],3],[2,[0,4]]]
[[[]],[[[],10,[8,0,5,5],[5,4,8,10,1],[6,8,0,3,5]],2,[9,[5],[9,2],[]],[8,[]]]]
</code></pre></div></div>

<p>Defining the first list as ‘a’, and the second list as ‘b’, if:</p>

<ul>
  <li>We are comparing two lists, then we compare elements in the two lists
    <ul>
      <li>If list ‘a’ terminates early (less elements than ‘b’), then the two lists are in order</li>
      <li>If list ‘b’ terminates early (less elements than ‘a’), then the two lists are not in order</li>
    </ul>
  </li>
  <li>We are comparing two values, then we just take the integers and directly compare them</li>
  <li>We are comparing a list and a value, in which we re-package the value as a singleton list, and attempt to compare them again.</li>
</ul>

<p>Sounds easy, but it was actually much more difficult than I imagined. I converted each comparison method above into their own function, and wrapped all three functions around a main function called “think” that decides which comparison method to choose based on the current tokens. I then confirmed that the list pairs are either greater, or less than one another. Hence, I was able to discard all thoughts related to equality.</p>

<p>Now, time to think about each case step by step, which I only thought was a good idea in hindsight. Let’s say the current character in ‘a’ and ‘b’ are ‘x’ and ‘y’:</p>

<ol>
  <li>If ‘x’ and ‘y’ are ‘[’ then we use the list comparison method</li>
  <li>If ‘x’ and ‘y’ does not have any list-related symbols (‘[’ and ‘]’), then we use the value comparison method</li>
  <li>Else:
    <ol>
      <li>If ‘x’ denotes the end of the list and ‘y’ is a value, we compare the number of lists open in ‘a’ and ‘b’ at the moment, and return 1 or -1 if they are not the same. Otherwise, we get the successor of x, and start from step 1 again. This allows us to reach a point where we can compare two values, or return early if the list sizes assert that they’re unequal.</li>
      <li>If ‘x’ is a value and ‘y’ denotes the end of the list, we compare the number of lists open in ‘a’ and ‘b’ at the moment, and return 1 or -1 if they are not the same value. Otherwise, we get the successor of y, and start from step 1 again.</li>
      <li>If both ‘x’ and ‘y’ denotes the end of the list, we compare the number of lists open in ‘a’ and ‘b’ just in case, and gets the successor of both ‘x’ and ‘y’, repeating step 1.</li>
    </ol>
  </li>
  <li>Else, if we can tell that ‘x’ is a value while ‘y’ is a list, we use the re-packaging comparison method</li>
  <li>Else, if we can tell that ‘x’ is a list while ‘y’ is a value, we use the re-packaging comparison method, but we negate the value we acquire from the method.</li>
</ol>

<p>Embarrasingly enough, it took me a long time to figure out that two digit numbers exist within our problem-space; I’ve been comparing ASCII for a few hours not knowing why my solution didn’t work.</p>

<p>With the steps described above, it becomes possible to define a recursive function that steps through the list, building kinda like a syntax tree on the stack:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">comparevaluethenlist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">comparevalue</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">comparelist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">think</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>

<span class="kt">int</span> <span class="nf">comparevaluethenlist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">l_levels</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">r_levels</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">c</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">think</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">'['</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="n">comparelist</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">res</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="o">||</span> <span class="n">res</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">comparevalue</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">l_levels</span><span class="o">--</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&lt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&gt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">']'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">r_levels</span><span class="o">--</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&lt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&gt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">']'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">l_levels</span><span class="o">--</span><span class="p">;</span>
    <span class="n">r_levels</span><span class="o">--</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&lt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&gt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span>
      <span class="k">return</span> <span class="n">comparevaluethenlist</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span>
      <span class="k">return</span> <span class="o">-</span><span class="n">comparevaluethenlist</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">comparevalue</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>

  <span class="kt">char</span> <span class="n">numBufA</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">numBufB</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">tokA_com</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="sc">','</span><span class="p">),</span> <span class="o">*</span><span class="n">tokA_brac</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="sc">']'</span><span class="p">),</span> 
    <span class="o">*</span><span class="n">tokB_com</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="sc">','</span><span class="p">),</span> <span class="o">*</span><span class="n">tokB_brac</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="sc">']'</span><span class="p">);</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">tokA</span> <span class="o">=</span> <span class="p">(</span><span class="n">tokA_com</span> <span class="o">&lt;</span> <span class="n">tokA_brac</span> <span class="o">&amp;&amp;</span> <span class="n">tokA_com</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">?</span> <span class="n">tokA_com</span> <span class="o">:</span> <span class="n">tokA_brac</span><span class="p">;</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">tokB</span> <span class="o">=</span> <span class="p">(</span><span class="n">tokB_com</span> <span class="o">&lt;</span> <span class="n">tokB_brac</span> <span class="o">&amp;&amp;</span> <span class="n">tokB_com</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">?</span> <span class="n">tokB_com</span> <span class="o">:</span> <span class="n">tokB_brac</span><span class="p">;</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">numBufA</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">tokA</span> <span class="o">-</span> <span class="n">a</span><span class="p">);</span>
  <span class="n">numBufA</span><span class="p">[</span><span class="n">tokA</span> <span class="o">-</span> <span class="n">a</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>

  <span class="n">strncpy</span><span class="p">(</span><span class="n">numBufB</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">tokB</span> <span class="o">-</span> <span class="n">b</span><span class="p">);</span>
  <span class="n">numBufB</span><span class="p">[</span><span class="n">tokB</span> <span class="o">-</span> <span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">a_i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">b_i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">a_i</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">numBufA</span><span class="p">);</span>
  <span class="n">b_i</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">numBufB</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">a_i</span> <span class="o">&gt;</span> <span class="n">b_i</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">a_i</span> <span class="o">&lt;</span> <span class="n">b_i</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="n">a</span> <span class="o">+=</span> <span class="n">tokA</span> <span class="o">-</span> <span class="n">a</span><span class="p">;</span>
  <span class="n">b</span> <span class="o">+=</span> <span class="n">tokB</span> <span class="o">-</span> <span class="n">b</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">','</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>

  <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">comparelist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">l_levels</span><span class="o">++</span><span class="p">;</span>
  <span class="n">r_levels</span><span class="o">++</span><span class="p">;</span>
  <span class="n">a</span><span class="o">++</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>

  <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="p">}</span>


<span class="kt">int</span> <span class="nf">parse</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">line1</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">line2</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">comparelist</span><span class="p">(</span><span class="n">line1</span><span class="p">,</span> <span class="n">line2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">accum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;;</span>
  <span class="kt">char</span> <span class="n">line1</span><span class="p">[</span><span class="mi">1000</span><span class="p">],</span> <span class="n">line2</span><span class="p">[</span><span class="mi">1000</span><span class="p">];</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"input.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="k">do</span> <span class="p">{</span>
    <span class="n">count</span><span class="o">++</span><span class="p">;</span>
    <span class="n">fscanf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">line1</span><span class="p">);</span>
    <span class="n">fscanf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">line2</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="n">parse</span><span class="p">(</span><span class="n">line1</span><span class="p">,</span> <span class="n">line2</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">val</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">accum</span> <span class="o">+=</span> <span class="n">count</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">feof</span><span class="p">(</span><span class="n">f</span><span class="p">));</span>
  <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"Result: %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">accum</span><span class="p">);</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After some hours of debugging, I also had to introduce <code class="language-plaintext highlighter-rouge">c</code> to maintain information that we are currently within a list that has been <em>upgraded</em> from a value for the sake of comparison, so that we can return early upon encountering a <code class="language-plaintext highlighter-rouge">,</code>. This has by far the most corner cases in this problem.</p>

<h2 id="part-2-12">Part 2</h2>

<p>Part 2 repurposes the <code class="language-plaintext highlighter-rouge">think</code> function into a binary comparison function. Luckily, I have already defined <code class="language-plaintext highlighter-rouge">think</code> to return values required by the <code class="language-plaintext highlighter-rouge">qsort</code> standard library function, so I simply used that, and appended <code class="language-plaintext highlighter-rouge">[[2]]</code> and <code class="language-plaintext highlighter-rouge">[[6]]</code> into the <code class="language-plaintext highlighter-rouge">input.txt</code> file, and multiplied their indices after sorting to acquire the final solution:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">comparevaluethenlist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">comparevalue</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">comparelist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">think</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span>

<span class="kt">int</span> <span class="nf">comparevaluethenlist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">l_levels</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">r_levels</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">c</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">think</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">'['</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">res</span> <span class="o">=</span> <span class="n">comparelist</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">res</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="o">||</span> <span class="n">res</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">comparevalue</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">l_levels</span><span class="o">--</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&lt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&gt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">']'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">r_levels</span><span class="o">--</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&lt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&gt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">']'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">']'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">l_levels</span><span class="o">--</span><span class="p">;</span>
    <span class="n">r_levels</span><span class="o">--</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&lt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">l_levels</span> <span class="o">&gt;</span> <span class="n">r_levels</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

    <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span>
      <span class="k">return</span> <span class="n">comparevaluethenlist</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">'['</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">']'</span><span class="p">)</span>
      <span class="k">return</span> <span class="o">-</span><span class="n">comparevaluethenlist</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">comparevalue</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">char</span> <span class="n">numBufA</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">numBufB</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">tokA_com</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="sc">','</span><span class="p">),</span> <span class="o">*</span><span class="n">tokA_brac</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="sc">']'</span><span class="p">),</span> 
    <span class="o">*</span><span class="n">tokB_com</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="sc">','</span><span class="p">),</span> <span class="o">*</span><span class="n">tokB_brac</span> <span class="o">=</span> <span class="n">strchr</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="sc">']'</span><span class="p">);</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">tokA</span> <span class="o">=</span> <span class="p">(</span><span class="n">tokA_com</span> <span class="o">&lt;</span> <span class="n">tokA_brac</span> <span class="o">&amp;&amp;</span> <span class="n">tokA_com</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">?</span> <span class="n">tokA_com</span> <span class="o">:</span> <span class="n">tokA_brac</span><span class="p">;</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">tokB</span> <span class="o">=</span> <span class="p">(</span><span class="n">tokB_com</span> <span class="o">&lt;</span> <span class="n">tokB_brac</span> <span class="o">&amp;&amp;</span> <span class="n">tokB_com</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">?</span> <span class="n">tokB_com</span> <span class="o">:</span> <span class="n">tokB_brac</span><span class="p">;</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">numBufA</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">tokA</span> <span class="o">-</span> <span class="n">a</span><span class="p">);</span>
  <span class="n">numBufA</span><span class="p">[</span><span class="n">tokA</span> <span class="o">-</span> <span class="n">a</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>

  <span class="n">strncpy</span><span class="p">(</span><span class="n">numBufB</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">tokB</span> <span class="o">-</span> <span class="n">b</span><span class="p">);</span>
  <span class="n">numBufB</span><span class="p">[</span><span class="n">tokB</span> <span class="o">-</span> <span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">a_i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">b_i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">a_i</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">numBufA</span><span class="p">);</span>
  <span class="n">b_i</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">numBufB</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">a_i</span> <span class="o">&gt;</span> <span class="n">b_i</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">a_i</span> <span class="o">&lt;</span> <span class="n">b_i</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="n">a</span> <span class="o">+=</span> <span class="n">tokA</span> <span class="o">-</span> <span class="n">a</span><span class="p">;</span>
  <span class="n">b</span> <span class="o">+=</span> <span class="n">tokB</span> <span class="o">-</span> <span class="n">b</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">!=</span> <span class="sc">','</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>

  <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">comparelist</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">a</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">b</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">l_levels</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">r_levels</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">l_levels</span><span class="o">++</span><span class="p">;</span>
  <span class="n">r_levels</span><span class="o">++</span><span class="p">;</span>
  <span class="n">a</span><span class="o">++</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">a</span><span class="o">++</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">==</span> <span class="sc">','</span><span class="p">)</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>

  <span class="k">return</span> <span class="n">think</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">l_levels</span><span class="p">,</span> <span class="n">r_levels</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">comparison</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span><span class="o">*</span> <span class="n">line1</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span><span class="o">*</span> <span class="n">line2</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">comparelist</span><span class="p">((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span> <span class="n">line1</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span> <span class="n">line2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">char</span> <span class="n">lines</span><span class="p">[</span><span class="mi">1000</span><span class="p">][</span><span class="mi">1000</span><span class="p">];</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"input.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
  <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">feof</span><span class="p">(</span><span class="n">f</span><span class="p">))</span>
    <span class="n">fscanf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">lines</span><span class="p">[</span><span class="n">count</span><span class="o">++</span><span class="p">]);</span>
  <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>

  <span class="n">qsort</span><span class="p">(</span><span class="n">lines</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="mi">1000</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">),</span> <span class="n">comparison</span><span class="p">);</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">strcmp</span><span class="p">(</span><span class="n">lines</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="s">"[[2]]"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
      <span class="n">result</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">strcmp</span><span class="p">(</span><span class="n">lines</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="s">"[[6]]"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
      <span class="n">result</span> <span class="o">*=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"Result: %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<hr />

<h1 id="day-14">Day 14</h1>

<p>Bury me in sand, please.</p>

<h2 id="part-1-13">Part 1</h2>

<p>Today’s problem involved the following sub-problems:</p>

<ol>
  <li>Drawing lines on a grid;</li>
  <li>Simulating the behaviour of sand particles, where:
    <ol>
      <li>If it can go down, it goes down</li>
      <li>If it can’t go down, but can go bottom left, do that</li>
      <li>If it can’t go down, but can go bottom right, do that</li>
      <li>If it can’t go anywhere, settle the sand and move on to the next sand</li>
    </ol>
  </li>
</ol>

<p>What about the size of the grid? Well, since our input is fixed, we really don’t have to figure that out; just guess a large enough size, I’m sure that won’t come back to bite me in the future :new_moon_with_face:. The first sub-problem was easily solved like so:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">grid</span> <span class="o">=</span> <span class="p">[[</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">600</span><span class="p">)]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">200</span><span class="p">)]</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">line</span><span class="p">:</span>
      <span class="n">xys</span> <span class="o">=</span> <span class="p">[</span><span class="nf">tuple</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">y</span><span class="p">:</span> <span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">),</span> <span class="n">x</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">)))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">-&gt;</span><span class="sh">'</span><span class="p">]</span>
      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">xys</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
        <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">xys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">xys</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
        <span class="k">while</span> <span class="nf">abs</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="n">x2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
          <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
          <span class="n">x1</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="n">x1</span> <span class="o">&gt;</span> <span class="n">x2</span> <span class="k">else</span> <span class="mi">1</span>

        <span class="k">while</span> <span class="nf">abs</span><span class="p">(</span><span class="n">y1</span> <span class="o">-</span> <span class="n">y2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
          <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
          <span class="n">y1</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="n">y1</span> <span class="o">&gt;</span> <span class="n">y2</span> <span class="k">else</span> <span class="mi">1</span>
      
        <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
</code></pre></div></div>

<p>The input looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>498,4 -&gt; 498,6 -&gt; 496,6
503,4 -&gt; 502,4 -&gt; 502,9 -&gt; 494,9
</code></pre></div></div>

<p>So, when parsing each line, we need to strip spaces, filter out <code class="language-plaintext highlighter-rouge">-&gt;</code>, and split the resultant string by <code class="language-plaintext highlighter-rouge">,</code>. We also want to convert each list of strings into a tuple of integers, so we also do that in the same line.</p>

<p>For each adjacent <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code>, we attempt to draw the walls that will affect sand interactions.</p>

<p>To solve the next sub-problem, we convert the behavior in to a bunch of if statements, and keep looping until one grain of sand enters the void, defined by anything falling out of <code class="language-plaintext highlighter-rouge">y = 200</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">voided</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">settled_grains</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">voided</span><span class="p">:</span>
  <span class="n">grain_x</span><span class="p">,</span> <span class="n">grain_y</span> <span class="o">=</span> <span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="n">is_occupied</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">==</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span> <span class="ow">or</span> <span class="n">x</span> <span class="o">==</span> <span class="sh">'</span><span class="s">+</span><span class="sh">'</span>
  <span class="n">settled</span> <span class="o">=</span> <span class="bp">False</span>
  <span class="k">while</span> <span class="ow">not</span> <span class="n">settled</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">200</span><span class="p">:</span>
      <span class="n">voided</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="k">break</span>
    <span class="k">elif</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]):</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">grain_x</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]):</span>
      <span class="n">grain_x</span> <span class="o">-=</span> <span class="mi">1</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">grain_x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="mi">600</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]):</span>
      <span class="n">grain_x</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">settled</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">+</span><span class="sh">'</span>

  <span class="k">if</span> <span class="ow">not</span> <span class="n">voided</span><span class="p">:</span>
    <span class="n">settled_grains</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>

<p>Piecing it all together:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">grid</span> <span class="o">=</span> <span class="p">[[</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">600</span><span class="p">)]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">200</span><span class="p">)]</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">line</span><span class="p">:</span>
      <span class="n">xys</span> <span class="o">=</span> <span class="p">[</span><span class="nf">tuple</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">y</span><span class="p">:</span> <span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">),</span> <span class="n">x</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">)))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">-&gt;</span><span class="sh">'</span><span class="p">]</span>
      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">xys</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
        <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">xys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">xys</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
        <span class="k">while</span> <span class="nf">abs</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="n">x2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
          <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
          <span class="n">x1</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="n">x1</span> <span class="o">&gt;</span> <span class="n">x2</span> <span class="k">else</span> <span class="mi">1</span>

        <span class="k">while</span> <span class="nf">abs</span><span class="p">(</span><span class="n">y1</span> <span class="o">-</span> <span class="n">y2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
          <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
          <span class="n">y1</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="n">y1</span> <span class="o">&gt;</span> <span class="n">y2</span> <span class="k">else</span> <span class="mi">1</span>

        <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>

<span class="n">voided</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">settled_grains</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">voided</span><span class="p">:</span>
  <span class="n">grain_x</span><span class="p">,</span> <span class="n">grain_y</span> <span class="o">=</span> <span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="n">is_occupied</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">==</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span> <span class="ow">or</span> <span class="n">x</span> <span class="o">==</span> <span class="sh">'</span><span class="s">+</span><span class="sh">'</span>
  <span class="n">settled</span> <span class="o">=</span> <span class="bp">False</span>
  <span class="k">while</span> <span class="ow">not</span> <span class="n">settled</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">200</span><span class="p">:</span>
      <span class="n">voided</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="k">break</span>
    <span class="k">elif</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]):</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">grain_x</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]):</span>
      <span class="n">grain_x</span> <span class="o">-=</span> <span class="mi">1</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">grain_x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="mi">600</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]):</span>
      <span class="n">grain_x</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">settled</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">+</span><span class="sh">'</span>

  <span class="k">if</span> <span class="ow">not</span> <span class="n">voided</span><span class="p">:</span>
    <span class="n">settled_grains</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="nf">print</span><span class="p">(</span><span class="n">settled_grains</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-13">Part 2</h2>

<p>In this part, we realize that the void doesn’t exist (damn it, there goes one option). Instead, there is an infinite floor at <code class="language-plaintext highlighter-rouge">max_y + 2</code>, where <code class="language-plaintext highlighter-rouge">max_y</code> is the largest <code class="language-plaintext highlighter-rouge">y</code> found while parsing the lines.</p>

<p>Luckily for me, that was simple to do; we just store the maximum <code class="language-plaintext highlighter-rouge">y</code> every time we see one:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="n">highest_y</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">y1</span><span class="p">,</span> <span class="n">y2</span><span class="p">,</span> <span class="n">highest_y</span><span class="p">)</span>
</code></pre></div></div>

<p>Then, after reading the entire input, we just fill that <code class="language-plaintext highlighter-rouge">y</code> with the floor symbol:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">grid</span><span class="p">[</span><span class="n">highest_y</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="sh">'</span><span class="s">#</span><span class="sh">'</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">600</span><span class="p">)]</span>
</code></pre></div></div>

<p>Next, our stop condition has changed to sand particles settling at <code class="language-plaintext highlighter-rouge">(500, 0)</code>, meaning that the generator of sand particles will subsequently be blocked.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">else</span><span class="p">:</span>
      <span class="n">settled</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">o</span><span class="sh">'</span>
      <span class="nf">if </span><span class="p">(</span><span class="n">grain_x</span><span class="p">,</span> <span class="n">grain_y</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span>
        <span class="n">settled_grains</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="n">stop</span> <span class="o">=</span> <span class="bp">True</span>
        <span class="k">break</span>
</code></pre></div></div>

<p>However, all these changes weren’t enough, as I was greeted by the “wrong answer” prompt on AOC. Turns out, due to the floor, the sand particles tend to create large pyramids. This means that there is a large base, which can’t fit into our grid. Incidentally, I decided to re-assign settled grains as <code class="language-plaintext highlighter-rouge">'o'</code>, to differentiate between falling grains and settled grains.</p>

<p>Luckily, since we know our sand particles are generated from <code class="language-plaintext highlighter-rouge">(500, 0)</code>, we know for sure that the maximum <code class="language-plaintext highlighter-rouge">x</code> is somewhere around <code class="language-plaintext highlighter-rouge">750</code> due to how equilateral triangles work. To be safe, we increase the grid size all the way to <code class="language-plaintext highlighter-rouge">1000</code>. So, the final code looks like this.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">grid</span> <span class="o">=</span> <span class="p">[[</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">)]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">200</span><span class="p">)]</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
  <span class="n">highest_y</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">line</span><span class="p">:</span>
      <span class="n">xys</span> <span class="o">=</span> <span class="p">[</span><span class="nf">tuple</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">y</span><span class="p">:</span> <span class="nf">int</span><span class="p">(</span><span class="n">y</span><span class="p">),</span> <span class="n">x</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">)))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">-&gt;</span><span class="sh">'</span><span class="p">]</span>
      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">xys</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
        <span class="n">x1</span><span class="p">,</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">xys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">x2</span><span class="p">,</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">xys</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
        <span class="n">highest_y</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">y1</span><span class="p">,</span> <span class="n">y2</span><span class="p">,</span> <span class="n">highest_y</span><span class="p">)</span>
        <span class="k">while</span> <span class="nf">abs</span><span class="p">(</span><span class="n">x1</span> <span class="o">-</span> <span class="n">x2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
          <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
          <span class="n">x1</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="n">x1</span> <span class="o">&gt;</span> <span class="n">x2</span> <span class="k">else</span> <span class="mi">1</span>

        <span class="k">while</span> <span class="nf">abs</span><span class="p">(</span><span class="n">y1</span> <span class="o">-</span> <span class="n">y2</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
          <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
          <span class="n">y1</span> <span class="o">+=</span> <span class="o">-</span><span class="mi">1</span> <span class="k">if</span> <span class="n">y1</span> <span class="o">&gt;</span> <span class="n">y2</span> <span class="k">else</span> <span class="mi">1</span>

        <span class="n">grid</span><span class="p">[</span><span class="n">y1</span><span class="p">][</span><span class="n">x1</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
<span class="n">grid</span><span class="p">[</span><span class="n">highest_y</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="sh">'</span><span class="s">#</span><span class="sh">'</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">)]</span>

<span class="n">stop</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">settled_grains</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">stop</span><span class="p">:</span>
  <span class="n">grain_x</span><span class="p">,</span> <span class="n">grain_y</span> <span class="o">=</span> <span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
  <span class="n">is_occupied</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">==</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span> <span class="ow">or</span> <span class="n">x</span> <span class="o">==</span> <span class="sh">'</span><span class="s">o</span><span class="sh">'</span>
  <span class="n">settled</span> <span class="o">=</span> <span class="bp">False</span>
  <span class="k">while</span> <span class="ow">not</span> <span class="n">settled</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">200</span><span class="p">:</span>
      <span class="n">stop</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="k">break</span>
    <span class="k">elif</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]):</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">grain_x</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]):</span>
      <span class="n">grain_x</span> <span class="o">-=</span> <span class="mi">1</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">grain_x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="mi">1000</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nf">is_occupied</span><span class="p">(</span><span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">grain_x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]):</span> <span class="c1">#and not is_occupied(grid[grain_y][grain_x + 1]):
</span>      <span class="n">grain_x</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="n">grain_y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">settled</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="n">grid</span><span class="p">[</span><span class="n">grain_y</span><span class="p">][</span><span class="n">grain_x</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">o</span><span class="sh">'</span>
      <span class="nf">if </span><span class="p">(</span><span class="n">grain_x</span><span class="p">,</span> <span class="n">grain_y</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span>
        <span class="n">settled_grains</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="n">stop</span> <span class="o">=</span> <span class="bp">True</span>
        <span class="k">break</span>

  <span class="k">if</span> <span class="ow">not</span> <span class="n">stop</span><span class="p">:</span>
    <span class="n">settled_grains</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="nf">print</span><span class="p">(</span><span class="n">settled_grains</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h1 id="day-15">Day 15</h1>

<p>Today was an excellent lesson in how time &amp; space can grow into sizes that would be noticeable.</p>

<h2 id="part-1-14">Part 1</h2>

<p>In pure logical terms, there are two entities in question: the sensor, and the beacon. Both of these entities have a position, and can be mapped with the relation: <code class="language-plaintext highlighter-rouge">sensor -&gt; beacon</code>.</p>

<p>The problem constraints that the position are integers, and each relation <code class="language-plaintext highlighter-rouge">sensor -&gt; beacon</code> represents the sensor to its closest beacon in Manhattan distance.</p>

<blockquote>
  <p>Manhattan distance is the distance in the x-axis + the distance in the y-axis, which is different from typical distance that is typically the hypotenuse of x and y.</p>
</blockquote>

<p>With the constraints out of the way, behold the question: get the number of positions that is not within the Manhattan distance of any <code class="language-plaintext highlighter-rouge">sensor -&gt; beacon</code> relation. The position is constraint by y, so we essentially get a row of positions that fulfils the condition.</p>

<p>At first, I thought about performing a BFS on every source, and then marking visited nodes. Then, I just count the number of unmarked nodes, and we’d be done. Of course, this works, but subsequently, the puzzle input looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sensor at x=2832148, y=322979: closest beacon is at x=3015667, y=-141020
Sensor at x=1449180, y=3883502: closest beacon is at x=2656952, y=4188971
</code></pre></div></div>

<p>which I interpreted as “aw crap, I’d need like a hundred gigabytes of memory to store a grid that size”. Instead, let’s approach the problem from another angle: we take the possible positions, which is defined by the minimum <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> seen in the input minus the largest distance we know, to the maximum <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> plus the largest distance. Luckily for us, since <code class="language-plaintext highlighter-rouge">y</code> is constrained to a single row, we only need to process one row, and <code class="language-plaintext highlighter-rouge">x</code> columns.</p>

<p>Then, calculate the Manhattan distance from the possible positions to every sensor, and check if they are less than the distance within the <code class="language-plaintext highlighter-rouge">sensor -&gt; beacon</code> relation. If they are, then those positions are considered visited; otherwise, those positions are unvisited. Finally, just count the number of unvisited positions, as required of us.</p>

<p>The above text is summarized as:</p>

<ol>
  <li>Parse input</li>
  <li>For each unvisited position, for each sensor, check if distance from sensor to position is less than relation distance</li>
  <li>If all distances are more than relation distance, count it as unvisited</li>
  <li>Repeat 2 until all possible positions have been tested</li>
  <li>Return number of unvisited position</li>
</ol>

<p>Code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">min_x</span><span class="p">,</span> <span class="n">min_y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="n">max_x</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="n">max_dist</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">coordinate_map</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="n">beacons</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="n">tokens</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
    <span class="n">s_x</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">s_y</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">:</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">b_x</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">8</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">b_y</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">9</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">min_x</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">s_x</span><span class="p">,</span> <span class="n">b_x</span><span class="p">,</span> <span class="n">min_y</span><span class="p">)</span>
    <span class="n">min_y</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">s_y</span><span class="p">,</span> <span class="n">b_y</span><span class="p">,</span> <span class="n">min_y</span><span class="p">)</span>
    <span class="n">max_x</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">s_x</span><span class="p">,</span> <span class="n">b_x</span><span class="p">,</span> <span class="n">max_x</span><span class="p">)</span>
    <span class="n">max_y</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">s_y</span><span class="p">,</span> <span class="n">b_y</span><span class="p">,</span> <span class="n">max_y</span><span class="p">)</span>
    <span class="n">dist</span> <span class="o">=</span> <span class="nf">abs</span><span class="p">(</span><span class="n">b_x</span> <span class="o">-</span> <span class="n">s_x</span><span class="p">)</span> <span class="o">+</span> <span class="nf">abs</span><span class="p">(</span><span class="n">b_y</span> <span class="o">-</span> <span class="n">s_y</span><span class="p">)</span>
    <span class="n">max_dist</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">max_dist</span><span class="p">,</span> <span class="n">dist</span><span class="p">)</span>

    <span class="n">coordinate_map</span><span class="p">[(</span><span class="n">s_x</span><span class="p">,</span> <span class="n">s_y</span><span class="p">)]</span> <span class="o">=</span> <span class="n">dist</span>
    <span class="n">beacons</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">b_x</span><span class="p">,</span> <span class="n">b_y</span><span class="p">))</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>

<span class="n">target_y</span> <span class="o">=</span> <span class="mi">2000000</span>
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">min_x</span> <span class="o">-</span> <span class="n">max_dist</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">+</span> <span class="n">max_dist</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
  <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">coordinate_map</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
    <span class="n">s_x</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">=</span> <span class="n">k</span>
    <span class="n">dist</span> <span class="o">=</span> <span class="nf">abs</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">s_x</span><span class="p">)</span> <span class="o">+</span> <span class="nf">abs</span><span class="p">(</span><span class="n">target_y</span> <span class="o">-</span> <span class="n">s_y</span><span class="p">)</span>
    <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">target_y</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">beacons</span> <span class="ow">and</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">target_y</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">coordinate_map</span> <span class="ow">and</span> <span class="n">dist</span> <span class="o">&lt;=</span> <span class="n">v</span><span class="p">:</span>
      <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="k">break</span>

<span class="nf">print</span><span class="p">(</span><span class="n">count</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-14">Part 2</h2>

<p>Part 2 requires us to limit our search space and find one position that all beacons cannot reach; the problem guarantees that there is only 1 such position within the x and y constraints. Our y-constraint is released, which creates a huge problem for us; now, our constraints are x between 0 to 4000000 and y between 0 to 4000000.</p>

<p>If I were to draw a grid, and assuming each unit of data we talk about here is 1 byte, that’s like 16 terabytes of data. ‘Tis but a small issue, let’s just buy more RAM.</p>

<p>Luckily, part 1 doesn’t really store anything in a grid; we have great space complexity, so why not just use it? Turns out, we will experience time issues; even though the algorithm is O(x * n) in time-complexity, where <code class="language-plaintext highlighter-rouge">x</code> is the column size of the theoretical grid and <code class="language-plaintext highlighter-rouge">n</code> is the number of sensors, the algorithm in this new context is now O(y * x * n), since <code class="language-plaintext highlighter-rouge">y</code> is no longer just a constant. <code class="language-plaintext highlighter-rouge">n</code> is a small number, so it basically doesn’t matter, but <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> <em>multiplied</em> together is <em>huge</em>. Suffice to say, the code doesn’t finish with a few hours.</p>

<p>Instead, let’s slightly change how we approach the problem; instead of finding unreachable locations line by line, we make the following observations instead:</p>

<ul>
  <li>The unreachable location <em>must</em> exist outside the boundary of the Manhattan distance in the <code class="language-plaintext highlighter-rouge">sensor -&gt; beacon</code> relation.</li>
  <li>Since there is only <em>one</em> unreachable location, the unreachable location <em>must</em> be within Manhattan distance + 1, but not within Manhattan distance.</li>
  <li>The unreachable location is, well, unreachable from all the sensors.</li>
</ul>

<p>Hence, we can generate all the points between Manhattan distance and Manhattan distance + 1.</p>

<p>However, this presents a problem; if the Manhattan distance is some absurd size, like 100000, and we have 16 sensors, then we have an absurd number of generated points, which should be 16 * 4 * 100000 = 6400000 points. If each point takes 16 bytes to store, as each number is an Int, then we get 102,400,000 bytes, which is 102.4GB of RAM. No biggie, just buy more RAM, amirite?</p>

<p>Well ok, we’ve reduced the storage our solution requires from 16TB to 102.4GB, which is 0.64% of the original size we needed, which is <strong>an improvement</strong> :tada:. However, that’s not good enough. So what do we do instead?</p>

<p>We make sacrifices in time. Now, for <em>every</em> <code class="language-plaintext highlighter-rouge">sensor</code> position, we generate all the unreachable locations from that one sensor position, and check if the unreachable locations is also unreachable from every <em>other</em> <code class="language-plaintext highlighter-rouge">sensor</code> position. Rinse and repeat until we find that one bloody point.</p>

<p>Originally, if we burned 102.4GB of RAM, then our time complexity would be O(m * n), where <code class="language-plaintext highlighter-rouge">m</code> is the number of points generated, <code class="language-plaintext highlighter-rouge">n</code> is the number of <code class="language-plaintext highlighter-rouge">sensor</code> positions. Now, we burn a cool 100MB of RAM, and have a time complexity of O(m * n^2). In this particular case, I feel that this is a perfectly reasonable trade-off for our problem.</p>

<p>Hence, the Python code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">coordinate_map</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="n">beacons</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="n">tokens</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
    <span class="n">s_x</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">s_y</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">:</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">b_x</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">8</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">b_y</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">tokens</span><span class="p">[</span><span class="mi">9</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">dist</span> <span class="o">=</span> <span class="nf">abs</span><span class="p">(</span><span class="n">b_x</span> <span class="o">-</span> <span class="n">s_x</span><span class="p">)</span> <span class="o">+</span> <span class="nf">abs</span><span class="p">(</span><span class="n">b_y</span> <span class="o">-</span> <span class="n">s_y</span><span class="p">)</span>

    <span class="n">coordinate_map</span><span class="p">[(</span><span class="n">s_x</span><span class="p">,</span> <span class="n">s_y</span><span class="p">)]</span> <span class="o">=</span> <span class="n">dist</span>
    <span class="n">beacons</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">b_x</span><span class="p">,</span> <span class="n">b_y</span><span class="p">))</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">sensor_barrier_coords</span><span class="p">(</span><span class="n">sensor_pos</span><span class="p">):</span>
  <span class="n">s_x</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">=</span> <span class="n">sensor_pos</span>
  <span class="n">dist</span> <span class="o">=</span> <span class="n">coordinate_map</span><span class="p">[</span><span class="n">sensor_pos</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="n">res</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>

  <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">dist</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
      <span class="n">res</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">s_x</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">+</span> <span class="p">(</span><span class="n">dist</span> <span class="o">-</span> <span class="n">i</span><span class="p">)))</span>
      <span class="n">res</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">s_x</span> <span class="o">-</span> <span class="n">i</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">-</span> <span class="p">(</span><span class="n">dist</span> <span class="o">-</span> <span class="n">i</span><span class="p">)))</span>
      <span class="n">res</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">s_x</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">-</span> <span class="p">(</span><span class="n">dist</span> <span class="o">-</span> <span class="n">i</span><span class="p">)))</span>
      <span class="n">res</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">s_x</span> <span class="o">-</span> <span class="n">i</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">+</span> <span class="p">(</span><span class="n">dist</span> <span class="o">-</span> <span class="n">i</span><span class="p">)))</span>

  <span class="k">return</span> <span class="n">res</span>

<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">coordinate_map</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
  <span class="k">for</span> <span class="n">pos</span> <span class="ow">in</span> <span class="nf">sensor_barrier_coords</span><span class="p">(</span><span class="n">k</span><span class="p">):</span>
    <span class="n">exclusive</span> <span class="o">=</span> <span class="bp">True</span>
    <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">pos</span>
    <span class="k">if</span> <span class="n">pos</span> <span class="ow">in</span> <span class="n">beacons</span> <span class="ow">or</span> <span class="n">pos</span> <span class="ow">in</span> <span class="n">coordinate_map</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="k">if</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">4000000</span> <span class="ow">or</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">y</span> <span class="o">&gt;</span> <span class="mi">4000000</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="k">for</span> <span class="n">k1</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">coordinate_map</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
      <span class="n">s_x</span><span class="p">,</span> <span class="n">s_y</span> <span class="o">=</span> <span class="n">k1</span>

      <span class="n">dist</span> <span class="o">=</span> <span class="nf">abs</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">s_x</span><span class="p">)</span> <span class="o">+</span> <span class="nf">abs</span><span class="p">(</span><span class="n">y</span> <span class="o">-</span> <span class="n">s_y</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">dist</span> <span class="o">&lt;=</span> <span class="n">v</span><span class="p">:</span>
        <span class="n">exclusive</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">break</span>

    <span class="k">if</span> <span class="n">exclusive</span><span class="p">:</span>
      <span class="nf">print</span><span class="p">(</span><span class="n">x</span> <span class="o">*</span> <span class="mi">4000000</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span>
      <span class="nf">exit</span><span class="p">()</span>
</code></pre></div></div>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">x * 4000000 + y</code> is just the problem statement’s instruction on how to encode the answer for AOC to check if the result is valid.</p>
</blockquote>

<hr />

<h1 id="day-16">Day 16</h1>

<p>This day was, for lack of a better phrase, really difficult. Part 1 was relatively simple, although I did struggle for a day to get it working, while I needed some hints for part 2.</p>

<h2 id="part-1-15">Part 1</h2>

<p>Part 1 presents an input that looks something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II
</code></pre></div></div>

<p>To understand this problem, there are a few pieces of important information that we need to extract from the context:</p>

<ul>
  <li>Valve <code class="language-plaintext highlighter-rouge">XX</code> denotes a node;</li>
  <li><code class="language-plaintext highlighter-rouge">flow rate=xx;</code> denotes a weight to the node;</li>
  <li><code class="language-plaintext highlighter-rouge">... DD, II, BB</code> denotes what the node is connected to.</li>
</ul>

<p>Each of the valves must be “turned on” to have an impact on the context. The highest sum over a period of 30 units of time will be the solution to the problem.</p>

<p>If we were to directly translate the input to a graph without much thought, we will end up with a undirected cyclic graph, which for lack of a better term, is a pain to work with.</p>

<p>Hence, I decided to boil it down using Dijkstra’s Algorithm - before that, I got myself a refresher on how to properly implement priority queues with a flat array, which is possible because it is a complete binary tree.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">heap_rep</span> <span class="o">=</span> <span class="p">[]</span>

<span class="k">def</span> <span class="nf">queue_add</span><span class="p">(</span><span class="n">val</span><span class="p">):</span>
  <span class="k">global</span> <span class="n">heap_rep</span>
  <span class="n">heap_rep</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
  <span class="n">curr_ind</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">heap_rep</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>

  <span class="c1"># =&gt; odd number = left child, even number = right child
</span>  <span class="n">parent</span> <span class="o">=</span> <span class="p">(</span><span class="n">curr_ind</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">curr_ind</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="nf">else </span><span class="p">(</span><span class="n">curr_ind</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
  <span class="k">while</span> <span class="n">parent</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">parent</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">curr_ind</span><span class="p">]:</span>
    <span class="n">heap_rep</span><span class="p">[</span><span class="n">parent</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">curr_ind</span><span class="p">]</span> <span class="o">=</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">curr_ind</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">parent</span><span class="p">]</span>
    <span class="n">curr_ind</span> <span class="o">=</span> <span class="n">parent</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="p">(</span><span class="n">curr_ind</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">curr_ind</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="nf">else </span><span class="p">(</span><span class="n">curr_ind</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>

<span class="k">def</span> <span class="nf">queue_pop</span><span class="p">():</span>
  <span class="k">global</span> <span class="n">heap_rep</span>
  <span class="n">retval</span> <span class="o">=</span> <span class="n">heap_rep</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">heap_rep</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">heap_rep</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">ueap_rep</span> <span class="o">=</span> <span class="n">heap_rep</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

  <span class="n">indx</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">left_child</span> <span class="o">=</span> <span class="n">indx</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span>
  <span class="n">right_child</span> <span class="o">=</span> <span class="n">indx</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span>

  <span class="nf">while </span><span class="p">(</span><span class="n">left_child</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">heap_rep</span><span class="p">)</span> <span class="ow">and</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">indx</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">left_child</span><span class="p">])</span> <span class="ow">or</span> <span class="p">(</span><span class="n">right_child</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">heap_rep</span><span class="p">)</span> <span class="ow">and</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">indx</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">right_child</span><span class="p">]):</span>
    <span class="k">if</span> <span class="n">right_child</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">heap_rep</span><span class="p">)</span> <span class="ow">and</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">left_child</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">right_child</span><span class="p">]:</span>
      <span class="n">heap_rep</span><span class="p">[</span><span class="n">indx</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">right_child</span><span class="p">]</span> <span class="o">=</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">right_child</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">indx</span><span class="p">]</span>
      <span class="n">indx</span> <span class="o">=</span> <span class="n">right_child</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">heap_rep</span><span class="p">[</span><span class="n">indx</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">left_child</span><span class="p">]</span> <span class="o">=</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">left_child</span><span class="p">],</span> <span class="n">heap_rep</span><span class="p">[</span><span class="n">indx</span><span class="p">]</span>
      <span class="n">indx</span> <span class="o">=</span> <span class="n">left_child</span>

    <span class="n">left_child</span> <span class="o">=</span> <span class="n">indx</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span>
    <span class="n">right_child</span> <span class="o">=</span> <span class="n">indx</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span>
  <span class="k">return</span> <span class="n">retval</span>

<span class="nf">queue_add</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">18</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">31</span><span class="p">)</span>
<span class="nf">queue_add</span><span class="p">(</span><span class="mi">45</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="n">heap_rep</span><span class="p">)</span>
<span class="k">while</span> <span class="nf">len</span><span class="p">(</span><span class="n">heap_rep</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
  <span class="nf">print</span><span class="p">(</span><span class="nf">queue_pop</span><span class="p">())</span>
</code></pre></div></div>

<blockquote>
  <p>NOTE: Yes, the code looks ugly. It was meant to be a refresher after all!</p>
</blockquote>

<p>Then, I used <a href="https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/">GeeksForGeeks’s</a> picture of their graph as reference to test my Dijkstra’s algorithm:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># testing implementation of d algo
</span>
<span class="n">list_of_distances</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">association_list</span> <span class="o">=</span> <span class="p">[]</span>

<span class="c1"># add some values
</span><span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">11</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span> <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">9</span><span class="p">),</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">14</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">9</span><span class="p">),</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">14</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">2</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">),</span> <span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span> <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">11</span><span class="p">),</span> <span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">1</span><span class="p">)])</span>
<span class="n">association_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span> <span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">)])</span>

<span class="c1"># calculate distances
</span><span class="n">list_of_distances</span> <span class="o">=</span> <span class="p">[</span><span class="mi">999999</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">association_list</span><span class="p">))]</span>
<span class="n">list_of_distances</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>

<span class="c1"># non-priority queue implementation
</span><span class="n">spt_set</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
<span class="k">while</span> <span class="nf">len</span><span class="p">(</span><span class="n">spt_set</span><span class="p">)</span> <span class="o">!=</span> <span class="nf">len</span><span class="p">(</span><span class="n">list_of_distances</span><span class="p">):</span>
  <span class="n">min_index</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">min_distance</span> <span class="o">=</span> <span class="mi">999999</span>
  <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">list_of_distances</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">spt_set</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="n">min_distance</span><span class="p">:</span>
      <span class="n">min_index</span> <span class="o">=</span> <span class="n">k</span>
      <span class="n">min_distance</span> <span class="o">=</span> <span class="n">v</span>

  <span class="n">spt_set</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">min_index</span><span class="p">)</span>

  <span class="k">for</span> <span class="n">association</span> <span class="ow">in</span> <span class="n">association_list</span><span class="p">[</span><span class="n">min_index</span><span class="p">]:</span>
    <span class="n">list_of_distances</span><span class="p">[</span><span class="n">association</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">min_distance</span> <span class="o">+</span> <span class="n">association</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">list_of_distances</span><span class="p">[</span><span class="n">association</span><span class="p">[</span><span class="mi">0</span><span class="p">]])</span>

<span class="c1"># get path from one to another
</span><span class="nf">print</span><span class="p">(</span><span class="n">list_of_distances</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">9</span><span class="p">):</span>
  <span class="n">target</span> <span class="o">=</span> <span class="n">i</span>
  <span class="n">path</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span><span class="p">]</span>
  <span class="k">while</span> <span class="n">target</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
    <span class="n">min_dist</span> <span class="o">=</span> <span class="mi">999999</span>
    <span class="n">min_ind</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">association</span> <span class="ow">in</span> <span class="n">association_list</span><span class="p">[</span><span class="n">target</span><span class="p">]:</span>
      <span class="n">dist</span> <span class="o">=</span> <span class="n">list_of_distances</span><span class="p">[</span><span class="n">association</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span> <span class="o">+</span> <span class="n">association</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
      <span class="k">if</span> <span class="n">dist</span> <span class="o">&lt;</span> <span class="n">min_dist</span><span class="p">:</span>
        <span class="n">min_ind</span> <span class="o">=</span> <span class="n">association</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">min_dist</span> <span class="o">=</span> <span class="n">dist</span>
    <span class="n">path</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">min_ind</span><span class="p">)</span>
    <span class="n">target</span> <span class="o">=</span> <span class="n">min_ind</span>

  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">-&gt;</span><span class="sh">'</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="nf">str</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">reversed</span><span class="p">(</span><span class="n">path</span><span class="p">)]))</span>
</code></pre></div></div>

<p>Great, warm-up done. Let’s talk about the problem now.</p>

<p>The distance between each node (that is connected anyway), is actually just 1 unit; so, we boil down those 1-unit nodes into edges. When those nodes become edges, we realize that <strong>information about how we traverse from one node to another is lost</strong>. In other words, we could be doing crazy things like walking back and forth a node but not actually turning on the valve at that node <em>gasp</em>. Thankfully, that is <strong>exactly what we want</strong>. The conversion process looks something like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_distances</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">associations</span><span class="p">):</span>
  <span class="n">to_visit</span> <span class="o">=</span> <span class="nc">PriorityQueue</span><span class="p">()</span>
  <span class="n">distances</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
  <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">associations</span><span class="p">.</span><span class="nf">keys</span><span class="p">():</span>
    <span class="n">distances</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="mi">999999</span>

  <span class="n">distances</span><span class="p">[</span><span class="n">source</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">to_visit</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="n">source</span><span class="p">))</span>

  <span class="k">while</span> <span class="ow">not</span> <span class="n">to_visit</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">node</span> <span class="o">=</span> <span class="n">to_visit</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>
    <span class="n">association</span> <span class="o">=</span> <span class="n">associations</span><span class="p">[</span><span class="n">node</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">neighbor</span> <span class="ow">in</span> <span class="n">association</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
      <span class="k">if</span> <span class="n">distances</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">distances</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">distances</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="o">=</span> <span class="n">distances</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
        <span class="n">to_visit</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">distances</span><span class="p">[</span><span class="n">neighbor</span><span class="p">],</span> <span class="n">neighbor</span><span class="p">))</span>

  <span class="k">return</span> <span class="n">distances</span>
</code></pre></div></div>

<p>Let’s talk about the structure we get from this. If we were to pass <code class="language-plaintext highlighter-rouge">source</code> the root node, we’ll get the minimum spanning tree (i.e. the minimum distance from the current node to any other node in the graph). So, if we were to iterate through a list of <em>all of the nodes with a valve that has a flow rate</em>, then we’ll get a map of minimum spanning trees from all nodes. Some questions you may now have is:</p>

<ol>
  <li>
    <p>Wouldn’t the minimum spanning tree from every other node simply be an adjustment of the distance traveled from the starting node, to the ending node?</p>

    <p>Me: No, because remember that we lost information about how to actually traverse from one node to another; we only know the distance. Imagine a cyclic graph, <code class="language-plaintext highlighter-rouge">A &lt;-&gt; B &lt;-&gt; C &lt;-&gt; D &lt;-&gt; A</code>, and only <code class="language-plaintext highlighter-rouge">A</code>, <code class="language-plaintext highlighter-rouge">B</code> and <code class="language-plaintext highlighter-rouge">D</code> are nodes with valves, which means a minimum spanning tree that looks like this: <code class="language-plaintext highlighter-rouge">A &lt;-1-&gt; B, A &lt;-3-&gt; D</code>. If I was at <code class="language-plaintext highlighter-rouge">A</code>, and I first go to <code class="language-plaintext highlighter-rouge">D</code>, I travel a distance of <code class="language-plaintext highlighter-rouge">3</code>. How would I then travel to <code class="language-plaintext highlighter-rouge">B</code>? We know that the distance from <code class="language-plaintext highlighter-rouge">A</code> to <code class="language-plaintext highlighter-rouge">D</code> is <code class="language-plaintext highlighter-rouge">3</code>, and the distance from <code class="language-plaintext highlighter-rouge">A</code> to <code class="language-plaintext highlighter-rouge">B</code> is <code class="language-plaintext highlighter-rouge">1</code>. So is the answer <code class="language-plaintext highlighter-rouge">4</code>? Of course not, there is a shorter path that connected <code class="language-plaintext highlighter-rouge">B</code> to <code class="language-plaintext highlighter-rouge">D</code> through <code class="language-plaintext highlighter-rouge">C</code>, which means the answer is actually <code class="language-plaintext highlighter-rouge">2</code>. but we wouldn’t have known that with just the minimum spanning tree of <code class="language-plaintext highlighter-rouge">A</code>. So, we necessarily must generate the spanning tree of all the node with valves.</p>
  </li>
  <li>
    <p>What is the resulting structure?</p>

    <p>Me: Before I answer this question, let me go through what went through my head for over half a day. “This structure must be a web, because each node has it’s own minimum spanning tree!” Naturally, I thought that I ended up with a 3D fully connected web. It took me a while before I was able to re-interpret the graph as a directed acyclic graph, a.k.a a tree. Realizing it is a tree has many benefits, which includes: being able to actually solve the problem. To see how it is a tree, remember that the graph has lost all information about paths through the actual nodes. Then, each node is now represented as actually <em>turning on</em> the nodes, because remember, with information lost about paths, it also suggests that someone could navigate the through the nodes with valves to reach a more important valve before coming back later. Since you are unable to turn on a valve twice, this means that in a graph, the arrow always points outwards, and there will never be a situation where a path will point back to itself. Hence, it is directed and acyclic, which makes the resulting structure a tree.</p>
  </li>
  <li>
    <p>How does this new structure solve the problem?</p>
  </li>
</ol>

<p>Now that it’s represented as a tree, we can use a variety of ways (like I tried to do) to solve the problem. However, there is one extremely important thing about the problem that makes it challenging to use conventional graph search algorithms: we are <em>maximizing</em> our sum.</p>

<p>All pathfinding algorithm <em>minimizes</em> paths. In a nutshell, this means we have to either look for fantastic heuristics that can turn our maximizing problem into minimizing problems, or figure out another way.</p>

<p>Heuristics are hard, particularly because approximate ones may not yield an accurate result, while an accurate one will either take too long to compute, or is very challenging to define. For instance, A* Search and Dijkstra both require heuristics to make decisions on what to explore next; if we had heuristics that kept on increasing in value, the pathfinding algorithm will be stuck on a single path, and <strong>we end up with an inaccurate result</strong>. Even if we were to solve that problem by inversing the heuristic, we still find that our reliance on the accumulated pressure, which is always increasing, causes the heuristics to produce inaccurate results. Heuristics work the best if it is calculated between two nodes, and does not have any context-wide variables, such as time, which is required to calculate the total pressure amassed between any two nodes.</p>

<p>Then, you may ask. What about using a slightly inaccurate heuristic, such as <code class="language-plaintext highlighter-rouge">time / pressure</code>? The larger the time, the more unideal that path. The lower the pressure amassed, the more unideal the path. Perfect!</p>

<p>Perfect?</p>

<p>Well, I tried it out, and it somehow worked for the example, but not the actual input. The rationale is simple: it’s actually <code class="language-plaintext highlighter-rouge">(w1 * time) / (w2 * pressure)</code>, where <code class="language-plaintext highlighter-rouge">w1</code> and <code class="language-plaintext highlighter-rouge">w2</code> are arbitrary weights dictating how important time and pressure is. This is the nature of approximation - we need to declare how important something is to the other. However, for our use-case, we need precise answers; hence, even approximate heuristics are not suitable.</p>

<p>There is likely a proper heuristic that can be used for this particular problem, but I decided that it is no longer worth the effort. Instead, I explored BFS and DFS.</p>

<p>I didn’t think too much about BFS, because I had a gut feeling that it wouldn’t be suitable for the rest of the puzzle; turns out, in part 2, where I actually implement BFS because I ran out of options, I was actually right. The space complexity of BFS is <code class="language-plaintext highlighter-rouge">|V|</code>, which is synonymous with every node in existence. When we reach part 2, we can see why storing <code class="language-plaintext highlighter-rouge">|V|</code> is a terrible idea. Meanwhile, for DFS, the space complexity is however many edges we have for the node we are currently processing, which is <code class="language-plaintext highlighter-rouge">|E|</code>. In a nutshell, for our problem in particular, the storage complexity of DFS is beneficial.</p>

<p>DFS is great because we can do anything with it; even a problem like maximizing accumulated sums. Although there are better ways to do it, like <a href="/2022/01/25/duty-planning-with-linear-programming/">linear programming</a>, the nature of the problem probably disallows us to express the problem as a linear equation (I tried boiling it down to a linear equation, but after spending a fair bit of time, I decided not to).</p>

<p>So, after figuring out that it’s a tree, and DFS is the way forward, and attempting to implement the other searches as an experiment, I ended up with a simple implementation like so:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">queue</span> <span class="kn">import</span> <span class="n">PriorityQueue</span>

<span class="k">def</span> <span class="nf">get_distances</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">associations</span><span class="p">):</span>
  <span class="n">to_visit</span> <span class="o">=</span> <span class="nc">PriorityQueue</span><span class="p">()</span>
  <span class="n">distances</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
  <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">associations</span><span class="p">.</span><span class="nf">keys</span><span class="p">():</span>
    <span class="n">distances</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="mi">999999</span>

  <span class="n">distances</span><span class="p">[</span><span class="n">source</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">to_visit</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="n">source</span><span class="p">))</span>

  <span class="k">while</span> <span class="ow">not</span> <span class="n">to_visit</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">node</span> <span class="o">=</span> <span class="n">to_visit</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>
    <span class="n">association</span> <span class="o">=</span> <span class="n">associations</span><span class="p">[</span><span class="n">node</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">neighbor</span> <span class="ow">in</span> <span class="n">association</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
      <span class="k">if</span> <span class="n">distances</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">distances</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">distances</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="o">=</span> <span class="n">distances</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
        <span class="n">to_visit</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">distances</span><span class="p">[</span><span class="n">neighbor</span><span class="p">],</span> <span class="n">neighbor</span><span class="p">))</span>

  <span class="k">return</span> <span class="n">distances</span>

<span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">time</span><span class="p">,</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">):</span>
  <span class="k">if</span> <span class="n">time</span> <span class="o">&gt;=</span> <span class="mi">30</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">pressure</span>

  <span class="n">distances</span> <span class="o">=</span> <span class="nf">get_distances</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">associations</span><span class="p">)</span>
  <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">pressure</span>

  <span class="k">for</span> <span class="n">impt_node</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">:</span>
    <span class="n">node</span><span class="p">,</span> <span class="p">(</span><span class="n">point_pressure</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">impt_node</span>
    <span class="k">if</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="n">new_time</span> <span class="o">=</span> <span class="n">time</span> <span class="o">+</span> <span class="n">distances</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
    <span class="n">new_pressure</span> <span class="o">=</span> <span class="n">pressure</span> <span class="o">+</span> <span class="n">point_pressure</span> <span class="o">*</span> <span class="p">(</span><span class="mi">30</span> <span class="o">-</span> <span class="n">new_time</span><span class="p">)</span>
    <span class="n">new_visited</span> <span class="o">=</span> <span class="n">visited</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
    <span class="n">new_visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
    <span class="n">res</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">new_time</span><span class="p">,</span> <span class="n">new_pressure</span><span class="p">,</span> <span class="n">new_visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">res</span> <span class="o">&gt;</span> <span class="n">best_pressure</span><span class="p">:</span>
      <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">res</span>

  <span class="k">return</span> <span class="n">best_pressure</span>

<span class="n">associations</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">''</span><span class="p">:</span>
    <span class="n">associations</span><span class="p">[</span><span class="n">line</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span> <span class="o">=</span> <span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">line</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">;</span><span class="sh">'</span><span class="p">).</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">]),</span>
      <span class="p">[</span><span class="n">valve</span><span class="p">.</span><span class="nf">strip</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">valve</span> <span class="ow">in</span> <span class="n">line</span><span class="p">[</span><span class="mi">9</span><span class="p">:]])</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">dfs</span><span class="p">(</span><span class="sh">'</span><span class="s">AA</span><span class="sh">'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nf">set</span><span class="p">(),</span> <span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">associations</span><span class="p">.</span><span class="nf">items</span><span class="p">()</span> <span class="k">if</span> <span class="n">v</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">]))</span>
</code></pre></div></div>

<p>And wouldn’t you know, it worked!</p>

<h2 id="part-2-15">Part 2</h2>

<p>This part is the main reason why I spent 4 days to write the blog post from Day 16 to Day 19. The problem introduces a new entity that can explore the graph, which is affectionately chosen to be an elephant, and cuts the amount of time we have to explore the nodes to 26 units of time.</p>

<p>To save you the trouble from thinking about it: no, a double for-loop in DFS doesn’t work. Well, it would, if you run the program for 16 hours (actual calculations), but it is definitely not the intended solution.</p>

<p>Of course, it didn’t stop me from trying:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">info_source_1</span><span class="p">,</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">,</span> <span class="n">depth</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
  <span class="n">source_1</span><span class="p">,</span> <span class="n">time_1</span> <span class="o">=</span> <span class="n">info_source_1</span>
  <span class="n">source_2</span><span class="p">,</span> <span class="n">time_2</span> <span class="o">=</span> <span class="n">info_source_2</span>

  <span class="k">if</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="ow">and</span> <span class="n">time_1</span> <span class="o">&lt;</span> <span class="mi">26</span><span class="p">:</span>
    <span class="k">return</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">info_source_2</span><span class="p">,</span> <span class="n">info_source_1</span><span class="p">,</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">,</span> <span class="n">depth</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>

  <span class="k">if</span> <span class="n">time_1</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="ow">and</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">pressure</span>

  <span class="n">distances_1</span> <span class="o">=</span> <span class="n">distances_map</span><span class="p">[</span><span class="n">source_1</span><span class="p">]</span>
  <span class="n">distances_2</span> <span class="o">=</span> <span class="n">distances_map</span><span class="p">[</span><span class="n">source_2</span><span class="p">]</span>
  <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">pressure</span>

  <span class="k">for</span> <span class="n">impt_node_1</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">:</span>
    <span class="n">node_1</span><span class="p">,</span> <span class="p">(</span><span class="n">point_pressure_1</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">impt_node_1</span>
    <span class="k">if</span> <span class="n">node_1</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="n">new_time_1</span> <span class="o">=</span> <span class="n">time_1</span> <span class="o">+</span> <span class="n">distances_1</span><span class="p">[</span><span class="n">node_1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
    <span class="n">new_visited</span> <span class="o">=</span> <span class="n">visited</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
    <span class="n">new_visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node_1</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span><span class="p">:</span>
      <span class="n">new_pressure</span> <span class="o">=</span> <span class="n">pressure</span> <span class="o">+</span> <span class="n">point_pressure_1</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time_1</span><span class="p">)</span>
      <span class="n">res</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">((</span><span class="n">node_1</span><span class="p">,</span> <span class="n">new_time_1</span><span class="p">),</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">new_pressure</span><span class="p">,</span> <span class="n">new_visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">,</span> <span class="n">depth</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">res</span> <span class="o">&gt;</span> <span class="n">best_pressure</span><span class="p">:</span>
        <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">res</span>
      <span class="k">continue</span>

    <span class="k">for</span> <span class="n">impt_node_2</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">:</span>
      <span class="n">node_2</span><span class="p">,</span> <span class="p">(</span><span class="n">point_pressure_2</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">impt_node_2</span>
      <span class="k">if</span> <span class="n">node_2</span> <span class="ow">in</span> <span class="n">visited</span> <span class="ow">or</span> <span class="n">node_1</span> <span class="ow">is</span> <span class="n">node_2</span><span class="p">:</span>
        <span class="k">continue</span>

      <span class="n">new_time_2</span> <span class="o">=</span> <span class="n">time_2</span> <span class="o">+</span> <span class="n">distances_2</span><span class="p">[</span><span class="n">node_2</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
      <span class="n">new_pressure</span> <span class="o">=</span> <span class="n">pressure</span> <span class="o">+</span> <span class="n">point_pressure_1</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time_1</span><span class="p">)</span> <span class="o">+</span> <span class="n">point_pressure_2</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time_2</span><span class="p">)</span>
      <span class="n">new_visited_inner</span> <span class="o">=</span> <span class="n">new_visited</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
      <span class="n">new_visited_inner</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node_2</span><span class="p">)</span>
      <span class="n">res</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">((</span><span class="n">node_1</span><span class="p">,</span> <span class="n">new_time_1</span><span class="p">),</span> <span class="p">(</span><span class="n">node_2</span><span class="p">,</span> <span class="n">new_time_2</span><span class="p">),</span> <span class="n">new_pressure</span><span class="p">,</span> <span class="n">new_visited_inner</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">,</span> <span class="n">depth</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">res</span> <span class="o">&gt;</span> <span class="n">best_pressure</span><span class="p">:</span>
        <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">res</span>

  <span class="k">return</span> <span class="n">best_pressure</span>
</code></pre></div></div>

<p>While it worked for the example input, it doesn’t work (i.e. doesn’t finish within acceptable time) for the real input. This is because there are <code class="language-plaintext highlighter-rouge">15! * 14! = 114,000,816,848,279,961,600,000</code> possible combinations for the algorithm to run through.</p>

<p>So, what next? I tried BFS as well:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">bfs</span><span class="p">(</span><span class="n">info_source_1</span><span class="p">,</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">):</span>
  <span class="n">q</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>
  <span class="n">p</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>

  <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">info_source_1</span><span class="p">,</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nf">set</span><span class="p">(),</span> <span class="mi">0</span><span class="p">,</span> <span class="p">[]))</span>
  <span class="n">set_of_all_important_nodes</span> <span class="o">=</span> <span class="nf">set</span><span class="p">([</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">])</span>
  <span class="n">found_pressure</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">found_depth</span> <span class="o">=</span> <span class="mi">999999</span>

  <span class="k">while</span> <span class="ow">not</span> <span class="n">q</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
    <span class="n">info_source_1</span><span class="p">,</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">visited</span><span class="p">,</span> <span class="n">depth</span><span class="p">,</span> <span class="n">path</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>

    <span class="k">if</span> <span class="n">depth</span> <span class="o">&gt;</span> <span class="n">found_depth</span><span class="p">:</span>
      <span class="k">break</span>

    <span class="n">source_1</span><span class="p">,</span> <span class="n">time_1</span> <span class="o">=</span> <span class="n">info_source_1</span>
    <span class="n">source_2</span><span class="p">,</span> <span class="n">time_2</span> <span class="o">=</span> <span class="n">info_source_2</span>

    <span class="k">if</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="ow">and</span> <span class="n">time_1</span> <span class="o">&lt;</span> <span class="mi">26</span><span class="p">:</span>
      <span class="n">source_1</span><span class="p">,</span> <span class="n">source_2</span> <span class="o">=</span> <span class="n">source_2</span><span class="p">,</span> <span class="n">source_1</span>
      <span class="n">time_1</span><span class="p">,</span> <span class="n">time_2</span> <span class="o">=</span> <span class="n">time_2</span><span class="p">,</span> <span class="n">time_1</span>
    <span class="k">elif</span> <span class="n">time_1</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="ow">and</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span><span class="p">:</span>
      <span class="k">continue</span>
    <span class="k">elif</span> <span class="nf">len</span><span class="p">(</span><span class="n">visited</span> <span class="o">&amp;</span> <span class="n">set_of_all_important_nodes</span><span class="p">)</span> <span class="o">==</span> <span class="nf">len</span><span class="p">(</span><span class="n">important_nodes</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">pressure</span> <span class="o">&gt;</span> <span class="n">found_pressure</span><span class="p">:</span>
        <span class="n">found_pressure</span> <span class="o">=</span> <span class="n">pressure</span>
        <span class="n">found_depth</span> <span class="o">=</span> <span class="n">depth</span>

    <span class="n">distances_1</span> <span class="o">=</span> <span class="n">distances_map</span><span class="p">[</span><span class="n">source_1</span><span class="p">]</span>
    <span class="n">distances_2</span> <span class="o">=</span> <span class="n">distances_map</span><span class="p">[</span><span class="n">source_2</span><span class="p">]</span>
    <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">pressure</span>

    <span class="k">for</span> <span class="n">impt_node_1</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">:</span>
      <span class="n">node_1</span><span class="p">,</span> <span class="p">(</span><span class="n">point_pressure_1</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">impt_node_1</span>
      <span class="k">if</span> <span class="n">node_1</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
        <span class="k">continue</span>

      <span class="n">new_time_1</span> <span class="o">=</span> <span class="n">time_1</span> <span class="o">+</span> <span class="n">distances_1</span><span class="p">[</span><span class="n">node_1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
      <span class="n">new_visited</span> <span class="o">=</span> <span class="n">visited</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
      <span class="n">new_visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node_1</span><span class="p">)</span>

      <span class="k">if</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span><span class="p">:</span>
        <span class="n">new_pressure</span> <span class="o">=</span> <span class="n">pressure</span> <span class="o">+</span> <span class="n">point_pressure_1</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time_1</span><span class="p">)</span>
        <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">(((</span><span class="n">node_1</span><span class="p">,</span> <span class="n">new_time_1</span><span class="p">),</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">new_pressure</span><span class="p">,</span> <span class="n">new_visited</span><span class="p">,</span> <span class="n">depth</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">path</span><span class="p">))</span>

      <span class="k">for</span> <span class="n">impt_node_2</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">:</span>
        <span class="n">node_2</span><span class="p">,</span> <span class="p">(</span><span class="n">point_pressure_2</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">impt_node_2</span>
        <span class="k">if</span> <span class="n">node_2</span> <span class="ow">in</span> <span class="n">visited</span> <span class="ow">or</span> <span class="n">node_1</span> <span class="ow">is</span> <span class="n">node_2</span><span class="p">:</span>
          <span class="k">continue</span>

        <span class="n">new_time_2</span> <span class="o">=</span> <span class="n">time_2</span> <span class="o">+</span> <span class="n">distances_2</span><span class="p">[</span><span class="n">node_2</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
        <span class="n">new_pressure</span> <span class="o">=</span> <span class="n">pressure</span> <span class="o">+</span> <span class="n">point_pressure_1</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time_1</span><span class="p">)</span> <span class="o">+</span> <span class="n">point_pressure_2</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time_2</span><span class="p">)</span>
        <span class="n">new_visited_inner</span> <span class="o">=</span> <span class="n">new_visited</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
        <span class="n">new_visited_inner</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node_2</span><span class="p">)</span>
        <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">(((</span><span class="n">node_1</span><span class="p">,</span> <span class="n">new_time_1</span><span class="p">),</span> <span class="p">(</span><span class="n">node_2</span><span class="p">,</span> <span class="n">new_time_2</span><span class="p">),</span> <span class="n">new_pressure</span><span class="p">,</span> <span class="n">new_visited_inner</span><span class="p">,</span> <span class="n">depth</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">path</span> <span class="o">+</span> <span class="p">[(</span><span class="n">node_1</span><span class="p">,</span> <span class="n">node_2</span><span class="p">)]))</span>

  <span class="k">return</span> <span class="n">found_pressure</span>
</code></pre></div></div>

<p>The BFS mechanism uses a gimmick to break out early, because I reasoned that beyond a certain depth, we approach diminishing returns. Needless to say, BFS worked on the example input, but not on the actual input, due to space complexity.</p>

<p>I went berserk and also implemented Dijkstra’s to find the minimum spanning tree, but in hindsight, I have no idea what I was trying to accomplish with it.</p>

<p>Eventually, I gave up and went to bed. On and off, I would try my hand again, including attempting to use permutations to shuffle the order of valves to open, but again, due to space complexity, this was infeasible.</p>

<p>Finally, I decided to look for inspiration. Without looking at the solutions, I looked through the Reddit post, and found a post by <a href="https://www.reddit.com/r/adventofcode/comments/zn6k1l/comment/j0ffso8/?utm_source=share&amp;utm_medium=web2x&amp;context=3">betaveros</a> (at time of writing, the top on the leaderboard), which contained a sentence that gave me the inspiration to settle on the answer: “one person first, then the same DFS for the other over all unopened valves”.</p>

<p>If I may: “god damn it”! I’ve thought about this at one point, but my implementation was naive: I simply made one explorer explore half the list, and the other explorer explore the other half of the list. However, this failed because obviously, not <em>all</em> possibilities were considered.</p>

<p>However, let’s think about it another way. Assume I have 6 valves to open. If I were to open the valves alone, I may not be able to finish within the 26 measly minutes given to me. So, the whole point of teamwork is to split up the work. Hence, two explorers should open roughly 3 valves each. However, recall that once a valve has been opened, <strong>it cannot be opened again</strong>. Hence, all I need to do is to perform DFS on 3 valves, then change the actor to the other explorer, and perform DFS on the remaining 3 valves. Hence, instead of searching through <code class="language-plaintext highlighter-rouge">6! * 5!</code> possibilities, I am now at <code class="language-plaintext highlighter-rouge">6!</code> possibilities, which is definitely doable within human time.</p>

<p>Supersizing to the current problem, we now have an opportunity to restrict the problem to <code class="language-plaintext highlighter-rouge">15!</code> possibilities, which may be a huge number, but definitely much smaller than <code class="language-plaintext highlighter-rouge">15! * 14!</code> possibilities. Hence, the new DFS is implemented as such:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">info_source_1</span><span class="p">,</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">):</span>
  <span class="n">source_1</span><span class="p">,</span> <span class="n">time_1</span> <span class="o">=</span> <span class="n">info_source_1</span>
  <span class="n">source_2</span><span class="p">,</span> <span class="n">time_2</span> <span class="o">=</span> <span class="n">info_source_2</span>

  <span class="k">if</span> <span class="n">time_1</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="ow">and</span> <span class="n">time_2</span> <span class="o">&gt;=</span> <span class="mi">26</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">path</span>
  <span class="k">elif</span> <span class="n">time_1</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="ow">or</span> <span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">visited</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">important_nodes</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span> <span class="ow">and</span> <span class="n">time_2</span> <span class="o">!=</span> <span class="mi">9999</span><span class="p">):</span>
    <span class="k">return</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">info_source_2</span><span class="p">,</span> <span class="p">(</span><span class="n">source_1</span><span class="p">,</span> <span class="mi">9999</span><span class="p">),</span> <span class="n">pressure</span><span class="p">,</span> <span class="n">visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">)</span>

  <span class="n">distances</span> <span class="o">=</span> <span class="n">distances_map</span><span class="p">[</span><span class="n">source_1</span><span class="p">]</span>
  <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">pressure</span>

  <span class="k">for</span> <span class="n">impt_node</span> <span class="ow">in</span> <span class="n">important_nodes</span><span class="p">:</span>
    <span class="n">node</span><span class="p">,</span> <span class="p">(</span><span class="n">point_pressure</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="n">impt_node</span>
    <span class="k">if</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="n">new_time</span> <span class="o">=</span> <span class="n">time_1</span> <span class="o">+</span> <span class="n">distances</span><span class="p">[</span><span class="n">node</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span>
    <span class="n">new_visited</span> <span class="o">=</span> <span class="n">visited</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
    <span class="n">new_visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>

    <span class="n">new_pressure</span> <span class="o">=</span> <span class="n">pressure</span> <span class="o">+</span> <span class="n">point_pressure</span> <span class="o">*</span> <span class="p">(</span><span class="mi">26</span> <span class="o">-</span> <span class="n">new_time</span><span class="p">)</span>
    <span class="n">res</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">((</span><span class="n">node</span><span class="p">,</span> <span class="n">new_time</span><span class="p">),</span> <span class="n">info_source_2</span><span class="p">,</span> <span class="n">new_pressure</span><span class="p">,</span> <span class="n">new_visited</span><span class="p">,</span> <span class="n">important_nodes</span><span class="p">,</span> <span class="n">distances_map</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">res</span> <span class="o">&gt;</span> <span class="n">best_pressure</span><span class="p">:</span>
      <span class="n">best_pressure</span> <span class="o">=</span> <span class="n">res</span>

  <span class="k">return</span> <span class="n">best_pressure</span>
</code></pre></div></div>

<p>So, applying this diff (<code class="language-plaintext highlighter-rouge">&lt;</code> is part 1, <code class="language-plaintext highlighter-rouge">&gt;</code> part 2) to the part 1 solution, and running the program for roughly 20 minutes will give us the final result.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">22,24c22,29
</span><span class="gd">&lt; def dfs(source, time, pressure, visited, important_nodes):
&lt;   if time &gt;= 30:
&lt;     return pressure
</span><span class="p">---
</span><span class="gi">&gt; def dfs(info_source_1, info_source_2, pressure, visited, important_nodes, distances_map):
&gt;   source_1, time_1 = info_source_1
&gt;   source_2, time_2 = info_source_2
&gt;
&gt;   if time_1 &gt;= 26 and time_2 &gt;= 26:
&gt;     return pressure, path
&gt;   elif time_1 &gt;= 26 or (len(visited) + 1 &gt; len(important_nodes) // 2 and time_2 != 9999):
&gt;     return dfs(info_source_2, (source_1, 9999), pressure, visited, important_nodes, distances_map)
</span><span class="p">26c31
</span><span class="gd">&lt;   distances = get_distances(source, associations)
</span><span class="p">---
</span><span class="gi">&gt;   distances = distances_map[source_1]
</span><span class="p">34,35c39
</span><span class="gd">&lt;     new_time = time + distances[node] + 1
&lt;     new_pressure = pressure + point_pressure * (30 - new_time)
</span><span class="p">---
</span><span class="gi">&gt;     new_time = time_1 + distances[node] + 1
</span><span class="p">38c42,44
</span><span class="gd">&lt;     res = dfs(node, new_time, new_pressure, new_visited, important_nodes)
</span><span class="p">---
</span><span class="gi">&gt;
&gt;     new_pressure = pressure + point_pressure * (26 - new_time)
&gt;     res = dfs((node, new_time), info_source_2, new_pressure, new_visited, important_nodes, distances_map)
</span><span class="p">52c58,60
</span><span class="gd">&lt; print(dfs('AA', 0, 0, set(), [(k,v) for k, v in associations.items() if v[0] &gt; 0]))
</span><span class="p">---
</span><span class="gi">&gt; important_elements = [(k,v) for k, v in associations.items() if v[0] &gt; 0]
&gt; distances_map = {k: get_distances(k, associations) for k in associations.keys()}
&gt; print(dfs(('AA', 0), ('AA', 0), 0, set(), important_elements, distances_map))
</span></code></pre></div></div>

<hr />

<h1 id="day-17">Day 17</h1>

<p>Wha…? Is this Tetris?</p>

<h2 id="part-1-16">Part 1</h2>

<p>Yeah, this is almost like tetris. Given a bunch of blocks, which are the horizontal line, cross, L-shape, vertical line and square, we are tasked to get the height of the tetris board after 2022 tetrominos sets on the board. The tetrominos follow a sequence of movements, which is our input; it looks something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt;&gt;&gt;&lt;&lt;&gt;&lt;&gt;&gt;&lt;&lt;&lt;&gt;&gt;&lt;&gt;&gt;&gt;&lt;&lt;&lt;&gt;&gt;&gt;&lt;&lt;&lt;&gt;&lt;&lt;&lt;&gt;&gt;&lt;&gt;&gt;&lt;&lt;&gt;&gt;
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">&gt;</code> stands for right, and <code class="language-plaintext highlighter-rouge">&lt;</code> stands for left. The sequence of movements repeats. The tetrominos themselves follow the standard set of rules, which are:</p>

<ol>
  <li>The tetrinome cannot hit the boundaries of the board (which is defined as width of 7 and height of infinity)</li>
  <li>If the bottom of the tetrinome collides with any other settled tetrinomes, or the bottom of the board, settle the tetrinomes.</li>
</ol>

<p>So, the sub-problems are:</p>

<ol>
  <li>Represent the tetrinomes, define procedures for movement;
    <ol>
      <li>Moving left &amp; right based on sequence</li>
      <li>Moving down after left &amp; right</li>
    </ol>
  </li>
  <li>Figure out if the tetrinome has landed</li>
  <li>Figure out maximum height of the board at the end of 2022 block landings</li>
</ol>

<p>I decided to represent the tetrinome positions as a set of positions, and adjust the positions based on how the block is falling. So, the code is as follows:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">shapes</span> <span class="o">=</span> <span class="p">[[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">)],</span>
  <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)],</span>
  <span class="p">[(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">)],</span>
  <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)],</span>
  <span class="p">[(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]]</span>
<span class="n">offset</span> <span class="o">=</span> <span class="p">{</span>
  <span class="sh">'</span><span class="s">&lt;</span><span class="sh">'</span><span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="sh">'</span><span class="s">&gt;</span><span class="sh">'</span><span class="p">:</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">shape_offset</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">width</span> <span class="o">=</span> <span class="mi">7</span>

<span class="n">sequence</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">read</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
<span class="n">positions</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>

<span class="n">shape_i</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">dropped</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">current_block</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">+</span> <span class="n">shape_offset</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">3</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">shapes</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
<span class="n">board_max_y</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">dropped</span> <span class="o">&lt;</span> <span class="mi">2022</span><span class="p">:</span>
  <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">sequence</span><span class="p">:</span>
    <span class="n">current_block</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">+</span> <span class="n">offset</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">y</span> <span class="o">+</span> <span class="n">offset</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="mi">1</span><span class="p">])</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">current_block</span><span class="p">]</span>
    <span class="n">xs</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">(</span><span class="n">current_block</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">b</span><span class="p">:</span> <span class="n">b</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    <span class="n">ys</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">(</span><span class="n">current_block</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">b</span><span class="p">:</span> <span class="n">b</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">xs</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">min_y</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">=</span> <span class="n">ys</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">ys</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>

    <span class="k">if</span> <span class="n">min_x</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">max_x</span> <span class="o">&gt;=</span> <span class="n">width</span> <span class="ow">or</span> <span class="nf">len</span><span class="p">(</span><span class="nf">set</span><span class="p">(</span><span class="n">current_block</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">):</span>
      <span class="n">current_block</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">-</span> <span class="n">offset</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">y</span> <span class="o">-</span> <span class="n">offset</span><span class="p">[</span><span class="n">s</span><span class="p">][</span><span class="mi">1</span><span class="p">])</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">current_block</span><span class="p">]</span>
    <span class="n">before_down_block</span> <span class="o">=</span> <span class="n">current_block</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
    <span class="n">current_block</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">current_block</span><span class="p">]</span>

    <span class="k">if</span> <span class="n">min_y</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="ow">or</span> <span class="nf">len</span><span class="p">(</span><span class="nf">set</span><span class="p">(</span><span class="n">current_block</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">):</span>
      <span class="n">dropped</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="n">positions</span> <span class="o">|=</span> <span class="nf">set</span><span class="p">(</span><span class="n">before_down_block</span><span class="p">)</span>
      <span class="n">board_max_y</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">positions</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">])[</span><span class="mi">1</span><span class="p">]</span>
      <span class="n">current_block</span> <span class="o">=</span> <span class="p">[(</span><span class="n">x</span> <span class="o">+</span> <span class="n">shape_offset</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">board_max_y</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="n">shapes</span><span class="p">[</span><span class="n">shape_i</span><span class="p">]]</span>
      <span class="n">shape_i</span> <span class="o">=</span> <span class="n">shape_i</span> <span class="o">+</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">shape_i</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">shapes</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="mi">0</span>

    <span class="k">if</span> <span class="n">dropped</span> <span class="o">&gt;=</span> <span class="mi">2022</span><span class="p">:</span>
      <span class="k">break</span>

<span class="nf">print</span><span class="p">(</span><span class="n">board_max_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-16">Part 2</h2>

<p>Ah yes, following the pattern we’ve seen in Day 16, we experience another expansion of the problem statement beyond what is reasonable to do with our original algorithm. Our goal now is simple: instead of getting the height at 2022 blocks, we want 1000000000000 (that’s 12 zeros, which means this is 1 trillion). Obviously, not feasible.</p>

<p>Turns out, this problem can be boiled down into a simple sequencing problem. I began by hypothesizing that at <em>some</em> point, there must be a pattern for height increments; there are a limited number of blocks, and a limited number of sequences. In logical hindsight, this is likely due to the pigeonhole principle - I’ll reach a point where I’m going through the exact same blocks for the exact same sequences.</p>

<p>To confirm this experimentally, I inserted a print statement to figure out if this was true:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">stats</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="bp">...</span>
    <span class="k">if</span> <span class="n">board_max_y</span> <span class="o">-</span> <span class="n">new_max_y</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">stats</span><span class="p">:</span>
      <span class="n">stats</span><span class="p">[</span><span class="n">new_max_y</span> <span class="o">-</span> <span class="n">board_max_y</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">stats</span><span class="p">[</span><span class="n">new_max_y</span> <span class="o">-</span> <span class="n">board_max_y</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>

    <span class="k">if</span> <span class="n">s_i</span> <span class="o">%</span> <span class="nf">len</span><span class="p">(</span><span class="n">sequence</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># on the actual input, this is (s_i + 1) % ...
</span>      <span class="nf">print</span><span class="p">(</span><span class="n">stats</span><span class="p">)</span>
<span class="bp">...</span>
  <span class="nf">print</span><span class="p">(</span><span class="n">stats</span><span class="p">)</span>
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">s_i</code> is the index in the sequence.</p>

<p>I quickly realised that the pattern holds beyond the first statement; this implies that after a certain starting sequence, the sequence <strong>started to repeat</strong>, implying a predictable increase in height for a fixed increase in block drops.</p>

<p>In the code, I added a cheeky little comment that says the actual input would require me to change the condition to <code class="language-plaintext highlighter-rouge">s_i + 1</code>. Why?</p>

<p>Let’s use the actual numbers: the sequence given in the example has 40 tokens, while the sequence given in the actual input has 10091 tokens. <code class="language-plaintext highlighter-rouge">s_i</code> is bounded from 0 to 39 in the example, while <code class="language-plaintext highlighter-rouge">s_i</code> is bounded from 0 to 10090 in the actual input. Hence, <code class="language-plaintext highlighter-rouge">s_i % len(sequence) == 0</code> is only true when <code class="language-plaintext highlighter-rouge">s_i</code> is any multiple of 40 in the example, while <code class="language-plaintext highlighter-rouge">(s_i + 1) % len(sequence) == 0</code> is true when <code class="language-plaintext highlighter-rouge">s_i</code> is only true when <code class="language-plaintext highlighter-rouge">s_i</code> is a multiple of 10090. This is not a co-incidence, because 40 and 10090 are divisible by the number of possible blocks in the context, 5 (also each number’s greatest common factor).</p>

<p>Intuitively, this means that at <code class="language-plaintext highlighter-rouge">40</code> and <code class="language-plaintext highlighter-rouge">10090</code> sequences respectively, it encapsulates a multiple of block drops for all 5 bocks perfectly. Remember the pigeonhole principle? Let’s say I have 20 pigeons and 10 holes. If we dictate each pigeon to always fly into adjacent holes, then necessarily, each hole must have 2 pigeons (without dictating the behaviour of the pigeon, we could have 1 hole with 19 pigeons). The same applies for this context; 40 sequences and 5 blocks, where each sequence will always apply to the next block in order, then necessarily, each block must have 8 sequences associated to it, always.</p>

<p>So, with sequences repeating every <code class="language-plaintext highlighter-rouge">40</code> or <code class="language-plaintext highlighter-rouge">10090</code> sequences, we can <strong>bypass the need to simulate falling tetromines</strong>, and just simulate height differences instead.</p>

<p>Okay, so we now have the theory. How do we translate this to practice?</p>

<p>Turns out, we are able to “shortcut” most of 1000000000000 block drops, by estimating as much as we can with just pure mathematics.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">estimated_height</span> <span class="o">=</span> <span class="nf">int</span><span class="p">((</span><span class="mi">1000000000000</span> <span class="o">-</span> <span class="n">blocks_1</span><span class="p">)</span> <span class="o">/</span> <span class="n">blocks_difference</span><span class="p">)</span> <span class="o">*</span> <span class="n">repeat_height_difference</span>
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">blocks_difference</code> is the number of blocks dropped from a sequence of <code class="language-plaintext highlighter-rouge">40</code> to <code class="language-plaintext highlighter-rouge">80</code>, or <code class="language-plaintext highlighter-rouge">10090</code> to <code class="language-plaintext highlighter-rouge">20181</code>, and <code class="language-plaintext highlighter-rouge">repeat_height_difference</code> is the height difference between two repeating sequences. I will discuss how to get this later.</p>

<p>Then, we process the rest of the blocks using the sequences we derived:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">remaining_blocks</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1000000000000</span> <span class="o">-</span> <span class="n">blocks_1</span><span class="p">)</span> <span class="o">%</span> <span class="n">blocks_difference</span>

<span class="n">remaining_height</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">height_epoch_x_i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">remaining_blocks</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
  <span class="n">remaining_height</span> <span class="o">+=</span> <span class="n">height_epoch_x</span><span class="p">[</span><span class="n">height_epoch_x_i</span><span class="p">]</span>
  <span class="n">remaining_blocks</span> <span class="o">-=</span> <span class="mi">1</span>
  <span class="k">if</span> <span class="n">height_epoch_x_i</span> <span class="o">&gt;=</span> <span class="nf">len</span><span class="p">(</span><span class="n">height_epoch_x</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
    <span class="n">height_epoch_x_i</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">else</span><span class="p">:</span>
    <span class="n">height_epoch_x_i</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">height_epoch_x</code> is the height difference per sequence.</p>

<p>Now, how do we get <code class="language-plaintext highlighter-rouge">blocks_difference</code>, <code class="language-plaintext highlighter-rouge">height_epoch_x</code>, and <code class="language-plaintext highlighter-rouge">repeat_height_difference</code>? We know from experimental data that starting from a <em>certain number of blocks</em>, the sequence holds. Hence, we need to acquire this <em>certain number of blocks</em>, which is tied to the number of sequences processed (they need to be multiples of <code class="language-plaintext highlighter-rouge">40</code> or <code class="language-plaintext highlighter-rouge">10090</code>) and then continue simulating metronomes until we get the sequences from one multiple of <code class="language-plaintext highlighter-rouge">40</code> / <code class="language-plaintext highlighter-rouge">10090</code> to the next.</p>

<p>Hence, the code diff to get the final answer is as follows:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">15a16
</span><span class="gi">&gt; # Figuring out the pattern
</span><span class="p">16a18
</span><span class="gi">&gt; s_i = 0
</span><span class="p">20,38c22,35
</span><span class="gd">&lt; while dropped &lt; 2022:
&lt;   for s in sequence:
&lt;     current_block = [(x + offset[s][0], y + offset[s][1]) for x, y in current_block]
&lt;     xs = sorted(current_block, key=lambda b: b[0])
&lt;     ys = sorted(current_block, key=lambda b: b[1])
&lt;     min_x, max_x = xs[0][0], xs[-1][0]
&lt;     min_y, max_y = ys[0][1], ys[-1][1]
&lt;
&lt;     if min_x &lt; 0 or max_x &gt;= width or len(set(current_block) &amp; positions):
&lt;       current_block = [(x - offset[s][0], y - offset[s][1]) for x, y in current_block]
&lt;     before_down_block = current_block.copy()
&lt;     current_block = [(x, y - 1) for x, y in current_block]
&lt;
&lt;     if min_y &lt;= 0 or len(set(current_block) &amp; positions):
&lt;       dropped += 1
&lt;       positions |= set(before_down_block)
&lt;       board_max_y = max(positions, key=lambda x: x[1])[1]
&lt;       current_block = [(x + shape_offset, y + board_max_y + 4) for x, y in shapes[shape_i]]
&lt;       shape_i = shape_i + 1 if shape_i &lt; len(shapes) - 1 else 0
</span><span class="p">---
</span><span class="gi">&gt; runs = 2
&gt; height_epoch_1 = []
&gt; height_epoch_x = []
&gt; si_1 = 0
&gt; si_difference = 0
&gt; blocks_1 = 0
&gt; blocks_difference = 0
&gt; while runs &gt; 0:
&gt;   s = sequence[s_i % len(sequence)]
&gt;   current_block = [(x + offset[s][0], y + offset[s][1]) for x, y in current_block]
&gt;   xs = sorted(current_block, key=lambda b: b[0])
&gt;   ys = sorted(current_block, key=lambda b: b[1])
&gt;   min_x, max_x = xs[0][0], xs[-1][0]
&gt;   min_y, max_y = ys[0][1], ys[-1][1]
</span><span class="p">40,41c37,45
</span><span class="gd">&lt;     if dropped &gt;= 2022:
&lt;       break
</span><span class="p">---
</span><span class="gi">&gt;   if min_x &lt; 0 or max_x &gt;= width or len(set(current_block) &amp; positions):
&gt;     current_block = [(x - offset[s][0], y - offset[s][1]) for x, y in current_block]
&gt;   before_down_block = current_block.copy()
&gt;   current_block = [(x, y - 1) for x, y in current_block]
&gt;
&gt;   if min_y &lt;= 0 or len(set(current_block) &amp; positions):
&gt;     dropped += 1
&gt;     positions |= set(before_down_block)
&gt;     new_max_y = max(positions, key=lambda x: x[1])[1]
</span><span class="p">43c47,86
</span><span class="gd">&lt; print(board_max_y + 1)
</span><span class="p">---
</span><span class="gi">&gt;     if runs == 2:
&gt;       height_epoch_1.append(new_max_y - board_max_y)
&gt;     else:
&gt;       height_epoch_x.append(new_max_y - board_max_y)
&gt;
&gt;     if (s_i + 1) % len(sequence) == 0:
&gt;       if runs == 2:
&gt;         si_1 = s_i
&gt;         blocks_1 = dropped
&gt;       else:
&gt;         si_difference = s_i - si_1
&gt;         blocks_difference = dropped - blocks_1
&gt;       runs -= 1
&gt;     board_max_y = max(positions, key=lambda x: x[1])[1]
&gt;     current_block = [(x + shape_offset, y + board_max_y + 4) for x, y in shapes[shape_i]]
&gt;     shape_i = shape_i + 1 if shape_i &lt; len(shapes) - 1 else 0
&gt;
&gt;   s_i += 1
&gt;   if runs &lt;= 0:
&gt;     break
&gt;
&gt; # Use the pattern to engineer the simulation
&gt; repeat_start_height = len(height_epoch_1)
&gt; repeat_height_difference = sum(height_epoch_x)
&gt;
&gt; estimated_height = int((1000000000000 - blocks_1) / blocks_difference) * repeat_height_difference
&gt; remaining_blocks = (1000000000000 - blocks_1) % blocks_difference
&gt;
&gt; remaining_height = 0
&gt; height_epoch_x_i = 0
&gt; while remaining_blocks &gt;= 0:
&gt;   remaining_height += height_epoch_x[height_epoch_x_i]
&gt;   remaining_blocks -= 1
&gt;   if height_epoch_x_i &gt;= len(height_epoch_x) - 1:
&gt;     height_epoch_x_i = 0
&gt;   else:
&gt;     height_epoch_x_i += 1
&gt;
&gt; height = int(estimated_height) + remaining_height + sum(height_epoch_1)
&gt; print(height)
</span></code></pre></div></div>

<hr />

<h1 id="day-18">Day 18</h1>

<p>Lava and whatnot, oh my!</p>

<h2 id="part-1-17">Part 1</h2>

<p>So, we have a bunch of positions that represent whether it contains lava particles. We want to find the surface area of the lava particles that make up the water droplets.</p>

<p>The problem, in programmer terms, is to accumulate (6 - number of edges) in all possible vertices of a graph.</p>

<p>This straight-forward problem is broken down into a graph problem, which can be traversed using any of the graph traversal algorithms. Each missing edge (i.e. 6 - number of edges) count towards a global variable, which represents the solution.</p>

<p>So, the solution is as follows:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">queue</span> <span class="kn">import</span> <span class="n">Queue</span>
<span class="n">positions</span> <span class="o">=</span> <span class="nf">set</span><span class="p">([</span><span class="nf">tuple</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nf">int</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">)))</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()])</span>

<span class="n">possibilities</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
  <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="p">]</span>
<span class="n">area</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">visited</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
<span class="n">q</span> <span class="o">=</span> <span class="nc">Queue</span><span class="p">()</span>

<span class="k">while</span> <span class="nf">len</span><span class="p">(</span><span class="n">visited</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">)</span> <span class="o">!=</span> <span class="nf">len</span><span class="p">(</span><span class="n">positions</span><span class="p">):</span>
  <span class="n">d</span> <span class="o">=</span> <span class="p">(</span><span class="n">positions</span> <span class="o">-</span> <span class="n">visited</span><span class="p">).</span><span class="nf">pop</span><span class="p">()</span>
  <span class="n">visited</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
  <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>

  <span class="k">while</span> <span class="ow">not</span> <span class="n">q</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
    <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>

    <span class="nf">for </span><span class="p">(</span><span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">,</span> <span class="n">dz</span><span class="p">)</span> <span class="ow">in</span> <span class="n">possibilities</span><span class="p">:</span>
      <span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">,</span> <span class="n">nz</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">dx</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">dy</span><span class="p">,</span> <span class="n">z</span> <span class="o">+</span> <span class="n">dz</span>
      <span class="nf">if </span><span class="p">(</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">,</span> <span class="n">nz</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">positions</span><span class="p">:</span>
        <span class="n">area</span> <span class="o">+=</span> <span class="mi">1</span>
      <span class="nf">elif </span><span class="p">(</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">,</span> <span class="n">nz</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
        <span class="n">visited</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">,</span> <span class="n">nz</span><span class="p">))</span>
        <span class="n">q</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">nx</span><span class="p">,</span> <span class="n">ny</span><span class="p">,</span> <span class="n">nz</span><span class="p">))</span>

<span class="nf">print</span><span class="p">(</span><span class="n">area</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-17">Part 2</h2>

<p>Now, we want to only find the external surface area; meaning, any surface area that is surrounded by lava should not be considered. There were two main ways I could approach this:</p>

<ol>
  <li>From each of the positions of the nodes, if the node can reach <code class="language-plaintext highlighter-rouge">0</code> in any dimension, or the maximum of any dimension, then accumulate the area. Otherwise, don’t accumulate the area.</li>
  <li>I perform BFS on everything outside the positions of the nodes instead. If the node is touching a position, then count that into our area. As BFS can only explore connected nodes, this means that each time <code class="language-plaintext highlighter-rouge">q</code> is empty, we have explored one connected body. A connected body that touches <code class="language-plaintext highlighter-rouge">0</code> in any dimension or maximum in any dimension must be a liquid / water vapour. Otherwise, it is trapped gas between all the positions.</li>
</ol>

<p>I decided to do step 2. So, I broke down the problem as:</p>

<ol>
  <li>Get complement of the set of lava-filled positions</li>
  <li>Add padding of at least 1 in all dimensions</li>
  <li>Run through DFS, add extra condition that if the node is touching a position, count into area.</li>
</ol>

<p>So the diff to implement part 2 is:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">2c2,16
</span><span class="gd">&lt; positions = set([tuple(map(lambda x: int(x), line.strip().split(','))) for line in open('input.txt', 'r').readlines()])
</span><span class="p">---
</span><span class="gi">&gt; from itertools import product
&gt; positions = set()
&gt; max_x, max_y, max_z = 0, 0, 0
&gt; with open('input.txt', 'r') as f:
&gt;   line = f.readline().strip()
&gt;   while line:
&gt;     pos = tuple(map(lambda x: int(x), line.split(',')))
&gt;     max_x = max(max_x, pos[0])
&gt;     max_y = max(max_y, pos[1])
&gt;     max_z = max(max_z, pos[2])
&gt;
&gt;     positions.add(pos)
&gt;     line = f.readline().strip()
&gt;
&gt; positions_prime = {(x, y, z) for x, y, z in product(range(-1, max_x + 2), range(-1, max_y + 2), range(-1, max_z + 2)) if (x, y, z) not in positions}
</span><span class="p">12,13c26,27
</span><span class="gd">&lt; while len(visited &amp; positions) != len(positions):
&lt;   d = (positions - visited).pop()
</span><span class="p">---
</span><span class="gi">&gt; while len(visited &amp; positions_prime) != len(positions_prime):
&gt;   d = (positions_prime - visited).pop()
</span><span class="p">16a31,32
</span><span class="gi">&gt;   isOutside = False
&gt;   areaInContact = 0
</span><span class="p">19a36,38
</span><span class="gi">&gt;     if not (0 &lt; x &lt; max_x and 0 &lt; y &lt; max_y and 0 &lt; z &lt; max_z):
&gt;       isOutside = True
&gt;
</span><span class="p">22,24c41,44
</span><span class="gd">&lt;       if (nx, ny, nz) not in positions:
&lt;         area += 1
&lt;       elif (nx, ny, nz) not in visited:
</span><span class="p">---
</span><span class="gi">&gt;       if (nx, ny, nz) in positions:
&gt;         areaInContact += 1
&gt;
&gt;       if (nx, ny, nz) not in visited and (nx, ny, nz) in positions_prime:
</span><span class="p">26a47,49
</span><span class="gi">&gt;
&gt;   if isOutside:
&gt;     area += areaInContact
</span></code></pre></div></div>

<hr />

<h1 id="day-19">Day 19</h1>

<p>Right, another entry down for the count. This day was quite similar to Day 16, because it involves doing a search on a space that is too large for comfort.</p>

<h2 id="part-1-18">Part 1</h2>

<p>So, we have a blueprint, which are defined like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Blueprint 1:
  Each ore robot costs 4 ore.
  Each clay robot costs 2 ore.
  Each obsidian robot costs 3 ore and 14 clay.
  Each geode robot costs 2 ore and 7 obsidian.

Blueprint 2:
  Each ore robot costs 2 ore.
  Each clay robot costs 3 ore.
  Each obsidian robot costs 3 ore and 8 clay.
  Each geode robot costs 3 ore and 12 obsidian.
</code></pre></div></div>

<p>That allows us to build robots that gather resources to build even more robots to gather even more resources and so on. We start with 1 ore robot, and each robot will take 1 time unit to build, before it can contribute to our resource pool. Our goal is to calculate a score for each of the blueprint, and print out a linear combination of the score. The score is defined as the number of geodes the blueprint can possibly generate within 24 units of time.</p>

<p>Following the pattern I saw in Day 16, I quickly eliminated the typical graph search algorithms, and decided that DFS was the way to go. So, let’s think about <em>when</em> we want to call DFS.</p>

<p>A direct approach would be to, for every time unit, call DFS in an attempt to expend resources build every type of robot. Instinctively, I knew that this search space was too huge to consider.</p>

<p>Instead, we have to do a <em>good enough</em> approximation of what <em>may</em> happen. Here are some considerations:</p>

<ul>
  <li>If I’m being optimal about my robot-building, which I must be due to time unit limitation, I <em>should</em> only have enough resources to build a maximum of one robot per time unit. This eliminates the assumption that I could potentially concurrently build multiple robots in one go, as doing so would imply saving up for resources, which reduces contributions from the robots that could already have been built over the 24 time units.</li>
  <li>If, at any point of time, I am able to build a geode robot, I <strong>must</strong> do so, since I’m trying to maximize the number of geodes. I don’t even have to consider building any other robot in that minute.</li>
  <li>If, at any point of time, I am able to build a obsidian robot, I <em>should</em> do so, and ignore every other possibility. This assumption very rarely is false, as it is possible to save the resources for a greater benefit down the road. For this part, I assume the latter.</li>
  <li>If I am able to build a clay or ore robot, I should also consider saving resources for a greater benefit down the road.</li>
  <li>If, at any point of time, I have too much ore (which happens because DFS may keep choosing to save resources / build ore robots), I can completely prune the branch, because I am definitely getting further away from the objective.</li>
</ul>

<p>The considerations help to reduce the search space into something that is completes within reasonable time (~10 minutes), and doesn’t waste the CPU cycle looking at graphs that don’t matter in the grand scheme of things. Putting together all the considerations, and some trial and error later, I end up with the following implementation:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">blueprints</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
    <span class="n">blueprints</span><span class="p">.</span><span class="nf">append</span><span class="p">((</span><span class="nf">int</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">6</span><span class="p">]),</span> <span class="nf">int</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">12</span><span class="p">]),</span> <span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">18</span><span class="p">]),</span> <span class="nf">int</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">21</span><span class="p">])),</span> <span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">27</span><span class="p">]),</span> <span class="nf">int</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">30</span><span class="p">]))))</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="n">resources</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">bot_count</span><span class="o">=</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">new_bot_count</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">minutes</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
  <span class="n">best_quality</span> <span class="o">=</span> <span class="p">(</span><span class="n">resources</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">resources</span><span class="p">,</span> <span class="n">bot_count</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">minutes</span> <span class="o">&gt;</span> <span class="mi">24</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">best_quality</span>

  <span class="n">ores</span><span class="p">,</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span> <span class="o">=</span> <span class="n">resources</span>
  <span class="n">ore_bots</span><span class="p">,</span> <span class="n">clay_bots</span><span class="p">,</span> <span class="n">obsidian_bots</span><span class="p">,</span> <span class="n">geode_bots</span> <span class="o">=</span> <span class="n">bot_count</span>

  <span class="n">ores</span> <span class="o">+=</span> <span class="n">ore_bots</span>
  <span class="n">clays</span> <span class="o">+=</span> <span class="n">clay_bots</span>
  <span class="n">obsidians</span> <span class="o">+=</span> <span class="n">obsidian_bots</span>
  <span class="n">geodes</span> <span class="o">+=</span> <span class="n">geode_bots</span>

  <span class="n">bot_count</span> <span class="o">=</span> <span class="nf">tuple</span><span class="p">(</span><span class="nf">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="nf">zip</span><span class="p">(</span><span class="n">bot_count</span><span class="p">,</span> <span class="n">new_bot_count</span><span class="p">)))</span>

  <span class="n">minutes</span> <span class="o">+=</span> <span class="mi">1</span>
  <span class="k">if</span> <span class="n">minutes</span> <span class="o">==</span> <span class="mi">24</span><span class="p">:</span>
    <span class="nf">return </span><span class="p">(</span><span class="n">geodes</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span><span class="p">,</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span><span class="p">),</span> <span class="n">bot_count</span><span class="p">)</span>

  <span class="n">maximum_ores_required</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">blueprint</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">3</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
  <span class="k">if</span> <span class="n">ores</span> <span class="o">&gt;=</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">3</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">obsidians</span> <span class="o">&gt;=</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">3</span><span class="p">][</span><span class="mi">1</span><span class="p">]:</span>
    <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span> <span class="o">-</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">3</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span> <span class="o">-</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">3</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">geodes</span><span class="p">),</span>
      <span class="n">bot_count</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="n">minutes</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">best_quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
      <span class="n">best_quality</span> <span class="o">=</span> <span class="n">quality</span>
  <span class="k">elif</span> <span class="n">ores</span> <span class="o">&gt;=</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">clays</span> <span class="o">&gt;=</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">1</span><span class="p">]:</span>
    <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span> <span class="o">-</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">clays</span> <span class="o">-</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span><span class="p">),</span>
      <span class="n">bot_count</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">minutes</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">best_quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
      <span class="n">best_quality</span> <span class="o">=</span> <span class="n">quality</span>

    <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span><span class="p">,</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span><span class="p">),</span> <span class="n">bot_count</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">minutes</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">best_quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
      <span class="n">best_quality</span> <span class="o">=</span> <span class="n">quality</span>
  <span class="k">else</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">ores</span> <span class="o">&gt;=</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
      <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span> <span class="o">-</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span><span class="p">),</span>
        <span class="n">bot_count</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">minutes</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">best_quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
        <span class="n">best_quality</span> <span class="o">=</span> <span class="n">quality</span>

    <span class="c1"># snapback pruning: don't accumulate just ores
</span>    <span class="k">if</span> <span class="n">ores</span> <span class="o">&gt;=</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">and</span> <span class="n">ores</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">maximum_ores_required</span><span class="p">:</span>
      <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span> <span class="o">-</span> <span class="n">blueprint</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span><span class="p">),</span>
        <span class="n">bot_count</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">minutes</span><span class="p">)</span>
      <span class="k">if</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">best_quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
        <span class="n">best_quality</span> <span class="o">=</span> <span class="n">quality</span>

    <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">,</span> <span class="p">(</span><span class="n">ores</span><span class="p">,</span> <span class="n">clays</span><span class="p">,</span> <span class="n">obsidians</span><span class="p">,</span> <span class="n">geodes</span><span class="p">),</span> <span class="n">bot_count</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">minutes</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">best_quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
      <span class="n">best_quality</span> <span class="o">=</span> <span class="n">quality</span>

  <span class="k">return</span> <span class="n">best_quality</span>

<span class="n">accum_quality</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">blueprint</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">blueprints</span><span class="p">):</span>
  <span class="n">quality</span> <span class="o">=</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">blueprint</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">quality</span><span class="p">)</span>
  <span class="n">accum_quality</span> <span class="o">+=</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">quality</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="nf">print</span><span class="p">(</span><span class="n">accum_quality</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-18">Part 2</h2>

<p>The question increased the depth of the tree by adjusting the time unit from 24 to 32, and cutting down the number of blueprints to search to 3. This is a significant adjustment, as increasing tree depth exponentially increases the number of nodes to traverse. Hence, to create an algorithm that completes within reasonable time, we need to make even more assumptions of what <em>may</em> happen.</p>

<p>Based on observations, there are only a <em>few</em> blueprints with its highest number of geodes actually depending on saving resources whenever it could build an obsidian robot instead. Furthermore, by adjusting the time unit to 32, time can be wisely spent to build an obsidian robot, then providing resources to build a geode robot. Hence, the probability to save for resources when it could build an obsidian robot is decreased drastically.</p>

<p>As it turns out, the assumptions are true in our particular case, and removing just that one possibility from the previous algorithm allowed my solution to complete within human time (also, the scoring function changed as required by the question):</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">11c11
</span><span class="gd">&lt;   if minutes &gt; 24:
</span><span class="p">---
</span><span class="gi">&gt;   if minutes &gt; 32:
</span><span class="p">25c25
</span><span class="gd">&lt;   if minutes == 24:
</span><span class="p">---
</span><span class="gi">&gt;   if minutes == 32:
</span><span class="p">39,42d38
</span><span class="gd">&lt;
&lt;     quality = dfs(blueprint, (ores, clays, obsidians, geodes), bot_count, (0, 0, 0, 0), minutes)
&lt;     if quality[0] &gt; best_quality[0]:
&lt;       best_quality = quality
</span><span class="p">63c59
</span><span class="gd">&lt; accum_quality = 0
</span><span class="p">---
</span><span class="gi">&gt; accum_quality = 1
</span><span class="p">67c63
</span><span class="gd">&lt;   accum_quality += (i + 1) * quality[0]
</span><span class="p">---
</span><span class="gi">&gt;   accum_quality *= quality[0]
</span></code></pre></div></div>

<p>Is there a faster way? Probably, using heuristics and the other search algorithms. Do I want to implement it? Not this year!</p>

<hr />

<h1 id="day-20">Day 20</h1>

<p>This puzzle highlights the power of Python, because I don’t have to think about huge numbers at all. Having done Days 17, 18and 20 in a row, I didn’t bother trying to make the code run faster in Day 20; it’s plenty fast compared to all the graph traversal I’ve done!</p>

<h2 id="part-1-19">Part 1</h2>

<p>This problem is one of the easier ones among all of the challenges in AOC 2022 so far. Essentially, given a list of numbers, we need to rearrange the numbers such that each of the numbers are moved according to the value they represent. So, I solved it by:</p>

<ol>
  <li>Adding an identifier to every number (since the numbers provided are not unique)</li>
  <li>Duplicating that list, calling it <code class="language-plaintext highlighter-rouge">mutable</code></li>
  <li>Referencing the original list, remove and insert the numbers in <code class="language-plaintext highlighter-rouge">mutable</code> based on the value of the int</li>
  <li>Access the elements in the array based on procedure described by the solution as required, and calculate the answer</li>
</ol>

<p>Here is code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">movements</span> <span class="o">=</span> <span class="p">[(</span><span class="n">i</span><span class="p">,</span> <span class="nf">int</span><span class="p">(</span><span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">()))</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">())]</span>
<span class="n">mutable</span> <span class="o">=</span> <span class="n">movements</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
<span class="n">zero_tuple</span> <span class="o">=</span> <span class="nf">tuple</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">movements</span><span class="p">:</span>
  <span class="n">ind</span> <span class="o">=</span> <span class="n">mutable</span><span class="p">.</span><span class="nf">index</span><span class="p">((</span><span class="n">i</span><span class="p">,</span> <span class="n">m</span><span class="p">))</span>
  <span class="n">mutable</span><span class="p">.</span><span class="nf">pop</span><span class="p">(</span><span class="n">ind</span><span class="p">)</span>

  <span class="n">new_ind</span> <span class="o">=</span> <span class="n">ind</span> <span class="o">+</span> <span class="n">m</span>
  <span class="k">if</span> <span class="n">new_ind</span> <span class="o">&gt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">movements</span><span class="p">):</span>
    <span class="n">new_ind</span> <span class="o">%=</span> <span class="nf">len</span><span class="p">(</span><span class="n">movements</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
  <span class="k">elif</span> <span class="n">new_ind</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
    <span class="n">new_ind</span> <span class="o">+=</span> <span class="nf">len</span><span class="p">(</span><span class="n">movements</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
  <span class="n">mutable</span><span class="p">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">new_ind</span><span class="p">,</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">m</span><span class="p">))</span>

  <span class="k">if</span> <span class="n">m</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
    <span class="n">zero_tuple</span> <span class="o">=</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span>
<span class="n">zero_ind</span> <span class="o">=</span> <span class="n">mutable</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">zero_tuple</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="n">mutable</span><span class="p">[(</span><span class="n">zero_ind</span> <span class="o">+</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">%</span> <span class="nf">len</span><span class="p">(</span><span class="n">movements</span><span class="p">)][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span>
  <span class="n">mutable</span><span class="p">[(</span><span class="n">zero_ind</span> <span class="o">+</span> <span class="mi">2000</span><span class="p">)</span> <span class="o">%</span> <span class="nf">len</span><span class="p">(</span><span class="n">movements</span><span class="p">)][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span>
  <span class="n">mutable</span><span class="p">[(</span><span class="n">zero_ind</span> <span class="o">+</span> <span class="mi">3000</span><span class="p">)</span> <span class="o">%</span> <span class="nf">len</span><span class="p">(</span><span class="n">movements</span><span class="p">)][</span><span class="mi">1</span><span class="p">])</span>
</code></pre></div></div>

<h2 id="part-2-19">Part 2</h2>

<p>The only thing that changed were the input numbers. In Python, integers have no bounds. Then, we just perform the mixing operation 10 times, so the code is almost the same, but indented to fit the new for loop:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">1c1
</span><span class="gd">&lt; movements = [(i, int(line.strip())) for i, line in enumerate(open('input.txt', 'r').readlines())]
</span><span class="p">---
</span><span class="gi">&gt; movements = [(i, 811589153 * int(line.strip())) for i, line in enumerate(open('input.txt', 'r').readlines())]
</span><span class="p">4,6c4,7
</span><span class="gd">&lt; for i, m in movements:
&lt;   ind = mutable.index((i, m))
&lt;   mutable.pop(ind)
</span><span class="p">---
</span><span class="gi">&gt; for _ in range(10):
&gt;   for i, m in movements:
&gt;     ind = mutable.index((i, m))
&gt;     mutable.pop(ind)
</span><span class="p">8,13c9,16
</span><span class="gd">&lt;   new_ind = ind + m
&lt;   if new_ind &gt; len(movements):
&lt;     new_ind %= len(movements) - 1
&lt;   elif new_ind &lt;= 0:
&lt;     new_ind += len(movements) - 1
&lt;   mutable.insert(new_ind, (i, m))
</span><span class="p">---
</span><span class="gi">&gt;     new_ind = ind + m
&gt;     if new_ind &gt; len(movements):
&gt;       new_ind %= len(movements) - 1
&gt;     elif new_ind &lt;= 0:
&gt;       new_ind += len(movements) - 1
&gt;       factor = ((-new_ind) // (len(movements) - 1)) + 1
&gt;       new_ind += (factor * (len(movements) - 1))
&gt;     mutable.insert(new_ind, (i, m))
</span><span class="p">15,16c18,19
</span><span class="gd">&lt;   if m == 0:
&lt;     zero_tuple = (i, m)
</span><span class="p">---
</span><span class="gi">&gt;     if m == 0:
&gt;       zero_tuple = (i, m)
</span></code></pre></div></div>

<h2 id="note-on-optimization">Note on optimization</h2>

<p>If I were to optimize this, it’ll probably be similar to Day 17; since finite sequences are involved, repeats are bound to happen. However, the effort-to-result ratio is probably not worth it.</p>

<hr />

<h1 id="day-21">Day 21</h1>

<p>Today we have expression evaluations. It’s quite a simple day, although I spent an embarrassing amount of time trying to figure out why my part 2 solution didn’t work. More below.</p>

<h2 id="part-1-20">Part 1</h2>

<p>We have a bunch of expressions that uses a bunch of symbols, like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32
</code></pre></div></div>

<p>All we have to do is to evaluate the value at <code class="language-plaintext highlighter-rouge">root</code>. Quite immediately, I got reminded of Prolog, which is a logic programming language that work on constraints. From what I know, Prolog does a depth-first search to obtain the results based on the constraints defined just like our input.</p>

<p>So, I thought about using trees to express the expression. However, I quickly realised that it would take too much effort; instead, a much faster way is probably to use a hash table, where the key is the symbol to be evaluated, and the value is the expression to evaluate.</p>

<p>Then, I jump to the root symbol, and recursively evaluate the constituent symbols until I figure out the final answer. Seems simple enough!</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">operation_map</span> <span class="o">=</span> <span class="p">{</span>
  <span class="sh">'</span><span class="s">+</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">-</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">/</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">*</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span>
<span class="p">}</span>

<span class="n">expressions</span> <span class="o">=</span> <span class="p">{</span><span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">:</span><span class="sh">'</span><span class="p">):</span> <span class="n">expr</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="k">for</span> <span class="n">expr</span> <span class="ow">in</span> <span class="p">[</span><span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">).</span><span class="nf">readlines</span><span class="p">()]}</span>
<span class="k">def</span> <span class="nf">evaluate</span><span class="p">(</span><span class="n">expr</span><span class="p">):</span>
  <span class="k">if</span> <span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">isdigit</span><span class="p">():</span>
    <span class="k">return</span> <span class="nf">int</span><span class="p">(</span><span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
  <span class="k">else</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">operation_map</span><span class="p">[</span><span class="n">expr</span><span class="p">[</span><span class="mi">1</span><span class="p">]](</span><span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">]]),</span>
      <span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="n">expr</span><span class="p">[</span><span class="mi">2</span><span class="p">]]))</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="sh">'</span><span class="s">root</span><span class="sh">'</span><span class="p">])))</span>
</code></pre></div></div>

<h2 id="part-2-20">Part 2</h2>

<p>Part 2 redefines the problem:</p>

<ul>
  <li>We now have an unknown within the symbols, which is <code class="language-plaintext highlighter-rouge">humn</code> (the original value of <code class="language-plaintext highlighter-rouge">humn</code> is now discarded)</li>
  <li><code class="language-plaintext highlighter-rouge">root</code> is now <code class="language-plaintext highlighter-rouge">a = b</code>.</li>
</ul>

<p>I decided to approach the problem mathematically, by performing inverse operations. Suppose we have an equation, <code class="language-plaintext highlighter-rouge">a = b op c</code>, where <code class="language-plaintext highlighter-rouge">a</code>, <code class="language-plaintext highlighter-rouge">b</code>, and <code class="language-plaintext highlighter-rouge">c</code> are unknowns. If we want to find the value of <code class="language-plaintext highlighter-rouge">b</code>, then we can rearrange the equation as: <code class="language-plaintext highlighter-rouge">b = a 'op c</code>. Then, we see how the new <code class="language-plaintext highlighter-rouge">root</code> fits into the picture; since <code class="language-plaintext highlighter-rouge">root</code> is essentially <code class="language-plaintext highlighter-rouge">lhs = rhs</code>, this implies that if:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root: a = c
a: x + y
c: b + z
</code></pre></div></div>

<p>If, again, we want to find <code class="language-plaintext highlighter-rouge">b</code>, then <code class="language-plaintext highlighter-rouge">b = c - z</code>, and since <code class="language-plaintext highlighter-rouge">root: a = c</code>, so <code class="language-plaintext highlighter-rouge">b = a - z</code>, therefore <code class="language-plaintext highlighter-rouge">b = x + y - z</code>. So this means we need to consider the following to change our equation:</p>

<ol>
  <li>Store associations in three different variants: <code class="language-plaintext highlighter-rouge">symbol = left op right</code>, <code class="language-plaintext highlighter-rouge">left = symbol op' right</code> and <code class="language-plaintext highlighter-rouge">right = symbol op' left</code>;</li>
  <li>Figure out the rules for how to get <code class="language-plaintext highlighter-rouge">op'</code>;</li>
  <li>For a target node, i.e. the variable <code class="language-plaintext highlighter-rouge">humn</code> in our case, find it within the associations <code class="language-plaintext highlighter-rouge">left = ...</code> or <code class="language-plaintext highlighter-rouge">right = ...</code></li>
  <li>Then, recursively evaluate <code class="language-plaintext highlighter-rouge">symbol</code> within the associations <code class="language-plaintext highlighter-rouge">left = ...</code> and <code class="language-plaintext highlighter-rouge">right = ...</code>. This will inverse our operators. For <code class="language-plaintext highlighter-rouge">right</code>, use the normal association <code class="language-plaintext highlighter-rouge">symbol = ...</code> to evaluate it.</li>
  <li>If we find that <code class="language-plaintext highlighter-rouge">symbol</code> is root, we evaluate the other operand with the normal association <code class="language-plaintext highlighter-rouge">symbol = ...</code>. This essentially does the operation <code class="language-plaintext highlighter-rouge">left = right</code> within our evaluation.</li>
  <li>Finally, once all the functions return, we end up with what <code class="language-plaintext highlighter-rouge">humn</code> must be.</li>
</ol>

<p>The main assumption being made here is that the input cannot repeat a symbol twice (specifically, not the target symbol we are finding). Otherwise, the inverse operation approach here probably wouldn’t work.</p>

<p>Next, let’s figure out the rules to get <code class="language-plaintext highlighter-rouge">op</code>:</p>

<ul>
  <li>If it’s <code class="language-plaintext highlighter-rouge">+</code>, then transmute it to <code class="language-plaintext highlighter-rouge">symbol - operand</code></li>
  <li>If it’s <code class="language-plaintext highlighter-rouge">*</code>, then transmute it to <code class="language-plaintext highlighter-rouge">symbol / operand</code></li>
  <li>If it’s <code class="language-plaintext highlighter-rouge">-</code>, then transmute it to <code class="language-plaintext highlighter-rouge">symbol + right_operand</code> and <code class="language-plaintext highlighter-rouge">symbol _ left_operand</code>, where <code class="language-plaintext highlighter-rouge">_</code> effectively performs <code class="language-plaintext highlighter-rouge">left_operand - symbol</code>. I forgot to this, which caused me an hour or so to discover, as this does not affect the example input :sweat_smile:</li>
  <li>If it’s <code class="language-plaintext highlighter-rouge">/</code>, then transmute it to <code class="language-plaintext highlighter-rouge">symbol * right_operand</code> and <code class="language-plaintext highlighter-rouge">symbol \ left_operand</code> where <code class="language-plaintext highlighter-rouge">\</code> effectively performs <code class="language-plaintext highlighter-rouge">left_operand / symbol</code>. I remembered this, but unluckily for me it wasn’t used at all</li>
</ul>

<p>With that out of the way, we can finally implement it:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">operation_map</span> <span class="o">=</span> <span class="p">{</span>
  <span class="sh">'</span><span class="s">+</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">-</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">_</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">y</span> <span class="o">-</span> <span class="n">x</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">/</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="s">*</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span><span class="p">,</span>
  <span class="sh">'</span><span class="se">\\</span><span class="sh">'</span><span class="p">:</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">y</span> <span class="o">/</span> <span class="n">x</span>
<span class="p">}</span>

<span class="n">expressions</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="n">left_expressions</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="n">right_expressions</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="n">tokens</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">)</span>
    <span class="n">symbol</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">'</span><span class="s">:</span><span class="sh">'</span><span class="p">)</span>
    <span class="n">expressions</span><span class="p">[</span><span class="n">symbol</span><span class="p">]</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    
    <span class="k">if</span> <span class="ow">not</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">isdigit</span><span class="p">():</span>
      <span class="n">left</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span>
      <span class="n">op</span> <span class="o">=</span> <span class="n">tokens</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>

      <span class="k">if</span> <span class="n">op</span> <span class="o">==</span> <span class="sh">'</span><span class="s">+</span><span class="sh">'</span><span class="p">:</span>
        <span class="n">left_expressions</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">,</span> <span class="n">right</span><span class="p">]</span>
        <span class="n">right_expressions</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">,</span> <span class="n">left</span><span class="p">]</span>
      <span class="k">elif</span> <span class="n">op</span> <span class="o">==</span> <span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">:</span>
        <span class="n">left_expressions</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">+</span><span class="sh">'</span><span class="p">,</span> <span class="n">right</span><span class="p">]</span>
        <span class="n">right_expressions</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">_</span><span class="sh">'</span><span class="p">,</span> <span class="n">left</span><span class="p">]</span>
      <span class="k">elif</span> <span class="n">op</span> <span class="o">==</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span>
        <span class="n">left_expressions</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">*</span><span class="sh">'</span><span class="p">,</span> <span class="n">right</span><span class="p">]</span>
        <span class="n">right_expressions</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="se">\\</span><span class="sh">'</span><span class="p">,</span> <span class="n">left</span><span class="p">]</span>
      <span class="k">else</span><span class="p">:</span>
        <span class="n">left_expressions</span><span class="p">[</span><span class="n">left</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">,</span> <span class="n">right</span><span class="p">]</span>
        <span class="n">right_expressions</span><span class="p">[</span><span class="n">right</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">symbol</span><span class="p">,</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">,</span> <span class="n">left</span><span class="p">]</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">evaluate</span><span class="p">(</span><span class="n">expr</span><span class="p">):</span>
  <span class="k">if</span> <span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">isdigit</span><span class="p">():</span>
    <span class="k">return</span> <span class="nf">int</span><span class="p">(</span><span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
  <span class="k">else</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">operation_map</span><span class="p">[</span><span class="n">expr</span><span class="p">[</span><span class="mi">1</span><span class="p">]](</span><span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="n">expr</span><span class="p">[</span><span class="mi">0</span><span class="p">]]),</span>
      <span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="n">expr</span><span class="p">[</span><span class="mi">2</span><span class="p">]]))</span>

<span class="k">def</span> <span class="nf">evaluate_unknown</span><span class="p">(</span><span class="n">expr</span><span class="p">):</span>
  <span class="k">if</span> <span class="n">expr</span> <span class="ow">in</span> <span class="n">left_expressions</span><span class="p">:</span>
    <span class="p">(</span><span class="n">symbol</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">operand</span><span class="p">)</span> <span class="o">=</span> <span class="n">left_expressions</span><span class="p">[</span><span class="n">expr</span><span class="p">]</span>
  <span class="k">else</span><span class="p">:</span>
    <span class="p">(</span><span class="n">symbol</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">operand</span><span class="p">)</span> <span class="o">=</span> <span class="n">right_expressions</span><span class="p">[</span><span class="n">expr</span><span class="p">]</span>
  <span class="k">if</span> <span class="n">symbol</span> <span class="o">==</span> <span class="sh">'</span><span class="s">root</span><span class="sh">'</span><span class="p">:</span>
    <span class="k">return</span> <span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="n">operand</span><span class="p">])</span>
  <span class="k">return</span> <span class="n">operation_map</span><span class="p">[</span><span class="n">op</span><span class="p">](</span><span class="nf">evaluate_unknown</span><span class="p">(</span><span class="n">symbol</span><span class="p">),</span> <span class="nf">evaluate</span><span class="p">(</span><span class="n">expressions</span><span class="p">[</span><span class="n">operand</span><span class="p">]))</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">int</span><span class="p">(</span><span class="nf">evaluate_unknown</span><span class="p">(</span><span class="sh">'</span><span class="s">humn</span><span class="sh">'</span><span class="p">)))</span>
</code></pre></div></div>

<hr />

<h1 id="day-22">Day 22</h1>

<p>I’m embarrassed to say this, but I spent <em>way</em> too long on this day, even though it should be fundamentally simple.</p>

<h2 id="part-1-21">Part 1</h2>

<p>Part 1’s context is actually quite simple; given a maze-like structure, navigate it with the instructions given in the input. If, during any point of navigation, the navigator falls off, then we warp the navigator to the other side of the map.</p>

<p>So, we just need to consider the min x, max x, min y and max y to do the problem. Here is a helpful snippet to print the boards being traversed:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">print_board</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">direction</span><span class="p">,</span> <span class="n">instruction</span><span class="p">):</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="se">\033</span><span class="s">[2J</span><span class="sh">'</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="se">\033</span><span class="s">[H</span><span class="sh">'</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">direction</span><span class="p">,</span> <span class="n">instruction</span><span class="p">)</span>
  <span class="n">y_output</span> <span class="o">=</span> <span class="p">(</span><span class="n">y</span> <span class="o">//</span> <span class="mi">50</span><span class="p">)</span> <span class="o">*</span> <span class="mi">50</span>
  <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">y_output</span><span class="p">,</span> <span class="n">y_output</span> <span class="o">+</span> <span class="mi">50</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">col</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">max</span><span class="p">(</span><span class="n">boundary_xs</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">t</span><span class="p">:</span> <span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">])[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
      <span class="nf">if </span><span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="n">row</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">direction</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">&gt;</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">direction</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">v</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">direction</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">&lt;</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">direction</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">^</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
      <span class="nf">elif </span><span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="n">row</span><span class="p">)</span> <span class="ow">in</span> <span class="n">tiles</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">tiles</span><span class="p">[(</span><span class="n">col</span><span class="p">,</span> <span class="n">row</span><span class="p">)],</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
      <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">()</span>
  <span class="nf">print</span><span class="p">()</span>
  <span class="nf">print</span><span class="p">()</span>
  <span class="nf">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</code></pre></div></div>

<p>With some level of consideration to speed, I’ve decided to sacrifice my otherwise very free RAM to store way more dictionaries and lists than I really needed to. Here is how I solved it in the end:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="nb">reduce</span>

<span class="n">tiles</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="n">boundary_xs</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
<span class="n">boundary_ys</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
<span class="n">instructions</span> <span class="o">=</span> <span class="sh">''</span>
<span class="n">start_pos</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">movement_map</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
  <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="p">]</span>
<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">rstrip</span><span class="p">()</span>
  <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="mi">999</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span>
    <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">c</span> <span class="o">!=</span> <span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">:</span>
        <span class="n">tiles</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="n">c</span>
        <span class="n">min_x</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">min_x</span><span class="p">)</span>
        <span class="n">max_x</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">max_x</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
          <span class="n">boundary_ys</span> <span class="o">+=</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">[(</span><span class="mi">999</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
        <span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="nf">min</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">y</span><span class="p">),</span>
          <span class="nf">max</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">y</span><span class="p">))</span>

        <span class="k">if</span> <span class="n">start_pos</span> <span class="o">==</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
          <span class="n">start_pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>

    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">rstrip</span><span class="p">()</span>
    <span class="n">boundary_xs</span><span class="p">.</span><span class="nf">append</span><span class="p">((</span><span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span><span class="p">))</span>
    <span class="n">y</span> <span class="o">+=</span> <span class="mi">1</span>
  <span class="n">instructions</span> <span class="o">=</span> <span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">a</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">a</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">y</span><span class="p">]</span> <span class="k">if</span> <span class="n">y</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">L</span><span class="sh">'</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">R</span><span class="sh">'</span> <span class="k">else</span>  <span class="n">a</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">y</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="sh">''</span><span class="p">],</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">(),</span> <span class="p">[</span><span class="sh">''</span><span class="p">])</span>

<span class="n">direction</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">start_pos</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">instruction</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">instructions</span><span class="p">):</span>
  <span class="n">steps</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">instruction</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="nf">len</span><span class="p">(</span><span class="n">instructions</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="n">instruction</span><span class="p">)</span>
  <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="n">boundary_xs</span><span class="p">[</span><span class="n">y</span><span class="p">]</span>
  <span class="n">min_y</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">=</span> <span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">]</span>
  <span class="k">while</span> <span class="n">steps</span><span class="p">:</span>
    <span class="n">diff</span> <span class="o">=</span> <span class="n">movement_map</span><span class="p">[</span><span class="n">direction</span><span class="p">]</span>
    <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">diff</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">y</span> <span class="o">+</span> <span class="n">diff</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="nf">while </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">tiles</span> <span class="ow">and</span> <span class="p">(</span><span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">tiles</span><span class="p">:</span>
      <span class="k">if</span> <span class="n">new_x</span> <span class="o">&gt;</span> <span class="n">max_x</span><span class="p">:</span>
        <span class="n">new_x</span> <span class="o">=</span> <span class="n">min_x</span>
        <span class="k">continue</span>
      <span class="k">elif</span> <span class="n">new_x</span> <span class="o">&gt;</span> <span class="n">min_x</span><span class="p">:</span>
        <span class="n">new_x</span> <span class="o">=</span> <span class="n">new_x</span> <span class="o">+</span> <span class="n">diff</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
      <span class="k">elif</span> <span class="n">new_x</span> <span class="o">&lt;</span> <span class="n">min_x</span><span class="p">:</span>
        <span class="n">new_x</span> <span class="o">=</span> <span class="n">max_x</span>
        <span class="k">continue</span>

      <span class="k">if</span> <span class="n">new_y</span> <span class="o">&gt;</span> <span class="n">max_y</span><span class="p">:</span>
        <span class="n">new_y</span> <span class="o">=</span> <span class="n">min_y</span>
        <span class="k">continue</span>
      <span class="k">elif</span> <span class="n">new_y</span> <span class="o">&gt;</span> <span class="n">min_y</span><span class="p">:</span>
        <span class="n">new_y</span> <span class="o">=</span> <span class="n">new_y</span> <span class="o">+</span> <span class="n">diff</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
      <span class="k">elif</span> <span class="n">new_y</span> <span class="o">&lt;</span> <span class="n">min_y</span><span class="p">:</span>
        <span class="n">new_y</span> <span class="o">=</span> <span class="n">max_y</span>
        <span class="k">continue</span>

    <span class="k">if</span> <span class="n">tiles</span><span class="p">[(</span><span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">)]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">:</span>
      <span class="k">break</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span>
      <span class="n">steps</span> <span class="o">-=</span> <span class="mi">1</span>

  <span class="n">dirchange</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="nf">len</span><span class="p">(</span><span class="n">instructions</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="bp">None</span>
  <span class="k">if</span> <span class="n">dirchange</span> <span class="o">==</span> <span class="sh">'</span><span class="s">L</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">direction</span> <span class="o">-=</span> <span class="mi">1</span>
    <span class="k">if</span> <span class="n">direction</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
      <span class="n">direction</span> <span class="o">+=</span> <span class="nf">len</span><span class="p">(</span><span class="n">movement_map</span><span class="p">)</span>
  <span class="k">elif</span> <span class="n">dirchange</span> <span class="o">==</span> <span class="sh">'</span><span class="s">R</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">direction</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="n">direction</span> <span class="o">%=</span> <span class="nf">len</span><span class="p">(</span><span class="n">movement_map</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="mi">1000</span> <span class="o">*</span> <span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">direction</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="part-2-21">Part 2</h2>

<p>Now the maze becomes a cube. I first tried to map the co-ordinates to 3D, which was fine, until I realised I needed to find a way to fold the cube. After hours of thinking, drawing stuff till I went insane, I decided it was not worth the hassle.</p>

<p>So I decided to hard-code the relationship between each side in the cube. However, because there is no generalization, debugging exactly <em>what</em> went wrong was ungodly. Thankfully, someone who has solved this beforehand provided a great cube visualizer that I used to debug my script, written by <a href="https://nanot1m.github.io/adventofcode2022/day22/index.html">nanot1m</a>. I also ran my script against another solution to check the output per instruction, only to find out that one of my functions that mapped the sides have the wrong offset.</p>

<p>So after roughly 5 hours, here is the final code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="nb">reduce</span>
<span class="n">face_width</span> <span class="o">=</span> <span class="mi">50</span>
<span class="n">tiles</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
<span class="n">boundary_xs</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
<span class="n">boundary_ys</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
<span class="n">instructions</span> <span class="o">=</span> <span class="sh">''</span>
<span class="n">start_pos</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">movement_map</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
  <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="p">]</span>

<span class="n">cube_connection_operations</span> <span class="o">=</span> <span class="p">{</span>
  <span class="mi">1</span><span class="p">:</span> <span class="p">[</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="c1"># 2
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># 3
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="mi">49</span> <span class="o">-</span> <span class="n">y</span><span class="p">),</span> <span class="mi">0</span><span class="p">),</span> <span class="c1"># 4
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span> <span class="o">%</span> <span class="mi">50</span><span class="p">)</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">*</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="c1"># 6
</span>  <span class="p">],</span>
  <span class="mi">2</span><span class="p">:</span> <span class="p">[</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="mi">49</span> <span class="o">-</span> <span class="n">y</span><span class="p">),</span> <span class="mi">2</span><span class="p">),</span> <span class="c1"># 5
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">99</span><span class="p">,</span> <span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="n">x</span> <span class="o">%</span> <span class="mi">50</span><span class="p">),</span> <span class="mi">2</span><span class="p">),</span> <span class="c1"># 3
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="c1"># 1
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">%</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">50</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="c1"># 6
</span>  <span class="p">],</span>
  <span class="mi">3</span><span class="p">:</span> <span class="p">[</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">50</span><span class="p">),</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="c1"># 2
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># 5
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># 4
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="c1"># 1
</span>  <span class="p">],</span>
  <span class="mi">4</span><span class="p">:</span> <span class="p">[</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="c1"># 5
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># 6
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">50</span><span class="p">,</span> <span class="p">(</span><span class="mi">49</span> <span class="o">-</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">50</span><span class="p">)),</span> <span class="mi">0</span><span class="p">),</span> <span class="c1"># 1
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">50</span><span class="p">,</span> <span class="mi">50</span> <span class="o">+</span> <span class="n">x</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="c1"># 3
</span>  <span class="p">],</span>
  <span class="mi">5</span><span class="p">:</span> <span class="p">[</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">149</span><span class="p">,</span> <span class="p">(</span><span class="mi">49</span> <span class="o">-</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">50</span><span class="p">)),</span> <span class="mi">2</span><span class="p">),</span> <span class="c1"># 2
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">49</span><span class="p">,</span> <span class="mi">3</span> <span class="o">*</span> <span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="n">x</span> <span class="o">%</span> <span class="mi">50</span><span class="p">),</span> <span class="mi">2</span><span class="p">),</span> <span class="c1"># 6
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="c1"># 4
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="c1"># 3
</span>  <span class="p">],</span>
  <span class="mi">6</span><span class="p">:</span> <span class="p">[</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">50</span><span class="p">),</span> <span class="mi">149</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="c1"># 5
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># 2
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="n">y</span> <span class="o">%</span> <span class="mi">50</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="c1"># 1
</span>    <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1"># 4
</span>  <span class="p">]</span>
<span class="p">}</span>

<span class="n">cube_toplefts</span> <span class="o">=</span> <span class="mi">6</span> <span class="o">*</span> <span class="p">[</span><span class="bp">None</span><span class="p">]</span>
<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">rstrip</span><span class="p">()</span>
  <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="mi">999</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span>
    <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">c</span> <span class="o">!=</span> <span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">:</span>
        <span class="n">tiles</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="n">c</span>
        <span class="n">min_x</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">min_x</span><span class="p">)</span>
        <span class="n">max_x</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">max_x</span><span class="p">)</span>

        <span class="n">side_exist</span> <span class="o">=</span> <span class="bp">False</span>
        <span class="k">for</span> <span class="n">topleft</span> <span class="ow">in</span> <span class="n">cube_toplefts</span><span class="p">:</span>
          <span class="k">if</span> <span class="n">topleft</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">tx</span><span class="p">,</span> <span class="n">ty</span> <span class="o">=</span> <span class="n">topleft</span>
            <span class="k">if</span> <span class="n">tx</span> <span class="o">&lt;=</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">tx</span> <span class="o">+</span> <span class="n">face_width</span> <span class="ow">and</span> <span class="n">ty</span> <span class="o">&lt;=</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">ty</span> <span class="o">+</span> <span class="n">face_width</span><span class="p">:</span>
              <span class="n">side_exist</span> <span class="o">=</span> <span class="bp">True</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="n">side_exist</span><span class="p">:</span>
          <span class="n">cube_toplefts</span><span class="p">[</span><span class="n">cube_toplefts</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="bp">None</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
          <span class="n">boundary_ys</span> <span class="o">+=</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">[(</span><span class="mi">999</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
        <span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="nf">min</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">y</span><span class="p">),</span>
          <span class="nf">max</span><span class="p">(</span><span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">y</span><span class="p">))</span>

        <span class="k">if</span> <span class="n">start_pos</span> <span class="o">==</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
          <span class="n">start_pos</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>

    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">rstrip</span><span class="p">()</span>
    <span class="n">boundary_xs</span><span class="p">.</span><span class="nf">append</span><span class="p">((</span><span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span><span class="p">))</span>
    <span class="n">y</span> <span class="o">+=</span> <span class="mi">1</span>
  <span class="n">instructions</span> <span class="o">=</span> <span class="nf">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">a</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">a</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">y</span><span class="p">]</span> <span class="k">if</span> <span class="n">y</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">L</span><span class="sh">'</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">R</span><span class="sh">'</span> <span class="k">else</span>  <span class="n">a</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">y</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="sh">''</span><span class="p">],</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">(),</span> <span class="p">[</span><span class="sh">''</span><span class="p">])</span>

<span class="n">direction</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">start_pos</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">instruction</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">instructions</span><span class="p">):</span>
  <span class="n">steps</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">instruction</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="nf">len</span><span class="p">(</span><span class="n">instructions</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="n">instruction</span><span class="p">)</span>
  <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="n">boundary_xs</span><span class="p">[</span><span class="n">y</span><span class="p">]</span>
  <span class="n">min_y</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">=</span> <span class="n">boundary_ys</span><span class="p">[</span><span class="n">x</span><span class="p">]</span>

  <span class="n">cube_side</span> <span class="o">=</span> <span class="n">cube_toplefts</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="nf">next</span><span class="p">(</span><span class="nf">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">topleft</span><span class="p">:</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">face_width</span> <span class="ow">and</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">face_width</span><span class="p">,</span> <span class="n">cube_toplefts</span><span class="p">)))</span>

  <span class="k">while</span> <span class="n">steps</span><span class="p">:</span>
    <span class="n">diff</span> <span class="o">=</span> <span class="n">movement_map</span><span class="p">[</span><span class="n">direction</span><span class="p">]</span>
    <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">diff</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">y</span> <span class="o">+</span> <span class="n">diff</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">new_direction</span> <span class="o">=</span> <span class="n">direction</span>
    <span class="n">topleft</span> <span class="o">=</span> <span class="n">cube_toplefts</span><span class="p">[</span><span class="n">cube_side</span><span class="p">]</span>
    <span class="n">fell_out</span> <span class="o">=</span> <span class="ow">not</span> <span class="p">(</span><span class="n">topleft</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">new_x</span> <span class="o">&lt;</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">face_width</span> <span class="ow">and</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">new_y</span> <span class="o">&lt;</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">face_width</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">fell_out</span><span class="p">:</span>
      <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">,</span> <span class="n">new_direction</span> <span class="o">=</span> <span class="n">cube_connection_operations</span><span class="p">[</span><span class="n">cube_side</span> <span class="o">+</span> <span class="mi">1</span><span class="p">][</span><span class="n">direction</span><span class="p">](</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">tiles</span><span class="p">[(</span><span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">)]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">:</span>
      <span class="k">break</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">direction</span> <span class="o">=</span> <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span><span class="p">,</span> <span class="n">new_direction</span>
      <span class="n">cube_side</span> <span class="o">=</span> <span class="n">cube_toplefts</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="nf">next</span><span class="p">(</span><span class="nf">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">topleft</span><span class="p">:</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">face_width</span> <span class="ow">and</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">topleft</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">face_width</span><span class="p">,</span> <span class="n">cube_toplefts</span><span class="p">)))</span>
      <span class="n">steps</span> <span class="o">-=</span> <span class="mi">1</span>

  <span class="n">dirchange</span> <span class="o">=</span> <span class="n">instruction</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="nf">len</span><span class="p">(</span><span class="n">instructions</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">else</span> <span class="bp">None</span>
  <span class="k">if</span> <span class="n">dirchange</span> <span class="o">==</span> <span class="sh">'</span><span class="s">L</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">direction</span> <span class="o">-=</span> <span class="mi">1</span>
    <span class="k">if</span> <span class="n">direction</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
      <span class="n">direction</span> <span class="o">+=</span> <span class="nf">len</span><span class="p">(</span><span class="n">movement_map</span><span class="p">)</span>
  <span class="k">elif</span> <span class="n">dirchange</span> <span class="o">==</span> <span class="sh">'</span><span class="s">R</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">direction</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="n">direction</span> <span class="o">%=</span> <span class="nf">len</span><span class="p">(</span><span class="n">movement_map</span><span class="p">)</span>

<span class="nf">print</span><span class="p">(</span><span class="mi">1000</span> <span class="o">*</span> <span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">direction</span><span class="p">)</span>
</code></pre></div></div>

<p>It’s ugly, the process is error-prone, I’m tired, this’ll do. I’ve put off plans for this man!</p>

<hr />

<h1 id="day-23">Day 23</h1>

<p>Today’s puzzle was much more manageable than the previous days! TGIF &amp; Merry Christmas, amirite?</p>

<h2 id="part-1-22">Part 1</h2>

<p>We follow our hero’s journey as we now have to scatter elves in a fixed way. I spent roughly 30 minutes debugging why my code didn’t work, only to realise that I haven’t fully digested the specifications. Lesson learnt!</p>

<p>Okay so, we have an input like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>....#..
..###.#
#...#.#
.#...##
#.###..
##.#.##
.#..#..
</code></pre></div></div>

<p>Each little hashtag moves according to a certain set of rules, which varies by the round number. The rules are:</p>

<ol>
  <li>Check all 8 positions to the side of hashtag. If no hashtag, do not move.</li>
  <li>Check north, north-east, north-west. If there is another hashtag there, move on. Otherwise, attempt to move north.</li>
  <li>Check south, south-east, south-west. Attempt to move south if no hashtag.</li>
  <li>Check west, north-west, south-west. Attempt to move west if no hashtag.</li>
  <li>Check east, north-east, south-east. Attempt to move east if no hashtag.</li>
  <li>Otherwise, do not move.</li>
</ol>

<p>“Attempt” to move can become “actually” moved if all hashtags end up having unique positions.</p>

<p>After every round of movement, steps 2 to 5 are rearranged to 3, 4, 5, 2. Essentially the first considered position is now the last considered position, and the second becomes the first, and so on.</p>

<p>With that, here’s a helpful little function to print the board:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">print_board</span><span class="p">():</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="se">\033</span><span class="s">[0J</span><span class="sh">'</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="se">\033</span><span class="s">[H</span><span class="sh">'</span><span class="p">)</span>
  <span class="n">pos_sorted_x</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">positions</span><span class="p">),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
  <span class="n">pos_sorted_y</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">positions</span><span class="p">),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>

  <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="n">pos_sorted_x</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">pos_sorted_x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">min_y</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">=</span> <span class="n">pos_sorted_y</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">pos_sorted_y</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>
  <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">min_y</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
      <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">positions</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
      <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">()</span>
</code></pre></div></div>

<p>And here is the solution:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">positions</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
  <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span>

  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">c</span> <span class="o">==</span> <span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">:</span>
        <span class="n">positions</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
    <span class="n">y</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">generate_decisions</span><span class="p">(</span><span class="n">rounds</span><span class="p">):</span>
  <span class="n">decisions</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
  <span class="nf">for </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">in</span> <span class="n">positions</span><span class="p">:</span>
    <span class="n">intersect_results</span> <span class="o">=</span> <span class="p">[</span>
      <span class="nf">len</span><span class="p">({(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)}</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span>
      <span class="nf">len</span><span class="p">({(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)}</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span>
      <span class="nf">len</span><span class="p">({(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)}</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span>
      <span class="nf">len</span><span class="p">({(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)}</span> <span class="o">&amp;</span> <span class="n">positions</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">,</span>
    <span class="p">]</span>
    <span class="k">if</span> <span class="nf">all</span><span class="p">(</span><span class="n">intersect_results</span><span class="p">):</span>
      <span class="n">decisions</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
      <span class="k">continue</span>

    <span class="k">for</span> <span class="n">iterator</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">intersect_results</span><span class="p">)):</span>
      <span class="n">i</span> <span class="o">=</span> <span class="p">(</span><span class="n">rounds</span> <span class="o">+</span> <span class="n">iterator</span><span class="p">)</span> <span class="o">%</span> <span class="nf">len</span><span class="p">(</span><span class="n">intersect_results</span><span class="p">)</span>

      <span class="n">match</span> <span class="n">i</span><span class="p">:</span>
        <span class="n">case</span> <span class="mi">0</span><span class="p">:</span>
          <span class="k">if</span> <span class="n">intersect_results</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
            <span class="n">decisions</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
            <span class="k">break</span>
        <span class="n">case</span> <span class="mi">1</span><span class="p">:</span>
          <span class="k">if</span> <span class="n">intersect_results</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span>
            <span class="n">decisions</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
            <span class="k">break</span>
        <span class="n">case</span> <span class="mi">2</span><span class="p">:</span>
          <span class="k">if</span> <span class="n">intersect_results</span><span class="p">[</span><span class="mi">2</span><span class="p">]:</span>
            <span class="n">decisions</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
            <span class="k">break</span>
        <span class="n">case</span> <span class="mi">3</span><span class="p">:</span>
          <span class="k">if</span> <span class="n">intersect_results</span><span class="p">[</span><span class="mi">3</span><span class="p">]:</span>
            <span class="n">decisions</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
            <span class="k">break</span>

    <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">decisions</span><span class="p">:</span>
      <span class="n">decisions</span><span class="p">[(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)]</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
  <span class="k">return</span> <span class="n">decisions</span>

<span class="k">def</span> <span class="nf">count_empty</span><span class="p">():</span>
  <span class="n">pos_sorted_x</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">positions</span><span class="p">),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
  <span class="n">pos_sorted_y</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">(</span><span class="nf">list</span><span class="p">(</span><span class="n">positions</span><span class="p">),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">p</span><span class="p">:</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>

  <span class="n">min_x</span><span class="p">,</span> <span class="n">max_x</span> <span class="o">=</span> <span class="n">pos_sorted_x</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">pos_sorted_x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">min_y</span><span class="p">,</span> <span class="n">max_y</span> <span class="o">=</span> <span class="n">pos_sorted_y</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">pos_sorted_y</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>

  <span class="nf">return </span><span class="p">((</span><span class="n">max_x</span> <span class="o">-</span> <span class="n">min_x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">max_y</span> <span class="o">-</span> <span class="n">min_y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="o">-</span> <span class="nf">len</span><span class="p">(</span><span class="n">positions</span><span class="p">)</span>

<span class="n">rounds</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">rounds</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">:</span>
  <span class="n">decisions</span> <span class="o">=</span> <span class="nf">generate_decisions</span><span class="p">(</span><span class="n">rounds</span><span class="p">)</span>
  <span class="n">hits</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">()</span>
  <span class="n">new_positions</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>

  <span class="k">for</span> <span class="n">result_pos</span> <span class="ow">in</span> <span class="n">decisions</span><span class="p">.</span><span class="nf">values</span><span class="p">():</span>
    <span class="k">if</span> <span class="n">result_pos</span> <span class="ow">in</span> <span class="n">hits</span><span class="p">:</span>
      <span class="n">hits</span><span class="p">[</span><span class="n">result_pos</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">hits</span><span class="p">[</span><span class="n">result_pos</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>

  <span class="k">for</span> <span class="n">original_pos</span><span class="p">,</span> <span class="n">result_pos</span> <span class="ow">in</span> <span class="n">decisions</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
    <span class="k">if</span> <span class="n">hits</span><span class="p">[</span><span class="n">result_pos</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
      <span class="n">new_positions</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">original_pos</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
      <span class="n">new_positions</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">result_pos</span><span class="p">)</span>
  
  <span class="n">positions</span> <span class="o">=</span> <span class="n">new_positions</span>
  <span class="n">rounds</span> <span class="o">+=</span> <span class="mi">1</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">count_empty</span><span class="p">())</span>
</code></pre></div></div>

<h2 id="part-2-22">Part 2</h2>

<p>Today’s part two is the most natural out of all the part twos I have attempted in this year’s AOC. Simply, we remove the boundaries of rounds, and figure out when all the hashtags run out of moves. So, basically, we just keep running until <code class="language-plaintext highlighter-rouge">positions == new_positions</code>. Hence, our diff would be:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">61c61
</span><span class="gd">&lt; while rounds &lt; 10:
</span><span class="p">---
</span><span class="gi">&gt; while True:
</span><span class="p">78d77
</span><span class="gd">&lt;   positions = new_positions
</span><span class="p">79a79,81
</span><span class="gi">&gt;   if positions == new_positions:
&gt;     break
&gt;   positions = new_positions
</span><span class="p">81c83
</span><span class="gd">&lt; print(count_empty())
</span><span class="p">---
</span><span class="gi">&gt; print(rounds)
</span></code></pre></div></div>

<p>It’s not the fastest piece of code ever, but for the amount of effort I put in, being able to get the answer in five seconds is reasonable enough.</p>

<hr />

<h1 id="day-24">Day 24</h1>

<p>Today’s puzzle is about path-finding, but on crack.</p>

<h2 id="part-1-23">Part 1</h2>

<p>Let’s examine an example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#.######
#&gt;&gt;.&lt;^&lt;#
#.&lt;..&lt;&lt;#
#&gt;v.&gt;&lt;&gt;#
#&lt;^v^^&gt;#
######.#
</code></pre></div></div>

<p>The arrows, which are <code class="language-plaintext highlighter-rouge">&gt;v&lt;^</code> are moving obstacles in the board, moving towards the direction suggested by the arrows. These arrows can overlap, and warp around the board. Our goal is to perform path-finding through this board, and output the <strong>shortest possible path</strong>.</p>

<p>Okay, what’s the best method? The first method I immediately thought of is to implement a path searching algorithm, and find the shortest path at every step. <em>However</em>, this is largely inefficient, because when there are as many obstacles as shown in the board above, then too much effort is put into re-calculating the path at every step due to obstacles to the path.</p>

<p>Instead, let’s include the moving obstacles into our path search algorithm; at every step, we clone the board, move the obstacles, figure out the best next step, and repeat the process ad-infinitum until we reach the target position. To effectively do this, we must invent an algorithm that quickly converges to the target position, without searching unnecessary paths.</p>

<p>For this, I chose to use A* search.</p>

<p>As usual, here is a useful function to print the board:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">print_board</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">hs</span><span class="p">,</span> <span class="n">steps</span><span class="p">):</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="se">\033</span><span class="s">[2J</span><span class="sh">'</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="se">\033</span><span class="s">[H</span><span class="sh">'</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">Steps:</span><span class="sh">'</span><span class="p">,</span> <span class="n">steps</span><span class="p">)</span>
  <span class="n">px</span><span class="p">,</span> <span class="n">py</span> <span class="o">=</span> <span class="n">p</span>
  <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">width</span><span class="p">):</span>
      <span class="nf">if </span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="n">start_position</span><span class="p">)</span> <span class="ow">or</span> <span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="n">end_position</span><span class="p">):</span>
        <span class="nf">if </span><span class="p">(</span><span class="n">px</span><span class="p">,</span> <span class="n">py</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">E</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s"> </span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">continue</span>

      <span class="k">if</span> <span class="n">x</span> <span class="o">%</span> <span class="p">(</span><span class="n">width</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
      <span class="k">elif</span> <span class="n">y</span> <span class="o">%</span> <span class="p">(</span><span class="n">height</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
      <span class="nf">elif </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="n">p</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">E</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
      <span class="k">else</span><span class="p">:</span>
        <span class="n">hasDir</span> <span class="o">=</span> <span class="mi">0</span>
        <span class="n">lastDir</span> <span class="o">=</span> <span class="sh">'</span><span class="s">^</span><span class="sh">'</span>
        <span class="k">for</span> <span class="n">c</span><span class="p">,</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">directions</span><span class="p">):</span>
          <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> <span class="ow">in</span> <span class="n">hs</span><span class="p">:</span>
            <span class="n">lastDir</span> <span class="o">=</span> <span class="n">directions</span><span class="p">[</span><span class="n">c</span><span class="p">]</span>
            <span class="n">hasDir</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">hasDir</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">elif</span> <span class="n">hasDir</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="n">hasDir</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
          <span class="nf">print</span><span class="p">(</span><span class="n">lastDir</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="sh">''</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">()</span>
  <span class="nf">print</span><span class="p">()</span>
  <span class="nf">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</code></pre></div></div>

<p>And here is the search implemented:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">queue</span> <span class="kn">import</span> <span class="n">PriorityQueue</span>

<span class="n">directions</span> <span class="o">=</span> <span class="sh">'</span><span class="s">&gt;v&lt;^</span><span class="sh">'</span>
<span class="n">directions_movement</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
  <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">]</span>
<span class="n">hurricanes</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
<span class="n">width</span><span class="p">,</span> <span class="n">height</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">input.txt</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">r</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
  <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
  <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">width</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
  <span class="k">while</span> <span class="n">line</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">line</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">directions</span><span class="p">:</span>
         <span class="n">hurricanes</span><span class="p">.</span><span class="nf">append</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">directions</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">c</span><span class="p">)))</span>
    <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">readline</span><span class="p">().</span><span class="nf">strip</span><span class="p">()</span>
    <span class="n">y</span> <span class="o">+=</span> <span class="mi">1</span>
  <span class="n">height</span> <span class="o">=</span> <span class="n">y</span>

<span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="n">isPlayer</span><span class="p">):</span>
  <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">c</span>  <span class="o">=</span> <span class="n">pos</span>
  <span class="n">diff</span> <span class="o">=</span> <span class="n">directions_movement</span><span class="p">[</span><span class="n">c</span><span class="p">]</span>
  <span class="n">x</span> <span class="o">+=</span> <span class="n">diff</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  <span class="n">y</span> <span class="o">+=</span> <span class="n">diff</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>

  <span class="k">if</span> <span class="n">isPlayer</span><span class="p">:</span>
    <span class="nf">return </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>

  <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="n">width</span> <span class="o">-</span> <span class="mi">2</span><span class="p">:</span>
    <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span>
  <span class="k">elif</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
    <span class="n">x</span> <span class="o">=</span> <span class="n">width</span> <span class="o">-</span> <span class="mi">2</span>

  <span class="k">if</span> <span class="n">y</span> <span class="o">&gt;</span> <span class="n">height</span> <span class="o">-</span> <span class="mi">2</span><span class="p">:</span>
    <span class="n">y</span> <span class="o">=</span> <span class="mi">1</span>
  <span class="k">elif</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
    <span class="n">y</span> <span class="o">=</span> <span class="n">height</span> <span class="o">-</span> <span class="mi">2</span>
  <span class="nf">return </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>

<span class="n">start_position</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">end_position</span> <span class="o">=</span> <span class="p">(</span><span class="n">width</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="n">height</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">visited</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
<span class="n">p</span> <span class="o">=</span> <span class="nc">PriorityQueue</span><span class="p">()</span>

<span class="n">p</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="n">start_position</span><span class="p">,</span> <span class="n">hurricanes</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
<span class="n">found</span> <span class="o">=</span> <span class="bp">False</span>

<span class="k">while</span> <span class="ow">not</span> <span class="n">p</span><span class="p">.</span><span class="nf">empty</span><span class="p">():</span>
  <span class="n">old_heuristic</span><span class="p">,</span> <span class="p">(</span><span class="n">px</span><span class="p">,</span> <span class="n">py</span><span class="p">),</span> <span class="n">current_hurricanes</span><span class="p">,</span> <span class="n">steps</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="nf">get</span><span class="p">()</span>
  <span class="n">steps</span> <span class="o">+=</span> <span class="mi">1</span>

  <span class="c1"># move hurricanes
</span>  <span class="n">new_hurricanes</span> <span class="o">=</span> <span class="nf">list</span><span class="p">()</span>
  <span class="k">for</span> <span class="n">pos</span> <span class="ow">in</span> <span class="n">current_hurricanes</span><span class="p">:</span>
    <span class="n">new_hurricanes</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="nf">move</span><span class="p">(</span><span class="n">pos</span><span class="p">,</span> <span class="bp">False</span><span class="p">))</span>

  <span class="c1"># attempt to move
</span>  <span class="k">for</span> <span class="n">c</span><span class="p">,</span> <span class="n">direction</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">directions_movement</span><span class="p">):</span>
    <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="nf">move</span><span class="p">((</span><span class="n">px</span><span class="p">,</span> <span class="n">py</span><span class="p">,</span> <span class="n">c</span><span class="p">),</span> <span class="bp">True</span><span class="p">)</span>
    <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="n">end_position</span><span class="p">:</span>
      <span class="n">found</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="k">break</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="mi">0</span> <span class="o">&lt;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">width</span> <span class="o">-</span> <span class="mi">1</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">height</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
      <span class="k">continue</span>

    <span class="n">collides</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="nf">for </span><span class="p">(</span><span class="n">hx</span><span class="p">,</span> <span class="n">hy</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="ow">in</span> <span class="n">new_hurricanes</span><span class="p">:</span>
      <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">hx</span><span class="p">,</span> <span class="n">hy</span><span class="p">):</span>
        <span class="n">collides</span> <span class="o">=</span> <span class="bp">True</span>
        <span class="k">break</span>
    <span class="k">if</span> <span class="n">collides</span><span class="p">:</span>
      <span class="k">continue</span>

    <span class="n">new_heuristic</span> <span class="o">=</span> <span class="n">steps</span> <span class="o">+</span> <span class="nf">abs</span><span class="p">(</span><span class="n">end_position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="nf">abs</span><span class="p">(</span><span class="n">end_position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span>
    <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">steps</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
      <span class="n">p</span><span class="p">.</span><span class="nf">put</span><span class="p">((</span><span class="n">new_heuristic</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="n">new_hurricanes</span><span class="p">,</span> <span class="n">steps</span><span class="p">))</span>
      <span class="n">visited</span><span class="p">.</span><span class="nf">add</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">steps</span><span class="p">))</span>

  <span class="k">if</span> <span class="n">found</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">steps</span><span class="p">)</span>
    <span class="k">break</span>
</code></pre></div></div>

<h2 id="part-2-23">Part 2</h2>

<p>In part 2, I found a bug in my original code. If, right out of the gate, there is a hurricane blocking the path of the starting position, then the A* search will return prematurely with no results:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nf">if </span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="n">end_position</span><span class="p">:</span>
      <span class="n">found</span> <span class="o">=</span> <span class="bp">True</span>
      <span class="k">break</span>
</code></pre></div></div>

<p>To fix this, I simply check if the current position is the starting position; if it is, the subsequent block of code is executed, which has “stay still” as one of the possible actions to take.</p>

<p>Hence, after fixing the bug, I just move all of the path finding code to its own function, which will return the number of steps taken and the state of the board, and call it three times; once from start -&gt; end, end -&gt; start and start -&gt; end again.</p>

<p>Here is the final diff:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">46,49c46,67
</span><span class="gd">&lt; start_position = (1, 0)
&lt; end_position = (width - 2, height - 1)
&lt; visited = set()
&lt; p = PriorityQueue()
</span><span class="p">---
</span><span class="gi">&gt; def astar(start_position, end_position, hurricanes):
&gt;   visited = set()
&gt;   p = PriorityQueue()
&gt;
&gt;   p.put((0, start_position, hurricanes, 0))
&gt;   found = False
&gt;
&gt;   while not p.empty():
&gt;     old_heuristic, (px, py), current_hurricanes, steps = p.get()
&gt;     steps += 1
&gt;
&gt;     # move hurricanes
&gt;     new_hurricanes = list()
&gt;     for pos in current_hurricanes:
&gt;       new_hurricanes.append(move(pos, False))
&gt;
&gt;     # attempt to move
&gt;     for c, direction in enumerate(directions_movement):
&gt;       x, y, _ = move((px, py, c), True)
&gt;       if (x, y) == end_position:
&gt;         found = True
&gt;         break
</span><span class="p">51,52c69,84
</span><span class="gd">&lt; p.put((0, start_position, hurricanes, 0))
&lt; found = False
</span><span class="p">---
</span><span class="gi">&gt;       if not (0 &lt; x &lt; width - 1 and 0 &lt; y &lt; height - 1) \
&gt;         and (x, y) != start_position:
&gt;         continue
&gt;
&gt;       collides = False
&gt;       for (hx, hy, _) in new_hurricanes:
&gt;         if (x, y) == (hx, hy):
&gt;           collides = True
&gt;           break
&gt;       if collides:
&gt;         continue
&gt;
&gt;       new_heuristic = steps + abs(end_position[0] - x) + abs(end_position[1] - y)
&gt;       if (x, y, steps) not in visited:
&gt;         p.put((new_heuristic, (x, y), new_hurricanes, steps))
&gt;         visited.add((x, y, steps))
</span><span class="p">54,67c86,87
</span><span class="gd">&lt; while not p.empty():
&lt;   old_heuristic, (px, py), current_hurricanes, steps = p.get()
&lt;   steps += 1
&lt;
&lt;   # move hurricanes
&lt;   new_hurricanes = list()
&lt;   for pos in current_hurricanes:
&lt;     new_hurricanes.append(move(pos, False))
&lt;
&lt;   # attempt to move
&lt;   for c, direction in enumerate(directions_movement):
&lt;     x, y, _ = move((px, py, c), True)
&lt;     if (x, y) == end_position:
&lt;       found = True
</span><span class="p">---
</span><span class="gi">&gt;     if found:
&gt;       return current_hurricanes, steps
</span><span class="p">70,88c90,95
</span><span class="gd">&lt;     if not (0 &lt; x &lt; width - 1 and 0 &lt; y &lt; height - 1):
&lt;       continue
&lt;
&lt;     collides = False
&lt;     for (hx, hy, _) in new_hurricanes:
&lt;       if (x, y) == (hx, hy):
&lt;         collides = True
&lt;         break
&lt;     if collides:
&lt;       continue
&lt;
&lt;     new_heuristic = steps + abs(end_position[0] - x) + abs(end_position[1] - y)
&lt;     if (x, y, steps) not in visited:
&lt;       p.put((new_heuristic, (x, y), new_hurricanes, steps))
&lt;       visited.add((x, y, steps))
&lt;
&lt;   if found:
&lt;     print(steps)
&lt;     break
</span><span class="p">---
</span><span class="gi">&gt; start_position = (1, 0)
&gt; end_position = (width - 2, height - 1)
&gt; hurricanes, steps = astar(start_position, end_position, hurricanes)
&gt; hurricanes, backsteps = astar(end_position, start_position, hurricanes)
&gt; hurricanes, backbacksteps = astar(start_position, end_position, hurricanes)
&gt; print(backbacksteps + backsteps + steps - 2)
</span></code></pre></div></div>
<hr />

<h1 id="day-25">Day 25</h1>

<p>There’s only one part to this puzzle; and it’s probably the most fun I had in a puzzle thus far!</p>

<p>Nothing like alternate number representations to end of the advent eh? In this puzzle, we have a bunch of alien-looking numbers, like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1=-0-2
12111
2=0=
21
2=01
111
20012
112
1=-1=
1-12
12
1=
122
</code></pre></div></div>

<p>We eventually find out that each of these numbers are in base 5, but with a twist (as there usually is); <code class="language-plaintext highlighter-rouge">-</code> and <code class="language-plaintext highlighter-rouge">=</code> represent -1 and -2 respectively, and the maximum digit that can be represented is 2. From a list of these integers, we need to sum it out, and return our sum in the same format.</p>

<p>Okay, so there are two subproblems:</p>

<ol>
  <li>Converting this integer representation to base 10;</li>
  <li>Converting base 10 integers to this integer representation.</li>
</ol>

<p>The first sub-problem is really simple. All we have to do is to sum the value represented by each digit position, negative and all that: so, for example, <code class="language-plaintext highlighter-rouge">1=-0-2</code> can converted to an integer by this method: <code class="language-plaintext highlighter-rouge">2 + (-1) * 5 + 0 * 5^2 + (-1) ^ 5^3 + (-2) ^ 5^4 + 1 * 5^5 = 1747</code>. In Haskell, this is a <code class="language-plaintext highlighter-rouge">foldr</code> zipped with the position of each digit, something like that:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">snafuToInt</span> <span class="o">::</span> <span class="kt">SNAFU</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="n">snafuToInt</span> <span class="o">=</span> <span class="n">foldr</span> <span class="n">convert</span> <span class="mi">0</span> <span class="o">.</span> <span class="n">enumerate</span>
  <span class="kr">where</span>
    <span class="n">convert</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">digit</span><span class="p">)</span> <span class="n">acc</span> <span class="o">=</span> <span class="n">acc</span> <span class="o">+</span> <span class="p">(</span><span class="mi">5</span> <span class="o">^</span> <span class="n">i</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">snafuDigitToInt</span> <span class="n">digit</span><span class="p">)</span>
    <span class="n">enumerate</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">zip</span><span class="p">[(</span><span class="n">length</span> <span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="n">length</span> <span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">..</span> <span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="n">xs</span>
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">SNAFU</code> is just a <code class="language-plaintext highlighter-rouge">String</code>, <code class="language-plaintext highlighter-rouge">snafuDigitToInt</code> converts <code class="language-plaintext highlighter-rouge">-=012</code> to an integer, like <code class="language-plaintext highlighter-rouge">-1, -2, 0, 1, 2</code>.</p>

<p>To approach the second sub-problem, we must understand that we are in a situation where we need to perform <em>differences</em> to convert a normal base 10 integer to this strange version of an integer. Okay, what if it was to a normal base 5 integer? Normally, we would need to perform the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1747 % 5 = 2 (last digit is 2)

1747 / 5 = 349
349 % 5 = 4 (fourth digit is 4)

349 / 5 = 69
69 % 5 = 4 (third digit is 4)

69 / 5 = 13
13 % 5 = 3 (second digit is 3)

13 / 5 = 2
2 % 5 = 2 (first digit is 2)
</code></pre></div></div>

<p>As such, our base 5 reprsentation of 1747 is 23442. Now, let’s think about how our number system changes things. If we now want to represent, say, 8, in normal base 5, that would be <code class="language-plaintext highlighter-rouge">1 * 5^1 + 3</code>. In our unique representation, it’s <code class="language-plaintext highlighter-rouge">2 * 5^1 - 2</code>, whch means <code class="language-plaintext highlighter-rouge">2=</code>. We discover that the difference is actually just <code class="language-plaintext highlighter-rouge">1 * 5^1 + (3 - 5) + 5 = 2 * 5^1 - 2</code>, which is <code class="language-plaintext highlighter-rouge">2=</code>. Okay, what a bout a smaller number, like 6? That’s <code class="language-plaintext highlighter-rouge">1 * 5^1 + 1</code> for both normal base 5, and our unique base 5 (<code class="language-plaintext highlighter-rouge">11</code>).</p>

<p>Hence, we find out that should our normal base 5 digit exceed <code class="language-plaintext highlighter-rouge">2</code>, we need to perform <code class="language-plaintext highlighter-rouge">(5 - digit)</code> on it, to get the correct representation at that point. But doing so will offset our answer by 5; how do we intend to fix that? Let’s think about a larger number, say <code class="language-plaintext highlighter-rouge">74</code>. This is <code class="language-plaintext highlighter-rouge">2 * 5^2 + 4 * 5^1 + 4 * 5^0</code> in normal base 5. Using our logic above, to represent this in our unique number, we see that: <code class="language-plaintext highlighter-rouge">2 * 5^2 + (4 - 5) * 5^1 + (4 - 5) * 5^0</code> which is offset by <code class="language-plaintext highlighter-rouge">+ 5 * 5^1 + 5 * 5^0</code>, missing from the expression. Wait, isn’t that just <code class="language-plaintext highlighter-rouge">5^2 + 5^1</code>? If we apply this back to the unique number expression, then: <code class="language-plaintext highlighter-rouge">3 * 5^2 + (4 - 5 + 1) * 5^1 - 1 * 5^0</code>, which is just <code class="language-plaintext highlighter-rouge">3 * 5^2 - 1</code> which is <code class="language-plaintext highlighter-rouge">5*5^2 + (5 - 3) * 5 ^ 2 - 1</code>, which is <code class="language-plaintext highlighter-rouge">5^3 - 2*5^2 - 1</code> which finally translates to <code class="language-plaintext highlighter-rouge">1=0-</code> in our special integer representation.</p>

<p>What this whole shtick implies is that we need to <em>carry over</em> a 1 to the next significant digit, as long as our base 5 representation exceeds the maximum digit, 2.</p>

<p>With that finally out of the way, we can implement our logic:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">intToSnafu</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">SNAFU</span>
<span class="n">intToSnafu</span> <span class="n">x</span> <span class="o">=</span> <span class="n">reverse</span> <span class="o">$</span> <span class="n">convertDigits</span> <span class="n">x</span> <span class="mi">0</span> <span class="kt">[]</span>
  <span class="kr">where</span>
    <span class="n">convertDigits</span> <span class="n">num</span> <span class="n">carry</span> <span class="n">xs</span>
      <span class="o">|</span> <span class="n">num</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">carry</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">=</span> <span class="kt">[]</span>
      <span class="o">|</span> <span class="n">num'</span> <span class="o">+</span> <span class="n">carry</span> <span class="o">&gt;</span> <span class="mi">2</span> <span class="o">=</span> <span class="n">intToSnafuDigit</span> <span class="p">(</span><span class="n">num'</span> <span class="o">+</span> <span class="n">carry</span> <span class="o">-</span> <span class="mi">5</span><span class="p">)</span> <span class="o">:</span> <span class="n">convertDigits</span> <span class="n">num''</span> <span class="mi">1</span> <span class="n">xs</span>
      <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">intToSnafuDigit</span> <span class="p">(</span><span class="n">num'</span> <span class="o">+</span> <span class="n">carry</span><span class="p">)</span> <span class="o">:</span> <span class="n">convertDigits</span> <span class="n">num''</span> <span class="mi">0</span> <span class="n">xs</span>
      <span class="kr">where</span>
        <span class="n">num'</span> <span class="o">=</span> <span class="n">num</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">5</span>
        <span class="n">num''</span> <span class="o">=</span> <span class="n">floor</span> <span class="o">$</span> <span class="p">((</span><span class="n">fromIntegral</span> <span class="n">num</span><span class="p">)</span> <span class="o">/</span> <span class="mi">5</span><span class="p">)</span>
</code></pre></div></div>

<p>I’m reversing the list because I don’t want to do <code class="language-plaintext highlighter-rouge">++ []</code>, which increases my time complexity, however much that matters. Now that we have both of our conversion functions, we can finally do the problem, which is to sum all the numbers together in our special base 5 representation. The full code is as follows:</p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">import</span> <span class="nn">System.IO</span>

<span class="kr">type</span> <span class="kt">SNAFUDigit</span> <span class="o">=</span> <span class="kt">Char</span>
<span class="kr">type</span> <span class="kt">SNAFU</span> <span class="o">=</span> <span class="kt">String</span>

<span class="n">snafuDigitToInt</span> <span class="o">::</span> <span class="kt">SNAFUDigit</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="n">snafuDigitToInt</span> <span class="sc">'='</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span>
<span class="n">snafuDigitToInt</span> <span class="sc">'-'</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="n">snafuDigitToInt</span> <span class="sc">'0'</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">snafuDigitToInt</span> <span class="sc">'1'</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">snafuDigitToInt</span> <span class="sc">'2'</span> <span class="o">=</span> <span class="mi">2</span>

<span class="n">intToSnafuDigit</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">SNAFUDigit</span>
<span class="n">intToSnafuDigit</span> <span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="sc">'='</span>
<span class="n">intToSnafuDigit</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">=</span> <span class="sc">'-'</span>
<span class="n">intToSnafuDigit</span> <span class="mi">0</span> <span class="o">=</span> <span class="sc">'0'</span>
<span class="n">intToSnafuDigit</span> <span class="mi">1</span> <span class="o">=</span> <span class="sc">'1'</span>
<span class="n">intToSnafuDigit</span> <span class="mi">2</span> <span class="o">=</span> <span class="sc">'2'</span>

<span class="n">snafuToInt</span> <span class="o">::</span> <span class="kt">SNAFU</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="n">snafuToInt</span> <span class="o">=</span> <span class="n">foldr</span> <span class="n">convert</span> <span class="mi">0</span> <span class="o">.</span> <span class="n">enumerate</span>
  <span class="kr">where</span>
    <span class="n">convert</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">digit</span><span class="p">)</span> <span class="n">acc</span> <span class="o">=</span> <span class="n">acc</span> <span class="o">+</span> <span class="p">(</span><span class="mi">5</span> <span class="o">^</span> <span class="n">i</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">snafuDigitToInt</span> <span class="n">digit</span><span class="p">)</span>
    <span class="n">enumerate</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">zip</span><span class="p">[(</span><span class="n">length</span> <span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="n">length</span> <span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">..</span> <span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="n">xs</span>

<span class="n">intToSnafu</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">SNAFU</span>
<span class="n">intToSnafu</span> <span class="n">x</span> <span class="o">=</span> <span class="n">reverse</span> <span class="o">$</span> <span class="n">convertDigits</span> <span class="n">x</span> <span class="mi">0</span> <span class="kt">[]</span>
  <span class="kr">where</span>
    <span class="n">convertDigits</span> <span class="n">num</span> <span class="n">carry</span> <span class="n">xs</span>
      <span class="o">|</span> <span class="n">num</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">carry</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">=</span> <span class="kt">[]</span>
      <span class="o">|</span> <span class="n">num'</span> <span class="o">+</span> <span class="n">carry</span> <span class="o">&gt;</span> <span class="mi">2</span> <span class="o">=</span> <span class="n">intToSnafuDigit</span> <span class="p">(</span><span class="n">num'</span> <span class="o">+</span> <span class="n">carry</span> <span class="o">-</span> <span class="mi">5</span><span class="p">)</span> <span class="o">:</span> <span class="n">convertDigits</span> <span class="n">num''</span> <span class="mi">1</span> <span class="n">xs</span>
      <span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">intToSnafuDigit</span> <span class="p">(</span><span class="n">num'</span> <span class="o">+</span> <span class="n">carry</span><span class="p">)</span> <span class="o">:</span> <span class="n">convertDigits</span> <span class="n">num''</span> <span class="mi">0</span> <span class="n">xs</span>
      <span class="kr">where</span>
        <span class="n">num'</span> <span class="o">=</span> <span class="n">num</span> <span class="p">`</span><span class="n">mod</span><span class="p">`</span> <span class="mi">5</span>
        <span class="n">num''</span> <span class="o">=</span> <span class="n">floor</span> <span class="o">$</span> <span class="p">((</span><span class="n">fromIntegral</span> <span class="n">num</span><span class="p">)</span> <span class="o">/</span> <span class="mi">5</span><span class="p">)</span>

<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
  <span class="n">contents</span> <span class="o">&lt;-</span> <span class="n">readFile</span> <span class="s">"input.txt"</span>
  <span class="kr">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">intToSnafu</span> <span class="o">.</span> <span class="n">sum</span> <span class="o">.</span> <span class="n">map</span> <span class="n">snafuToInt</span> <span class="o">$</span> <span class="n">lines</span> <span class="n">contents</span>
  <span class="n">print</span> <span class="n">result</span>
</code></pre></div></div>

<p>And with that, we’ve completed Advent of Code 2022, the first time ever I’ve done so!</p>

<p><img src="/images/20221225_2.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Advent of Code 2022 Diagram" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Advent of Code Calendar 2022</p>

<hr />

<h1 id="conclusion">Conclusion</h1>

<p>I’ll probably update this blog post for formatting, English and clearer explanations after Christmas, but I will not change the published date.</p>

<p>AOC has been a fun experience for me to hone my skills in a way that did not feel too overbearing, yet fun and engaging. The puzzles taught me a lot, highlighting things that I should improve on. In a nutshell, the lessons were:</p>

<ul>
  <li>Fully understand the problem statement first</li>
  <li>Trust gut instinct on what <em>kind</em> of data structure is needed</li>
  <li>Using gut instinct, pen down how the algorithm will be like. Don’t try to fit everything in your head</li>
  <li>Test code regularly. If possible, test automatically</li>
</ul>

<p>I hope to do AOC next year too, hopefully with less mistakes!</p>

<p>Merry Christmas and Happy 2023, folks.</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="code" /><category term="algorithms" /><category term="advent" /><category term="of" /><category term="code" /><category term="2022" /><summary type="html"><![CDATA[:coffee: Hi!]]></summary></entry><entry><title type="html">Duty Planning with Linear Programming</title><link href="https://codingindex.xyz/2022/01/25/duty-planning-with-linear-programming/" rel="alternate" type="text/html" title="Duty Planning with Linear Programming" /><published>2022-01-25T09:13:00+00:00</published><updated>2022-01-25T09:13:00+00:00</updated><id>https://codingindex.xyz/2022/01/25/duty-planning-with-linear-programming</id><content type="html" xml:base="https://codingindex.xyz/2022/01/25/duty-planning-with-linear-programming/"><![CDATA[<p>Hello! It sure has been a while, huh? Here, have a coffee :coffee:.</p>

<p>Today, I’ll be writing about the hidden magical gem that is Linear Programming, available in your nearest spreadsheet program, be it <a href="https://www.libreoffice.org/discover/calc/">LibreOffice Calc</a>, Excel, or Google Sheets.</p>

<hr />

<h1 id="basics-of-linear-programming">Basics of Linear Programming</h1>

<p>Unlike the other posts you might have seen within my blog, Linear Programming isn’t actually programming. Rather, it is “a method to achieve the best outcome in a mathematical model whose requirements are represented by linear relationships”, according to <a href="https://en.wikipedia.org/wiki/Linear_programming">Wikipedia</a>.</p>

<p>For those who are uninitiated, or need a mini-not-so-professional-refresher, let us break down the definition.</p>

<h2 id="a-method-to-achieve-the-best-outcome">A method to achieve the best outcome</h2>

<p>Essentially, this is “optimization”. We construct an equation, and we try to either minimize or maximize it - you probably had some exposure to it in high school when they taught us how to differentiate.</p>

<p>However, in optimization, instead of figuring out if an equation should be minimized/maximized, we <em>define</em> if the equation should be minimized/maximized based on our requirements.</p>

<h2 id="whose-requirements-are-represented-by-linear-relationships">Whose requirements are represented by linear relationships</h2>

<p>Linear relationships are essentially either equalities, or inequalities (<code class="language-plaintext highlighter-rouge">=</code>, <code class="language-plaintext highlighter-rouge">&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;</code> and so on):</p>

<p><img class="matheqn" src="/images/20220125_1.svg" style="max-width: 200px; width: 100%; margin: 0 auto; display: block; border-radius: 0px;" alt="An example of a linear inequality" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">An example of a linear inequality | Source: Me</p>

<p>Since the relationships must be <em>linear</em>, it implies that equations like the following cannot be solved with Linear Programming:</p>

<p><img class="matheqn" src="/images/20220125_3.svg" style="max-width: 100px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="An example of a non-linear inequality" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">An example of a non-linear inequality | Source: Me</p>

<p>If inequalities like the above presents itself, the best course of action would be to use another kind of solver, like a nonlinear programming solver, or a Constraint Problem (CP) solver <a href="https://developers.google.com/optimization/cp#tools">like this one by Google</a>. However, chances are, that with a touch of creativity, most problems can be expressed as a linear programming problem.</p>

<h2 id="linear-programming">Linear Programming</h2>

<p>In a nutshell, given a bunch of inputs, lets say:</p>

<p><img class="matheqn" src="/images/20220125_2.svg" style="max-width: 100px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="A bunch of x" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">A bunch of inputs | Source: Me</p>

<p>We can define a bunch of constraints represented via <strong>linear</strong> relationships, like:</p>

<p><img class="matheqn" src="/images/20220125_1.svg" style="max-width: 200px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="An example of a linear inequality" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">An example of a linear inequality | Source: Me</p>

<p>For Linear Programming involving only two variables, we can visualize how it works with graphs. Let’s say our two variables are <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code>, and our constraints are:</p>

<p><img class="matheqn" src="/images/20220125_4.svg" style="max-width: 100px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="First Constraint" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">First Constraint | Source: Me</p>

<p><img class="matheqn" src="/images/20220125_5.svg" style="max-width: 100px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Second Constraint" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Second Constraint | Source: Me</p>

<p>We will find that the graph on <a href="https://www.desmos.com/calculator">Desmos</a> will look like this:</p>

<p><img src="/images/20220125_6.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Graph" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Graph. Green represents constraint 1, Blue represents constraint 2 | Source: Me</p>

<p>The intersected area (i.e. areas where both blue and green) are the solutions to the inequality (note that the intersection itself is not a solution, since both of our inequalities are not inclusive). Now, if we were to define an objective function, which is the function we want to minimize or maximize:</p>

<p><img class="matheqn" src="/images/20220125_7.svg" style="max-width: 50px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Objective Function" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Objective Function, 2x + y | Source: Me</p>

<p>And then plot it on the graph:</p>

<p><img src="/images/20220125_8.png" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Objective Function plotted on the graph" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Objective Function (purple) on the graph | Source: Me</p>

<p>We see that the intersection between the line, and the overlapping shaded areas contain all the values that satisfies both constraints, and also the objective function. All we need to do now is to determine what <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> should be if we choose to maximize or minimize our objective function. If it was to maximize our objective function, then the answer we seek is as close to the intersection as possible. Otherwise, if we were to minimize our objective, then the answer we seek should
technically be at another intersection, which isn’t possible with these particular constraints, hence, minimizing the objective would be “INFEASIBLE”.</p>

<p>To wrap up the example, performing linear programming would give us a few results:</p>
<ul>
  <li>The value of <code class="language-plaintext highlighter-rouge">x</code> if the objective function was minimized/maximized</li>
  <li>The value of <code class="language-plaintext highlighter-rouge">y</code> if the objective function was minimized/maximized</li>
  <li>The value of the objective function after minimizing / maximizing</li>
</ul>

<p>And subsequently, to wrap up generally:</p>
<ul>
  <li>The value of <code class="language-plaintext highlighter-rouge">x_1, x_2, x_3, ... x_n</code> if the objective function was minimized/maximized</li>
  <li>The value of the objective function after minimizing / maximizing</li>
</ul>

<p>With more variables, we are essentially working with linear constraints in n-dimensional graphs, which might sound difficult to visualize until you realize it doesn’t really matter, since the user is the one that defines the constraints anyway.</p>

<p>To learn more about how exactly to <em>solve</em> Linear Programming problems, look at the <a href="https://en.wikipedia.org/wiki/Simplex_algorithm">Simplex</a> algorithm. Good solvers would indicate if there is more than one possible answer, or if there is a “close-enough” solution should the entire system be infeasible - although, that is in no way necessary or universal in well-used solvers.</p>

<hr />

<h1 id="duty-planning">Duty Planning</h1>

<p>In my line of work (and probably most of yours, too), duty is a necessary part of work. As a software engineer, this could be translated to being on-call, as a doctor, it could be shifts to do ER, and so on. Needless to say, countless of psychological battles have been waged across the globe thanks to conflicts in agenda when it comes to planning for duty slots: “no weekends please”, or “no public holidays please”, or “my wife’s pregnant” or “I need to walk my pet rock”.</p>

<p>As a duty planner, if you were to ignore these claims, you would be seen as a cold-hearted human being. So I thought: why not just offload the work onto a computer program? Not only would this save time and be much fairer compared to a human (especially if you are also planning it yourself), you would be disguising your own stone-cold, immovable heart and instead blaming your inhumanness on a computer program.</p>

<p>Leveraging on Google Sheet’s integration with Google Forms, I modeled our own planning considerations as linear relationships, maximized preferred dates, and minimized the amount &amp; quality (defined by weekends and public holidays) of duty disparity between each duty personnel. Then, I solved them using Google’s Linear Optimization Service (GLOS).</p>

<blockquote>
  <p>Originally, I used the <a href="https://workspace.google.com/marketplace/app/opensolver/207251662973">OpenSolver</a> app on Google Sheets to solve, but I later realized how slow it was when I was developing the Google Sheet.</p>
</blockquote>

<p>Here is a <a href="https://gist.github.com/jameshi16/bf6583a09a05d490c6ad36d68d377105">GitHub snippet link</a> that contains all of the Google Apps Script used within the relevant Google Sheet. The Google Sheet itself is not open-source, since it contains sensitive data that I won’t try cleaning.</p>

<blockquote>
  <p>Did you know that Google Sheet collaboration isn’t actually simultaneous? The edits from each user just happens so quickly that you see it as simultaneous. Not only is this due to JavaScript browser engines being incapable of multi-processing, but also based on personal experience, where a script can hog out all of the users when busy.  Also, programmatically reading / writing each cell is extremely slow compared to bulk-writing an entire matrix into Google Sheets.</p>
</blockquote>

<p>Instead, allow me to explain how I managed to create linear relationships for some of our planning considerations.</p>

<p>Take <code class="language-plaintext highlighter-rouge">x_i_j</code> (synonymous to <code class="language-plaintext highlighter-rouge">x</code> generally) to be any duty date where <code class="language-plaintext highlighter-rouge">i</code> and <code class="language-plaintext highlighter-rouge">j</code> is the personnel and day respectively, and <code class="language-plaintext highlighter-rouge">b_i_j</code> (synonymous to <code class="language-plaintext highlighter-rouge">b</code> generally) to be any backup date, where the personnel is to serve as a backup for the duty personnel.</p>

<p><code class="language-plaintext highlighter-rouge">1</code> represents duty / backup on that day (depending on whether <code class="language-plaintext highlighter-rouge">x</code> or <code class="language-plaintext highlighter-rouge">b</code> is referred to), and <code class="language-plaintext highlighter-rouge">0</code> represents no duty / backup on that day.</p>

<h2 id="set-in-stone">Set-In-Stone</h2>

<p>As this is considered an “innovation” rather than an “invention”, it is meant to work as a transition between the old process (manually planning) to the new process (automatically planning). Hence, <strong>dates that are manually planted must not be changed</strong>. “Set-In-Stone” acquires non-empty cells, and adds a linear constraint for each affected cell:</p>

<ul>
  <li>If there is a duty slot on that day, then <code class="language-plaintext highlighter-rouge">x = 1</code>, <code class="language-plaintext highlighter-rouge">b = 0</code>.</li>
  <li>If there is no duty slot on that day (forced), then <code class="language-plaintext highlighter-rouge">x = 0</code>, <code class="language-plaintext highlighter-rouge">b = 0</code>.</li>
  <li>If the person is meant to be a backup personnel, then <code class="language-plaintext highlighter-rouge">x = 0</code>, <code class="language-plaintext highlighter-rouge">b = 1</code>.</li>
</ul>

<h2 id="daily-personnel">Daily personnel</h2>

<p>Every single day should have 1 personnel performing duties, while another personnel will be the backup. This is achieved simply by summing for all <code class="language-plaintext highlighter-rouge">i</code>, in the same <code class="language-plaintext highlighter-rouge">j</code>, for all duties / backups.</p>

<p><img class="matheqn" src="/images/20220125_9.svg" style="max-width: 200px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Sum" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Repeat this for every `j` | Source: Me</p>

<h2 id="no-consecutive-days">No consecutive days</h2>

<p>If we were to generate a duty timetable now without some specific constraints, the model would simply assign all the duty to one single person who is free. There are three ways that we are combating this:</p>

<ol>
  <li>Ensuring that there are no consecutive days being served by any given person (in this sub-header);</li>
  <li>Ensuring that everyone’s total points (sum of all of the days that they have served * a modifier based on weekend / public holiday) are <em>close</em> to the average points (pseudo-standard deviation, since we can’t break the linear property);</li>
  <li>Ensuring that all personnel has a chance to do duty, based on the projected number of duty per personnel available in the date range.</li>
</ol>

<p>For a single slot (i.e. the status of duty for a particular person on a particular day), consecutive days are prevented by using this clever little equation, iterating <code class="language-plaintext highlighter-rouge">x_ij</code> over all possible values of <code class="language-plaintext highlighter-rouge">j</code> for <code class="language-plaintext highlighter-rouge">a</code> number of days, where <code class="language-plaintext highlighter-rouge">a</code> is the limit to the number of days someone is allowed to serve.</p>

<p><img class="matheqn" src="/images/20220125_10.svg" style="max-width: 150px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Sum over all possible values of j for a number of days" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Clever Sum | Source: Me</p>

<p>In effect, this ensures that <code class="language-plaintext highlighter-rouge">a</code> days after a duty / backup (not shown) slot, there will not be any more duties.</p>

<h2 id="roughly-equal-points">Roughly equal points</h2>

<p>Without delving too deep into the point system details (as this is subjected to individual implementation), a common understanding between the planners and personnel involved alike are the roughly equal points that everyone should have.</p>

<p>As one of the pivotal factors to eliminate model bias, the points must be allocated fairly to each person. This is done quite simply by taking the projected amount of points (calculated by summing the possible points earned throughout the entire period, dividing by the number of days in the period), introducing a deviation variable, which dictates how many points can each person differ from one another, and then summing the points for each person, ensuring that it is between <code class="language-plaintext highlighter-rouge">point_avg -
deviation</code> to <code class="language-plaintext highlighter-rouge">point_avg + deviation</code>.</p>

<p><img class="matheqn" src="/images/20220125_11.svg" style="max-width: 200px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Approximate equation" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Repeat this for every i | Source: Me</p>

<p>In effect, this means that the model itself would determine the value of <code class="language-plaintext highlighter-rouge">deviation</code>, which means that we want to minimize this as much as possible.</p>

<h2 id="backup--actual-day-cannot-be-on-an-unavailable-date">Backup &amp; Actual Day cannot be on an unavailable date</h2>

<p>This is actually quite simple. After plotting each unavailable date onto a matrix, the <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">b</code> just has to be <code class="language-plaintext highlighter-rouge">0</code> to <code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">0</code> to <code class="language-plaintext highlighter-rouge">1</code>, if unavailable or available respectively.</p>

<h2 id="backup--actual-day-cannot-be-on-the-same-slot">Backup &amp; Actual Day cannot be on the same slot</h2>

<p>This is also pretty simple. <code class="language-plaintext highlighter-rouge">0 &lt; x + b &lt; 1</code> would do the trick: <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">b</code> cannot both be <code class="language-plaintext highlighter-rouge">1</code>, as that would result to <code class="language-plaintext highlighter-rouge">2</code>, which is greater than <code class="language-plaintext highlighter-rouge">1</code>. This prevents a person from simultaneously being his own backup.</p>

<h2 id="chance-to-do-duty">Chance to do duty</h2>

<p>This is done in two parts:</p>

<ol>
  <li>Everyone must do duty based on the projected average;</li>
  <li>Everyone must do a weekday duty the equal number of times, based on projected average.</li>
</ol>

<p>The constraints are quite simple:</p>

<ol>
  <li>Sum up everyone’s duty slots (barring points, all of <code class="language-plaintext highlighter-rouge">j</code> per <code class="language-plaintext highlighter-rouge">i</code>), and it must be greater or equal to the projected average;</li>
  <li>Sum up everyone’s duty slots but only on the weekdays, and it must be greater or equal to the projected average.</li>
</ol>

<p>Weekday is chosen solely due to the large availability compared to weekends - there may be less weekends than the amount of people you are planning for!</p>

<h2 id="unsaid-constraints">Unsaid Constraints</h2>

<p>For obvious reasons, <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">b</code> can only either be <code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">1</code>. <code class="language-plaintext highlighter-rouge">deviation</code> is a continuous variable, solely because points can be expressed as decimals.</p>

<hr />

<h2 id="the-objective">The Objective</h2>

<p>Combined together, the objective of our function is to prioritize &amp; maximize preferred slots (by modifying the points at the objective level, which has benefits over constraint level, as objective is suggestive, while constraints are requirements), while <em>minimizing</em> the deviation variable mentioned earlier.</p>

<p>In a nutshell we are <em>maximizing</em>:</p>

<p><img class="matheqn" src="/images/20220125_13.svg" style="max-width: 350px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Objective Function" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Objective Function | Source: Me</p>

<p>where <code class="language-plaintext highlighter-rouge">deviation</code> and <code class="language-plaintext highlighter-rouge">x</code> is as expected, while <code class="language-plaintext highlighter-rouge">s</code> is a matrix containing values that are either <code class="language-plaintext highlighter-rouge">0</code>, <code class="language-plaintext highlighter-rouge">1</code> or <code class="language-plaintext highlighter-rouge">2</code>. <code class="language-plaintext highlighter-rouge">1</code> represents a normal day, while <code class="language-plaintext highlighter-rouge">2</code> represents a preferred day; <code class="language-plaintext highlighter-rouge">0</code> is essentially <code class="language-plaintext highlighter-rouge">nil</code> - since there is already a <em>constraint</em> that prevents duty slots from being filled if personnel is unavailable.</p>

<p>The result:</p>

<p><img src="/images/20220125_12.png" style="max-width: 800px; width: 100%; margin: 0 auto; display: block; border-radius: 0px" alt="Result" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Beautiful Result | Source: Me</p>

<blockquote>
  <p>Green is duty, Yellow is backup, Black is unavailable, and Red is nothing. C is special consideration.</p>
</blockquote>

<hr />

<h1 id="conclusion">Conclusion</h1>

<p>Before I learnt about optimization functions through a mathematical nerd friend of mine, I always thought this kind of problem would be easier solved with things like Machine Learning, or brute-force search.</p>

<p>However, optimization functions not only reduces the search-space by a lot, it also ensures that the result is mathematically sound, and a hundred percent cold and calculating so that you can freeze anyone who decides you are being too inhumane in planning. Or, you could be soft like me and add in dates of consideration (like ethnic holidays). Feel free to use my Google Apps Script code, that is, after you figure out how to create a Google Sheet that it requires as input!</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="practical" /><category term="linear" /><category term="programming" /><category term="non-coding" /><summary type="html"><![CDATA[Hello! It sure has been a while, huh? Here, have a coffee :coffee:.]]></summary></entry><entry><title type="html">Activities Thus Far</title><link href="https://codingindex.xyz/2021/10/31/activities-thus-far/" rel="alternate" type="text/html" title="Activities Thus Far" /><published>2021-10-31T08:44:00+00:00</published><updated>2021-10-31T08:44:00+00:00</updated><id>https://codingindex.xyz/2021/10/31/activities-thus-far</id><content type="html" xml:base="https://codingindex.xyz/2021/10/31/activities-thus-far/"><![CDATA[<p>Happy Halloween! :tada:</p>

<p>It’s been a while, huh? I’ve completely broke my <a href="/2021/02/14/zerotier-openvpn-nat-part-1/">New Year’s resolution</a> of delivering 1 blog post every month, gotten listless in my life, and generally lost a great chunk of motivation to maintain my Gentoo installation. In terms of recreation during my weekends, I spend a great deal of time playing Gacha video games (as a free-to-play because I’m broke) and watching a <a href="/anime/">bunch of anime</a>.</p>

<p>It has been a few months since I last touched code for recreation - something I regret greatly. Sometimes, in moments of panic and anxiety for the future, I would log into HackerRank just to practice. Recently, I’ve been slowly going through the <a href="https://doc.rust-lang.org/book/title-page.html">Rust Programming Language</a> book, which has a notable “borrowing” concept to manage memory that piques my interest to explore it in the near future.</p>

<p>Of course, this isn’t all I’ve been doing for the past few months. I’ve picked up chess puzzles (not chess itself), learnt how to solve the Rubik’s cube, automated some parts of my job with simple excel skills, and:</p>

<ul>
  <li>Found out my Maxtor External HDD died</li>
  <li>Repaired my HP 23es monitor</li>
  <li>Created a ephemeral Windows 10 LiveCD with WinPE</li>
</ul>

<hr />

<h1 id="maxtor-external-hdd">Maxtor External HDD</h1>

<p>This external HDD has been with me for three years, containing many projects and Linux containers used for academic purposes and hackathons. My laptop, while a powerhouse, has limited storage, and could not meet the storage demand required to archive these projects. Hence, I rely on the external HDD as-if it was a built-in drive, which meant that it was plugged in at every moment the laptop is online.</p>

<p>Despite knowing that <a href="https://www.newegg.com/insider/how-long-do-hard-drives-and-ssds-last/">the typical hard drive lasts for 3 to 5 years</a>, I thought that there was no need to do predictive maintenance (like backing up) on the drive; furthermore, S.M.A.R.T was still returning an “ok” a few weeks prior to the failure. Alas, it failed spectacularly, and I was unable to recover the data on the drive with tools like <code class="language-plaintext highlighter-rouge">dd</code> and Live Recovery CDs like <a href="https://clonezilla.org/">CloneZilla</a>.</p>

<p>To troubleshoot further, I thought about what an external (implied: portable) HDD is typically made up of:</p>
<ol>
  <li>SATA to USB3.0 converter;</li>
  <li>2.5” HDD</li>
</ol>

<p>Cracking open my Maxtor drive, I found out that my Maxtor’s internal HDD uses a 1TB harddrive from Seagate. Inputting the serial number into the Seagate warranty website suggests that the internal HDD was made particularly for Maxtor HDDs.</p>

<p><img src="/images/20211031_1.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Internal HDD photo" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Internal HDD bundled with the adapter | Source: Me</p>

<p><img src="/images/20211031_2.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="SATA to USB3" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">SATA to USB3.0 adapter | Source: Me</p>

<p>I took the Maxtor internal HDD and plugged it into a desktop with a SATA cable to try recovering the data again.</p>

<p><img src="/images/20211031_3.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Plugging in the HDD" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Plugging in the HDD | Source: Me</p>

<p>To my dismay, it didn’t work: all of the sectors after the header sector is completely unreadable; furthermore, I’ve encrypted the drive with VeraCrypt, so chances are, the data is impractical to recover.</p>

<h2 id="harddrive-lifespan">Harddrive Lifespan</h2>

<p>It may seem like I am barking up the wrong tree here, but I am quite disappointed in the performance of Seagate drives in general. A majority of the drives in my possession are from Western Digital, which are fantastic drives that have lasted me 5 - 6 years since I’ve acquired them.</p>

<p>And it’s not just me; a majority of those in the tech community agrees that WD drives (particularly WD Black) are very reliable hard drives. In my life, I’ve owned two Seagate drives; both of them have failed, despite being newer than my WD Blue drives. I also own an old WD Passport from 2015, which has outlasted everything I’ve had in my possession, although it is an unfair comparison since I don’t run the drive unless necessary.</p>

<h2 id="solution">Solution</h2>

<p>In the first place, relying on the external HDD for daily, I/O intensive stuff is generally a bad idea :tm:. So, I decided to solve the root cause: my laptop did not have enough storage capacity for my needs. My laptop has a 512GB Samsung 960 M.2 SSD, which is partitioned somewhat in half for dual-booting Windows &amp; Ubuntu. This meant around 230GB for each operating system for programs, documents, Machine Learning models, and highly bloated IDEs.</p>

<p>Hence, I decided to get a 1TB Samsung 980 M.2 SSD:</p>

<p><img src="/images/20211031_4.webp" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Samsung 980" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Samsung 980 M.2 SSD | Source: <a href="https://www.samsung.com/sg/memory-storage/nvme-ssd/980-pro-pcle-4-0-nvme-m-2-ssd-1tb-mz-v8p1t0bw/">Samsung Official Site</a></p>

<p>I also decided to replace the external HDD with a new WD Passport, to archive projects that I won’t be working on anymore. This new management of storage will allow my HDD to last much longer, and allow me to bring items with my laptop to do productive work. Furthermore, the upgrade even increased my boot times quite a bit.</p>

<hr />

<h1 id="hp-23es-monitor">HP 23es Monitor</h1>

<p>One day, my display ceased to function. To troubleshoot it, I decided to open up both of my monitors and swap parts until I figured out that:</p>

<ol>
  <li>The panel itself was still functional;</li>
  <li>The power supply still outputs the correct voltage;</li>
  <li>The motherboard couldn’t output to the functional panel &amp; functional power supply.</li>
</ol>

<p>This is the motherboard in question:</p>

<p><img src="/images/20211031_5.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="23es Motherboard" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">23es Motherboard | Source: Me</p>

<p>I bought the monitor in a sale for $150, which is an insanely good price because on Amazon, it costs about $250. However, because I have basic repair skills, I instead bought the motherboard on <a href="https://aliexpress.ru/item/1005002610876248.html?sku_id=12000021382193912&amp;spm=a2g2w.productlist.0.0.2cf2f060HjY9rb">Aliexpress</a> for $30.</p>

<p>Sure enough, swapping out the parts fixed the issue; however, seeing as how this (perfectly normal, cool-less) monitor’s motherboard broke down after a mere 4 years of usage, the problem by and large is likely the humid yet dry environment the monitor is operating in, which causes wear and tear in its components. At the same time, I have an Acer monitor that has lived longer - hopefully, this was just a one-time fluke in the operating life expectancy of the HP 23es monitor.</p>

<hr />

<h1 id="ephemeral-windows-10-livecd">Ephemeral Windows 10 LiveCD</h1>

<p>Windows is a widely used operating system used worldwide in various environments and settings. Hence, there are games and tools built specifically for Windows alone. Linux has many tools to try and bridge the gap between Windows applications and the Linux operating system, either by porting the application, building an alternative, using WINE, or optimizing virtual machines to run only for Windows applications.</p>

<p>The last option stated above has gotten <em>really</em> good recently; by applying a technique known as <a href="https://github.com/joeknock90/Single-GPU-Passthrough">Single GPU passthrough</a>, Linux users can use applications that require hardware acceleration, most notably in video games or professional video editing software. However, this technique does not work on CPUs prior to Intel Broadwell, which is unfortunately exactly what I have on my <a href="/2019/05/01/thinkpad-x220/">x220</a>.</p>

<p>As an advocate of privacy, I love the concept of ephemeral runtimes: whatever changes done by any applications within an ephemeral runtime will not be committed to the system, which makes it simple to run “throwaway” applications should I only need them occasionally, rather than frequently. When I am done with the application, I can simply shutdown the system, and it was like I never used the application in the first place. Furthermore, it does not take up space in the harddrive like a traditional operating system would, meaning that I can have more space storing the files and application that affect my day-to-day life, which makes my setup more organized and purposeful.</p>

<p>Hence, I decided to create my own ephemeral Windows 10 LiveCD, using a tool called <a href="https://en.wikipedia.org/wiki/WinBuilder">Winbuilder</a>. To support 3D applications and as wide of an application spectrum as possible, I ensured that the following feaures were enabled:</p>

<ul>
  <li>Logon as Administrator</li>
  <li>DirectX</li>
  <li>DotNet</li>
  <li>VC++ redistributable</li>
</ul>

<p>Another consideration, when choosing which project to run within Winbuilder, is the difference between WinRE and WinPE. RE stands for Recovery Environment, while PE stands for Pre-installation Environment.</p>

<p>Even though WinRE is based on WinPE, WinPE loads network drivers and is a more complete environment compared to WinRE, which provides more recovery tools than operating system utility (<a href="https://www.tomsguide.com/us/winpe-winre-bootable,review-1191-6.html">Toms’ Guide</a>). Since what I need is essentially as complete of a Windows 10 environment as possible, the natural right answer is WinPE. Furthermore, in my testing, there were some applications that just refuses to run on WinRE, but runs perfectly fine on WinPE.</p>

<hr />

<p>Happy Coding</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="general" /><summary type="html"><![CDATA[Happy Halloween! :tada:]]></summary></entry><entry><title type="html">Making a Signal bot</title><link href="https://codingindex.xyz/2021/06/06/making-a-signal-bot/" rel="alternate" type="text/html" title="Making a Signal bot" /><published>2021-06-06T10:38:00+00:00</published><updated>2021-06-06T10:38:00+00:00</updated><id>https://codingindex.xyz/2021/06/06/making-a-signal-bot</id><content type="html" xml:base="https://codingindex.xyz/2021/06/06/making-a-signal-bot/"><![CDATA[<p>Yes, yes, okay, I get it. I missed the May deadline. Here, calm down and have a coffee :coffee:.</p>

<p>So, I don’t have many friends. The friends that I have are… strange, to say the least.</p>

<p>A while ago, I, <a href="https://modelconverge.xyz/">ModelConverge</a> and <a href="https://nikhilr.io/">nikhilr</a> migrated to Signal, to escape from the privacy policy change imposed by Whatsapp. While Whatsapp claims that the privacy policy change will only affect Whatsapp Business users, we had already wanted to migrate away from Whatsapp ever since Facebook acquired it; so the policy change by Whatsapp simply acted as a catalyst. We are hence glad to report that we were part of the masses that hugged <a href="https://www.forbes.com/sites/rachelsandler/2021/01/15/so-many-people-are-using-signal-it-caused-an-outage/?sh=7d7968493df2">Signal to death</a> during a mass migration to the Signal platform, especially after <a href="https://twitter.com/elonmusk/status/1347165127036977153?lang=en">Elon Musk’s tweet</a>.</p>

<blockquote>
  <p>For those of you living under a rock, Signal is an instant messenger just like Whatsapp. Many people migrated to Signal because: (i) it is <a href="https://github.com/signalapp">open-source</a>, (ii) it is run by a <a href="https://signalfoundation.org/">non-profit organization</a> and (iii) has <a href="https://signal.org/docs/">libraries &amp; specifications</a> for developers who want to leverage the Signal protocol or platform to build apps.</p>
</blockquote>

<h1 id="spark">Spark</h1>

<p>The Signal messenger is wonderful; but the users - they have too much power. One of my pals, <a href="https://nikhilr.io/">nikhilr</a> decided to change the group’s avatar photo, drastically changing the friendly democratic climate we shared, effectively serving as a declaration of war between all parties involved. What followed was a great group war that is described in history books as the pivotal moment of the greatest creation.</p>

<p><img src="/images/20210606_1.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Fighting a great war" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Fighting a great war in Signal | Source: Me</p>

<p>I couldn’t just sit idly by and watch as my enemy won battle after battle, getting foothold after foothold on my sanctuary; hence, as a responsible and perfectly rational adult, I decided to abandon all of the work society had me do, and built a Signal bot to eliminate my enemy’s only advantage (free time), and exploit his weakest point (the fact that he is human and hence slower).</p>

<p><img src="/images/20210606_2.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="The bot in action, but Nikhil disrupts the policy" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Using a bot to fight the war | Source: Me</p>

<p>As you can clearly see, before nikhilr decided to remove my privileges to edit the Group Avatar like a true savage undeserving of a respectful knight, my bot fought an admirable battle, stunning my enemies who displayed sheer awe towards my cunning plot.</p>

<p>Today, we won’t be building Group Contender Bot; instead, we’ll just be making a simple Signal bot, to jog your creativity and get you started.</p>

<hr />

<h1 id="considerations">Considerations</h1>

<p>Being a container nerd, I decided that my bot <em>must</em> be setup and run in a container. Automatically, this means that the Signal bot can be run from any platform that can run Docker; furthermore, this would deploy nicely on a home server running most services on <code class="language-plaintext highlighter-rouge">docker-compose</code>.</p>

<p>When searching for a way to interface with Signal, I found <a href="https://github.com/AsamK/signal-cli">Signal CLI</a>, which exposes a DBus interface for applications to interact with. Hence, all I needed to do was to get a library that could interface with the DBus, like <a href="https://pypi.org/project/pydbus/"><code class="language-plaintext highlighter-rouge">pydbus</code></a>.</p>

<h2 id="dbus">DBus</h2>

<p>Many Linux applications talk to each other over the System DBus; according to <a href="https://unix.stackexchange.com/a/604398">this StackOverflow post</a>, it is used as an alternative to <code class="language-plaintext highlighter-rouge">sudo</code>, by allowing a non-privileged application to perform inter-process communication (IPC) to a more privileged application through a bunch of exposed functions. Hence, the system DBus is also the default DBus used by many applications.</p>

<p>Because of the <code class="language-plaintext highlighter-rouge">non-privileged &lt;-&gt; privilege</code> method of communication, container software do not normally expose the System DBus to guest containers because it would open up a whole array of possible vulnerabilities. Thankfully, when digger deeper as to what DBus actually is, I found out that it is essentially a protocol slapped on top of a UNIX socket, meaning that theoretically, it should be possible to construct my own DBus instance <em>just</em> for Signal communication.</p>

<h2 id="python">Python</h2>

<p>The beauty of using the DBus to communicate implies that any language under the sun can be used; I decided to go with Python on an impulse with no clear thought; if I were to make a rational choice, I would have selected <a href="https://golang.org/">Golang</a>, for how simple it is to spawn Go routines for multiprocessing.</p>

<p>On the other hand, Python makes the code more understandable to a wider audience, given its simplicity, and how it is the “comfy” language for most people, allowing a wider audience to develop useful Signal bots.</p>

<hr />

<h1 id="pre-requisites">Pre-requisites</h1>

<p>So, let us build a Signal bot!</p>

<p>First and foremost, we need to install all of the dependencies. On an Ubuntu system, Signal CLI requires <code class="language-plaintext highlighter-rouge">default-jre</code>, while the <code class="language-plaintext highlighter-rouge">pydbus</code> package requires <code class="language-plaintext highlighter-rouge">build-essentials</code>, <code class="language-plaintext highlighter-rouge">libcairo2-dev</code> and <code class="language-plaintext highlighter-rouge">libgirepository1.0-dev</code>. As you can see, for a bot that will run in container, there are quite a lot of dependencies; hence, instead of polluting my otherwise pure host environment, I decided to create a <code class="language-plaintext highlighter-rouge">Dockerfile</code> to build me an environment that can handle Signal CLI.</p>

<div class="language-Dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> ubuntu:latest</span>

<span class="k">RUN </span>apt-get update <span class="o">&amp;&amp;</span> <span class="nv">DEBIAN_FRONTEND</span><span class="o">=</span><span class="s2">"noninteractive"</span> apt-get <span class="nb">install</span> <span class="nt">-y</span> python3 python3-pip default-jre coreutils curl wget libcairo2-dev libgirepository1.0-dev
<span class="k">WORKDIR</span><span class="s"> /tmp</span>
<span class="k">RUN </span>curl <span class="nt">-s</span> https://api.github.com/repos/AsamK/signal-cli/releases/latest <span class="se">\
</span>  | <span class="nb">grep</span> <span class="s2">"browser_download_url.*tar.gz"</span> <span class="se">\
</span>  | <span class="nb">cut</span> <span class="nt">-d</span> : <span class="nt">-f</span> 2,3 <span class="se">\
</span>  | <span class="nb">tr</span> <span class="nt">-d</span> <span class="se">\"</span> <span class="se">\
</span>  | <span class="nb">grep</span> <span class="s2">".gz$"</span> <span class="se">\
</span>  | wget <span class="nt">-qi</span> -
<span class="k">RUN </span><span class="nb">mkdir</span> <span class="nt">-p</span> /opt/cli <span class="o">&amp;&amp;</span> <span class="nb">mkdir</span> <span class="nt">-p</span> /opt/bot <span class="o">&amp;&amp;</span> <span class="nb">tar </span>xvf <span class="k">*</span>.tar.gz <span class="nt">-C</span> /opt/cli <span class="o">&amp;&amp;</span> <span class="nb">mv</span> /opt/cli/signal<span class="k">*</span> /opt/cli/signal
<span class="k">WORKDIR</span><span class="s"> /opt/bot</span>
<span class="k">RUN </span>pip <span class="nb">install </span>pydbus PyGObject
</code></pre></div></div>

<blockquote>
  <p>I adapted the <code class="language-plaintext highlighter-rouge">curl</code> command from this <a href="https://gist.github.com/steinwaywhw/a4cd19cda655b8249d908261a62687f8">GitHub gist</a> written by <a href="https://gist.github.com/steinwaywhw"><code class="language-plaintext highlighter-rouge">@steinwaywhw</code></a>.</p>
</blockquote>

<p>This creates an Ubuntu container with the latest Signal CLI install in <code class="language-plaintext highlighter-rouge">/opt/cli</code>, and the working directory planted in <code class="language-plaintext highlighter-rouge">/opt/bot</code>. To use this container for Signal bot development, you will keep some things at hand:</p>

<ol>
  <li>A phone number you have SMS access to;</li>
  <li>A directory to store your Signal secrets;</li>
  <li>A directory for your bot project;</li>
  <li>A name for your bot.</li>
</ol>

<p>Once you have figured out the phone number &amp; directories you want to use, set them in a terminal you’ll be using for Signal bot related work:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export PHONE_NUMBER="&lt;a phone number, with +countrycode prefixed&gt;"
export SIGNAL_CLI_DATA="&lt;a directory for signal secrets&gt;"
export SIGNAL_BOT_PROJECT="&lt;the directory to your bot project&gt;"
export SIGNAL_BOT_NAME="&lt;any alphanumeric name for your bot&gt;"
alias signal-cli='docker run -v "$SIGNAL_BOT_PROJECT:/opt/bot" -v "$SIGNAL_CLI_DATA:/root/.local/share/signal-cli" -e PHONE_NUMBER="$PHONE_NUMBER" signal-bot:latest /opt/cli/signal/bin/signal-cli'
</code></pre></div></div>

<p>For development purposes, we should first link the Signal CLI to our phone number, so that the bot can send and receive messages. To do this, we first copy + paste the <code class="language-plaintext highlighter-rouge">Dockerfile</code> to a local directory, and build the Docker image:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://gist.githubusercontent.com/jameshi16/71764cc0bac84adda717e9ddb0b44364/raw/2fff57fac78826e17ad097dcb4c7ed1e873ddb1e/Dockerfile
docker build . -t signal-bot:latest
</code></pre></div></div>

<p>Now, if you want to link to a phone number <em>you already use for daily Signal usage</em>, then run this command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>signal-cli link -n "$SIGNAL_BOT_NAME" &gt; /tmp/output &amp; \sleep 10 &amp;&amp; cat /tmp/output | curl -F-=\&lt;- https://qrenco.de &amp;&amp; fg
</code></pre></div></div>

<p>A QR code should be generated and then printed on your terminal window; scan the result with your phone’s Signal messenger. If you don’t know how, follow the <a href="https://support.signal.org/hc/en-us/articles/360007320551-Linked-Devices">guide on the official Signal Support</a>.</p>

<p>Your device should be linked.</p>

<p><strong>Otherwise</strong>, if you want to link a completely new phone number, then run this Signal CLI command through the container:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>signal-cli -u ${PHONE_NUMBER} register
</code></pre></div></div>

<p>You should receive a SMS with your OTP code to activate Signal. Copy that verification code before running this command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>signal-cli -u ${PHONE_NUMBER} verify &lt;insert verification code&gt;
</code></pre></div></div>

<h1 id="writing-the-bot">Writing the bot</h1>

<p>Now, we move to the stage where we write the bot. No matter what language the bot is written in, the bot needs at least two other running processes:</p>

<ol>
  <li>A DBus daemon; and</li>
  <li>A daemonized Signal CLI process.</li>
</ol>

<p>Hence, before we can even write the content required for the bot, we must first write an entrypoint script for the Docker container. Luckily, we can quite easily write this script:</p>

<p><code class="language-plaintext highlighter-rouge">entrypoint.sh</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="nb">set</span> <span class="nt">-e</span>

<span class="nb">export </span><span class="nv">DBUS_SESSION_BUS_ADDRESS</span><span class="o">=</span><span class="si">$(</span>dbus-daemon <span class="nt">--session</span> <span class="nt">--fork</span> <span class="nt">--print-address</span><span class="si">)</span>

<span class="nb">touch</span> /tmp/output.log
/opt/cli/signal/bin/signal-cli <span class="nt">-u</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PHONE_NUMBER</span><span class="k">}</span><span class="s2">"</span> daemon <span class="o">&gt;&gt;</span> /tmp/output.log 2&gt;&amp;1 &amp;
dbus-monitor <span class="nt">--session</span> <span class="o">&gt;&gt;</span> /tmp/output.log 2&gt;&amp;1 &amp; <span class="c"># comment this out if you no longer need to monitor the bus</span>
<span class="nb">sleep </span>20s <span class="o">&amp;&amp;</span> python3 /opt/bot/script.py <span class="o">&gt;&gt;</span> /tmp/output.log 2&gt;&amp;1 &amp;

<span class="nb">tail</span> <span class="nt">-f</span> /tmp/output.log
</code></pre></div></div>

<p>The script above assumes that you are executing a bot written in Python, with the entrypoint of that bot within <code class="language-plaintext highlighter-rouge">script.py</code> of your project folder, and also assumes that you have set the environmental variables right. Let’s test it out:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>alias run_bot="docker run -v \"$SIGNAL_BOT_PROJECT:/opt/bot\" -v \"$SIGNAL_CLI_DATA:/root/.local/share/signal-cli\" -e PHONE_NUMBER=\"$PHONE_NUMBER\" signal-bot:latest ./entrypoint.sh"
wget -O script.py https://gist.githubusercontent.com/jameshi16/71764cc0bac84adda717e9ddb0b44364/raw/fd3fc896bfe56d18741ba84c8c63d00f34c8434b/receive.py
run_bot
</code></pre></div></div>

<p>The script written by <code class="language-plaintext highlighter-rouge">mh-g</code>, modified by me to use a Session Bus instead, essentially reads every message pumped into Signal out onto the terminal window.</p>

<blockquote>
  <p>The purpose of <code class="language-plaintext highlighter-rouge">sleep 20s</code> is to give Signal CLI some time to: (i) start daemonizing, (ii) connect to the DBus, and (iii) synchronize messages a little before starting the actual script. Sometimes, this takes more than 20s, but for our purposes, it should be good enough. You may sometimes find your bot unresponsive during this stage; but trust me, it’ll work <em>eventually</em>, after catching up with all of the messages.</p>
</blockquote>

<p>Once you have verified the workability of your whole set up, it is time to write code to develop a signal bot. Let’s start with the <code class="language-plaintext highlighter-rouge">receive.py</code> sample code you downloaded to test the workability of your setup:</p>

<p><code class="language-plaintext highlighter-rouge">script.py</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python3
</span>
<span class="k">def</span> <span class="nf">msgRcv</span><span class="p">(</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">groupID</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">attachments</span><span class="p">):</span>
  <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">msgRcv called</span><span class="sh">"</span><span class="p">)</span>
  <span class="nf">print</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
  <span class="k">return</span>

<span class="kn">from</span> <span class="n">pydbus</span> <span class="kn">import</span> <span class="n">SessionBus</span>
<span class="kn">from</span> <span class="n">gi.repository</span> <span class="kn">import</span> <span class="n">GLib</span>

<span class="n">bus</span> <span class="o">=</span> <span class="nc">SessionBus</span><span class="p">()</span>
<span class="n">loop</span> <span class="o">=</span> <span class="n">GLib</span><span class="p">.</span><span class="nc">MainLoop</span><span class="p">()</span>

<span class="n">signal</span> <span class="o">=</span> <span class="n">bus</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">org.asamk.Signal</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">/org/asamk/Signal</span><span class="sh">'</span><span class="p">)</span>
<span class="n">signal</span><span class="p">.</span><span class="n">onMessageReceived</span> <span class="o">=</span> <span class="n">msgRcv</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">'</span><span class="s">__main__</span><span class="sh">'</span><span class="p">:</span>
  <span class="n">loop</span><span class="p">.</span><span class="nf">run</span><span class="p">()</span>
</code></pre></div></div>

<p>If you’ve linked the bot to a number that is <em>already</em> using Signal, then you would realize that this piece of code would only work when people other than yourself messages you. If you want to receive <em>all</em> messages, including the ones from yourself, then change:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">def msgRcv(timestamp, source, groupID, message, attachments):
</span>  print("msgRcv called")
  print(message)
  return
<span class="err">
</span><span class="gi">+ def msgSyncRcv(timestamp, source, destination, groupID, message, attachments):
+   msgRcv(timestamp, source, groupId, message, attachments)
+   return
</span><span class="err">
</span>...
<span class="err">
</span><span class="p">signal = bus.get('org.asamk.Signal', '/org/asamk/Signal')
signal.onMessageReceived = msgRcv
</span><span class="gi">+ signal.onSyncMessageReceived = msgSyncRcv
</span><span class="err">
</span><span class="p">if __name__ == '__main__':
</span></code></pre></div></div>

<p>And then run the bot again with the <code class="language-plaintext highlighter-rouge">run_bot</code> command.</p>

<p>Let’s make the bot respond to commands that start with the <code class="language-plaintext highlighter-rouge">/</code> prefix, by changing the contents of the <code class="language-plaintext highlighter-rouge">msgRcv</code> function:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">msgRcv</span> <span class="p">(</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">sender</span><span class="p">,</span> <span class="n">groupID</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">attachments</span><span class="p">):</span>
  <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">message</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span>
    <span class="n">signal</span><span class="p">.</span><span class="nf">sendGroupMessage</span><span class="p">(</span><span class="sh">"</span><span class="s">{:s} said {:s}</span><span class="sh">"</span><span class="p">.</span><span class="nf">format</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">message</span><span class="p">),</span> <span class="p">[],</span> <span class="n">groupID</span><span class="p">)</span>
  <span class="k">return</span>

</code></pre></div></div>

<p>Now, send a message to the bot with the <code class="language-plaintext highlighter-rouge">/</code> prefix, and you should see that the bot echos you like a parrot. With that, we now have a basic bot. For more things that the bot can do, check out <a href="https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc">Signal CLI’s DBus wiki</a>; all of the functions are available by de-capitalizing the first letter, and then accessing it as a sub-member of the <code class="language-plaintext highlighter-rouge">signal</code> object. This also includes the DBus signals listed on the manpage.</p>

<p>For a more complete guide, let’s make an 8-ball bot, which essentially just returns an 8-ball-esque response based on random probability.</p>

<p>8-ball has <a href="https://en.wikipedia.org/wiki/Magic_8-Ball#Design_and_usage">20 different answers</a>, which can be represented by the following Python list:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">responses</span> <span class="o">=</span> <span class="p">[</span><span class="sh">'</span><span class="s">It is Certain.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">It is decidedly so.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Without a doubt.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Yes definitely.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">You may rely on it.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">As I see it, yes.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Most likely.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Outlook good.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Yes.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Signs point to yes.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Reply hazy, try again.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ask again later.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Better not tell you now.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Cannot predict now.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Concentrate and ask again.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Don</span><span class="se">\'</span><span class="s">t count on it.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">My reply is no.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">My sources say no.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Outlook not so good.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Very doubtful.</span><span class="sh">'</span><span class="p">]</span>
</code></pre></div></div>

<p>For the <code class="language-plaintext highlighter-rouge">msgRcv</code> function, we basically just choose a random string within the list, and return it whenever we see 8 ball after the <code class="language-plaintext highlighter-rouge">/</code> prefix:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">random</span>

<span class="n">responses</span> <span class="o">=</span> <span class="p">[</span><span class="sh">'</span><span class="s">It is Certain.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">It is decidedly so.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Without a doubt.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Yes definitely.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">You may rely on it.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">As I see it, yes.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Most likely.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Outlook good.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Yes.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Signs point to yes.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Reply hazy, try again.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ask again later.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Better not tell you now.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Cannot predict now.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Concentrate and ask again.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Don</span><span class="se">\'</span><span class="s">t count on it.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">My reply is no.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">My sources say no.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Outlook not so good.</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Very doubtful.</span><span class="sh">'</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">msgRcv</span> <span class="p">(</span><span class="n">timestamp</span><span class="p">,</span> <span class="n">sender</span><span class="p">,</span> <span class="n">groupID</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">attachments</span><span class="p">):</span>
  <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">message</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">/</span><span class="sh">'</span><span class="p">:</span>
      <span class="k">if</span> <span class="sh">'</span><span class="s">8ball</span><span class="sh">'</span> <span class="ow">in</span> <span class="n">message</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span>
        <span class="n">signal</span><span class="p">.</span><span class="nf">sendGroupMessage</span><span class="p">(</span><span class="sh">'</span><span class="s">8ball: </span><span class="sh">'</span> <span class="o">+</span> <span class="n">random</span><span class="p">.</span><span class="nf">choice</span><span class="p">(</span><span class="n">responses</span><span class="p">),</span> <span class="p">[],</span> <span class="n">groupID</span><span class="p">)</span>
  <span class="k">return</span>
</code></pre></div></div>

<p>Full code for <code class="language-plaintext highlighter-rouge">script.py</code> can be found in <a href="https://gist.github.com/jameshi16/71764cc0bac84adda717e9ddb0b44364#file-script-py">my gist</a>. After editing the script, the bot can be run with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>run_bot
</code></pre></div></div>

<p>Now, on Signal, messaging <code class="language-plaintext highlighter-rouge">/8ball</code> should yield:</p>

<p><img src="/images/20210606_3.PNG" style="max-width: 200px; width: 100%; margin: 0 auto; display: block;" alt="8 ball response" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">A response from magical 8 ball | Source: Me</p>

<h1 id="docker-compose">Docker Compose</h1>

<p>The last part is probably the simplest part; writing the <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file. The template should be quite self-explanatory:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>
<span class="na">services</span><span class="pi">:</span>
  <span class="na">signal-bot</span><span class="pi">:</span>
    <span class="na">build</span><span class="pi">:</span> <span class="s">https://gist.githubusercontent.com/jameshi16/71764cc0bac84adda717e9ddb0b44364/raw/Dockerfile</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">signal-bot</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">/bin/bash -c "./entrypoint.sh"</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">${SIGNAL_CLI_DATA}:/root/.local/share/signal-cli</span>
      <span class="pi">-</span> <span class="s">${SIGNAL_BOT_PROJECT}:/opt/bot</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">PHONE_NUMBER=${PHONE_NUMBER}</span>
</code></pre></div></div>

<p>Then, fill in the relevant details in the <code class="language-plaintext highlighter-rouge">.env</code> file. If you have not shutdown the terminal you used in the <a href="#pre-requisite">pre-requisite</a> stage, then you can use this command to generate the <code class="language-plaintext highlighter-rouge">.env</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo -e "SIGNAL_CLI_DATA=${SIGNAL_CLI_DATA}\nSIGNAL_BOT_PROJECT=${SIGNAL_BOT_PROJECT}\nPHONE_NUMBER=${PHONE_NUMBER}" &gt; .env
docker-compose config
</code></pre></div></div>

<p>You should see all of the environment variables substituted. If they are all there, then you can run:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up
</code></pre></div></div>

<p>To see the bot in action; and run:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up -d
</code></pre></div></div>

<p>To detach it from the terminal, and run it in the background.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Welp, that was fun! I will make the source code for the Group Avatar Contender bot available soon; but don’t count on it to be online after this blog post. Hopefully, this blog post makes up for the missing one you would have otherwise gotten on May. There <em>should</em> be a separate blog post for June; until then, ciao!</p>

<p>Happy Coding,</p>

<p>CodingIndex</p>]]></content><author><name>James</name></author><category term="signal" /><category term="bot" /><category term="signal" /><summary type="html"><![CDATA[Yes, yes, okay, I get it. I missed the May deadline. Here, calm down and have a coffee :coffee:.]]></summary></entry><entry><title type="html">Coffee :coffee:</title><link href="https://codingindex.xyz/2021/04/25/coffee/" rel="alternate" type="text/html" title="Coffee :coffee:" /><published>2021-04-25T11:11:00+00:00</published><updated>2021-04-25T11:11:00+00:00</updated><id>https://codingindex.xyz/2021/04/25/coffee</id><content type="html" xml:base="https://codingindex.xyz/2021/04/25/coffee/"><![CDATA[<p>Initially, I planned to write about my findings after reverse-engineering some RPG Maker XP, VX, VX Ace, MV and Wolf Editor games (which is not illegal as long as I’m not creating a copy or changing the artifact in any way according to <a href="https://en.wikipedia.org/wiki/Reverse_engineering">Wikipedia</a>, which is definitely the best source of legal advice. But seriously though, I’m doing it for educational purpose and nothing else.). However, whenever life gives you lemons, you sigh <i>ugh</i> and wait for a better fruit.</p>

<p>Instead, I changed my plans and wanted to write about studying techniques for remembering content you have little interest in, since that has been my most utilized skill for the past few months. Then, I looked at my coffee, drank the coffee, thought about how interesting it was that coffee was banned a few times in history and decided that I was going to do a blog post about coffee instead.</p>

<p>So, let’s get started!</p>

<h1 id="why-do-we-drink-coffee">Why do we drink coffee</h1>

<p>Coffee contains caffeine, which is a drug that stimulates the brain and helps us stay awake and alert (ref. 1, 2, 3). The usage of coffee is ubiquitous for almost any profession and scenario that requires the worker to stay awake - artists, musicians, labourers, architects, engineers, and of course, programmers. For enterprise programmers, it helps them trudge through client complaints, unreasonable demands from product managers, and fixing failing unit tests - just to name a few. Caffeine also boosts mood, metabolism and physical performance (ref. 8).</p>

<p>Despite its name, <em>decaffeinated</em> coffee is not fully caffeine free; a cup of coffee containing 180mg of caffeine will end up having 5.4mg of caffeine when decaffeinated (ref. 11).</p>

<h1 id="how-much-coffee-can-we-ingest-in-a-day">How much coffee can we ingest in a day?</h1>

<p>For adults, the recommended daily caffeine intake is 400mg - which is about 4 or 5 cups of coffee per day (ref. 4). For reference, a cup conventionally contains 200ml to 250ml of coffee (ref. 5) (the former will limit coffee intake to 5 cups a day, while the latter will limit coffee to 4 cups a day) - this would mean that, in metric units, an adult can drink up to 1 litre of coffee a day.</p>

<p>There exists light coffee drinkers and heavy coffee drinkers differentiated by their caffeine tolerance (ref. 6), which affects the magnitude and duration of the undesirable side effects of coffee. An individual that is unsure of their caffeine intake should try to only ingest a maximum of 200mg of caffeine per day, which amounts to 2 or 2.5 cups a day.</p>

<p>Some figures for commonly ingested source of caffeine:</p>

<p><img src="/images/20210425_5.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Caffeine doses" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Caffeine doses in coffee | Source: <a href="https://www.apa.org/gradpsych/2015/11/coffee">Stacy Lu, APA (ref. 7)</a></p>

<p>Some more figures from ref. 6:</p>

<ul>
  <li>Coffee: 96mg per cup</li>
  <li>Energy Drink: 72mg per cup</li>
  <li>Green Tea: 29mg per cup</li>
  <li>Soft Drink: 34mg per cup</li>
</ul>

<h1 id="side-effects-of-coffee">Side-effects of coffee</h1>

<p>Recalling that the recommended maximum daily caffeine intake is 400mg, ingesting 3 times the amount will likely result to toxic effects (ref. 4). Some toxic effects include (ref. 8, 9):</p>
<ol>
  <li>Anxiety</li>
  <li>Sleep disorder</li>
  <li>Increased blood sugar levels</li>
  <li>Increased heart rate &amp; blood pressure</li>
</ol>

<p><img src="/images/20210425_6.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="Coffee" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Coffee | Photo by Nathan Dumlao on <a href="https://unsplash.com/photos/6VhPY27jdps">Unsplash (ref. 10)</a></p>

<h1 id="history-of-coffee">History of coffee</h1>

<p>The origins of coffee (as in the coffee beans) is highly debatable (ref. 12), although it can be said that due to its discovery since time immemorial, it has touched upon every aspect of human civilisation, including religion (as with the case with it replacing wine in the Muslim religion), politics (as with the various bans on coffee) and trade.</p>

<p>An interesting aspect of coffee was its use by America in World War I, and its relation to coffeehouses, which were cornerstones for people to “hang out”.</p>

<h2 id="world-war-i">World War I</h2>

<p>In World War I, coffee was considered to be an essential item in the America army (ref. 13) - it was so essential that coffee became a rationed item in November 29, 1942 (ref. 14). There were only two reasons any resources needed to be rationed in the context of war:</p>
<ol>
  <li>To ensure all civilians get access to said resource fairly;</li>
  <li>To ensure the military has access to said resource.</li>
</ol>

<p><img src="/images/20210425_7.jpg" style="max-width: 400px; width: 100%; margin: 0 auto; display: block;" alt="A propaganda poster for coffee rationing" /></p>
<p class="text-center text-gray lh-condensed-ultra f6">Coffee Rationing in US | Source: <a href="https://www.wearethemighty.com/articles/brief-history-coffee-field/">WeAreTheMighty (ref. 15)</a></p>

<p>As for the reasons why coffee was much needed in the trenches of World War I, it was to combat fatigue, much like how we use coffee nowadays. Having re-energized troops can make a huge difference in the context of war.</p>

<h2 id="coffeehouses">Coffeehouses</h2>

<p>Coffeehouses were popular places for people of all classes to “meet, discuss business, exchange ideas and debate the news of the day” (ref. 16). Having free debate and discussion between people of all social statuses lead to the spread of democracy. However, because coffeehouses were so commonplace and popular, some were filled with criminals, scoundrels and pimps (ref. 16), enough to warrant an attempt was made to ban coffeehouses in 1675.</p>

<p>Coffeehouses soon fell out of favour as the popularity of tea rose, but its impact on democracy can still be felt today.</p>

<h1 id="conclusion">Conclusion</h1>

<p>It is strange to make a blog post about a commodity that is widely enjoyed instead of a programming blog post; luckily, as my blog name suggests, I like to do “Random Shenanigans”, which means that once in a while, an informative blog post such as this is nice to have.</p>

<p>I hope you enjoyed this blog post, as much as I enjoyed writing it! Let’s hope that I get apples instead of lemons from life next time; I haven’t got a lemonade maker.</p>

<p>Happy Coding (&amp; enjoy a cup of coffee :coffee:),</p>

<p>CodingIndex</p>

<h1 id="references">References</h1>

<p>Here are a list of references informally attributed, because I’m too lazy to do a proper citation.</p>

<ol>
  <li><a href="https://www.webmd.com/vitamins/ai/ingredientmono-979/caffeine">https://www.webmd.com/vitamins/ai/ingredientmono-979/caffeine</a></li>
  <li><a href="https://www.healthline.com/nutrition/what-is-caffeine#what-it-is">https://www.healthline.com/nutrition/what-is-caffeine#what-it-is</a></li>
  <li><a href="https://www.healthline.com/health/caffeine-effects-on-body">https://www.healthline.com/health/caffeine-effects-on-body</a></li>
  <li><a href="https://www.fda.gov/consumers/consumer-updates/spilling-beans-how-much-caffeine-too-much">https://www.fda.gov/consumers/consumer-updates/spilling-beans-how-much-caffeine-too-much</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Cup_(unit)">https://en.wikipedia.org/wiki/Cup_(unit)</a></li>
  <li><a href="https://www.healthline.com/nutrition/caffeine-tolerance">https://www.healthline.com/nutrition/caffeine-tolerance</a></li>
  <li><a href="https://www.apa.org/gradpsych/2015/11/coffee">https://www.apa.org/gradpsych/2015/11/coffee</a></li>
  <li><a href="https://www.healthline.com/nutrition/caffeine-side-effects">https://www.healthline.com/nutrition/caffeine-side-effects</a></li>
  <li><a href="https://www.aarp.org/health/healthy-living/info-10-2013/coffee-for-health.html">https://www.aarp.org/health/healthy-living/info-10-2013/coffee-for-health.html</a></li>
  <li><a href="https://unsplash.com/photos/6VhPY27jdps?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditShareLink">https://unsplash.com/photos/6VhPY27jdps?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditShareLink</a></li>
  <li><a href="https://www.healthline.com/nutrition/caffeine-in-decaf">https://www.healthline.com/nutrition/caffeine-in-decaf</a></li>
  <li><a href="https://en.wikipedia.org/wiki/History_of_coffee">https://en.wikipedia.org/wiki/History_of_coffee</a></li>
  <li><a href="https://www.npr.org/sections/thesalt/2017/04/06/522071853/in-wwi-trenches-instant-coffee-gave-troops-a-much-needed-boost">https://www.npr.org/sections/thesalt/2017/04/06/522071853/in-wwi-trenches-instant-coffee-gave-troops-a-much-needed-boost</a></li>
  <li><a href="https://www.history.com/this-day-in-history/coffee-rationing-begins">https://www.history.com/this-day-in-history/coffee-rationing-begins</a></li>
  <li><a href="https://www.wearethemighty.com/articles/brief-history-coffee-field/">https://www.wearethemighty.com/articles/brief-history-coffee-field/</a></li>
  <li><a href="https://www.historic-uk.com/CultureUK/English-Coffeehouses-Penny-Universities/">https://www.historic-uk.com/CultureUK/English-Coffeehouses-Penny-Universities/</a></li>
</ol>]]></content><author><name>James</name></author><category term="coffee" /><category term="history" /><category term="coffee" /><summary type="html"><![CDATA[Initially, I planned to write about my findings after reverse-engineering some RPG Maker XP, VX, VX Ace, MV and Wolf Editor games (which is not illegal as long as I’m not creating a copy or changing the artifact in any way according to Wikipedia, which is definitely the best source of legal advice. But seriously though, I’m doing it for educational purpose and nothing else.). However, whenever life gives you lemons, you sigh ugh and wait for a better fruit.]]></summary></entry></feed>