<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://daringcuteseal.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://daringcuteseal.github.io/" rel="alternate" type="text/html" /><updated>2026-05-01T18:28:34+07:00</updated><id>https://daringcuteseal.github.io/feed.xml</id><title type="html">DaringCuteSeal</title><subtitle>DaringCuteSeal&apos;s website</subtitle><author><name>Cikitta</name></author><entry><title type="html">An Attempt for GOOD Web Form DX &amp;amp; UX (EXCELSIOR 2026)</title><link href="https://daringcuteseal.github.io/blog/infinite-power-web-forms/" rel="alternate" type="text/html" title="An Attempt for GOOD Web Form DX &amp;amp; UX (EXCELSIOR 2026)" /><published>2026-05-01T00:00:00+07:00</published><updated>2026-05-01T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/infinite-power-web-forms</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/infinite-power-web-forms/"><![CDATA[<h1 id="my-last-round-of-excelsior">My Last Round of EXCELSIOR</h1>

<p>Ah. Another year, another <a href="/blog/excelsior">EXCELSIOR</a>. So, EXCELSIOR is my school’s annual competition event. It’ll be my last year contributing to the event as part of the committee, so I figured I want to do a big favor of rewriting <a href="/blog/excelsior/#the-website">our hackish PHP site</a> with SvelteKit (and other modern web libraries). Last year, the tech team (not me, I was in design) deployed a (somewhat disasterous) AI slop site, so let’s settle it once and for all with a human-written future-proof website.</p>

<h1 id="web-form">Web Form</h1>
<p>The first task that I chose was rewriting the competition registration web form since it’s <em>the</em> one thing that needs to be rock stable after deployed. 2 years ago we had some people complaining about form submission failures and we couldn’t figure out why, so I decided to focus on this.</p>

<p>But oh, how web form suuucks. I mean, come on. HTML web forms have been here with us since the 90’s. I thought they’d be somewhat mature already? But no, web form can still turn into your nightmare.</p>

<h1 id="a-basic-form">A Basic Form</h1>

<p>A basic HTML form? Yeah, we’re all familiar with them. They’re simply form fields enclosed inside a <code class="language-plaintext highlighter-rouge">&lt;form&gt;</code> element. We can send the form data for submission by sending a request to the server (POST or GET). The data is then handled by whatever backend software is being run by the server.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">""</span> <span class="na">method=</span><span class="s">"get"</span> <span class="na">class=</span><span class="s">"form-example"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-example"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"name"</span><span class="nt">&gt;</span>Enter your name: <span class="nt">&lt;/label&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"name"</span> <span class="na">id=</span><span class="s">"name"</span> <span class="na">required</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-example"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"email"</span><span class="nt">&gt;</span>Enter your email: <span class="nt">&lt;/label&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"email"</span> <span class="na">name=</span><span class="s">"email"</span> <span class="na">id=</span><span class="s">"email"</span> <span class="na">required</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-example"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Subscribe!"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/form&gt;</span>

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

<p>Above is an example of <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form">a web form from MDN</a>.</p>

<p>There’s nothing wrong with it. Or, at least, that’s the case for most simple forms.</p>

<h1 id="1-true-arrays-objects-nonexistent">1. True Arrays? Objects? Nonexistent!</h1>

<p>Let’s now talk about competition registration forms, as that’s what I’m trying to build for EXCELSIOR. Suppose I’m making a form for a solo competition. I’d require their contact information, maybe a photo of themselves, letter from their school, and a proof of payment. Works like a charm.</p>

<p>But then, what about <em>team</em> competitions where you need to submit information for more than one person? <em>You’d think that HTML form handles arrays (like I did)? Oh, no it doesn’t.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></em> As absurd as it sounds, form fields are limited to primitive types, and that’s not even including arrays.</p>

<p>Now, here’s the strange part: our legacy website actually managed multi-participant information just fine. How did they do that? Well, you can get a little creative with naming. Our legacy site uses an index-based names for the form fields:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function</span> <span class="n">generate_text_field</span><span class="p">(</span><span class="nv">$form_name</span><span class="p">,</span> <span class="p">...)</span> <span class="p">{</span>
<span class="cp">?&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form-row"</span><span class="nt">&gt;</span>
	<span class="nt">&lt;input</span> <span class="na">name=</span><span class="s">"</span><span class="cp">&lt;?=</span> <span class="nv">$form_name</span> <span class="cp">?&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/div&gt;</span>	
<span class="cp">&lt;?php</span>
<span class="p">}</span>

<span class="c1">// ...</span>
<span class="k">for</span><span class="p">(</span><span class="nv">$f</span> <span class="o">=</span> <span class="mi">1</span> <span class="p">;</span> <span class="nv">$f</span> <span class="o">&lt;=</span> <span class="nv">$members_min</span> <span class="p">;</span> <span class="nv">$f</span><span class="o">++</span><span class="p">){</span>
    <span class="nf">generate_text_field</span><span class="p">(</span><span class="s2">"name"</span><span class="mf">.</span><span class="nv">$f</span><span class="p">,</span> <span class="s2">"Full name of participant #"</span><span class="mf">.</span><span class="nv">$f</span><span class="mf">.</span><span class="s2">""</span><span class="p">,</span> <span class="s2">"text"</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
    <span class="nf">generate_text_field</span><span class="p">(</span><span class="s2">"notelp"</span><span class="mf">.</span><span class="nv">$f</span><span class="p">,</span> <span class="s2">"Phone number"</span><span class="p">,</span> <span class="s2">"text"</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
    <span class="nf">generate_text_field</span><span class="p">(</span><span class="s2">"email"</span><span class="mf">.</span><span class="nv">$f</span><span class="p">,</span> <span class="s2">"Email"</span><span class="p">,</span> <span class="s2">"email"</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>

<span class="c1">// ...</span>

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

<p>So later during form processing, we’d just refer to the user input by the field name + its index. But obviously, this is not clean. A slight change in field naming convention can break everything. You can try to mitigate that by using functions to format the fields—but there’s still no guarantee that the functions are <em>really</em> used by both frontend code and backend code.</p>

<p>So, how do we make it safer? Obviously, have something type-safe. Have an array of participant objects or something. It’d be nice if we can represent the form data as a JSON object and have the schema be known by both the frontend and the backend. This way, I can have custom object types, arrays, and all the goodies! We can implement this from scratch ourselves by programatically submitting a JSON instead of a form data somehow, but I personally would rather not to. I opted to use <a href="https://superforms.rocks">SvelteKit Superforms</a> instead, which is a nice SvelteKit form library that does all those for me.</p>

<h2 id="json-form-with-superforms">JSON Form with Superforms</h2>

<p>Now, obviously, when we involve some nonstandard way to submit a form, we’re talking about sticking some JavaScript to the client page. But I’m fine with that; most people registering for our competition will probably have JavaScript anyway.</p>

<p>With Superforms, I can simply define a form schema like this:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// form-schema.ts</span>
<span class="k">export</span> <span class="kd">function</span> <span class="nx">getParticipantSchema</span><span class="p">(</span><span class="nx">enforceInstitution</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
		<span class="na">name</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...,</span>
		<span class="na">email</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nx">email</span><span class="p">()...,</span>
	<span class="p">})</span>
<span class="p">}</span>

<span class="k">export</span> <span class="kd">function</span> <span class="nx">getFormSchema</span><span class="p">(</span><span class="nx">enforceInstitution</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">,</span> <span class="nx">minParticipants</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">maxParticipants</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">addExtraQuestion</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
		<span class="na">participants</span><span class="p">:</span> <span class="nx">getParticipantSchema</span><span class="p">(</span><span class="nx">enforceInstitution</span><span class="p">)...,</span>
		<span class="na">institutionName</span><span class="p">:</span> <span class="nx">enforceInstitution</span> <span class="p">?</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...</span> <span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...,</span>
		<span class="na">compSlug</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">(),</span>
		<span class="na">extraQuestionAnswer</span><span class="p">:</span> <span class="nx">addExtraQuestion</span> <span class="p">?</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...</span> <span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...,</span>
		<span class="na">representative</span><span class="p">:</span> <span class="nx">minParticipants</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="p">?</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...</span> <span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">()...,</span>
	<span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice the conditional statements—we can have a conditional schema! how cool.</p>

<p>And then, we write some backend code that queries the database for the right competition data:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// +page.server.ts</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">load</span><span class="p">:</span> <span class="nx">PageServerLoad</span> <span class="o">=</span> <span class="k">async</span> <span class="p">({</span> <span class="nx">params</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="kd">const</span> <span class="nx">competitionData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">db</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">competitions</span><span class="p">.</span><span class="nx">findFirst</span><span class="p">({</span>
		<span class="na">where</span><span class="p">:</span> <span class="nx">eq</span><span class="p">(</span><span class="nx">competitions</span><span class="p">.</span><span class="nx">compSlug</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">slug</span><span class="p">)</span>
	<span class="p">})</span>

	<span class="k">if</span> <span class="p">(</span><span class="nx">competitionData</span> <span class="o">==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="nx">error</span><span class="p">(</span><span class="mi">404</span><span class="p">,</span> <span class="p">{</span>
			<span class="na">message</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Competition not found</span><span class="dl">"</span>
		<span class="p">})</span>
	<span class="p">}</span>

    <span class="c1">// ...</span>

	<span class="nx">form</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">superValidate</span><span class="p">(</span><span class="nx">preFilled</span><span class="p">,</span> <span class="nx">zod4</span><span class="p">(</span><span class="nx">getFormSchema</span><span class="p">(</span><span class="nx">competitionData</span><span class="p">.</span><span class="nx">enforceInstitution</span><span class="p">,</span> <span class="nx">competitionData</span><span class="p">.</span><span class="nx">membersMin</span><span class="p">,</span> <span class="nx">competitionData</span><span class="p">.</span><span class="nx">membersMax</span><span class="p">,</span> <span class="nx">competitionData</span><span class="p">.</span><span class="nx">extraQuestion</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)),</span> <span class="p">{</span> <span class="na">errors</span><span class="p">:</span> <span class="kc">false</span> <span class="p">});</span>

	<span class="k">return</span> <span class="p">{</span> <span class="nx">form</span><span class="p">,</span> <span class="p">...};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And lastly, for the frontend, simply bind our form fields to the form values object, like this:</p>
<pre><code class="language-svelte">&lt;!--+page.svelte--&gt;
{#each $form.participants as participant, i (i)}
    &lt;Input
        type="email"
        name="email"
        placeholder="nama@email.com"
        bind:value={participant.email}
    /&gt;
    ...
{/each}
</code></pre>

<p>Superforms will then handle turning the form into a JSON which the client sends, along with deserializing the data once they’re in the server. Oh, and, did I mention form validation too? For both frontend and backend?! I chose <a href="https://zod.dev">Zod</a> and Superforms did a nice job integrating it. There are other validators you can use with Superforms too, though.</p>

<p>So. Clean. I simply love it.</p>

<h1 id="2-file-handling-puzzle">2. File Handling Puzzle</h1>

<p>But wait, how about files? We can’t just represent a file as a JSON, can we? While that’s true, Superforms gets around this by having the file in the form data itself instead of being serialized further.</p>

<p>Now, this is where things got confusing for me. Superforms doesn’t have the documentation for how to have files, in an object, in an array. The only official documentation of file uploads <a href="https://superforms.rocks/concepts/files">is located here</a> where they use a proxy function (<code class="language-plaintext highlighter-rouge">fileProxy</code>) that connects user file uploads to the input field’s <code class="language-plaintext highlighter-rouge">FileList</code> via <code class="language-plaintext highlighter-rouge">bind:files</code>. And here’s the tricky part that stumped me: <code class="language-plaintext highlighter-rouge">fileProxy</code> takes a path to the file object, which in my case would be something like <code class="language-plaintext highlighter-rouge">participants[i].fileObject</code>. I was unsure if string interpolation would work at all there. I tried anyway since that seems faster than digging the source code, and apparently it works fine.</p>

<p>But, still. How do I manage a bunch of file proxies of different file objects together that are inside an array? And that’s when I realize that I’ve forgotten Svelte’s very superpower of componentization. Right, I can just stick the <code class="language-plaintext highlighter-rouge">fileProxy</code> inside a component so it manages the proxy on its own:</p>

<pre><code class="language-svelte">&lt;script lang="ts"&gt;
    // ...
	const proxy = fileProxy(form, `participants[${index}].testfile`);
&lt;/script&gt;

&lt;Field.Field&gt;
	&lt;Input
		type="file"
		name="testfile"
		accept="image/png, image/jpeg, image/webp, image/heif"
		bind:files={$proxy}
	/&gt;
&lt;/Field.Field&gt;

</code></pre>

<h1 id="end-thoughts">End Thoughts</h1>
<p>Great! We unlocked the ultimate web form experience for both developers and users. But, still, it’s absurd that we need to attach a JSON inside a form data and hack our way out to still get file access just to have a clean competition registration form. I really, really wish that HTML would support JSON form natively! (plus with file transport somehow)</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>You can technically name your fields with a “[]” suffix which is commonly used to denote an array. In your browser inspector you might even see your form data coming up as an array. However, in reality, HTML doesn’t do anything for you under the hood so it’s all up to the backend. PHP, for example, <a href="https://stackoverflow.com/questions/3314567/how-to-get-a-form-input-array-into-a-php-array">can parse the form data as an array</a>. Here’s an example of the raw data that one may get with three input fields called “name[]”: <code class="language-plaintext highlighter-rouge">name[]=first&amp;name[]=second&amp;name[]=third</code>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Cikitta</name></author><category term="website" /><category term="programming" /><category term="html" /><category term="web" /><summary type="html"><![CDATA[How to unlock infinite power with HTML web forms.]]></summary></entry><entry><title type="html">Advent of Code 2025 (and my personal solutions)!</title><link href="https://daringcuteseal.github.io/blog/aoc-2025/" rel="alternate" type="text/html" title="Advent of Code 2025 (and my personal solutions)!" /><published>2025-12-30T00:00:00+07:00</published><updated>2025-12-30T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/aoc-2025</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/aoc-2025/"><![CDATA[<p>Wheew, my first time finishing an <a href="https://adventofcode.com">Advent of Code</a> calendar! I think I’m the slowest participant to hit the finish line (I finished on Dec 29 :sweat_smile: But it’s okay, at least I did finish it.</p>

<p>Anyway, this year I mostly used Rust. I had these goals in mind:</p>

<ul class="task-list">
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><strong>No external libraries</strong></p>

    <p>Well, I thought I could do it until I finally got hit with problems that include parsing nightmare and iterator hell. Thus, I added <a href="https://github.com/rust-bakery/nom">nom</a> and <a href="https://github.com/rust-itertools/itertools/">itertools</a> to my dependencies list.</p>
  </li>
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><strong>All solutions must run under a second</strong></p>

    <p>All of them did except for my 9th day part 2 when ran with extra checks (still gives correct answer without it, though).</p>
  </li>
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><strong>Do not implement any naive solution that comes right off my mind</strong></p>

    <p>Yep, except when I don’t think there’s a better solution than the one I can think of right away.</p>
  </li>
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><strong>Try my best before giving up</strong></p>

    <p>I did, except for the last day. It was too suspicious and I couldn’t help but to check the trick.</p>
  </li>
  <li class="task-list-item">
    <p><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" /><strong>Just Chill™</strong></p>

    <p>For sure.</p>
  </li>
</ul>

<p>Overall, it was fun I guess! I learned a ton of stuff along the way. I’d like to share my solutions’ breakdowns too (I haven’t reviewed much of others’ solutions yet, though!).</p>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/1">Day 1: Secret Entrance</a></p>

  <p>You’re tasked to find the password to enter the North Pole base by using a rotating dial.</p>
</blockquote>

<p>This problem is actually the only one I answered with a C++ code.</p>

<h2 id="1a">1a</h2>

<p>The first part is pretty standard modular arithmetic. The idea is to simply to simulate the operations and count how many times the dial points to 0 after an operation.</p>

<p>The problem, though: C++’s modulo doesn’t work with negative dividend. I had to write a custom modulus function that works with negative numbers:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">long</span> <span class="kt">long</span> <span class="nf">modn</span><span class="p">(</span><span class="kt">long</span> <span class="kt">long</span> <span class="n">x</span><span class="p">,</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="p">(</span><span class="o">-</span><span class="n">x</span> <span class="o">%</span> <span class="n">n</span><span class="p">)))</span> <span class="o">%</span> <span class="n">n</span><span class="p">;</span>
	<span class="k">else</span> <span class="k">return</span> <span class="n">x</span> <span class="o">%</span> <span class="n">n</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="1b">1b</h2>

<p>Now this one is supposed to be easy, but damn, the amount of silly mistakes I made-</p>

<p>I “initially attempted to check only the landing position” (this was left in my C++ source file; I don’t remember anymore). But obviously, that’s incorrect since it’s no different than the first part.</p>

<p>I finally got the logic right the second time. The occurrence of 0 for each operation is the number of times it rotated completely plus 1 if it passed 0 during the “redundant” rotation, 0 otherwise.</p>

<p>The redundant rotation check is a bit tricky for me, at least the left rotation one. I initially did the checks like this:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">...</span>
<span class="k">if</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="sc">'R'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">occurrence</span> <span class="o">+=</span> <span class="p">(</span><span class="n">num</span> <span class="o">/</span> <span class="mi">100</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">pos</span> <span class="o">+</span> <span class="p">(</span><span class="n">num</span> <span class="o">%</span> <span class="mi">100</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">100</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">pos</span> <span class="o">=</span> <span class="n">modn</span><span class="p">(</span><span class="n">pos</span> <span class="o">+</span> <span class="n">num</span><span class="p">,</span> <span class="mi">100</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="n">instruction</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'L'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">occurrence</span> <span class="o">+=</span> <span class="p">(</span><span class="n">num</span> <span class="o">/</span> <span class="mi">100</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">pos</span> <span class="o">-</span> <span class="p">(</span><span class="n">num</span> <span class="o">%</span> <span class="mi">100</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">pos</span> <span class="o">=</span> <span class="n">modn</span><span class="p">(</span><span class="n">pos</span> <span class="o">-</span> <span class="n">num</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">...</span>
</code></pre></div></div>

<p>However, I just couldn’t get the answer correctly. As it turns out, if we get the left instruction and the initial position was already 0, the redundant rotation cannot ever pass 0 anymore. This fixed it:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">...</span>
<span class="p">}</span> <span class="k">else</span> <span class="nf">if</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="sc">'L'</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">occurrence</span> <span class="o">+=</span> <span class="p">(</span><span class="n">num</span> <span class="o">/</span> <span class="mi">100</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">pos</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">pos</span> <span class="o">-</span> <span class="p">(</span><span class="n">num</span> <span class="o">%</span> <span class="mi">100</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">...</span>
</code></pre></div></div>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/2">Day 2: Gift Shop</a></p>

  <p>You’re tasked to find all invalid IDs among several product ID ranges (they were added by a young elf).</p>
</blockquote>

<p>This is the point where I read the problem and go, “damn it, C++ ain’t gonna cut it”. I could not imagine myself writing the loops and all that in C++, let alone debugging when things go wrong.</p>

<h2 id="2a">2a</h2>

<p>My naive idea was this: uh, just loop through the ranges and check if it’s an invalid pattern?</p>

<p>But eh, of course, I’m not allowed to implement naive solutions, remember? <em>Maybe there is some kind of pattern</em>, so I continued checking. And indeed, we can easily know the first valid pattern that comes after the start of the range.</p>

<p>There are two cases for our start of range: either the number of digits is odd, or it’s even. In the case that it’s odd, we can simply repeat the first $ \lceil\frac{n}{2}\rceil $ digits twice, then continue generating the next patterns from there until we hit the dead end.</p>

<p>In the case that it’s even, however, we got two possibilities: repeat the first $ \frac{n}{2} $ digits twice, or repeat the first $ \frac{n}{2} $ digits, plus one, twice. The plus one is needed when the number formed by the first $ \frac{n}{2} $ digits is less than the other half. For example, the number 812813 has its first half (812) less than its other half (813), so the first valid pattern is going to be (812 + 1) repeated twice, i.e, 813813.</p>

<p>And after we know how to generate the invalid patterns, we just sum them up.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">range</span> <span class="k">in</span> <span class="n">ranges</span> <span class="p">{</span>
    <span class="o">...</span>

    <span class="c1">// if number of digit is odd</span>
    <span class="k">if</span> <span class="nf">digits</span><span class="p">(</span><span class="n">start</span><span class="p">)</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">{</span>
        <span class="c1">// set the repeatable chunk to the number from the first ceil(n/2) digits</span>
        <span class="o">...</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="o">...</span>
        <span class="c1">// slice our number to two</span>
        <span class="k">let</span> <span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">)</span> <span class="o">=</span> <span class="nf">slice2</span><span class="p">(</span><span class="n">start</span><span class="p">);</span>
        <span class="c1">// do the first half and right half comparison</span>
        <span class="k">if</span> <span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span> <span class="p">{</span>
            <span class="n">segment</span> <span class="o">=</span> <span class="n">left</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">segment</span> <span class="o">=</span> <span class="n">left</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// loop until we hit dead end</span>
    <span class="o">...</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<h2 id="2b">2b</h2>

<p><em>Oh no, generalization!</em> My first thought is that this <em>probably</em> has pattern as well. But unfortunately, I couldn’t find any and even if one exists, it’ll probably be too convoluted. I did still attempt to write the smart version but it failed miserably because I missed a bunch of edge cases.</p>

<p>Anyway, I actually wrote two working solutions later. In the first one, I looped through every single number from every single range and see if it’s invalid (by checking if it’s a certain number repeated n times). However, this turned out to be somewhat slow.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">for</span> <span class="n">range</span> <span class="k">in</span> <span class="n">ranges</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="n">start</span><span class="o">..=</span><span class="n">end</span> <span class="p">{</span>
        <span class="k">if</span> <span class="nf">is_pattern</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">sum</span> <span class="o">+=</span> <span class="n">i</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<p>In the second code (inspired by <a href="https://github.com/sbxte/aoc-rs/blob/master/2025%2Fday02%2Fsrc%2Fp2.rs">@sbxte on GitHub</a>), I tried to build invalid patterns from the ground up until I hit a dead end, i.e stop when the number of digits in my repeatable chunk is greater than half the number of digits of the range’s end. Turns out, this is way faster.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">for</span> <span class="n">range</span> <span class="k">in</span> <span class="n">ranges</span> <span class="p">{</span>
    <span class="c1">// to prevent duplicates, e.g 1 four times and 11 twice should be the same</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">checked_patterns</span><span class="p">:</span> <span class="n">HashSet</span><span class="o">&lt;</span><span class="nb">i64</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">HashSet</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
    <span class="o">...</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">chunk</span><span class="p">:</span> <span class="nb">i64</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">len</span><span class="p">;</span>

    <span class="nv">'a</span><span class="p">:</span> <span class="k">loop</span> <span class="p">{</span>
        <span class="c1">// set the length appropriately to the first one possible so we don't</span>
        <span class="c1">// spend time checking the obviously wrong (valid) ones</span>
        <span class="k">if</span> <span class="n">digits_start</span> <span class="o">%</span> <span class="n">digits_chunk</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
            <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">digits_start</span> <span class="o">/</span> <span class="n">digits_chunk</span><span class="p">)</span><span class="nf">.max</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">digits_start</span> <span class="o">/</span> <span class="n">digits_chunk</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span><span class="nf">.max</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="k">mut</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">repeat_num</span><span class="p">(</span><span class="n">chunk</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
        <span class="k">while</span> <span class="n">result</span> <span class="o">&lt;=</span> <span class="n">end</span> <span class="p">{</span>
            <span class="c1">// keep repeating until we hit the end range</span>
            <span class="o">...</span>
        <span class="p">}</span>
        <span class="n">chunk</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>

        <span class="c1">// if the length of the digits of our chunk is now inappropriate</span>
        <span class="k">if</span> <span class="nf">digits</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nf">digits</span><span class="p">(</span><span class="n">end</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span> <span class="p">{</span>
            <span class="k">break</span> <span class="nv">'a</span><span class="p">;</span>
        <span class="p">}</span>

    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/3">Day 3: Lobby</a></p>

  <p>You’re tasked to determine the maximum joltage you can make from each bank (by picking subset of numbers).</p>
</blockquote>

<h2 id="3a">3a</h2>

<p>For this part, we can simply loop through the string and set the first number to be the highest we have found, so long as it’s not the last number of the string. We do this for the second number as well.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">line</span> <span class="k">in</span> <span class="n">lines</span> <span class="p">{</span>
    <span class="o">...</span>

    <span class="k">for</span> <span class="n">d</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">length</span> <span class="p">{</span>
        <span class="c1">// first digit</span>
        <span class="k">if</span> <span class="n">digits</span><span class="p">[</span><span class="n">d</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">max_first_digit</span> <span class="o">&amp;&amp;</span> <span class="n">d</span> <span class="o">&lt;</span> <span class="n">length</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">{</span>
            <span class="n">max_first_digit</span> <span class="o">=</span> <span class="n">digits</span><span class="p">[</span><span class="n">d</span><span class="p">];</span>
            <span class="n">max_second_digit</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="c1">// second</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">digits</span><span class="p">[</span><span class="n">d</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">max_second_digit</span> <span class="p">{</span>
            <span class="n">max_second_digit</span> <span class="o">=</span> <span class="n">digits</span><span class="p">[</span><span class="n">d</span><span class="p">];</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">total</span> <span class="o">+=</span> <span class="n">max_first_digit</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">+</span> <span class="n">max_second_digit</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="3b">3b</h2>

<p>The second part of the problem is the generalization of the first one. I guess I could have written 12 <code class="language-plaintext highlighter-rouge">else if</code> branches—but that would’ve been tiring, so I just used an array:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">line</span> <span class="k">in</span> <span class="n">lines</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">max_digits</span><span class="p">:</span> <span class="p">[</span><span class="nb">u32</span><span class="p">;</span> <span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">12</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">length</span> <span class="o">=</span> <span class="n">digits</span><span class="nf">.len</span><span class="p">();</span>

    <span class="k">for</span> <span class="n">d</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">length</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="mi">12</span> <span class="p">{</span>
            <span class="k">if</span> <span class="o">!</span><span class="p">(</span><span class="n">digits</span><span class="p">[</span><span class="n">d</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">max_digits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">d</span> <span class="o">&lt;=</span> <span class="n">length</span> <span class="o">-</span> <span class="p">(</span><span class="mi">12</span> <span class="o">-</span> <span class="n">i</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">continue</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="n">max_digits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">digits</span><span class="p">[</span><span class="n">d</span><span class="p">];</span>
            <span class="k">for</span> <span class="n">j</span> <span class="k">in</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">..</span><span class="mi">12</span> <span class="p">{</span>
                <span class="n">max_digits</span><span class="p">[</span><span class="n">j</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">break</span> <span class="nv">'inner</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/4">Day 4: Printing Department</a></p>

  <p>You’re tasked to help the elves remove rolls of papers so you can.. break through the wall to get to the cafeteria. Yup.</p>
</blockquote>

<h2 id="4a">4a</h2>

<p>I don’t really know a non-naive solution, so I just went with looping through each paper roll and checking if it’s accessible or not (by the said rules):</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="n">NEIGHBORS</span><span class="p">:</span> <span class="p">[(</span><span class="nb">i64</span><span class="p">,</span> <span class="nb">i64</span><span class="p">);</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</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="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="o">...</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="o">...</span>
<span class="k">for</span> <span class="n">row</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">map</span><span class="nf">.len</span><span class="p">()</span> <span class="p">{</span>
    <span class="nv">'b</span><span class="p">:</span> <span class="k">for</span> <span class="n">col</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">cols</span> <span class="p">{</span>
        <span class="c1">// no paper roll</span>
        <span class="k">if</span> <span class="o">!</span><span class="n">map</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">col</span><span class="p">]</span> <span class="p">{</span>
            <span class="k">continue</span> <span class="nv">'b</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">let</span> <span class="k">mut</span> <span class="n">neighbors_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="nv">'c</span><span class="p">:</span> <span class="k">for</span> <span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> <span class="k">in</span> <span class="n">NEIGHBORS</span> <span class="p">{</span>
            <span class="c1">// check neighbor</span>
            <span class="o">...</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">neighbors_count</span> <span class="o">&lt;</span> <span class="mi">4</span> <span class="p">{</span>
            <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>

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

<p>Maybe there’s some kind of trickery you can do here, but then again, this is already sub-optimal as it’s $ O(RC) $ where $ R $ = rows and $ C $ = columns. I don’t think anything can beat that, because you need to check all paper rolls anyway.</p>

<h2 id="4b">4b</h2>

<p>Now we’re asked to simulate the paper roll removal. To be fair, I couldn’t think of a non-naive solution here, either. However, I used a small optimization trick: keeping track of the neighbor count for each paper roll. This way, I don’t have to keep recalculating the neighbors to know which paper rolls I can remove. Then, I just basically do a multi-pass sweeping from top to bottom and remove all paper rolls I can remove.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// count neighbors, like from part a</span>
<span class="o">...</span>

<span class="k">loop</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">done</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>
    <span class="k">for</span> <span class="n">row</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">map</span><span class="nf">.len</span><span class="p">()</span> <span class="p">{</span>
        <span class="nv">'b</span><span class="p">:</span> <span class="k">for</span> <span class="n">col</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">cols</span> <span class="p">{</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">map</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">col</span><span class="p">]</span> <span class="p">{</span>
                <span class="k">continue</span> <span class="nv">'b</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="k">if</span> <span class="n">neighbors</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">col</span><span class="p">]</span> <span class="o">&lt;</span> <span class="mi">4</span> <span class="p">{</span>
                <span class="c1">// erase the paper roll</span>
                <span class="n">map</span><span class="p">[</span><span class="n">row</span><span class="p">][</span><span class="n">col</span><span class="p">]</span> <span class="o">=</span> <span class="k">false</span><span class="p">;</span>

                <span class="n">done</span> <span class="o">=</span> <span class="k">false</span><span class="p">;</span>
                <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>

                <span class="o">...</span>
                <span class="nv">'c</span><span class="p">:</span> <span class="k">for</span> <span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> <span class="k">in</span> <span class="n">NEIGHBORS</span> <span class="p">{</span>
                    <span class="c1">// out of map</span>
                    <span class="k">if</span> <span class="o">!</span><span class="p">(</span><span class="n">new_r</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">new_r</span> <span class="o">&lt;</span> <span class="n">rows_i</span> <span class="o">&amp;&amp;</span> <span class="n">new_c</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">new_c</span> <span class="o">&lt;</span> <span class="n">cols_i</span><span class="p">)</span> <span class="p">{</span>
                        <span class="k">continue</span> <span class="nv">'c</span><span class="p">;</span>
                    <span class="p">}</span>

                    <span class="c1">// reduce the neighbors</span>
                    <span class="n">neighbors</span><span class="p">[</span><span class="n">new_r</span> <span class="k">as</span> <span class="nb">usize</span><span class="p">][</span><span class="n">new_c</span> <span class="k">as</span> <span class="nb">usize</span><span class="p">]</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="n">done</span> <span class="p">{</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/5">Day 5: Cafeteria</a></p>

  <p>You’re tasked to help the elves find fresh ingredient IDs from their new inventory management system.</p>
</blockquote>

<p>This line right here:</p>
<blockquote>
  <p>[…] “If only we hadn’t switched to the new inventory management system right before Christmas!” another Elf exclaims.</p>
</blockquote>

<p>It reminds me of the times when I upgrade my (Artix btw) Linux system right before an important task and it ruined my day. Oh well, I guess we can’t really blame the elves.</p>

<h2 id="5a">5a</h2>

<p>The first part screams point query in an interval tree. Basically, create an interval tree for the range of fresh IDs, insert the ranges, then just query the points (made with interval with same start and end).</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">for</span> <span class="n">q</span> <span class="k">in</span> <span class="n">queries</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">ranges</span><span class="nf">.contains</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">Int</span><span class="p">::</span><span class="nf">new_point</span><span class="p">(</span><span class="n">q</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<p>For the interval query, I wrote my own interval tree that augments a standard non-self-balancing BST (also my own—judging by the fact that it’s non-self-balancing). It was good enough for this purpose.</p>

<p>Funny enough, I thought that interval trees are for “containment” queries (i.e interval a is contained by b if <code class="language-plaintext highlighter-rouge">a.start &gt;= b.start</code> and <code class="language-plaintext highlighter-rouge">a.end &lt;= b.end</code>). I had a terrible time making a containment tree by reading guides about interval tree. Eventually, I did make it work, and I ended up with a tree that can do both overlapping range query and containment query. How fun (albeit pretty unnecessary).</p>

<h2 id="5b">5b</h2>

<p>For the second part, I use a <code class="language-plaintext highlighter-rouge">BTreeMap</code> that holds fresh ingredient ranges and orders the elements using ranges’ start as the sorting key. Whenever I add a new range, it and any overlapping ranges are merged into one. The list is then guaranteed to hold non-overlapping ranges, so then, I can simply loop through all the ranges and count how many fresh ingredients are there.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">while</span> <span class="n">reader</span><span class="nf">.get_line</span><span class="p">()</span><span class="o">?</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span>
    <span class="k">let</span> <span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span> <span class="o">=</span> <span class="n">reader</span><span class="py">.buffer</span><span class="nf">.trim</span><span class="p">()</span><span class="nf">.split_once</span><span class="p">(</span><span class="sc">'-'</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="o">...</span>

    <span class="c1">// delete any overlapping intervals</span>
    <span class="o">...</span>

    <span class="c1">// insert the new interval, which is the result of the new interval</span>
    <span class="c1">// merged with any other overlapping intervals.</span>
    <span class="n">ranges</span><span class="nf">.insert</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">let</span> <span class="k">mut</span> <span class="n">count</span><span class="p">:</span> <span class="nb">u64</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="k">in</span> <span class="n">ranges</span> <span class="p">{</span>
    <span class="n">count</span> <span class="o">+=</span> <span class="n">e</span> <span class="o">-</span> <span class="n">s</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">...</span>

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

<p>I used a lot of non-stable features here actually (not shown), like Cursors for BTree. I needed it for the sake of being able to use the <code class="language-plaintext highlighter-rouge">upper_bound</code> method to find where I should start doing overlap checks to this new interval. Coming from doing competitive programming with C++, I’m surprised this is not available in stable Rust.</p>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/6">Day 6: Trash Compactor</a></p>

  <p>You’re tasked to help a young cephalopod with her math homework.</p>
</blockquote>

<p>Okay, but, why do they even have those??</p>

<h2 id="6a">6a</h2>

<p>Anyway, this is starting to look somewhat cumbersome to parse, but at this point I still wanted to not have any external parsing libraries. Thus, my code consists of mostly me doing tedious string parsing:</p>

<p><img src="../image/aoc-6a.png" alt="An miniscule image of AoC 6a code with boilerplate parsing code" /></p>

<p>Since this is simply a problem that requires you to implement the rules they gave, the logic is pretty straightforward.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">total</span><span class="p">:</span> <span class="nb">u64</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">len</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">ops</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'*'</span> <span class="p">{</span>
        <span class="n">total</span> <span class="o">+=</span> <span class="n">num1</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">num2</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">num3</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">num4</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">ops</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'+'</span> <span class="p">{</span>
        <span class="n">total</span> <span class="o">+=</span> <span class="n">num1</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">num2</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">num3</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">num4</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>

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

<h2 id="6b">6b</h2>

<p>For the second part, my code is a bit long, but really what it’s just doing is parsing each number entries with padding (0). The maximum length of a number is obtained by making use of the problem gaps that exist in the operators row.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="nd">#[derive(Default,</span> <span class="nd">Debug)]</span>
<span class="k">struct</span> <span class="n">Problem</span> <span class="p">{</span>
    <span class="n">operation_type</span><span class="p">:</span> <span class="n">OperationType</span><span class="p">,</span>
    <span class="n">digits_len</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
    <span class="n">nums</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;&gt;&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Problem</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">evaluate</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">u64</span> <span class="p">{</span>
        <span class="o">...</span>
        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="n">index</span><span class="nf">.rev</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">tens_factor</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">formed_number</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

            <span class="c1">// we have to reverse the order because we are reading from top to</span>
            <span class="c1">// bottom, and the number with the smallest 10 factor is at the bottom</span>
            <span class="c1">// fortunately, both addition and multiplication are commutative.</span>
            <span class="k">for</span> <span class="n">num</span> <span class="k">in</span> <span class="k">self</span><span class="py">.nums</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.rev</span><span class="p">()</span> <span class="p">{</span>
                <span class="n">formed_number</span> <span class="o">+=</span> <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="n">num</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">{</span>
                    <span class="k">let</span> <span class="n">res</span> <span class="o">=</span> <span class="n">x</span> <span class="k">as</span> <span class="nb">u64</span> <span class="o">*</span> <span class="n">tens_factor</span><span class="p">;</span>
                    <span class="n">tens_factor</span> <span class="o">*=</span> <span class="mi">10</span><span class="p">;</span>
                    <span class="n">res</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="mi">0</span>
                <span class="p">};</span>
            <span class="p">}</span>

            <span class="k">match</span> <span class="k">self</span><span class="py">.operation_type</span> <span class="p">{</span>
                <span class="nn">OperationType</span><span class="p">::</span><span class="n">Addition</span> <span class="k">=&gt;</span> <span class="n">sum</span> <span class="o">+=</span> <span class="n">formed_number</span><span class="p">,</span>
                <span class="nn">OperationType</span><span class="p">::</span><span class="n">Multiplication</span> <span class="k">=&gt;</span> <span class="n">sum</span> <span class="o">*=</span> <span class="n">formed_number</span><span class="p">,</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="n">sum</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<p>And yes, I’m still doing manual string parsing (not shown).</p>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/7">Day 7: Laboratories</a></p>

  <p>You’re tasked to fix a broken teleporter so you can escape a North Pole research wing.</p>
</blockquote>

<h2 id="7a">7a</h2>

<p>The first part of the problem reminds me of another similar problem I’ve solved before, titled <a href="https://tlx.toki.id/problems/osn-2010/3D">Waterfall</a> (it’s written in Indonesian, by the way). Just that instead of finding the best spot for the tachyon beam to enter the manifold, we have to calculate the number of beam splits that will occur, given a starting spot.</p>

<p>Funny story, I got slightly confused with the definition of a beam “splitting” and I wrote the solution for part 2 when trying to solve part 1.</p>

<p>But anyway, my first part had this DP recurrence relation:</p>

\[f(r, c) = \begin{cases}
0 &amp; \text{if out of map} \\
0 &amp; \text{if visited already} \\
1 + f(r, c-1) + f(r, c+1) &amp; \text{if tile is splitter} \\
f(r + 1, c) &amp; \text{otherwise}

\end{cases}\]

<p>Essentially, whenever a beam hits a splitter, it will get split once—thus adding 1 to our total sum. It will also create 2 new beams, which might split again later, so we call <code class="language-plaintext highlighter-rouge">f(r, c - 1)</code> and <code class="language-plaintext highlighter-rouge">f(r, c + 1)</code> to get the number of splits that will occur for each of the newly created beams. When a beam doesn’t hit a splitter, it just travels down like normal. The “if visited already” here is needed so we don’t solve for the same table entry twice, as when two beams overlap, they’re considered as just one beam.</p>

<p>I wrote a recursive function to solve this proble. I know that it isn’t the best thing to do, but I just can’t help but to try it for this problem because in the Waterfall problem, I only used simple <code class="language-plaintext highlighter-rouge">for</code> loops. Experimentations are cool, yk?</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">type</span> <span class="n">Map</span> <span class="o">=</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">MapItem</span><span class="o">&gt;&gt;</span><span class="p">;</span>
<span class="k">type</span> <span class="n">Marker</span> <span class="o">=</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">bool</span><span class="o">&gt;&gt;</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">splits</span><span class="p">(</span><span class="n">marker</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Marker</span><span class="p">,</span> <span class="n">map</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Map</span><span class="p">,</span> <span class="n">row</span><span class="p">:</span> <span class="nb">i64</span><span class="p">,</span> <span class="n">col</span><span class="p">:</span> <span class="nb">i64</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">u64</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="k">if</span> <span class="n">row</span> <span class="o">&gt;=</span> <span class="n">rows</span> <span class="p">||</span> <span class="n">col</span> <span class="o">&gt;=</span> <span class="n">cols</span> <span class="p">||</span> <span class="n">col</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">{</span>
        <span class="c1">// base case: out of map so no splits will occur. return 0</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">marker</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="p">{</span>
        <span class="c1">// visited already, don't try to do it again (return 0).</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">map</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="o">==</span> <span class="nn">MapItem</span><span class="p">::</span><span class="n">Splitter</span> <span class="p">{</span>
        <span class="c1">// this is a splitter: split the beam.</span>
        <span class="n">marker</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>
        <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="nf">splits</span><span class="p">(</span><span class="n">marker</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">row</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">splits</span><span class="p">(</span><span class="n">marker</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">row</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="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// blank space: propagate downwards.</span>
        <span class="n">marker</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>
        <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">splits</span><span class="p">(</span><span class="n">marker</span><span class="p">,</span> <span class="n">map</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">col</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<h2 id="7b">7b</h2>

<p>This is yet another DP problem. To be specific, this is the counting-total-ways (the beam can travel) style. DP is important here because we do not want to waste resources calculating the number of ways a beam can travel down for the same tile (position) over and over again.</p>

<p>Thus, I came up with this (which I just copied from my previously deleted part 1 solution):</p>

\[f(r, c) = \begin{cases}
1 &amp; \text{if r = last row}\\
0 &amp; \text{if out of map}\\
f(r, c-1) + f(r, c+1) &amp; \text{if tile is splitter}\\
f(r + 1, c) &amp; \text{otherwise}

\end{cases}\]

<p>This one I think is simpler to think of. There are two ways a beam can travel when it hits a splitter, so obviously the timelines possible is the number of timelines possible if it travels to the left + if it travels to the right. If the beam isn’t in a splitter, it’ll just continue propagating downwards (i.e just 1 timeline possible).</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">timelines</span><span class="p">(</span><span class="n">dp</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">DP</span><span class="p">,</span> <span class="n">map</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Map</span><span class="p">,</span> <span class="n">row</span><span class="p">:</span> <span class="nb">i64</span><span class="p">,</span> <span class="n">col</span><span class="p">:</span> <span class="nb">i64</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">u64</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="k">if</span> <span class="n">row</span> <span class="o">==</span> <span class="n">rows</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">{</span>
        <span class="c1">// base case: we're at the last row. there's only 1 timeline.</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">col</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="p">||</span> <span class="n">col</span> <span class="o">&gt;=</span> <span class="n">cols</span> <span class="p">{</span>
        <span class="c1">// base case: we're out of the map (invalid column). no timeline</span>
        <span class="c1">// is even possible. return 0.</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="p">{</span>
        <span class="c1">// we already know the answer so return that.</span>
        <span class="k">return</span> <span class="n">val</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">map</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="o">==</span> <span class="nn">MapItem</span><span class="p">::</span><span class="n">Splitter</span> <span class="p">{</span>
        <span class="c1">// either it goes to the left or it goes to the right</span>
        <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">timelines</span><span class="p">(</span><span class="n">dp</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">row</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">timelines</span><span class="p">(</span><span class="n">dp</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">row</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="n">dp</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// only one possibility: travelling down again</span>
        <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">timelines</span><span class="p">(</span><span class="n">dp</span><span class="p">,</span> <span class="n">map</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">col</span><span class="p">);</span>
        <span class="n">dp</span><span class="p">[</span><span class="n">rw</span><span class="p">][</span><span class="n">cl</span><span class="p">]</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/8">Day 8: Playground</a></p>

  <p>You’re tasked to help the elves connect electrical junction boxes together so electricity reaches all decoration lights.</p>
</blockquote>

<h2 id="8a">8a</h2>

<p>So the elves are doing a greedy approach: repeatedly connect two junction boxes that are the closest together (luckily, this approach does result in the global minima—that’s <em>minimum spanning tree</em> for you!). This problem fits perfectly under the category of graph problems, so we’ll use graph-related terminologies here.</p>

<p>Obviously, to know which junction box to connect, we need to know which two pairs of the boxes are the closest at a time. Thus, we need to store the list of all pairs of boxes and their distances (i.e the edges and the distances as the weight), and we need a way to access the one with the shortest distance quickly. Binary heap would make a perfect data structure for that, as we can do $ O(1) $ query to get the pair of junction boxes closest at a time.</p>

<p>After two junction boxes are connected, they belong in the same component. To keep track of this set membership data, we can use a DSU (disjoint union set). Now, our objective is to keep track of the graph’s component sizes after the first 1000 connections.</p>

<p>To simplify my life (and further Advent of Code instances), I wrote a custom disjoint set union that:</p>

<ul>
  <li>Stores the number of components that exist, which is updated after each join operation. This allows $ O(1) $ time for number of components query.</li>
  <li>Stores a <code class="language-plaintext highlighter-rouge">BTreeMap</code> that stores [component size, component count] as [key, value]. The list is sorted ascending (from components of smallest sizes to largest sizes).</li>
  <li>Allows $ O(1) $ query of the size of a component that any given element belongs to.</li>
</ul>

<p>So now, my job is to do the first 1000 connections, then check the component sizes. Pretty easy with the help of my <code class="language-plaintext highlighter-rouge">ChonkDSU</code> (yes).</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">COUNT</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">aoc</span><span class="p">::{</span><span class="nn">chonk_dsu</span><span class="p">::</span><span class="n">ChonkDSU</span><span class="p">};</span>
<span class="o">...</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">dsu</span> <span class="o">=</span> <span class="nn">ChonkDSU</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">jboxes</span><span class="nf">.len</span><span class="p">());</span>

<span class="c1">// connect the first N pairs of junction boxes closest together.</span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">COUNT</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">edge</span> <span class="o">=</span> <span class="n">possible_connections</span><span class="nf">.pop</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="n">dsu</span><span class="nf">.unite</span><span class="p">(</span><span class="n">edge</span><span class="na">.0</span><span class="py">.a</span><span class="p">,</span> <span class="n">edge</span><span class="na">.0</span><span class="py">.b</span><span class="p">);</span>
<span class="p">}</span>

<span class="o">...</span>

<span class="c1">// count the sizes of the three largest circuits.</span>
<span class="k">for</span> <span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">count</span><span class="p">)</span> <span class="k">in</span> <span class="n">dsu</span><span class="nf">.get_components_map</span><span class="p">()</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.rev</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..*</span><span class="n">count</span><span class="nf">.min</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="mi">3</span> <span class="o">-</span> <span class="n">processed</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">result</span> <span class="o">*=</span> <span class="n">size</span><span class="p">;</span>
        <span class="n">processed</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="n">processed</span> <span class="o">==</span> <span class="mi">3</span> <span class="p">{</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<h2 id="8b">8b</h2>

<p>Oh great, it’s literally just part one but <em>actually</em> finish our circuit (and also take note of the last processed pair of junction boxes)!</p>

<p>There are many ways to check if we’re done connecting all junction boxes together in the same circuit, such as by checking the size of the component newly merged or by counting the number of components that exist. I went with the latter since it’s the easiest.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">last_a_idx</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">last_b_idx</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="c1">// this time, connect ALL of the junction boxes until we only have 1 component left.</span>
<span class="k">while</span> <span class="n">dsu</span><span class="nf">.n_components</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">edge</span> <span class="o">=</span> <span class="n">possible_connections</span><span class="nf">.pop</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="n">last_a_idx</span> <span class="o">=</span> <span class="n">edge</span><span class="na">.0</span><span class="py">.a</span><span class="p">;</span>
    <span class="n">last_b_idx</span> <span class="o">=</span> <span class="n">edge</span><span class="na">.0</span><span class="py">.b</span><span class="p">;</span>
    <span class="n">dsu</span><span class="nf">.unite</span><span class="p">(</span><span class="n">edge</span><span class="na">.0</span><span class="py">.a</span><span class="p">,</span> <span class="n">edge</span><span class="na">.0</span><span class="py">.b</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">let</span> <span class="n">x1</span> <span class="o">=</span> <span class="n">jboxes</span><span class="p">[</span><span class="n">last_a_idx</span><span class="p">]</span><span class="py">.x</span><span class="p">;</span>
<span class="k">let</span> <span class="n">x2</span> <span class="o">=</span> <span class="n">jboxes</span><span class="p">[</span><span class="n">last_b_idx</span><span class="p">]</span><span class="py">.x</span><span class="p">;</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"Result:</span><span class="se">\n</span><span class="s">x1: {x1}</span><span class="se">\n</span><span class="s">x2: {x2}</span><span class="se">\n</span><span class="s">x1 × x2 = {}"</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">...</span>

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

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/9">Day 9: Movie Theater</a></p>

  <p>You’re tasked to help the elves form the largest rectangle possible on their big tile movie theater floor.</p>
</blockquote>

<p><em>why do you guys flip row and column in the input?!</em></p>

<h2 id="9a">9a</h2>

<blockquote>
  <p>Before my explanation and commentary begins, to clarify, I do indexing like this (and I do believe this is <em>THE</em> only legal way when doing CS <code class="language-plaintext highlighter-rouge">^_^</code>):</p>

  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(x, y) -&gt; row x, column y

TOP-LEFT                       TOP-RIGHT
(0, 0), (0, 1), (0, 2), (0, 3), ...
(1, 0), (1, 1), (1, 2), (1, 3), ...
(2, 0), (2, 1), (2, 2), (2, 3), ...
(3, 0), (3, 1), (3, 2), (3, 3), ...
...
BOTTOM-LEFT                 BOTTOM-RIGHT

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

<p>A brute force would have probably worked just fine, but I wondered if doing a greedy approach was possible. I knew it has to be something like, pick the “most corner” points—but I had no idea of what quality even determines “corner-ness”.</p>

<p>Okay, let’s take an example of a corner: top-right corner. How do we determine if a point is definitely “more top-right corner” than the other? Let’s start with the most obvious case: it has smaller row number and bigger column number:</p>

<p><img src="../image/better-cornerness.png" alt="An image of two points a and b, with b having smaller row number and bigger column number than a" /></p>

<p>In this case, it’s clear that point B is a better candidate than point A. This is because, regardless of any other point we pick, the rectangle that would be formed by using point B and this other hypothetical point is always going to be bigger than if I had picked point A (so long as the hypothetical point has smaller or equal column number and bigger or equal row number than point A or B—that is, this hypothetical point it is valid as a <em>bottom-left</em> corner and not any other corner). So obviously, here, point B is “more top-right corner” than point A.</p>

<p>However, we run into trouble when we have cases like-</p>

<p>Point B having bigger row number than point A (bad), but bigger column number than point A (good):
<img src="../image/equal-cornerness-1.png" alt="An image of two points a and b, with point b having bigger row number than point a (bad) but bigger column number than point a (good)" /></p>

<p>Point B having smaller row number than point A (good), but smaller column number than point A (bad):
<img src="../image/equal-cornerness-2.png" alt="An image of two points a and b, with point b having smaller row number than point a (good) but smaller column number than point a (bad)" /></p>

<p>In this case, only one of the row and column of point B is better than A. So, which one is more “top-right corner”? Unfortunately, that depends on where the other (left-bottom) corner point lies on. In some cases, using point B can make you a better rectangle. In others, point A will do better:</p>

<p><img src="../image/equal-cornerness-example.png" alt="An image demonstrating that different left-bottom corner point location determines whether point A or B is better" /></p>

<p>Thus, we kind of have to pick both points in this case, because they’re better in some way than the other.</p>

<p>As it turns out, if we eliminate all the “obviously worse” points and keep the “somewhat equal” ones for each corners (top-right, top-left, bottom-right, bottom-left), we get a, surprise surprise: convex hull set. Ah yes, the convex hull set indeed contain the most “corner” points—so we’re back to square one. It does make sense—the two points that make the largest rectangle must be part of the convex hull set.</p>

<p><img src="../image/rectangle-part-of-convex-hull.png" alt="An image showing a rectangle formed from two corner points that are part of the convex hull set of a bunch of points" /></p>

<p>Anyway, just to be clear, we don’t need to try all pairs of the convex hull points. We can simply try the best rectangle we can get from picking a point from top-right and bottom-left, and try again from top-left and bottom-right.</p>

<p><img src="../image/convex-hull-sets.png" alt="An image showing the four corner sets of convex hull points" /></p>

<p>This is how I wrote my code:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="c1">// note: points are already sorted by the column.</span>
<span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">n_points</span> <span class="p">{</span>
    <span class="c1">// left to right</span>
    <span class="k">let</span> <span class="n">ltr</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>

    <span class="c1">// set local min/max for the current column</span>
    <span class="k">if</span> <span class="n">points</span><span class="p">[</span><span class="n">ltr</span><span class="p">]</span><span class="na">.0</span> <span class="o">&lt;</span> <span class="n">ltr_min</span> <span class="p">{</span>
        <span class="o">...</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="n">points</span><span class="p">[</span><span class="n">ltr</span><span class="p">]</span><span class="na">.0</span> <span class="o">&gt;</span> <span class="n">ltr_max</span> <span class="p">{</span>
        <span class="o">..</span>
    <span class="p">}</span>

    <span class="c1">// if after this it's different column.. or this is the last one, gotta push the points</span>
    <span class="k">if</span> <span class="n">points</span><span class="p">[</span><span class="n">ltr</span><span class="p">]</span><span class="na">.1</span> <span class="o">!=</span> <span class="n">ltr_next_col</span> <span class="p">||</span> <span class="n">ltr</span> <span class="o">==</span> <span class="n">n_points</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">ltr_min</span> <span class="o">&lt;</span> <span class="n">ltop_min</span> <span class="p">{</span>
            <span class="o">...</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="n">ltr_max</span> <span class="o">&gt;</span> <span class="n">lbot_max</span> <span class="p">{</span>
            <span class="o">...</span>
        <span class="p">}</span>

        <span class="c1">// if next one is available</span>
        <span class="k">if</span> <span class="n">ltr</span> <span class="o">&lt;=</span> <span class="n">n_points</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">{</span>
            <span class="n">ltr_next_col</span> <span class="o">=</span> <span class="n">points</span><span class="p">[</span><span class="n">ltr</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span><span class="na">.1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// right to left (yes we iterate from two directions.. at once because why not)</span>
    <span class="c1">// code same as above but mirrored</span>
    <span class="o">...</span>
<span class="p">}</span>

<span class="k">let</span> <span class="k">mut</span> <span class="n">max_area</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="c1">// left top and bottom right</span>
<span class="k">for</span> <span class="n">a</span> <span class="k">in</span> <span class="o">&amp;</span><span class="n">ltop</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">b</span> <span class="k">in</span> <span class="o">&amp;</span><span class="n">rbot</span> <span class="p">{</span>
        <span class="n">max_area</span> <span class="o">=</span> <span class="n">max_area</span><span class="nf">.max</span><span class="p">(</span><span class="nf">area</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="p">}</span>
<span class="p">}</span>

<span class="c1">// right top and bottom left, same as above but mirrored</span>
<span class="o">...</span>

<span class="nd">println!</span><span class="p">(</span><span class="s">"Max area: {max_area}"</span><span class="p">);</span>
<span class="o">...</span>

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

<p>For my input, it reduced the total checks needed from $ \binom{496}{2} = 122760 $ pair of points down to just 5308. Added with other operations necessary (sorting, etc.), I got a total of $ 496+4442+5308 = 10246 $ operations. This means that I reduced the work needed by 91.7%. Cool!</p>

<h2 id="9b">9b</h2>

<p>Now this one, I completely gave up doing any optimizations. Heck, I couldn’t think of a naive solution right away.</p>

<p>I sort of cheated by checking how other people do this part, and I found <a href="https://aoc.just2good.co.uk/2025/9">this post written by @derailed-dash on GitHub</a> that suggested the use of several pruning logic, edge-intersect check, and ray casting (to determine if the resulting rectangle is inside the geometry or not). I didn’t read exactly how they did it, so I wrote my own edge-intersect check implementation by augmenting two interval trees (for vertical edges and horizontal edges) and turning it into an edge intersection checker data structure (I called it an <code class="language-plaintext highlighter-rouge">OrthogonalEdgeTree</code>).</p>

<p>As for the ray cast, things get a little finicky. You see, this solution ain’t trivial at all because of how big the given polygon is. A ray cast is absolutely needed to determine if the resulting rectangle is outside of the polygon (thus, it’s invalid). However, doing ray cast in a gigantic space <em>millions</em> of time is.. not exactly feasible. My code ran for over 11 minutes and even after that, it still hasn’t spat out anything yet.</p>

<p>I then decided to delete the ray casting altogether and after that, my code finished executing in.. just under a second. Was the answer correct? Thankfully, yeah. So, I guess that unless your input data is very cursed (which I doubt Eric’s software will even hand to you), any (logical) heuristics you do is fine for this problem.</p>

<p>Here’s my logic (without the ray casting):</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">best_area</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="nv">'a</span><span class="p">:</span> <span class="k">for</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="k">in</span> <span class="nn">UniquePairs</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="n">points_len</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">area_curr</span> <span class="o">=</span> <span class="nf">area</span><span class="p">(</span><span class="o">&amp;</span><span class="n">points</span><span class="p">[</span><span class="n">a</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">points</span><span class="p">[</span><span class="n">b</span><span class="p">]);</span>

    <span class="c1">// if this is worse than our best then don't even try to check it..</span>
    <span class="k">if</span> <span class="n">area_curr</span> <span class="o">&lt;</span> <span class="n">best_area</span> <span class="p">{</span>
        <span class="k">continue</span> <span class="nv">'a</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// validate our rectangle by checking if each of the edges overlap with any of our current</span>
    <span class="c1">// edges</span>
    <span class="o">...</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">result</span> <span class="o">=</span> <span class="k">false</span><span class="p">;</span>

    <span class="k">if</span> <span class="n">edge_top</span><span class="py">.end</span> <span class="o">&gt;=</span> <span class="n">edge_top</span><span class="py">.start</span> <span class="p">{</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">result</span> <span class="p">||</span> <span class="n">edge_list</span><span class="nf">.overlaps</span><span class="p">(</span><span class="o">&amp;</span><span class="n">edge_top</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="n">edge_bottom</span><span class="py">.end</span> <span class="o">&gt;=</span> <span class="n">edge_bottom</span><span class="py">.start</span> <span class="p">{</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">result</span> <span class="p">||</span> <span class="n">edge_list</span><span class="nf">.overlaps</span><span class="p">(</span><span class="o">&amp;</span><span class="n">edge_bottom</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="o">...</span>

    <span class="k">if</span> <span class="n">result</span> <span class="p">{</span>
        <span class="k">continue</span> <span class="nv">'a</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">best_area</span> <span class="o">=</span> <span class="n">area_curr</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/10">Day 10: Factory</a></p>

  <p>You’re tasked to help the elves initialize their factory’s machines by pressing some available buttons.</p>
</blockquote>

<h2 id="10a">10a</h2>

<p>For this one I did a sort of breadth-first search among all possible states of the lights (stored as bitmasks), and use the given buttons to manipulate the state (also with bitwise operations). I stop when I have found a previously explored state to prevent dead loops. This would cap the search space to $ 2^{10} $ (the maximum number lights from the input seems to be 10) for each problem, or just 1024. So, pretty light and fast.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">fn</span> <span class="nf">part1</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">InputData</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="n">queue</span><span class="nf">.push_back</span><span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="n">data</span><span class="py">.lights_target</span><span class="p">));</span>
    <span class="n">added_to_queue</span><span class="nf">.insert</span><span class="p">(</span><span class="n">data</span><span class="py">.lights_target</span><span class="p">);</span>

    <span class="k">while</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">((</span><span class="n">depth</span><span class="p">,</span> <span class="n">curr</span><span class="p">))</span> <span class="o">=</span> <span class="n">queue</span><span class="nf">.pop_front</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// found the correct configuration</span>
        <span class="k">if</span> <span class="n">curr</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nf">Some</span><span class="p">(</span><span class="n">depth</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">for</span> <span class="n">mask</span> <span class="k">in</span> <span class="o">&amp;</span><span class="n">data</span><span class="py">.lights_combos</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">new_subset</span> <span class="o">=</span> <span class="n">curr</span> <span class="o">^</span> <span class="n">mask</span><span class="p">;</span>
            <span class="k">if</span> <span class="o">!</span><span class="n">added_to_queue</span><span class="nf">.contains</span><span class="p">(</span><span class="o">&amp;</span><span class="n">new_subset</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">added_to_queue</span><span class="nf">.insert</span><span class="p">(</span><span class="n">new_subset</span><span class="p">);</span>
                <span class="n">queue</span><span class="nf">.push_back</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">new_subset</span><span class="p">));</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="nb">None</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<p>To be fair, when I did this problem, I didn’t even realize that each button must be pressed at most just once (because pressing one twice would simply undo the changes). I only did the BFS implementation because it reminded me of some other similar problem I’ve solved before, and I wanted to see if the solution would work here too. Anyway, a brute-force solution by trying all possible button presses would cap the search space to $ 2^{13} $ (similarly, the maximum number of buttons from the input seems to be 13). This is just <em>slightly</em> worse than mine, but asymptotically still the same. If the brute force is paired with memoization, it’s going to be pretty much the same as mine (capped to $ 2^{10} $).</p>

<h2 id="10b">10b</h2>

<p>A vector of target joltages $ V $ can be represented as a combination of [several button presses times the contribution vector that the button gives]:</p>

\[V = v_1x_1 + v_2x_2 + v_3x_3 + \dots + v_nx_n\]

<p>And we have to find $ \sum x_i $ such that it is minimized among all possible solutions.</p>

<p>Oh, this one is trippy! I couldn’t really think of the solution myself, so I resorted to looking for hints from the internet. Most people used mixed integer linear programming solvers but I kind of didn’t wanna dive into too much math. I then found a <a href="https://www.reddit.com/r/adventofcode/comments/1pk87hl/comment/nvtd35q/?context=1">cool divide-and-conquer algorithm proposed by u/tenthmascot on Reddit</a>. Essentially, the algorithm relies on some key observations:</p>

<ul>
  <li>Let $ f(V) $ be defined as the minimum button presses needed to reach vector $ V $ (the target joltages). If $ V = {0, 0, 0, \dots} $, then $ f(V) = 0 $ as we do not need to press any button to get absolutely nothing.</li>
  <li>$ f(2V) = 2f(V) $. Or, equivalently, $ \frac{f(V)}{2} = f(\frac{V}{2}) $. As a simple proof, suppose that $ f(2V) &lt; 2f(V) $. Now, let’s work backwards. Notice that I can now theoretically reach vector $ V $ by doing $ f(2V) \div 2 $ button presses, i.e, $ f(2V) \div 2 &lt; f(V) $. This would contradict our definition of $ f(V) $, which is “the minimum button presses to reach vector $ V $”. Therefore, $ f(2V) \ge 2f(V) $, and since we trivially know too that $ f(2V) \le 2F(V) $, this concludes to $ f(2V) = 2f(V) $.</li>
  <li>When all of each button presses are even, the resulting vector must have all even elements too (since each button contributes even elements, and combination of even numbers will ultimately give us all even elements as well). <em>However</em>, the converse is not true. Not all resulting vector with all even elements are made up of even button presses. This is because, combination of arbitrary numbers may still give you even number.</li>
</ul>

<p>Let’s start with the simple case: all of our target vector’s elements are even.</p>

<p>The second key observation says that we can actually divide our work to two, i.e, $ f(V) = 2f(\frac{V}{2}) $, so that’s great. However, the third observation says that this might not always give us a valid result (if there’s an odd number of a button press, we can’t just divide our work into two).</p>

<p>Thus, we can divide our case to two: do not do any work, or try to do some other work that will make our elements still even. In this case, the extra work is pressing all button combinations (only at most once each, because twice, thrice, etc. returns the parity back) that give the same parity as the target vector so the parities cancel out to be all even.</p>

<p>Notice that we can write our equation as combination of some [even number of button presses, plus 1 or 0]:</p>

\[V = v_1(\lfloor \frac{x_1}{2} \rfloor + x_1 \mod 2) + \dots + v_n(\lfloor \frac{x_n}{2} \rfloor + x_n \mod 2)\]

<p>Thus, doing this “extra work” is guaranteed to always give us at least one valid (possible to form) vector, as we try to “trim away” all the $ x_i \mod 2 $ in our equation.</p>

<p>For the other case, that is, when our target vector’s elements are not all even, we can do something similar. We can do the “trimming” as well and then we can divide our work to two again, but obviously, this time we can’t just “do nothing”.</p>

<p>Notice the similarity of both the cases [when all of our target vector’s elements are not all even] and [when they are all even]. We always need to do the “trimming”, and as it turns out, we can generalize “doing nothing” as a work as well! This simplifies how we can write the algorithm:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">fn</span> <span class="nf">recurse</span><span class="p">(</span>
    <span class="n">joltages</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">lookup_map</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">bool</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span><span class="p">,</span> <span class="nb">usize</span><span class="o">&gt;&gt;</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// if all elements are 0</span>
    <span class="k">if</span> <span class="n">joltages</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.fold</span><span class="p">(</span><span class="k">true</span><span class="p">,</span> <span class="p">|</span><span class="n">acc</span><span class="p">,</span> <span class="n">v</span><span class="p">|</span> <span class="n">acc</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">v</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// no work needed</span>
        <span class="k">return</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// try to make all the joltages even</span>
        <span class="k">let</span> <span class="n">parity</span> <span class="o">=</span> <span class="n">joltages</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">v</span><span class="p">|</span> <span class="n">v</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span><span class="py">.collect</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">bool</span><span class="o">&gt;&gt;</span><span class="p">();</span>

        <span class="k">let</span> <span class="k">mut</span> <span class="n">best</span> <span class="o">=</span> <span class="nn">usize</span><span class="p">::</span><span class="n">MAX</span><span class="p">;</span>

        <span class="c1">// try all possible work we can try to do.</span>
        <span class="c1">// note: the empty button presses, i.e [0, 0, 0, ...] must</span>
        <span class="c1">// exist in the lookup map of all even numbers. this is what</span>
        <span class="c1">// i mean by "generalizing no work as work".</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">coeff</span><span class="p">,</span> <span class="n">presses</span><span class="p">)</span> <span class="k">in</span> <span class="n">lookup_map</span><span class="nf">.get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">parity</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">new_joltages</span><span class="p">)</span> <span class="o">=</span> <span class="nf">sub_two_vectors</span><span class="p">(</span><span class="o">&amp;</span><span class="n">joltages</span><span class="p">,</span> <span class="n">coeff</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="nf">recurse</span><span class="p">(</span>
                    <span class="n">new_joltages</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="n">x</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span><span class="py">.collect</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;&gt;</span><span class="p">(),</span>
                    <span class="n">lookup_map</span><span class="p">,</span>
                <span class="p">)</span> <span class="p">{</span>
                    <span class="n">best</span> <span class="o">=</span> <span class="n">best</span><span class="nf">.min</span><span class="p">(</span><span class="n">val</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">presses</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">best</span> <span class="o">==</span> <span class="nn">usize</span><span class="p">::</span><span class="n">MAX</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nb">None</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nf">Some</span><span class="p">(</span><span class="n">best</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">solve</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="n">InputData</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">usize</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="k">let</span> <span class="n">lookup_map</span> <span class="o">=</span> <span class="nf">get_lookup_map</span><span class="p">(</span><span class="o">&amp;</span><span class="n">input</span><span class="py">.joltage_req</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">btns</span><span class="p">);</span>
    <span class="nf">recurse</span><span class="p">(</span><span class="n">input</span><span class="py">.joltage_req</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">lookup_map</span><span class="p">)</span>
<span class="p">}</span>
<span class="o">...</span>

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

<p>The lookup map here defines which combination of button presses (again, at most once) is possible to form a given parity of the vector, and how many presses each pattern needed (minimum).</p>

<p>This is a really clever solution, honestly. I kind of had a similar idea—I knew well that certain button presses are “redundant”, but I wasn’t sure how to implement something like this.</p>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/11">Day 11: Reactor</a></p>

  <p>You’re tasked to help the elves figure out a communication issue between their new server rack and their reactor.</p>
</blockquote>

<p>Finally, the difficulty level got ramped down again :sob:</p>

<h2 id="11a">11a</h2>

<blockquote>
  <p>[..] every path [..]</p>
</blockquote>

<p>Well, that’s another graph problem (so more graph terms)! To be specific, this is a path counting problem for directed acyclic graph (DAG). I actually know well how to do this with pen and paper (cough.. it’s in <a href="/blog/compsci-osk-2025/">2025 OSN-K</a>), but I’ve never thought about solving it by code.</p>

<p>With pen and paper, I would use this kind of recurrence relation:</p>

\[f(v) = \begin{cases}
1 &amp; \text{if node is starting point}\\
0 &amp; \text{else if node has no ingoing edges}\\
\sum f(v.ingoing_i) &amp; \text{otherwise}

\end{cases}\]

<p>The solution I thought of right away was to write something recursive. However, I wanted to try with breadth-first search for efficiency. I initially wrote something basic like:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">while</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="o">=</span> <span class="n">queue</span><span class="nf">.pop_front</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="n">key</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">children</span> <span class="o">=</span> <span class="n">adj</span><span class="nf">.get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">key</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">for</span> <span class="n">c</span> <span class="k">in</span> <span class="n">children</span> <span class="p">{</span>
        <span class="o">*</span><span class="n">dp</span><span class="nf">.get_mut</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="o">+=</span> <span class="n">now</span><span class="p">;</span>
        <span class="k">if</span> <span class="o">!</span><span class="n">queued</span><span class="nf">.contains</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">queue</span><span class="nf">.push_back</span><span class="p">(</span><span class="o">*</span><span class="n">c</span><span class="p">);</span>
            <span class="n">queued</span><span class="nf">.insert</span><span class="p">(</span><span class="o">*</span><span class="n">c</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<p>And, well, it <em>did</em> give me the right answer. Then, after failing to solve for the second part, I realized that I wasn’t doing it correctly at all! I forgot that recursive solution will automatically give me dependency resolution (by solving path count for <em>all</em> nodes that come before the next ones), but implementing a BFS wouldn’t suddenly give me such.</p>

<p>Thankfully, adding a dependency check is somewhat easy. I borrowed the idea from Kahn’s algorithm so that my nodes will be processed in topological order, thus, giving me the dependency resolution needed. It works like a normal BFS, but the number of ingoing edges not processed yet from each node are stored, and a newly discovered node is only queued when it only has one ingoing edge left.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">while</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="o">=</span> <span class="n">queue</span><span class="nf">.pop_front</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">now</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="n">key</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">children</span> <span class="o">=</span> <span class="n">adj</span><span class="nf">.get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">key</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">for</span> <span class="n">c</span> <span class="k">in</span> <span class="n">children</span> <span class="p">{</span>
        <span class="o">*</span><span class="n">dp</span><span class="nf">.get_mut</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="o">+=</span> <span class="n">now</span><span class="p">;</span>

        <span class="c1">// only queue if it's already been processed by all the nodes before it.</span>
        <span class="k">if</span> <span class="n">ingoing</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mi">1</span> <span class="p">{</span>
            <span class="n">queue</span><span class="nf">.push_back</span><span class="p">(</span><span class="o">*</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="o">*</span><span class="n">ingoing</span><span class="nf">.get_mut</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<h2 id="11b">11b</h2>

<p>Now, this is essentially the same problem as before, but we have a new constraint: the paths must visit both <code class="language-plaintext highlighter-rouge">dac</code> and <code class="language-plaintext highlighter-rouge">fft</code> (in any order). Also, we now start from <code class="language-plaintext highlighter-rouge">svr</code> and not <code class="language-plaintext highlighter-rouge">you</code>.</p>

<p>I had two options: I can find <em>all</em> the paths and filter them, or, I can use the multiplication rule from FPC (fundamental principle of counting) to get the number of valid paths directly. That is:</p>

\[\sum \text{paths} = (\sum \text{svr} \rightarrow \text{dac})(\sum \text{dac} \rightarrow \text{fft}) (\sum \text{fft} \rightarrow \text{out}) + \\\\
(\sum \text{svr} \rightarrow \text{fft})(\sum \text{fft} \rightarrow \text{dac}) (\sum \text{dac} \rightarrow \text{out})\]

<p>I mean, this seemed like a lot, so I was gonna start filtering paths. However, I was too lazy for that as well, so I returned to my beloved FPC method since I can basically reuse the code for part 1 to solve this easily.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="nf">paths_count</span><span class="p">([</span><span class="sc">'s'</span><span class="p">,</span> <span class="sc">'v'</span><span class="p">,</span> <span class="sc">'r'</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">ingoing</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">adj</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">dp</span><span class="p">);</span>
<span class="k">let</span> <span class="n">svr_to_dac</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="p">[</span><span class="sc">'d'</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">,</span> <span class="sc">'c'</span><span class="p">]];</span>
<span class="k">let</span> <span class="n">svr_to_fft</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="p">[</span><span class="sc">'f'</span><span class="p">,</span> <span class="sc">'f'</span><span class="p">,</span> <span class="sc">'t'</span><span class="p">]];</span>

<span class="nf">paths_count</span><span class="p">([</span><span class="sc">'d'</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">,</span> <span class="sc">'c'</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">ingoing</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">adj</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">dp</span><span class="p">);</span>
<span class="k">let</span> <span class="n">dac_to_fft</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="p">[</span><span class="sc">'f'</span><span class="p">,</span> <span class="sc">'f'</span><span class="p">,</span> <span class="sc">'t'</span><span class="p">]];</span>
<span class="k">let</span> <span class="n">dac_to_out</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="p">[</span><span class="sc">'o'</span><span class="p">,</span> <span class="sc">'u'</span><span class="p">,</span> <span class="sc">'t'</span><span class="p">]];</span>

<span class="nf">paths_count</span><span class="p">([</span><span class="sc">'f'</span><span class="p">,</span> <span class="sc">'f'</span><span class="p">,</span> <span class="sc">'t'</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">ingoing</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">adj</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">dp</span><span class="p">);</span>
<span class="k">let</span> <span class="n">fft_to_dac</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="p">[</span><span class="sc">'d'</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">,</span> <span class="sc">'c'</span><span class="p">]];</span>
<span class="k">let</span> <span class="n">fft_to_out</span> <span class="o">=</span> <span class="n">dp</span><span class="p">[</span><span class="o">&amp;</span><span class="p">[</span><span class="sc">'o'</span><span class="p">,</span> <span class="sc">'u'</span><span class="p">,</span> <span class="sc">'t'</span><span class="p">]];</span>

<span class="c1">// total of paths that come from svr to out that crosses fft and dac (in any order)</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">total</span> <span class="o">=</span> <span class="n">svr_to_dac</span> <span class="o">*</span> <span class="n">dac_to_fft</span> <span class="o">*</span> <span class="n">fft_to_out</span><span class="p">;</span>
<span class="n">total</span> <span class="o">+=</span> <span class="n">svr_to_fft</span> <span class="o">*</span> <span class="n">fft_to_dac</span> <span class="o">*</span> <span class="n">dac_to_out</span><span class="p">;</span>
<span class="o">...</span>

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

<p>After realizing that the answer to my input for this part is quite literally 331,468,292,364,745 (331 trillion), I am oh-so-grateful for <em>not</em> attempting to filter paths. I forgot that the number of paths in a graph can go exponential.</p>

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

<blockquote>
  <p><a href="https://adventofcode.com/2025/day/12">Day 12: Christmas Tree Farm</a></p>

  <p>You’re tasked to help the elves arrange presents under their Christmas trees.</p>
</blockquote>

<p>There’s only one part that needs to be solved here.</p>

<p>Anyway, this problem is ridiculous. It’s NP-hard. If there exists a computationally cheap solution, the Tetris champions would absolutely go nuts over it.</p>

<p>We kind of have to brute force all configurations to be able to solve for all kinds of input, but I couldn’t imagine how long that would take. I guess I can cheat for certain inputs by determining if the Elves’ region is obviously too small or obviously too big. But if that fails, what on earth should I do?</p>

<p>This problem is way too suspicious, so I checked <a href="https://aoc.just2good.co.uk/2025/10">this other post written by @derailed-dash on GitHub</a>. Turns out, for their input (and mine), all of the regions fall under one of [the obviously too small category] or [the obviously too big category]. This means that there’s no need to do brute force at all :sob:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">while</span> <span class="n">reader</span><span class="nf">.get_line</span><span class="p">()</span><span class="o">?</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span>
    <span class="c1">// if this is the gift box definition</span>
    <span class="k">if</span> <span class="n">reader</span><span class="py">.buffer</span><span class="nf">.trim</span><span class="p">()</span><span class="nf">.split_once</span><span class="p">(</span><span class="sc">':'</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span><span class="na">.1</span><span class="nf">.is_empty</span><span class="p">()</span> <span class="p">{</span>
        <span class="o">...</span>
        <span class="n">gift_sizes</span><span class="nf">.push</span><span class="p">(</span><span class="n">count</span><span class="p">);</span>
    <span class="c1">// if this is the region definition</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="o">...</span>
        <span class="c1">// too small</span>
        <span class="k">if</span> <span class="n">blocks_sizes</span> <span class="o">&gt;</span> <span class="n">area</span> <span class="p">{</span>
            <span class="c1">// do nothing</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">area</span> <span class="o">&gt;=</span> <span class="n">gifts_cnt</span> <span class="o">*</span> <span class="mi">9</span> <span class="p">{</span>
            <span class="n">total</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="nd">println!</span><span class="p">(</span>
                <span class="c1">// i guess this would never happen :|</span>
                <span class="s">"[!] Cannot determine if gifts would fit this region: {}x{}, {} gifts"</span><span class="p">,</span>
                <span class="n">dimension</span><span class="na">.0</span><span class="p">,</span> <span class="n">dimension</span><span class="na">.1</span><span class="p">,</span> <span class="n">gifts_cnt</span>
            <span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>
</code></pre></div></div>

<p>And yeah, the caveat is, this doesn’t work for the sample cases they give which is absolutely hilarious:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[!] Cannot determine if gifts would fit this region: 4x4, 2 gifts
[!] Cannot determine if gifts would fit this region: 12x5, 7 gifts
Total: 1
</code></pre></div></div>

<p>Nonetheless, I’m happy so long as it works for the real input.</p>]]></content><author><name>Cikitta</name></author><category term="programming" /><summary type="html"><![CDATA[Snail-Me in 2025 Advent of Code]]></summary></entry><entry><title type="html">My OSN-P (Compsci Province-level Olympiad) Experience</title><link href="https://daringcuteseal.github.io/blog/compsci-osp-2025/" rel="alternate" type="text/html" title="My OSN-P (Compsci Province-level Olympiad) Experience" /><published>2025-08-19T00:00:00+07:00</published><updated>2025-08-19T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/compsci-osp-2025</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/compsci-osp-2025/"><![CDATA[<p>Alright, this is supposed to be a quick blog so I guess I’ll just whip everything up really quickly. Oh, and, if you haven’t, you can read my <a href="/blog/compsci-osk-2025/">previous blog about OSN-K</a> because that’s the whole reason I’m here.</p>

<p>Oh boy, I would like to skip talking about this exam (because I can barely do it) if it’s possible but I do have some memorable stuff from it.. x) And I hate how you can’t know your code verdict in OSN-P.</p>

<hr />

<h1 id="at-school">At School</h1>
<p>I took my OSN-P exam with some senior friends from my school earlier today. I guess I’m a bit pissed off with the exam date because we held several school events to celebrate Indonesia’s independence day today and so I couldn’t chip in. However, on the flip side, I got to bypass the flag ceremony held today—those often make me feel short of breath, so I’m grateful of that.</p>

<p>Some other OSN-P participants and I spent time “preparing” ourselves in a room near teachers’ office. Since the computer science exam starts at 1 PM, we had plenty of time to do stuff. We held a small programming match with problems rated as the easiest ones in CodeForces, but I literally struggled with the first one because I tried to outsmart the by-nature naive problem ahaha. Afterwards, I decided to watch some random science/compsci YouTube videos I can find, listen to some songs, and drank a cool beverage I grabbed from one of the food/drink stands for the independence event.</p>

<p>And then, at around 11 AM, we packed our luggages and made our way to the school where the exam will be held. Everyone from my city went there to do the OSN-P exam. We were brought to the canteen to eat something first but one of the seniors said that they would like to avoid eating anything to prevent getting sleepy. I thought it would be a great idea too, and so I didn’t eat anything before doing the exam.</p>

<h1 id="the-exam">The Exam</h1>
<p>We entered the exam room a bit earlier to set up everything correctly (our proctoring device, laptop, etc). I have prepared everything from home, except for one thing: setting up the Wi-Fi hotspot from my laptop for my phone (as the proctoring camera). I had just 5 minutes left, and since for the exam I used (Artix) Linux, and we all know it—there’s no way one can figure out how to configure networking in just a short amount of time. Fortunately, I still have plentiful of mobile data left, so I had to sacrifice about 2 gigabytes of mobile data just for the Zoom meeting..</p>

<p>Also, I didn’t expect the scene set-up to take forever. I hadn’t gone to the restroom yet, but there was no time left. I didn’t get the urge to pee yet or anything, so I started the exam anyway.</p>

<h2 id="the-string-problem">The String Problem</h2>
<p>So this was roughly the first problem of the exam:</p>

<blockquote>
  <p>Congratulations on passing to OSN-P! As a way to congratulate you, Mr. Dengklek will make you a “pretty OSN-P string”.</p>

  <p>A “pretty OSN-P string” is a string that:</p>
  <ul>
    <li>Consists of at least one letter of each O, S, N, and P.</li>
    <li>Does not contain letter(s) other than P after a single letter P.</li>
  </ul>

  <p>Example: “OSNSPP” is a pretty OSN-P string, but both “OSSSP” and “OSNPS” are not.</p>

  <p>Your task is to determine the length of the longest possible pretty OSN-P string Mr. Dengklek can make for you by deleting one or more letters from a given string S. If it isn’t possible to create such string, output “-1”. Otherwise, output the length of the string.</p>
</blockquote>

<p>Okay, that’s a cute problem. Actually, it looked like a question from last year, so I tried to think about it the same way I would with <a href="https://tlx.toki.id/problems/osnp-2024/A">last year’s problem</a>. And the great part is that, it works just fine.</p>

<p>Initially, I kept track of the amount of O, S, and N characters up to the i-th index of string S so I can count the length when I meet a letter P. However, I realized that that list alone is not enough, since to maximize the length of the target string, you’d want all the extra trailing P letters after finding one P letter, too. I then added the amount of P characters up to the i-th index of string S. So now, I can just add the amount of O, S, and N characters by the amount of P characters from the current index to the last index whenever I encounter a letter P. The maximum length from all those possible configurations would be the answer.</p>

<p>All good, but it took me plenty of time because of my stupid coding mistakes. One of them is:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">best</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">best</span><span class="p">,</span> <span class="n">cnt</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="mi">1</span> <span class="o">+</span> <span class="p">(</span><span class="n">p</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</code></pre></div></div>

<p>Why is the <code class="language-plaintext highlighter-rouge">+ 1 + (p[n-1] - p[i])</code> expression outside the <code class="language-plaintext highlighter-rouge">max</code> function 😭 yeah but thankfully I noticed that right away. I hope that I didn’t miss any edge case for this one..</p>

<h2 id="exploring-other-problems">Exploring Other Problems</h2>
<p>I knew it was going to be hard onwards after I finished that first question, so I hunted for something doable. Was there any? … No, I wouldn’t say so, but I decided to bet on this one:</p>

<h2 id="factorization-problem">Factorization Problem</h2>
<p>This was roughly the third problem of the exam:</p>

<blockquote>
  <p>Mr. Dengklek has a budget of N. In a store, he can buy one or more trays of eggs, where all the trays have size of <code class="language-plaintext highlighter-rouge">M × M</code> and each costs <code class="language-plaintext highlighter-rouge">M</code>. M is an arbitrary prime number. To make matters easier, Mr. Dengklek will buy trays with just one size. After buying the trays of eggs, he will end up with N/M trays of <code class="language-plaintext highlighter-rouge">M × M</code> eggs and he would like to sell the eggs back. Mr. Dengklek would be able to arrange the newly bought eggs in several rectangular configurations all with different rows a and heights b (resulting in tray of size a × b). Note that a × b tray and b × a tray are considered different. Because he doesn’t know what the customer’s preferred tray configuration, he wants to buy the tray of eggs that can result him in the most amount of possible row and heights configurations.</p>

  <p>What’s the tray size of the eggs that Mr. Dengklek should buy? If there are more than one possible answers, pick the one with smallest tray size.</p>

  <p>For example, if Mr. Dengklek has a budget of 60, then he can buy 30 of 2×2 eggs, 20 of 3×3 eggs, or 12 of 5×5 eggs. Each configuration will respectively result in 2² × 30 = 120 eggs, 3² × 20 = 180 eggs, and 5² × 12 = 300 eggs. For the 120 eggs, he has 16 possible configurations. For the 180 eggs, he has 18 possible configurations. For 300 eggs, he has 18 as well. Because the 3×3 tray is smaller and still give the most amount of configurations (18), he should pick eggs of 3×3 sized tray to buy.</p>
</blockquote>

<p>Ah, a math problem. To know the possible tray sizes of eggs Mr. Dengklek can buy, I can just do a prime factorization. Then, a factor-counting function can determine the amount of configurations he can make after buying the eggs. Now, I just have to find the tray that gives the most number of factors. Since the algorithms can be run in O(√N) time, I’m pretty sure the program can run safely below the 2 seconds limit.</p>

<p>Well, guess what, factor-counting and prime factorization were my FIRST ever look at competitive programming problems. If you look at <a href="https://osn.toki.id/data/pemrograman-kompetitif-dasar.pdf">TOKI’s competitive programming book</a>, the intro example codes were all about those discrete math problems. It took me a few days just to get the hang of the example implementations back then. I wondered if I could write the same functions in just an hour or less this day..</p>

<p>I slowly traced the problem and wrote the solution. However, I forgot how to write a factor-counting function midway x) after several times doing factorization by hand, I eventually figured it out though.</p>

<p>So, all good, right?</p>

<p>*tries test case 3 with N = 1000000007*</p>

<p>It’s slow?! I thought that my functions are all O(√N).</p>

<p>Oh, wait, the number of eggs Mr. Dengklek can end up with may be so humongous to the point where the square root of it is no longer small enough for the calculation to finish quickly. Also, integer overflow is concerning at this point. I had to do something. I broke down the equation of the total eggs he’ll end up with, and factorized each terms separately by using a hashmap to accumulate the exponents of each factors before finally counting the number of factors the number has by looping through the hashmap once.</p>

<table>
  <tbody>
    <tr>
      <td>And then, after several trial and errors, I finished the code! I Hope it really does work for large N :</td>
    </tr>
  </tbody>
</table>

<h2 id="other-problems">Other Problems</h2>

<p>As for the rest of the problems in the exam, I was completely clueless 💀 I’m not even sure if most of them are DP or can be solved by greedy, but I think most of them are indeed DP.</p>

<h2 id="freezing">Freezing</h2>

<p>The exam room got cold really quickly. We didn’t wear shoes inside the room; I could feel my feet freezing slowly. However, not just that: being in a cold place is an enemy against my typing skill. Maybe I actually coded a bit slower in the exam, I don’t know.</p>

<p>After a while of freezing, I suddenly got the urge to pee. I had to tell the supervisors that I need to go for a bit but apparently they said I only get 3 minutes. And, guess what? The restroom was crowded 😭 I dreaded in panic, but thankfully, half of them weren’t going to pee at all (Universe pranking me?) and I only had to wait for 2 people in line.</p>

<p>I then returned to my chair, still hopeless about the rest of the problems x)</p>

<h1 id="going-back-home">Going back Home</h1>
<p>Yeah I pretty much couldn’t figure out any solution in the last hour that I had. How sad. I guess that I’m a bit disappointed, but honestly, everyone I know of was only able to answer just one or two problem(s) as well.</p>

<p>We then drove back to our school after the exam, but the car was completely silent for the most part. I had no idea why my friends didn’t talk much. Anyway, I had to tell everyone about the exam so I didn’t bother to talk a lot to them either, hhaha.</p>

<h1 id="end-thoughts">End Thoughts</h1>

<p>I actually came out of the room just as happy as before the exam started.</p>

<p>Well, for one, now I can do TWO problems in a contest! I usually can only do one before 💀 (holy crap, that’s actually pathetic). And then, there’s also me proving to my past self that I <em>am able to write code faster than ever</em>! And besides, I still have next year. No biggie.</p>

<p>But also, I think I really enjoyed the companionship of competitive programming friends. That meant a whole lot more to me than the competition itself. I attended trainings with them for this OSN-P, and honestly, there’s always nothing better than having people to talk to about your stupid algorithm with O(2^N) time complexity and stack overflow potentials ;) I can’t see them anymore next year, and that’s unfortunate. I wish all the best for them.</p>

<p>So yeah. That’s probably it for this year! I’m proud as always to see myself improving. I wish that I won’t have to attend training sessions alone next year, will I?</p>]]></content><author><name>Cikitta</name></author><category term="competition" /><category term="compsci" /><category term="competitive-programming" /><summary type="html"><![CDATA[At least I could answer stuff.. right?]]></summary></entry><entry><title type="html">My OSN-K (Compsci City-level Olympiad) Experience</title><link href="https://daringcuteseal.github.io/blog/compsci-osk-2025/" rel="alternate" type="text/html" title="My OSN-K (Compsci City-level Olympiad) Experience" /><published>2025-06-25T00:00:00+07:00</published><updated>2025-06-25T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/compsci-osk-2025</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/compsci-osk-2025/"><![CDATA[<p>Hello-hello! It’s been a while, and the reason why I haven’t posted anything for a while is exactly because of this whole OSN-K competition preparation (and other school stuff, too).</p>

<p>My country, Indonesia, runs an annual Olympiad they call “OSN” (<em>Olimpiade Sains Nasional</em>/National Science Olympiad) for all sorts of subjects: from maths, physics, economics, geography, astronomy, and of course, <em>computer science</em> (just officially called “computer”). To get there, though, is no small feat—because you need to pass two of their exams first: OSN-K and OSN-P, each referring to the city-level and province-level exams, respectively. Oh, and, your (high)school must register you too to be able to join OSN-K—which may require another entrance exam depending on the school you attend.</p>

<p>Now, before we go further, I’d like to point out an interesting observation: this system where you have to pass two exams first—based on your school location—doesn’t really guarantee that the country will actually get the nation-wide top students.</p>

<p>Here’s an example:</p>

<ul>
  <li>A, B, and C are from city X.</li>
  <li>D, E, and F are from city Y.</li>
</ul>

<p>Let’s say that A and D won the city-level competition for wherever they live, respectively. Now, you can try to let both A and D compete together and see who would win. For example, A beats D. But it doesn’t really mean that D is second-best at this point, because if A is just so good, A might have beaten B and C (who both may be better than D) before both of them ever get a chance to continue to the next exam. It’s an unfair system, in my opinion..</p>

<p>With that out of the way, though, I’d like to share my funky experience with OSN-K.</p>

<h1 id="background">Background</h1>

<p>You can join OSN beginning from elementary level, but I never really got selected for any. They only had a few subjects too, like math and general science, and I never liked any of them—it’s only in high school that computer science subject (the only subject I like) began to be available.</p>

<p>The OSN-K computer science competition I joined contains questions similar to those you’d find in <a href="https://www.bebras.org">Bebras</a>, but with more math, plus extra code-reading. Historically, though, pure discrete math questions dominate the question sets. I must say, I like the older questions better— especially the questions from around 2016-2023 that all have more discrete math than mathematical logic with less headache-inducing lore for each questions, which I’m better at. But alas, I was born a little too late, it seems like.</p>

<p>Anyway, their OSN-K official syllabus mentions several topics, such as:</p>
<ul>
  <li>Discrete math</li>
  <li>Mathematical logic</li>
  <li>Boolean algebra</li>
  <li>Graph theory</li>
  <li>Data structures</li>
  <li>Popular algorithms</li>
  <li>…and general algorithm analyses</li>
</ul>

<p>Olympiad level questions are pain in the ass to get right (at least for the inexperienced me). And if you can’t get one right, good luck finding the answer—even search engines or LLMs may not be able to provide an answer for whatever your question is. Your best bet is to befriend someone who’s good at Olympiad type of questions.</p>

<p>I was lucky enough to have quite a few of them since the early days of my high school, but I must admit that I was quite shy to ask :p So, early on, I’d dug through questions and try to get answers myself based on help from the internet. I’d still ask people when I got completely stuck, though.</p>

<p>Now, during those “early high school days”, I didn’t study much. I mean, for one, the OSN-K entrance exam at my school wasn’t even held until like 2 months ago. Also, compsci and programming are fun but I never really liked to dive the nitty-gritty theories myself. However, some of my extra-nerdy friends that I knew back then were my main inspiration—they make problem solving seem magical—I felt like I need to have that kind of skills too, which fueled my intents to join OSN-K this year and study for it.</p>

<h1 id="the-club">The Club</h1>
<p>The first step to join OSN-K was to join the “club” for whichever subject you’re interested in. For me, it was the computer science club. They had an initial entrance exam, too. Frankly, I felt quite pissed off because I originally didn’t get in, somehow (I think I messed up my arithmetics so badly; I was sick that day). I don’t know the details, but a senior of mine helped me to pass in anyway. It seemed odd, because I wasn’t even close to them. Was I thankful? Maybe.. but if I really didn’t deserve a spot then I should have not been allowed to join anyway.</p>

<p>Then, my school provided OSN-K training for club members which you can enter by taking <em>another</em> entrance exam. The entrance exam was actually mandatory for all members, and so I did it. Thankfully, I was accepted (the passing grade wasn’t <em>that</em> high anyway?), so I was slightly more confident that I <em>really did</em> deserve a spot in the club.</p>

<h1 id="the-drilling">The Drilling</h1>

<p>Everything had gone well past this point, so I started studying for OSN-K—even if I didn’t know whether or not I will actually join it. It was fun, although I was quite inconsistent :) Just me being me, but maybe it was partly because of my 10000 other side hobbies and school organization things.</p>

<p>How did I study? Thankfully, <a href="https://osn.toki.id/arsip/kota">TOKI</a> (the team that creates all question sets for compsci OSN-K) gave a hell lot of free resources for studying—from a free competitive programming course, a free competitive programming handbook, literally every single question sets they have made, and more. Those were my main resources up to that point.</p>

<h1 id="mock-exams">Mock Exams</h1>
<p>I joined some mock exams too for OSN-K. Oof, I was pretty horrible at them. But that was because the mock exams were actually harder than the actual thing :skull: And added with the fact that my arithmetic speed is pretty darn average, I’d certainly flop in many mock exams—most of which were formatted like ancient OSN-K questions from the bronze age or something, all packed with human calculator-level kind of math.</p>

<p>The other reason I was so terrible at mock exams was because I didn’t study the stuff I didn’t like. For instance, I didn’t study boolean algebra—my biggest enemy at that time.</p>

<h1 id="yet-another-entrance-exam">Yet Another Entrance Exam</h1>

<p>I was surprised to learn that you need to take another entrance exam for the actual OSN-K (yes, I did not know this early on). Around the same time, I got an offer to join FLS3N—another prestigious national-level arts competition.. <em>BUT WITH AN INITIAL ENTRANCE ART SHOW OFF TEST TOO!</em> I had to make this most off-putting life decision again: <strong><em>compsci or visual art?</em></strong></p>

<p>I trembled. I definitely did not plan about FLS3N popping up in front of my face at all. Back in middle school, I joined FLS3N too, but I sucked so hard back then—and I felt like I wanted to do a redemption. Hmm.. I suddenly had an idea. I told my art teacher that I will join FLS3N under the condition that I fail my OSN-K entrance test ;P</p>

<p>Not gonna lie, I thought that maybe FLS3N would be better for me after all, since I feel like I hadn’t studied enough for OSN-K. I still had the next year for it, anyway. I then hoped that I would fail my OSN-K test so that I can have a chance to join FLS3N.</p>

<p>I took the entrance exam for OSN-K. It was mid? I couldn’t answer one or two (one of them is really vague), but the rest were fine. Then, yeah, I passed it and I was allowed to join. Man, I hated how my other grade 10 OSN-K participant kept on saying, “I actually did not want to join this, I don’t know why I passed” from since their OSN-K training exam up until the actual OSN-K entrance exam. Haha..</p>

<h1 id="final-drills">Final Drills</h1>
<p>I spent a month or two for some focused self-training. Then, I bought (yes) some question sets alongside their answers and spent another two weeks for absolute maxx drilling I could achieve. I faced all my hatred topics: boolean algebra, set theory, greedy, modular arithmetic, and more.</p>

<h1 id="the-day"><em>THE DAY</em>!</h1>

<p>I was literally so dramatic the day before the competition, I swear. I treated myself with a Ghibli film, played some arcade games, coded something in Rust (<a href="https://github.com/Daringcuteseal/prefic">Prefic</a>), and self-reflected so deep as if I was gonna leave the world the next day.. but fair enough, it was kind of relieving and crazy that I had gone that far—considering that I have no experience with Olympiads before.</p>

<p>Okay, now, here’s the fun part: the actual competition day.</p>

<p>We were given a free bread for breakfast, and then we got a bottle of cold mineral water—pretty interesting stuff. How did they even chill the water?</p>

<p>Afterwards, other students across subjects and I took formal pictures together. Then, we all went to our computer lab to take our respective exams. I sat down and wrote down my username and password. When they finally gave us our exam token, I gracefully typed it out and started the exam.</p>

<h1 id="the-exam-itself">The Exam Itself</h1>
<p><em>Pop</em>! I snapped into the exam questions. And what I saw the first time? *An image-based question*.</p>

<p>Wow, thanks, TOKI! Thank you for giving image-based questions that we have to copy to our paper first if we ever want to annotate it. Very much convenient.</p>

<p>And then there were more image-based questions, and other questions with pretty long lore that I had to read first. I absolutely hated the math and logic section—I was pretty unlucky there—making a few annoying mistakes I could have otherwise noticed if the exam didn’t have a time limit.</p>

<p>For instance, there’s this really annoying combinatorics problem. It was supposedly not too hard, but due to me dabbling with some harder similar problems the days before, my brain’s cache was pretty messed up. I started to analyze the question unnecessarily. I couldn’t finish that problem—dang it! It was quite literally a normal Fundamental Principle of Counting problem. I went galaxies away from that..</p>

<p>And then, I got into the code interpretation section. I was genuinely pissed off at that point, because it turned out that the math and logic part <em>DID NOT CONTAIN TOPICS THAT I STUDIED REALLY HARD</em>, like boolean algebra and set theory. However, I was soon relieved, because the code reading part is way easier—much, MUCH better than last year’s problem set. There’s only one problem: I didn’t have much time left! And while I did not skip any of the code reading problems initially, I could not do the last three ones because I only had 3 minutes left. It was so infuriating! I wish that I had started from the back of the page.</p>

<p>And then, 15 minutes before the exam ends, my keyboard decided to die randomly while I was trying to type out my answer. I legit panicked and called out the supervisor immediately. But as a compsci™ student who knows the best hardware advice ever <small>(turn it off and on again)</small>, I yanked out the keyboard’s USB interface and plugged it back—and thankfully, it arose from dead again. Phew.</p>

<p>And so, the exam ended just like that. I had no idea how to feel—months and months of preparations all poured into one single (messed up) day. However, I framed the exam as a milestone and not the end of anything. I’m proud that I took the courage to face the topics I was bad at, and even got hooked to them. That alone is more than good, right? :)</p>

<h1 id="bonus-result">BONUS: Result</h1>

<p>The results came out a few days ago. I ranked like 8th in my city and passed the exam LOL. Take that, whoever the hell rejected me from the computer club 💀 You were wrong about me.</p>

<p>Maybe I’ll write about OSN-P soon..</p>]]></content><author><name>Cikitta</name></author><category term="competition" /><category term="compsci" /><category term="competitive-programming" /><summary type="html"><![CDATA[My first time with national olympiads was.. a bit daunting..]]></summary></entry><entry><title type="html">A Practical Guide to Dynamic Programming (DP)</title><link href="https://daringcuteseal.github.io/blog/a-practical-guide-to-dp/" rel="alternate" type="text/html" title="A Practical Guide to Dynamic Programming (DP)" /><published>2025-03-03T00:00:00+07:00</published><updated>2025-03-03T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/a-practical-guide-to-dp</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/a-practical-guide-to-dp/"><![CDATA[<p>Ever wanted an algorithm to write an algorithm? Well, I did :) So I wrote this practical guide to DP (based on my personal experience)!</p>

<h1 id="introduction">Introduction</h1>
<p>DP is a really fascinating programming technique. It solves even the seemingly daunting problems in a <strong>linear</strong> time! Usually, problems that require you to <strong>calculate the amount of ways to solve a problem</strong> or to <strong>find the best way to solve a problem can be worked out with DP</strong>.</p>

<p>But, how does it work?</p>

<p>Well, actually, DP is an application of brute-forcing because it tries every single possible ways to solve a problem. <em>However</em>, this brute-force is optimized by storing the result of already solved sub-problems that the program previously encountered.</p>

<p>DP can be used in place of the <a href="https://en.wikipedia.org/wiki/Greedy_algorithm">greedy technique</a>. The difference between the two, is that with DP, you’d have to map out <em>every</em> possible ways to solve a problem (and either pick the optimum one or count the ways), while with greedy, the optimum way to solve the problem is already known. An algorithm utilizing DP is easier to factual-check because it would’ve seen <em>every</em> of the possible ways to solve a problem, so it is guaranteed that you get the most optimum result.</p>

<p>There are two ways to implement dynamic programming:</p>
<ul>
  <li>
    <p><strong>Top-down</strong></p>

    <p>A top-down implementation of DP is done recursively by solving the biggest problem first where we don’t know the solution to our problem yet. We then recursively go solve each one of those, ensuring that we only try to calculate the function call with a particular parameter once. This is done using <strong>memoization</strong>.</p>
  </li>
  <li>
    <p><strong>Bottom-up</strong></p>

    <p>A bottom-up implementation of DP is done by solving the problem that we already know the solution of. Then, we work our way up until we go to our main/biggest problem. This is done by tabulating the result.</p>
  </li>
</ul>

<h1 id="approaching-a-dp-problem">Approaching a DP Problem</h1>

<p>Here are my steps to solve a problem with DP:</p>
<ol>
  <li>Think about how you would build the solution <strong>systematically</strong> by hand. Since DP is just a brute-forcing algorithm, do not worry about how unoptimized it is—it <em>will</em> be anyways :)</li>
  <li>Write down the <strong>recursive formula</strong> for the problem, making use of what’s already known from the solved sub-problems.</li>
  <li>Determine its <strong>base case</strong>, i.e a specific sub-problem whose solution is already known.</li>
  <li>Implement its solution or calculate it.</li>
</ol>

<h1 id="example-case-building-a-number-string">Example Case: Building a Number String</h1>

<p>Let’s go through an example problem.</p>

<blockquote>
  <p>Given an array {0, 1, 2}, How many ways are there to create N-length string using the three possible numbers for each character, where each character is only allowed to have <strong>at most</strong> 1 difference from its neighboring character?</p>

  <p>Example (N = 2):</p>

  <p>{0, 0}, {0, 1}, {1, 0}, {1, 1}, {1, 2}, {2, 1}, {2, 2} = 7 ways</p>
</blockquote>

<h2 id="building-by-hand">Building By-Hand</h2>

<p>How would one manually build a string of length N with the said rules?</p>

<p>We’d want to loop from the first character to the last one. For N = 1 (i.e same thing—the first character), we only have three ways: {0}, {1}, and {2}.</p>

<p>Now, notice that we’re going to have to take the next step(s) differently depending on which number we pick. If we had picked 0, we only have choices {0} and {1} for the next character. If we had picked {1}, we’d have {0}, {1}, and {2}. If we had picked {2}, we’d have {1} and {2}.</p>

<p>So now, when we try to map out the amount of possibilities that we’re gonna have, we get this:</p>

<p><img src="/blog/image/dp-tree.png" alt="DP Tree" /></p>

<p>And to get the amount of proper arrangements, we’d just have to calculate all the amount of nodes in that tree.</p>

<h2 id="recursive-formula">Recursive Formula</h2>

<p>To get our formula, we’d first have to ask, <strong><em>what is our sub-problem exactly?</em></strong></p>

<p>The main rule of the formula-crafting is that it must <strong>not</strong> discriminate between differences of parameter, i.e all those 0, 1, and 2’s must be of the same sub-problem.</p>

<p>A helpful way to get our sub-problem is to go to our last step. Assume we’re at the edge of our problem-solving, i.e we’re at the last character. What exactly are we going to do? We are <strong>writing the (last) possible character from the choices that we have</strong>. And that, is our sub-problem, because (see below) it is recursive (i.e you need to answer a similar sub-problem to get that answer).</p>

<p>Our new question would be: <strong>how many ways are there to write the last possible character from the choices that we have</strong>? Intuitively, it’s just the ways to append 0 + the ways to append 1 + the ways to append 2.</p>

<p>But, comes the next problems. We don’t even know how to calculate those. So let’s jump through all of them:</p>
<ul>
  <li>
    <p><strong>To append 0</strong></p>

    <p>We <em>aren’t always</em> able to write 0. We can only write 0 if our previous character was 0 or 1. Therefore, the total ways to write 0 when length is $ x $ is <strong>the ways to write 0 with length $ x-1 $ + the ways to write 1 with length $ x-1 $</strong>. Those are all the possibilities that make us able to write 0 at position $ x $.</p>
  </li>
  <li>
    <p><strong>To append 1</strong></p>

    <p>Same thing here, we can only write 1 if our previous character was 0, 1, or 2. The ways to write 1 when length is $ x $ is <strong>the ways to write 0 with length $ x-1 $ + the ways to write 1 with length $ x-1 $ + the ways to write 2 with length $ x-1 $</strong>.</p>
  </li>
  <li>
    <p><strong>To append 2</strong></p>

    <p>Also the same. The ways to write 2 when length is $ x $ is <strong>the ways to write 1 with length $ x-1 $ + the ways to write 2 with length $ x-1 $</strong>.</p>
  </li>
</ul>

<p>Let’s simplify a bit. Assume that the solution to our original question is defined as f(x). And:</p>
<ul>
  <li>a(x): ways to append 0 with length x</li>
  <li>b(x): ways to append 1 with length x</li>
  <li>c(x): ways to append 2 with length x</li>
</ul>

<p>Therefore:</p>
<ul>
  <li>f(x) = a(x) + b(x) + c(x)</li>
  <li>a(x) = a(x-1) + b(x-1)</li>
  <li>b(x) = a(x-1) + b(x-1) + c(x-1)</li>
  <li>c(x) = b(x-1) + c(x-1)</li>
</ul>

<h2 id="making-our-base-cases">Making Our Base Case(s)</h2>

<p>We have to make our recursion finite, so we need a base case. A base case is a problem whose solution is already known.</p>

<p>Now, it’s helpful to go to the <em>start</em> of the problem instead in this case–when we don’t have anything yet. When we first append a character (this is f(x)), there’s only 3 choices: append 0, 1, or 2. Or, in other words, f(1) = 3.</p>

<p>But since f(x) is composed out of a(x), b(x), and c(x), it’s a lot more helpful to find the base cases for those functions:</p>
<ul>
  <li>
    <p><strong>a(x)</strong></p>

    <p>When x = 1, it means the amount of ways to append 0 with length 1. There’s nothing previously, so obviously, there’s only one way to append 0 which is our first step in building our string.</p>
  </li>
  <li>
    <p><strong>b(x)</strong></p>

    <p>Same thing as above. There’s only one way.</p>
  </li>
  <li>
    <p><strong>c(x)</strong></p>

    <p>Same thing as above.</p>
  </li>
</ul>

<p>Now this does check out, as f(x) = a(x) + b(x) + c(x) and f(1) = 1 + 1 + 1 = 3.</p>

<h2 id="our-formula">Our Formula</h2>

\[f(x) = \begin{cases}
a(x) + b(x) + c(x)  &amp; \text{if x &gt; 1} \\
3 &amp; \text{if x = 1}

\end{cases}\]

<p>And</p>

\[a(x) = \begin{cases}
a(x-1) + b(x-1) &amp; \text{if x &gt; 1}\\
1 &amp; \text{if x = 1}
\end{cases}\]

\[b(x) = \begin{cases}
a(x-1) + b(x-1) + c(x-1) &amp; \text{x &gt; 1}\\
1 &amp; \text{if x = 1}
\end{cases}\]

\[c(x) = \begin{cases}
b(x-1) + c(x-1) &amp; \text{if x &gt; 1}\\
1 &amp; \text{if x = 1}
\end{cases}\]

<h2 id="calculating--implementation">Calculating &amp; Implementation</h2>

<p>I’ll use bottom-up DP to fill a table, then display all the function call values (C++). Bottom-up approach is usually more preferred as we don’t get affected by function call overhead—and we can do a lot more interesting stuff with it!</p>

<p><em>Disclaimer: why C++? It’s nice for competitive programming scripts haha</em></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</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">int</span> <span class="n">N</span><span class="p">;</span>
	<span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">f</span><span class="p">[</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
	<span class="kt">int</span> <span class="n">a</span><span class="p">[</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
	<span class="kt">int</span> <span class="n">b</span><span class="p">[</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
	<span class="kt">int</span> <span class="n">c</span><span class="p">[</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>

	<span class="n">a</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">b</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">c</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">f</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>

	<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"f("</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="s">") = "</span> <span class="o">&lt;&lt;</span> <span class="n">f</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</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">2</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</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">b</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">b</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</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">b</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">c</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">c</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">b</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">c</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">f</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
		<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"f("</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">") = "</span> <span class="o">&lt;&lt;</span> <span class="n">f</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So now, when we give some value for N:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$&gt; ./program
10
f(1) = 3
f(2) = 7
f(3) = 17
f(4) = 41
f(5) = 99
f(6) = 239
f(7) = 577
f(8) = 1393
f(9) = 3363
f(10) = 8119
</code></pre></div></div>

<p>it will print the filled table.</p>

<h2 id="complexity-analysis">Complexity Analysis</h2>
<p>The time complexity of this is always O(N), as the loop iterates over N times. The space complexity, however, is O(N) as well because we store all the resulting solution of our sub-problems. We will optimize it in the next section.</p>

<h2 id="optimizing">Optimizing</h2>

<p>Notice that we only need to access our previous result, because in all our recurrence relations, we only use the parameter $ x-1 $.</p>

<p>This means that storing the whole table is redundant, as we can simply only store the previous result:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</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">int</span> <span class="n">N</span><span class="p">;</span>
	<span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">a_prev</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">b_prev</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">c_prev</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">f_prev</span> <span class="o">=</span> <span class="n">a_prev</span> <span class="o">+</span> <span class="n">b_prev</span> <span class="o">+</span> <span class="n">c_prev</span><span class="p">;</span>

	<span class="kt">int</span> <span class="n">f</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">c</span><span class="p">;</span>

	<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"f("</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="s">") = "</span> <span class="o">&lt;&lt;</span> <span class="n">f_prev</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</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">2</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">a</span> <span class="o">=</span> <span class="n">a_prev</span> <span class="o">+</span> <span class="n">b_prev</span><span class="p">;</span>
		<span class="n">b</span> <span class="o">=</span> <span class="n">a_prev</span> <span class="o">+</span> <span class="n">b_prev</span> <span class="o">+</span> <span class="n">c_prev</span><span class="p">;</span>
		<span class="n">c</span> <span class="o">=</span> <span class="n">b_prev</span> <span class="o">+</span> <span class="n">c_prev</span><span class="p">;</span>
		<span class="n">f</span> <span class="o">=</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="o">+</span> <span class="n">c</span><span class="p">;</span>
		<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"f("</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">") = "</span> <span class="o">&lt;&lt;</span> <span class="n">f</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>

		<span class="n">a_prev</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span>
		<span class="n">b_prev</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span>
		<span class="n">c_prev</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span>
		<span class="n">f_prev</span> <span class="o">=</span> <span class="n">f</span><span class="p">;</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And with this, we get O(N) time complexity and O(1) space complexity. Awesome!</p>

<h1 id="references">References</h1>
<ul>
  <li><a href="https://osn.toki.id/data/pemrograman-kompetitif-dasar.pdf">TOKI’s Competitive Programming Guidebook</a></li>
</ul>]]></content><author><name>Cikitta</name></author><category term="programming" /><category term="tutorial" /><category term="competitive-programming" /><summary type="html"><![CDATA[Ever wanted an algorithm to write an algorithm? Well, I did :) So I wrote this practical guide to DP (based on my personal experience)!]]></summary></entry><entry><title type="html">A SCHOOL Photo Booth?!</title><link href="https://daringcuteseal.github.io/blog/a-school-photo-booth/" rel="alternate" type="text/html" title="A SCHOOL Photo Booth?!" /><published>2025-02-10T00:00:00+07:00</published><updated>2025-02-10T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/a-school-photo-booth</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/a-school-photo-booth/"><![CDATA[<p>Alright. For us here, photo booths are already common. We had it in <a href="https://daringcuteseal.github.io/blog/excelsior/">Excelsior</a>, and maybe other events. I don’t really know, I’m new in this high school. Well it was all fun and game—except that, I would have never thought that the day where <em>I’m</em> the organizer would come. And yet it did. Last Wednesday, (February 5th) we had a Chinese New Year event and we opened a small photo booth near our front gate. I wouldn’t say small, but for some reason, some people didn’t even know that we existed..</p>

<p>The equipments for the booth had been prepared a few days back: a DSLR camera, some photo paper, a photo booth software, and my printer (like, I actually brought it to my school). We also got some props for use—most are from school but one of my team members asked their friend to bring a snake plush—best prop ever.</p>

<h1 id="a-divine-intuition">A Divine Intuition</h1>

<p>We actually knew that our camera didn’t wanna communicate properly with our photo booth software, but I had figured out why—or at least, I thought so, because the next day while I was <em>actually</em> setting up the camera, it decided to not work <em>again</em>. And under that panic mode I hypothesized that maybe the software couldn’t read the raw files that the camera sent, so we changed the output format to JPEG from the camera and then everything went in peace again.</p>

<p>And that was weird, because I did not recall doing that the previous day. All I did was to set the camera mode to automatic and inserted an SD card to it. Oh well, at least we got it to work.</p>

<h1 id="racing-against-time">Racing Against Time</h1>
<p>A few minutes before we opened the booth, I noticed a degradation of our print quality after some testing. I thought that it was my printer’s usual hiccups, as the colors would get messed up after a while of printer. Oh well, it was apparently because I forgot to unlock the cartridge after it was carried here. I quickly went to do some head cleaning, praying that it would be finished before anything actually starts.</p>

<p>Alas, the recess bell soon rang and people rushed to our place. Okay, not rush, there were just a few, but people <em>were</em> already waiting. My printer wasn’t done yet, but we could still take photos anyways so it was fine. And the moment I truly needed it to print, it actually worked. Phew, that was close. Then I professionally™ hit the print button and completed our first order.</p>

<h1 id="unending-stream-of-people">Unending Stream of People</h1>
<p>..and the orders went on endlessly during the second recess (when the rest of the Chinese events had started). Oh boy, that was fun. But for some reason my order sentence processing skills were so bad, I had to ask my friend to repeat what our friends wanted a few times. The words were like, all just nonsense numbers to me—because yes we have 3 photo strip designs, and I don’t know whose idea it was (<small>mine</small>) to label our designs by <em>numbers</em> instead of by their color—so each time somebody comes, the instruction given to me would sound like “two two once, one two twice” or “two three two, one one two” or whatever. Sure I do maths, numbers, but I just cannot cope with these. :cryingemoji:</p>

<h1 id="the-peak-event">The Peak Event</h1>
<p>I went to watch some performance during the peak event with my friends. We were veryy late though which was disappointing. I forgot why, probably because I had some extra photo orders qwq</p>

<h1 id="heyy-its-a-booth-for-five">Heyy it’s a booth for five!</h1>
<p>After the event is done we all got free dimsum provided by our school..! Totally the weirdest thing to get (at least when written like this), but we did. And let me tell ya they actually tasted good.</p>

<p>Okay, even after the whole event is already finished, <em>there were still a queue of people</em>. Urgh. So then I continued to stand by the booth while feasting on my dimsums.</p>

<p>Anyways, let’s get to the funnier part. One teacher proceeded to come with <strong>her whole class</strong> to take a picture at our place. They consisted of around 20 people or so. Heyy, it’s a booth for five! But a student from grade 11 (I assume from her homeroom?) brought their wide DSLR lens and swapped ours with their lens and saved the day :3 I had some talk with them and it turns out that they were one of the people who managed <a href="https://daringcuteseal.github.io/blog/excelsior/">Excelsior’s</a> photo booth last year (2024), aha.</p>

<h1 id="and-no-im-slightly-annoyed">And No, I’m Slightly Annoyed</h1>
<p>So yeah thanks booth, I totally like not getting any time to spare for enjoying the Chinese New Year event for myself—it was the last event for 12 graders; I didn’t get a lot of time to spend for my 12 grade friends. Sad. But I mean, taking pictures is also fun, I guess :D And hey we raised like roughly a million Rupiahs (gross)!</p>]]></content><author><name>Cikitta</name></author><category term="school" /><category term="personal" /><category term="tech" /><summary type="html"><![CDATA[Hands-on experience in opening a booth. That was fun.]]></summary></entry><entry><title type="html">(Finally) Getting the Position I want in Student Council</title><link href="https://daringcuteseal.github.io/blog/student-council-in-tech/" rel="alternate" type="text/html" title="(Finally) Getting the Position I want in Student Council" /><published>2025-02-09T00:00:00+07:00</published><updated>2025-02-09T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/student-council-in-tech</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/student-council-in-tech/"><![CDATA[<p>Think I’ll keep it short here. My school assignments are kinda killing me right now :) So anyway, a while back (still 2025) I was inaugurated as a member of my school student council’s tech division. It’s this year, though. My high school has a bit different schedule compared to my middle school it turns out. We’re slightly more late. Usually, in my middle school, I would have been inaugurated already by the previous year.</p>

<p>I’ll rewind a bit though.</p>
<h1 id="the-interview">The Interview</h1>

<p>It was a normal interview. Had it like last year. The results took forever to be announced. The interview was actually scary. They literally took notes about me there, judging various aspects bout me as I speak. Except that.. somehow the teacher didn’t pay attention haha. But anyways, I had prepared my strategies: pretend I love IT sooo much ;) <small>(I do, but phrase everything in a really cool™ way)</small>. And plus, I was the coordinator for tech division back in middle school so I already got free plus points.</p>

<h1 id="so-far-so-good">So Far So Good</h1>
<p>Thankfully, this year, I don’t need to actually manage the Instagram account directly. I’m just a member. I actually expected a lot less work but it turned out that I was wrong! High school student council is <em>dead serious</em>.</p>

<p>Ah yeah, I didn’t write anything about my middle school student council experience. In a nutshell, I never got the positions I wanted. During the first year, I barely did anything meaningful and during the second year, I didn’t really enjoy working with my team as the coordinator.</p>

<p>Okay, back to high school student council. This organization is really <em>dead serious</em>. The amount of tasks I get is actually quite decent considering I’m the only grade 10 student in the tech team (the rest are from grade 11, okay I’m proud of that). So I’ve done like a few designs maybe? Not too many, I’m more involved with giving feedbacks and handling events. So far it’s great, we all work cooperatively together. Really nice team, I love it.</p>

<h1 id="catch-no-website">Catch: No Website??</h1>
<p>Here’s the less exciting part of the tech team: we proposed to get a website for our student council but it was rejected. The reason? Somehow because “we already have official school site”. Yet, I saw another campus of my school having a site for their student council, <strong>written with React!!!</strong> <small>And also by the way <a href="https://daringcuteseal.github.io/blog/excelsior/">our Excelsior site</a> exists</small>. How is that fair? Back in middle school I got to make one. <a href="https://osisphi.wordprses.com">It’s here, by the way (in Indonesian).</a>
Depending on my future outlooks, I may or may not want to repropose that site again next year. Maybe if I’ll get trusted to be the coordinator.</p>

<h1 id="the-first-amazing-event">The First Amazing Event</h1>
<p>My first scbool event as a student council organizer was the Chinese New Year event. I don’t know why we have one, but we do somehow. It’s our debut actually, we didn’t have it previously. The tech division had two events there: our photo booth and a photography competition. I was the PIC of the photo booth. And I’ll talk about it on my following blog post! ;)</p>]]></content><author><name>Cikitta</name></author><category term="personal" /><category term="school" /><summary type="html"><![CDATA[Tech division we go!]]></summary></entry><entry><title type="html">The C++ An Hour and Half Mistake</title><link href="https://daringcuteseal.github.io/blog/the-hour-and-half-mistake/" rel="alternate" type="text/html" title="The C++ An Hour and Half Mistake" /><published>2025-01-26T00:00:00+07:00</published><updated>2025-01-26T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/the-hour-and-half-mistake</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/the-hour-and-half-mistake/"><![CDATA[<p>Yesterday evening, I was in a programming competition. It was problem solving-styled though, while software engineering is usually more of my cup of tea. But anyway, we were a team of three and we were trying to solve 8 questions.</p>

<p>I quickly scrolled through each of the questions. I saw a decimal-to-binary base conversion question which I thought would be easy, so I gave it a shot. Well, it was only theoretically easy.</p>

<p>It was my end’s fault: I did not understand the second task of the question before starting to write the code. The second task was actually to count the longest repeating 1’s or 0’s for each of the test cases. They didn’t explicitly say “the longest”, though, so it baffled me for a while. I dismissed that part and proceeded to write the base conversion first.</p>

<p>I tried doing the calculation inside my recursive function, but it was a bit confusing, so I decided to opt for a string-generating function instead. Let’s say that I ended up having a function similar to this:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">string</span> <span class="nf">convert_number</span><span class="p">(</span><span class="kt">int</span> <span class="n">number</span><span class="p">)</span> <span class="p">{</span>
	<span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">number</span><span class="p">;</span>
	<span class="n">string</span> <span class="n">s</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">num_pow</span> <span class="o">=</span> <span class="n">floor</span><span class="p">(</span><span class="n">log2</span><span class="p">(</span><span class="n">number</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="n">num_pow</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">pw</span> <span class="o">=</span> <span class="n">pow</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
		<span class="kt">int</span> <span class="n">digit</span> <span class="o">=</span> <span class="n">n</span> <span class="o">/</span> <span class="n">pw</span><span class="p">;</span>
		<span class="n">s</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="kt">char</span><span class="p">(</span><span class="n">digit</span><span class="p">));</span>
		<span class="n">n</span> <span class="o">-=</span> <span class="n">digit</span> <span class="o">*</span> <span class="n">pw</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">s</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Now, there’s a part I had to look up online: how to turn a single digit of number into an ASCII character. I quickly skimmed a guide and read about <code class="language-plaintext highlighter-rouge">static_cast&lt;char&gt;(x)</code> and <code class="language-plaintext highlighter-rouge">char(x)</code> which I thought was plausible.</p>

<p>And so I did that, only to get even more baffled because the string I was returning was <em>literally empty</em>. Or at least, I thought it was empty.</p>

<p><em>Don’t laugh at me.</em> Yeah, that conversion literally translates the integer into the corresponding ASCII character based on the code. So the 0’s and 1’s turn into <code class="language-plaintext highlighter-rouge">NUL</code>s and <code class="language-plaintext highlighter-rouge">SOH</code>s, respectively. But I was really in panic mode, so I didn’t notice that until like 30 minutes before the <em>whole</em> competition ends (we had 2 and a half hour). I should’ve added an offset to the resulting character (i.e <code class="language-plaintext highlighter-rouge">'0' + x</code>) to get the proper ASCII character.</p>

<p><em>Lesson learned: read an article with my eyes wide open even when I’m under pressure. Saves so much time and headache.</em></p>

<h1 id="my-misfortune-doesnt-end">My Misfortune Doesn’t End..</h1>
<p>After that one question, I looked for another good question. I grabbed a seemingly mind-boggling but short one. It’s usually better for me since I get easily overwhelmed with long questions.</p>

<p>This is roughly the question (with the story stripped away):</p>

<blockquote>
  <p>Given an <code class="language-plaintext highlighter-rouge">N</code> x <code class="language-plaintext highlighter-rouge">N</code> map matrix containing either 1 or 0 for each of the cells—where 1 represents land and 0 represents sea, determine the amount of islands that exist and the area of the largest island. Vertically and horizontally continuous 1’s are counted as one island.</p>

  <p>Example:</p>

  <p>N = 5</p>

  <p>1 0 0 0 1</p>

  <p>0 1 1 1 0</p>

  <p>0 1 1 0 0</p>

  <p>1 0 0 0 0</p>

  <p>0 0 0 0 0</p>

  <p>Answer: 4 islands and the largest area is 5.</p>
</blockquote>

<p>Now for this one, I felt enlightened as if I ascended to some miraculous place and was told the answer by The Divine themself. Jokes aside, this is actually dead simple, so okay. Just travel the map while taking note of which cells have been visited and the size of an island when we found a land. But let me tell you, recursion is one of my weakest areas of programming, yet my hypothetical recursive algorithm from my imagination actually worked after just one try of implementation..</p>

<p>..plus one or two tries of debugging. Which took around 15 minutes. And, guess what, the code was complete 13 minutes <em>after</em> the competition had ended.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">check_tile</span><span class="p">(</span><span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="o">&amp;</span><span class="n">map</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;&gt;</span> <span class="o">&amp;</span><span class="n">travelled_list</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// beyond the edges</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="n">w</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">y</span> <span class="o">&gt;</span> <span class="n">h</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

    <span class="c1">// travelled already</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">travelled_list</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="nb">true</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span>

    <span class="c1">// no island</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">map</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="mi">0</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">travelled_list</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="nb">true</span><span class="p">;</span>
		<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// there's an island</span>
		<span class="n">travelled_list</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="nb">true</span><span class="p">;</span>

        <span class="c1">// size of a particular island is the size of the tile itself (1) plus the size of the island nearby.</span>
		<span class="k">return</span> <span class="mi">1</span> <span class="o">+</span>
        <span class="n">check_tile</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">travelled_list</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="o">+</span>
        <span class="n">check_tile</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">travelled_list</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="o">+</span>
        <span class="n">check_tile</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">travelled_list</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="o">+</span>
        <span class="n">check_tile</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">travelled_list</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="p">}</span>

<span class="kt">void</span> <span class="n">solve</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">map</span><span class="p">)</span> <span class="p">{</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">h</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">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">w</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
			<span class="kt">int</span> <span class="n">area</span> <span class="o">=</span> <span class="n">check_tile</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">travelled_list</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">);</span>
			<span class="k">if</span> <span class="p">(</span><span class="n">area</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">islands</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
				<span class="n">largest</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">max</span><span class="p">(</span><span class="n">largest</span><span class="p">,</span> <span class="n">area</span><span class="p">);</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">largest</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span> <span class="o">&lt;&lt;</span> <span class="n">islands</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>..goodbye I am so not getting into the finals. Answering only two questions out of eight and only submitting one? That’s stupid!</p>

<p><strong>So in conclusion: humans can get pretty stupid under pressure. But also brilliant sometimes. You’re very lucky when the latter happens to you. The first one is rare. Experiencing both at the same time is even more rare but still sucks.</strong></p>

<h1 id="wait-youre-with-your-team-though">Wait, You’re With Your Team, Though?</h1>
<p>Admittedly, they’re not <em>that</em> good at programming. They barely helped with anything meaningful yesterday. I still appreciate their efforts, though. It was still fun anyways :P However, I did not really enjoy the teamwork (there’s nearly none anyways for yesterday<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> in particular). Why are good or even decent programmers so rare yet so common?</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>The competition was held in two days. During the first one they helped me, but I’m discussing the second day here. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Cikitta</name></author><category term="programming" /><category term="C" /><category term="C++" /><category term="competition" /><summary type="html"><![CDATA[Type casting gone wrong, during a competition!]]></summary></entry><entry><title type="html">I Changed My Mind. Wayland is (kinda) AMAZING!</title><link href="https://daringcuteseal.github.io/blog/i-changed-my-mind-wayland-is-amazing/" rel="alternate" type="text/html" title="I Changed My Mind. Wayland is (kinda) AMAZING!" /><published>2025-01-14T00:00:00+07:00</published><updated>2025-01-14T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/i-changed-my-mind-wayland-is-amazing</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/i-changed-my-mind-wayland-is-amazing/"><![CDATA[<p>Ever since the dawn of time, the X Windowing System has been the predominant graphical windowing system used amongst Linux users. This has changed recently as several Linux distributions with relatively high market share (such as <a href="https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/">Fedora</a>, <a href="https://www.omgubuntu.co.uk/2021/01/ubuntu-21-04-will-use-wayland-by-default">Ubuntu</a>, <a href="https://wiki.debian.org/Wayland#GNOME_.28supported_since_3.20.2B-.29">Debian</a>, etc.) started to make Wayland the default choice for users.</p>

<p>Okay, I wouldn’t say <em>that</em> “recently”. For instance, Fedora 25 was released way back in 2016<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and had already shipped GNOME with Wayland by default. The option to use X11 was still there though—up until the latest version (as of when I wrote this post), Fedora 41<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. In another case, Ubuntu shipped Wayland by default again with its 21.04 release after receiving negative feedback back when Canonical first poked with Wayland in 17.10<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. It’s now kinda obvious that Wayland has only veeery recently becoming <em>actually</em> usable.</p>

<p>My first ever Linux distribution was Ubuntu 20.04 LTS which came with X11 by default. Funny enough, exactly 4 years ago (as of when I wrote this post) marks that date. I never really used Wayland in any meaningful way since then—at least up until recently. Maybe I did try GNOME and KDE Plasma under Wayland a few times back then, but honestly, I’m never really a big fan of full-fledged, out-of-the-box desktop environments where Wayland happens to be a lot more mature that time, so I had to rely on X11 to prevent myself from getting headache.</p>

<h1 id="an-excuse-to-try-wayland">An Excuse to Try Wayland</h1>
<p>A while ago, I was handed an <a href="https://www.asus.com/laptops/for-home/vivobook/vivobook-flip-14-tp401/">ASUS VivoBook Flip 14</a>. It’s around 4 years old, but definitely still usable. I then started to use it as a school laptop because my main laptop is quite massive. I was unsure about which distro I should use and how to configure my desktop though, as I’m not a big fan of simply copying from my old setup. Plus, my old i3/Qtile setup was mainly keyboard-focused and is pain to navigate with touchscreen. I went with <a href="https://chimera-linux.org/">Chimera Linux</a> and GNOME for a fresh start, and it was surprisingly great for something that uses Wayland, musl, FreeBSD core utilities, and its own package repository. Truly impressive what desktop Linux has evolved into.</p>

<h1 id="first-use-of-gnome-under-wayland">First Use of GNOME Under Wayland</h1>
<p>At that point I was really enjoying modern GNOME (I had never used GNOME &gt;=40 for a long time). I’m really impressed with how it handles my lid switch pretty well—the automatic on-screen keyboard works perfectly. My touchscreen works in majority of apps and actually makes sense, i.e it’s not just emulating a mouse but actually behaves like how you expect in phones/tablets. My stylus pen (which, by the way, uses Microsoft’s Pen Protocol) also works out of the box which was veery surprising for me.</p>

<p>Whoops, no video or anything, though. I did not have the plan to write this post back then :P</p>

<h1 id="piecing-together-a-desktop-with-wayland">Piecing Together A Desktop with Wayland</h1>

<p>Unfortunately, I gotta admit, Chimera is simply too annoying for daily use. As something that I only use once or twice a week it is actually more than perfect, but they flat out refuse to package old software (like Qt 5) and therefore their dependents (like <a href="https://krita.org">Krita</a>). As time goes on, I started to use this ASUS laptop more and more often, and I then decided to hop into something more usable for now. I was a bit overwhelmed about the choices available at first, but I decided to just go with <a href="https://artixlinux.org">Artix Linux</a> since it’s what I daily drive on my other laptop.</p>

<p>Again, as I do not like out-of-the-box desktop environments because of its “learn to use” rather than “define how to use” approach, I had to make another confusing choice of which window manager I should use. My friend began experimenting with <a href="https://hyprland.org/">Hyprland</a> quite some time ago, and then I recommended it to my other friend <small>(somehow it was his first taste of Linux desktop, he’s a madman)</small>. After seeing that they both have 0 complaint I decided to try Hyprland for myself.</p>

<p>It wasn’t my first time with Hyprland, though. Quite some time ago, I tried <a href="https://cachyos.org/">CachyOS</a> and went with Hyprland as well (on my other laptop), but I stopped customizing it after I failed to configure plug-ins through <code class="language-plaintext highlighter-rouge">hyprpm</code>. It kept on complaining about unmet dependencies despite me having everything it mentioned already. After some digging of the source code, it turned out that <code class="language-plaintext highlighter-rouge">hyprpm</code> did not mention <code class="language-plaintext highlighter-rouge">pkgconf</code> as its dependency -_- But don’t worry, that’s fixed already as of now.</p>

<p>So then, after having everything ready, I started ricing my setup =D Not too big of a tweak, I only changed key-binds and customized my <a href="https://github.com/Alexays/Waybar">Waybar</a> status bar with relevant information. I also used <a href="https://github.com/outfoxxed/hy3">hy3</a> to match how i3 manages windows. After a while, my desktop is ready for use!</p>

<p><img src="/blog/image/grim-hyprland.png" alt="Hyprland rice" /></p>

<p>..well, almost. The problem is that, this is barely leveraging the fact that the laptop is a convertible. I still have touchscreen gestures and on-screen keyboard to take care of.</p>

<h2 id="gestures">Gestures</h2>

<p><a href="https://github.com/horriblename/hyprgrass">Hyprgrass</a> is an awesome Hyprland plug-in that provides you the ability to assign dispatchers to touch events. I have these:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hyprgrass-bind = , swipe:4:d, hy3:killactive
hyprgrass-bind = , swipe:4:u, fullscreen, 0
hyprgrass-bind = , swipe:4:r, hy3:movewindow, r, visible
hyprgrass-bind = , swipe:4:l, hy3:movewindow, l, visible
hyprgrass-bind = , swipe:3:d, exec, $menu

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

<p>I definitely do not plan on using my window manager solely with my fingers since that sounds hilarious (or not really, but still), so that’s more than enough for me.</p>

<p>Oh yeah, speaking of touchscreen, I used <a href="https://github.com/nwg-piotr/nwg-drawer">nwg-drawer</a> for my app/power menu (<code class="language-plaintext highlighter-rouge">$menu</code> above) since it’s the most touchscreen friendly standalone solution that I know of.</p>

<p>I can do these:</p>

<div style="text-align: center">
    <video loop="" controls="" width="500">
        <source src="/blog/image/hyprland-gestures.mp4" type="video/mp4" />
    </video>
    <p><i>Touchscreen Controls</i></p>
</div>

<p>(Ignore the keyboard for now, I will talk about that under the next section!)</p>

<h2 id="on-screen-keyboard">On-Screen Keyboard</h2>

<p>I found <a href="https://gitlab.gnome.org/World/Phosh/squeekboard">Squeekboard</a> which is originally written for Phosh, GNOME’s mobile environment. It actually worked out-of-the-box, except for the automatic triggers which I had to take care of myself.</p>

<p>Squeekboard follows the rule that it will show up when an input field is focused and the <code class="language-plaintext highlighter-rouge">screen-keyboard-enabled</code> has its value set to <code class="language-plaintext highlighter-rouge">true</code> under <code class="language-plaintext highlighter-rouge">org.gnome.desktop.a11y.applications</code> in the user’s dconf configuration schema. The first point is straightforward, but the second one we need to tweak a bit with <code class="language-plaintext highlighter-rouge">gsettings</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true
</code></pre></div></div>

<p>With that set and Squeekboard launched, an on-screen keyboard should appear under compatible apps (all GTK+ apps and several others).</p>

<p>Now, I need to set that <code class="language-plaintext highlighter-rouge">dconf</code> value to <code class="language-plaintext highlighter-rouge">false</code> when I’m not rotating it to use it like a tablet. This was automatic under <code class="language-plaintext highlighter-rouge">GNOME</code> but I’m not sure about Hyprland, so I just looked for sources of events.</p>

<p>Apparently, you can use <code class="language-plaintext highlighter-rouge">libinput</code> to listen to events emitted by the lid switch (you can use <code class="language-plaintext highlighter-rouge">libinput list-devices</code> to get the event device path):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$&gt; sudo libinput debug-events --device /dev/input/event20
-event20  DEVICE_ADDED                Intel HID switches                seat0 default group1  cap:S
 event20  SWITCH_TOGGLE               +0.000s	switch tablet-mode state 1
 event20  SWITCH_TOGGLE               +1.496s	switch tablet-mode state 0
</code></pre></div></div>

<p>Now with a little bit of shell script magic I can write a magic ““daemon”” that toggles the <code class="language-plaintext highlighter-rouge">dconf</code> configuration value:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="c"># Tablet mode daemon.</span>
<span class="nv">TMPFILE</span><span class="o">=</span><span class="s2">"/tmp/.squeektablet"</span>

<span class="k">if</span> <span class="o">[[</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$TMPFILE</span><span class="s2">"</span> <span class="o">]]</span>
<span class="k">then
	</span><span class="nb">echo</span> <span class="s2">"Daemon already running. Remove </span><span class="nv">$TMPFILE</span><span class="s2"> to proceed."</span>
	<span class="nb">exit </span>1
<span class="k">fi

</span>cleanup<span class="o">()</span> <span class="o">{</span>
	<span class="nb">rm</span> <span class="s2">"</span><span class="nv">$TMPFILE</span><span class="s2">"</span>
<span class="o">}</span>

<span class="nb">touch</span> <span class="s2">"</span><span class="nv">$TMPFILE</span><span class="s2">"</span>
<span class="nb">trap </span>cleanup SIGINT SIGABRT

<span class="nv">dev</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>libinput list-devices | <span class="nb">grep</span> <span class="s2">"HID switches"</span> <span class="nt">--context</span> 1 | <span class="nb">tail</span> <span class="nt">-n</span> 1 | <span class="nb">awk</span> <span class="nt">-F</span> <span class="s1">'           '</span> <span class="s1">'{print $2}'</span><span class="si">)</span><span class="s2">"</span>

<span class="c"># If this is being executed the first time then the user must have</span>
<span class="c"># triggered the tablet mode to on.</span>
<span class="c"># May also not be the case, but we don't want</span>
<span class="c"># inefficiencies.</span>
toggle-squeekboard <span class="s2">"tablet"</span>

<span class="c"># Sleep to make sure the device is already available</span>
<span class="nb">sleep </span>2s
libinput debug-events <span class="nt">--device</span> <span class="s2">"</span><span class="nv">$dev</span><span class="s2">"</span> | <span class="k">while </span><span class="nb">read </span>log
<span class="k">do
	</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$log</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> /tmp/squeekboardd_log
	<span class="k">if </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$log</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="s2">"state 1"</span> &amp;&gt;/dev/null
	<span class="k">then
		</span>toggle-squeekboard <span class="s2">"tablet"</span>
	<span class="k">elif </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$log</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="s2">"state 0"</span> &amp;&gt;/dev/null
	<span class="k">then
		</span>toggle-squeekboard <span class="s2">"disable"</span>
	<span class="k">fi
done</span>
</code></pre></div></div>

<p>Where the <code class="language-plaintext highlighter-rouge">toggle-squeekboard</code> just comes from:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="nv">status</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>gsettings get org.gnome.desktop.a11y.applications screen-keyboard-enabled<span class="si">)</span><span class="s2">"</span>

<span class="k">if</span> <span class="o">[[</span> <span class="nv">$# </span><span class="o">==</span> 0 <span class="o">]]</span>
<span class="k">then
	if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$status</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"true"</span> <span class="o">]]</span>
	<span class="k">then
		</span>gsettings <span class="nb">set </span>org.gnome.desktop.a11y.applications screen-keyboard-enabled <span class="nb">false
	</span><span class="k">else
		</span>gsettings <span class="nb">set </span>org.gnome.desktop.a11y.applications screen-keyboard-enabled <span class="nb">true
	</span><span class="k">fi
	</span><span class="nb">exit
</span><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">"tablet"</span> <span class="o">]]</span>
<span class="k">then
	</span>gsettings <span class="nb">set </span>org.gnome.desktop.a11y.applications screen-keyboard-enabled <span class="nb">true
</span><span class="k">else
	</span>gsettings <span class="nb">set </span>org.gnome.desktop.a11y.applications screen-keyboard-enabled <span class="nb">false
</span><span class="k">fi</span>
</code></pre></div></div>

<p>The toggling logic in the script, is uhm, weird, but it’s fine for me. Definitely customizable if you’re willing to copy my approach.</p>

<p>However, I found out that simply launching the daemon does not work well. This is because the input device is not registered before I <em>actually</em> rotate my lid by 360°, as you can see with this kernel message example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[   40.320906] intel-hid INT33D5:00: switch event received, enable switches supports
[   40.321033] input: Intel HID switches as /devices/platform/INT33D5:00/input/input28
</code></pre></div></div>

<p>and only then the input path would actually exist. This meant that I have to start the daemon <em>after</em> the input has been registered. Sounds like a udev rule moment, doesn’t it?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/udev/rules.d/30-tablet.rules:

ACTION=="add", SUBSYSTEM=="input", ATTRS{name}=="Intel HID switches", RUN+="/usr/bin/user-squeekboard"
</code></pre></div></div>

<p>and lastly, the <code class="language-plaintext highlighter-rouge">user-squeekboard</code> is a hacky script that switches to my user and executes the actual Squeekboard daemon (because otherwise it would get executed under the root user):</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">PATH</span><span class="o">=</span><span class="s2">"/usr/bin"</span> <span class="nv">XDG_RUNTIME_DIR</span><span class="o">=</span>/run/user/1000 su cikitta <span class="nt">-c</span> <span class="s1">'hyprctl -i 0 dispatch exec squeekboardd'</span> &amp;&gt; /tmp/.squeekboard_daemon__log
<span class="nb">exit</span> <span class="nv">$?</span>
</code></pre></div></div>

<p>All those sound really janky and dirty, <em>but at least it works</em> :) Maybe one day I will write a daemon that executes a user-defined command while listening to input device events, that’s going to be useful for standalone window managers/compositors.</p>

<h2 id="bonus-alternative-input-method">(Bonus) Alternative Input Method?</h2>

<p>I was trying to set up several input methods that use <a href="https://github.com/ibus/ibus">IBus</a> and I had set up all the needed environment variables under <code class="language-plaintext highlighter-rouge">/etc/environment</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/environment:

GTK_IM_MODULE=ibus
QT_IM_MODULE=ibus
XMODIFIERS=@im=ibus
</code></pre></div></div>

<p>However, I realized that it broke my Squeekboard instead. Spent hours of debugging just to found out that it was a few lines of variables that’s messing up my entire workflow! Ughh… I’m not really sure how to set IBus alongside it, let me know under the comments section if you do know how to! :)</p>

<h2 id="is-it-perfect">Is It Perfect?</h2>

<p>I haven’t encountered any major issues so far. But yeah, touchscreen support is still slightly whacky. For example, I can’t even properly double-click to enter a directory in Thunar :P But other than that I don’t think anything is hindering me from daily driving it.</p>

<h1 id="under-the-hood-look">Under The Hood Look</h1>

<h2 id="first-impressions-about-wayland">First Impressions about Wayland</h2>

<p>Wayland handles inputs reaaaalllly well<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>. I am especially impressed with the touchscreen, lid switch, and stylus handling (although I haven’t tried those in X11, so this isn’t really a comparison). I thought that I have to poke around with configuration files for exotic peripherals, like in X11. Yes, somehow it’s the 21st century and I still had to write X11 configurations (notably with my Huion graphics tablet)<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>.</p>

<p>The other thing I really like about Wayland is how it has protocols for specific features of a desktop. Or, honestly, Wayland is just a bunch of protocols anyways. For instance, think the <a href="https://wayland.app/protocols/virtual-keyboard-unstable-v1">virtual keyboard protocol</a>. It is highly specialized on that specific virtual keyboard feature. Then, compositors can adapt and implement it.</p>

<p>On the contrary, X11 is mostly focused on the displaying side of the job and not other desktop environment-supporting features. Some things are kinda hacked together in X11, creating a Frankenstein desktop that consists of programs that work on their own way talking to the display server.</p>

<p>And also, one last thing I’m really impressed about is the fact that I can actually share my screen with <a href="https://vencord.dev/">Vencord</a> under Hyprland with the helf of its XDG Portal. This is crazy cool.</p>

<h2 id="architecture-design">Architecture Design</h2>

<p>While it is true that Wayland is designed to replace X11<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup>, it really is a reinvention of the wheel (just see the <a href="https://wayland.freedesktop.org/architecture.html">high-level overview of the architecture</a>). As you probably already know, Wayland protocols removes the need for an extra display rendering middleman between applications and the kernel. Wayland allows applications to directly talk to its compositor (analogous to a window manager under X11), as opposed to X11 where applications need to talk to the display server and don’t directly communicate with the window manager. Wayland’s approach definitely increases performance and maintainability.</p>

<p>With this kind of architecture, each compositor would have to handle many, <em>many</em> things on its own: input, multi-monitors, etc. From the user’s perspective, this can cause slight annoyance as this means that they have to reconfigure every single compositor they attempt use for even the most basic settings like keyboard layout and input options. I wouldn’t say that it’s necessarily a major deal breaker, though. After all, it’s not like you switch compositor every month, right? <em>Riiight?</em> Having a centralized place to configure everything isn’t bad either—it’s sort of the kind of thing you expect from big, major operating systems.</p>

<h2 id="okay-whats-really-wrong-with-wayland-then">Okay, What’s REALLY Wrong With Wayland Then?</h2>

<p>In my opinion, there are really two reasons why Wayland is still a problem: lack of adoption and lack of agreement upon protocols and its details. The first one is certainly expected. With lack of support in several areas, Wayland became less usable, so some people are hesitant to use it, which then causes less adoption, and it loops. But more and more people are already doing the shift slowly! At one point it wouldn’t be a problem anymore. The last one, however, is really something worth looking at. As Wayland is still a big work in progress, it is still receiving a lot of updates and there has been a lot of disagreements throughout its development. <a href="https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/269">Here’s an instance of a protocol disagreement</a>. But all these really arose due to the large fragmentation in the Linux desktop, so it’s to be expected.</p>

<h1 id="final-thoughts">Final Thoughts</h1>
<p>Wayland is finally mature enough for daily use and it’s fantastic. Even though its architecture and attempt at replacing the crusty X11 is still a really hot debate, I can see it having a big potential in becoming the next display architecture for modern Linux desktop as it contains native protocols (still work in progress) that support many features a modern desktop would have. I would totally recommend trying out Wayland.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>https://fedoramagazine.org/whats-new-fedora-25-workstation/ <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>https://linuxiac.com/x11-is-no-longer-part-of-fedora-workstation-41/ <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>https://www.omgubuntu.co.uk/2021/01/ubuntu-21-04-will-use-wayland-by-default <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Or, rather, Hyprland. But with the help of Wayland so that gets the credit as well. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>My Huion is not detected as a Wacom tablet directly which it’s compatible with so I have to force it to use the wacom driver. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>https://wayland.freedesktop.org/faq.html#heading_toc_j_4 <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Cikitta</name></author><category term="hardware" /><category term="linux" /><category term="wayland" /><summary type="html"><![CDATA[Ever since the dawn of time, the X Windowing System has been the predominant graphical windowing system used amongst Linux users. This has changed recently as several Linux distributions with relatively high market share (such as Fedora, Ubuntu, Debian, etc.) started to make Wayland the default choice for users.]]></summary></entry><entry><title type="html">Happy New Year 2025!</title><link href="https://daringcuteseal.github.io/blog/happy-new-year-2025/" rel="alternate" type="text/html" title="Happy New Year 2025!" /><published>2025-01-02T00:00:00+07:00</published><updated>2025-01-02T00:00:00+07:00</updated><id>https://daringcuteseal.github.io/blog/happy-new-year-2025</id><content type="html" xml:base="https://daringcuteseal.github.io/blog/happy-new-year-2025/"><![CDATA[<p>Ahh, another year has passed. Welcome, 2025!</p>

<p>Last year was crazy—I did some remarkable things. Kinda. There are always things to be disappointed about—as how humans always been, but I’m proud of myself nevertheless! I’m really thankful for everyone who has been part of my journey last year and I’m looking forward to write even more stories together! :) I wish to find new experiences in my life, meet new people, learn a bunch of new things, try to stay persistent and also be a lil more organized X)</p>

<p>My biggest goal for this year is probably to try to get my draft comic, Threaded!, published. It’s a lil comic that revolves around low level Linux. I think you’d like it :)</p>

<p>So, as usual, good luck everyone!!</p>

<h1 id="bonus-bits">Bonus Bits</h1>

<h2 id="my-holiday">My Holiday</h2>

<p>Nothing special, really. I went camping again, as usual (I often do that, it’s almost an annual thing). The view there is nice:</p>

<p><img src="/blog/image/mountain-2024.jpg" alt="Mountain view" /></p>

<h2 id="looking-back-at-2023">Looking back at 2023</h2>
<p>I have this whole time capsule thing where I dump random interesting things I may find interesting in the future. I’m supposed to open it at the very end of a year. This is the first time I’m able to open it, so I was very excited.</p>

<p>The problem is, I wasn’t at home during the last few days of 2024 and I forgot to bring my time capsule box, so I had to open it in 2025 which is a bit disappointing. But eh, it is what it is.</p>

<p><img src="/blog/image/2023-box.jpg" alt="2023 time capsule box" /></p>

<p>The box is hilarious to say the least. I actually do not remember writing some deep messages back then. Past me gave some really supportive and constructive messages it seems like. Past me also asked about some things in the future (well, now it’s now). My favorite questions from that are probably, “Do you like high school?” (oh heck yeah) and, “Did you get ranked in middle school?” (also kind of yes but not first). I also talked about my past concerns there, and it’s kinda awesome seeing that the things I once worried had come to fruition and I can finally answer all those uncertainties.</p>

<p>My favorite thing from the box is probably this.. let’s say a “handheld beach” ^o^ I don’t even remember where I collected the sand, corals, and shells from, but I think it predicted something in 2024 that’s sea-related (spoiler alert: <a href="/blog/excelsior/">my school event</a>), ahaha.</p>

<p><img src="/blog/image/2023-box-2.jpeg" alt="2023 time capsule box" /></p>

<h2 id="neo">Neo!</h2>

<p><a href="/blog/the-origin-of-neo">Neo</a> turned a year old yesterday! (by the time I wrote this blog =D)</p>]]></content><author><name>Cikitta</name></author><category term="personal" /><summary type="html"><![CDATA[Ahh, another year has passed. Welcome, 2025!]]></summary></entry></feed>