<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ezntek's site</title><link>http://ezntek.com/</link><description>Recent content on ezntek's site</description><generator>Hugo -- 0.147.2</generator><language>en</language><lastBuildDate>Thu, 27 Nov 2025 15:32:09 +0800</lastBuildDate><atom:link href="http://ezntek.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Adrenaline, the Clutch Factor and Math</title><link>http://ezntek.com/personal/adrenaline-the-clutch-factor-and-math-20231127t1532/</link><pubDate>Thu, 27 Nov 2025 15:32:09 +0800</pubDate><guid>http://ezntek.com/personal/adrenaline-the-clutch-factor-and-math-20231127t1532/</guid><description>What the hell was that?!</description><content:encoded><![CDATA[<p>My inactivity on my website and my laziness towards actually making it look better is justified by the fact that it&rsquo;s exam season, and I do the IB diploma program. It is very rigorous, they torture you with the exam, the exam board itself is&hellip;very questionable, and I have to do 6 very heavy weight subjects, all with coursework.</p>
<h2 id="math">Math</h2>
<p>I picked the hardest math that the IBDP (or just the IB) has to offer, Higher Level Math: Analysis and Approaches, or just HL Math AA, or HLAA. Analysis and Approaches means that it has more pure math content and less of the weird statistical graph theory geometry stuff that Applications and Interpretations has to offer. It&rsquo;s also because universities <em>love</em> HLAA students, especially for Computer Science.</p>
<p>In the final exams, you are given 3 papers, paper 1 being non-calculator, paper 2 being calculator, and paper 3 being the super abstract wack paper where they throw you some random concept in the field of mathematics, a short text describing how it works, and then expect you to answer a bunch of questions on it.</p>
<p>Today was the day of my math exam. It&rsquo;s just a semester exam, the first one in fact. It tests you on 6 months&rsquo; worth of content, and it goes to your IB DP predicted grade (at least here in my school), which is sent to universities as a part of the application process. We are only given a 1 hour long paper 1 and a 1 hour long paper 2 in the same exam room, where you put your calculator on the floor face-down for the first part.</p>
<p>I prepared a hell of a lot for it, 12 past papers (6 paper 1&rsquo;s and 6 paper 2&rsquo;s), and two fat calculus worksheets (300something marks in total), and some textbook questions. I felt pretty confident until I left home. I remembered that the exam started at <em>12:45pm</em>, so I left home at around 12:00 pm, so that I could have some time to breathe at school.</p>
<h2 id="the-lift-of-realization">The lift of realization</h2>
<p>I went in the lift, going to put my exam timetable in the favorites folder of my phone&rsquo;s photo gallery. I look at the time stated where the exam would begin, just because I wanted to make sure that it did start at 12:45&hellip;</p>
<p><em><strong>whoops, it actually starts at 11:45am</strong></em>.</p>
<p>Don&rsquo;t ask me how I didn&rsquo;t check. It was 12:03 in the lift, and my mind started racing before my heart could catch up. Oh shit, oh shit, I would be late.</p>
<p>As I cycled to school, my mind raced with all that could happen in the exam. Would I get the full 1 hour for paper 1? could I sit the exam alone, or would I have to join everyone else? The cycle was insane, I never cycle on my bike&rsquo;s max gear, but I did, for the whole journey. I live quite close to the school, so I just had to churn out all the power that I could through my legs into my bike. I got stopped by 2 red lights, the last of which was near my school, where my mom called saying that the secretaries wanted me to go to the 6th floor (the floor of my school where the high school office was. Whoops. I was cooked. That was 12:09.</p>
<p>I reached the office floor, where I was greeted by the timetabler. He was the one in charge of escorting students who are late into exam halls, and I bumped into him just as he was walking into a lift. He told me to come.</p>
<h2 id="the-exam-hall">The exam hall</h2>
<p>Pin drop silence. That lift ride was hilarious, he asked me how long I&rsquo;ve been in the school for, and kind of very slightly ridiculed me for mistaking 11:45 for 12:45. IGCSE Exams did in fact start at 12:45, but oh well. At the door, he told me to just go sit in the far corner by the window, and just start writing.</p>
<p>I ducked and speedwalked to my seat the moment I entered the hall. It must have been after 12:15, because I remembered that I had 40 minutes to sit the paper. Apparently, they actually started the exam 10 minutes late, at 12:55. I don&rsquo;t know if they were waiting for me. If they were, thanks to them, because that meant I would have only missed 20 minutes worth of time (I did) and not 30 minutes.</p>
<p>I almost threw my calculator on the ground and I just started writing my paper 1. I was severely dehydrated and both my legs and butt hurted from that cycle. The invigilator put my bottle on a table out of arm&rsquo;s reach. Thanks.</p>
<p>The questions started flying off the pages, it was as if the whole world was in slo-mo as my 160bpm+ heart supplied the side of my brain panicking to calm it down and the other half who actually had to sit the paper simultaneously. The equations started levitating off the pages; I couldn&rsquo;t think of anything but the paper. I didn&rsquo;t even lay my eyes off the pages. For one of the questions I got confused as I got a really really weird fraction, whereas the question asked for an integer, but I just pressed on.</p>
<p>I ended up finishing paper 1, the entire thing, and fixed 2 questions where I knew I went wrong. Phew, that was surely crazy.</p>
<h2 id="the-rest-of-the-exam">The rest of the exam</h2>
<p>I still had to sit paper 2. The adrenaline still pumped through my veins, I couldn&rsquo;t even comprehend where I was, physically and mentally. I finally had access to my water bottle since it was during the time between the two papers, where we had to grab paper 2 and our calculators off the floor. I just did the paper, I was in a state of hyperfocus, albeit calmer than when I sat the first paper. The questions seemed far easier than I thought; the teacher who wrote this paper previously wrote one of our unit tests; that paper still gave nightmares to my friends for lets say&hellip; a long time. This time around though, it wasn&rsquo;t that bad.</p>
<p>I drank water as usual, and my shit-slow TI-84 Plus CE equipped with a 48MHz 8/16/24 bit eZ80 CPU was my bottleneck. It still carried the rest of the exam though, I applied my usual optimizations (like turning off detect asymptotes off) and things went by far faster.</p>
<h2 id="how">How??</h2>
<p>I attribute this to the <a href="https://en.wikipedia.org/wiki/Clutch_%28sports%29">clutch factor</a>. Speedrunners and athletes use this term to describe when someone performs better than expected under extreme pressure. I was under pressure; I knew that this whole fiasco would bring my grade down a good chunk, but I had to push through, and I did. Adrenaline saved me, thank you very much adrenaline, I owe you a cookie.</p>
<h2 id="after">After</h2>
<p>After the test, I rushed out of the exam hall. I was still disoriented. As of writing this article (right now it is around 3:57pm), I still have no idea as to what really happened. It really felt like I was in spectator mode. After the exam, I eventually did find my friends and talk to them; they thought that paper 1 was far easier than the warzone that paper 2 was. I personally think that P1 was harder, just because I was in a very disoriented state. Paper 2 was so much better, because I actually had the whole hour to sit it.</p>
<p>Moral of the story, please don&rsquo;t be late to your exams. But if you are, use your quick thinking. The clutch factor was what carried me, it will carry you too.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Creating an IGCSE Pseudocode Interpreter (part 2)</title><link>http://ezntek.com/posts/creating-an-igcse-pseudocode-interpreter-pt-2-20250913t1349/</link><pubDate>Sat, 13 Sep 2025 13:49:15 +0800</pubDate><guid>http://ezntek.com/posts/creating-an-igcse-pseudocode-interpreter-pt-2-20250913t1349/</guid><description>I&amp;#39;m done. Almost a year later!</description><content:encoded><![CDATA[<h2 id="prelude">Prelude</h2>
<p>Check out the first part of this series, of course. This article won&rsquo;t feature too much on the interpreter design, but how to effectively use it to your own benefit.</p>
<p>TL;DR, I tried to write an IGCSE pseudocode interpreter a while ago, and I finished! Find it <a href="https://github.com/ezntek/beancode">here</a> at <a href="https://github.com/ezntek/beancode">https://github.com/ezntek/beancode</a>. Or, install it directly with <code>pip install beancode --break-system-packages</code> <em>(this will not break your system packages, because beancode has no system dependencies, and will not break anything despite the scary flag)</em></p>
<p>Jump <a href="/posts/creating-an-igcse-pseudocode-interpreter-pt-2-20250913t1349/#extension-features">here</a> if you only want to learn about the special and extra features of the interpreter and functional quirks.</p>
<h1 id="intro">Intro</h1>
<p><img alt="A demo of beancode running on two ThinkPads next to each other, an X230 and a T440p" loading="lazy" src="/img/beancode/irl_demo.jpg"></p>
<p>I published my <a href="/posts/creating-an-igcse-pseudocode-interpreter-pt-1-20240918t1045/">first article</a> almost a year ago; on the 18th of September, 2024. It is now the 13th September, 2025. What&rsquo;s happened since then?</p>
<p>Well, a lot of school stuff. I was in grade 10 at the time, with quite a bit of spare time. However, I just didn&rsquo;t get around to doing much of anything!</p>
<p>Originally I wanted to write a pseudocode <em>compiler</em> in C, but I got stuck at the parser. If you look at my really old code somewhere in <a href="https://github.com/ezntek/beanwarehq">https://github.com/ezntek/beanwarehq</a>, I actually tried to write a bottom-up parser. Young ezntek really did discover another parsing technique just then, however implemented horribly and barely functional.</p>
<p>I then tried to write one in Zig. In one afternoon I ported the lexer over, but that didn&rsquo;t end up going anywhere. Sometime during march of this year, I just had enough waiting; I had a bunch of other projects and a lot of schoolwork to do (I am, after all, a leader of the computer programming club at my school, and the author of a bunch of IGCSE CS revision resources), but I really just wanted to get <em>something</em> done.</p>
<p>I tried to follow Crafting Interpreters&hellip;in Python. I knew the language, it was slow, horrible, but great for doing hacky stuff like the thing I envisioned (just a single top-down recursive-descent parser), and that ended up going&hellip;really well.</p>
<p>So what do we have now?</p>
<p><img alt="A demo of Raylib running on beancode version 0.3.3" loading="lazy" src="/img/beancode/raylib_demo.png"></p>
<p>&hellip;what? By the way, yes, it is running on my interpreter. It&rsquo;s called <em>beancode</em>. Grab it <a href="https://github.com/ezntek/beancode">here</a>; it&rsquo;s called <code>beancode</code>. Here&rsquo;s the source code for the demo:</p>
<pre tabindex="0"><code>INCLUDE_FFI &#34;beanray&#34;

CALL InitWindow(800, 600, &#34;hello, world!&#34;)
CALL SetTargetFPS(60)

WHILE NOT WindowShouldClose() DO
    CALL BeginDrawing

    CALL ClearBackground(RAYWHITE)
    CALL DrawFPS(20, 20)

    CALL DrawRectangleRec({ 100, 100, 200, 150 }, RED)
    CALL DrawRectangleRec({ 175, 150, 225, 150 }, GREEN)
    CALL DrawRectangleRec({ 125, 200, 175, 150 }, BLUE)

    CALL DrawText(&#34;Hello from Pseudocode!!!&#34;, 200, 400, 40, BLACK)
    CALL DrawText(&#34;Pseudocode to Python Raylib Bindings by ezntek&#34;, 200, 450, 20, BLACK)

    CALL EndDrawing
ENDWHILE

CALL CloseWindow
</code></pre><h2 id="architecture">Architecture</h2>
<p>This program is actually very simple. Now, I will elaborate on the architecture in a later post, and post design-related shotcomings. This is the overall structure:</p>
<ol>
<li>Lexer</li>
<li>Parser</li>
<li>Interpreter</li>
</ol>
<p>For those who know, this is a <em>tree-walking</em> interpreter. Basically, the source file is parsed into a tree-like data strcuture which represents the source file&rsquo;s layout and structure. Then, the structure is traversed by Python to evaluate expression adn to <em>do stuff</em>.</p>
<h3 id="lexer">Lexer</h3>
<p>The lexer is quite stupid. You can find it in <code>beancode/lexer.py</code>. It is so stupid and scuffed that it doesn&rsquo;t even support streaming tokens properly (although I could hack it in, in theory). It also does really weird stuff. There&rsquo;s logic in the lexer that actually does some &ldquo;parsing&rdquo; because this was a hasty AF fix.
<code>-4</code> is treated as a single literal <code>-4</code> and not <code>minus, literal(4)</code>. Wow! I forgot why I did this honestly&hellip;</p>
<p>Otherwise, it works&hellip;barely. It is also really bad at detecting unterminated string literals. At some point it just screams and gives up on reporting proper position data.</p>
<h3 id="parser">Parser</h3>
<p>The parser is equally as cursed, if not more than the parser. It is recursive-descent and does not use any precedence tables (what the hell are those). Position data reporting is cursed as hell, but whatever. It cannot detect many parsing errors at once, it will stop whenever it sees an error. It also doesn&rsquo;t use the consume and expect pattern that I later learned about, its more like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-py" data-lang="py"><span style="display:flex;"><span>token <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>consume()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> token<span style="color:#f92672">.</span>kind <span style="color:#f92672">!=</span> <span style="color:#f92672">&lt;</span>wanted kind<span style="color:#f92672">&gt;</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">raise</span> BCError(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;expected &lt;token kind&gt;, but got </span><span style="color:#e6db74">{</span>token<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>, token<span style="color:#f92672">.</span>pos)
</span></span></code></pre></div><p>You will see this pattern literally everywhere :&gt;</p>
<h3 id="interpreter">Interpreter</h3>
<p>This is super scuffed as well. Intrinsics (i.e. &ldquo;Library Routines&rdquo; as Cambridge calls them) are implemented literally as Python functions, dispatched with a massive switch-case. The number of arguments are literally just stored in a global hash map. I could go on for hours regarding the cursedness of this thing and how many hacks this thing has implemented. It does literally no optimizations, and global and local variables behave VERY WEIRDLY.</p>
<h3 id="the-repl">The REPL</h3>
<p>The REPL works on a bunch of input statements. Since each component so far was a reusable class that I could call intermediate methods on, I just stitched them together to make a REPL. I abused Python&rsquo;s <code>readline</code> module a lot for basic REPL features like shell history, and moving around in the current input buffer.</p>
<ul>
<li>Each line is fed into the lexer, and then parser, and run in a global interpreter context.</li>
<li>If EOF is reached during parsing or lexing, the parser throws an exception with the <code>eof</code> flag set to <code>True</code>. The REPL then catches the exception and enters continuation mode.
<ul>
<li>The usual prompt is <code>&gt;&gt; </code>, but for continuation mode, it is <code>.. </code>. The current input buffer before the continuation is saved in a <code>StringIO</code>, and every time a new line is entered, the parser tries to analyze the <em>entire input so far</em>, and then keeps going if EOF is reached. Otherwise, it returns back to the parent main REPL runner.</li>
</ul>
</li>
<li>Position reporting actually works! (Surprisingly, errors work fine for the most part).</li>
<li>Some errors report as having position <code>line 0, column 0</code>, which is a really annoying feature I left in because I didn&rsquo;t want to null the position field in the error class. <em>I&rsquo;ll fix it if it&rsquo;s actually that bad</em>.</li>
</ul>
<h1 id="extension-features">Extension Features</h1>
<p>I will from now assume that you know how to write Pseudocode.</p>
<ul>
<li>You can use lowercase keywords! Begone the days of screaming your code, you can just write lowercase words like <code>for</code>, <code>next</code>, <code>if</code>, <code>while</code>, etc.</li>
<li>You can include other beancode files with <code>INCLUDE &quot;filename&quot;</code>, which is the file name you want to include, with the extension.
<ul>
<li>Mark a symbol (variable declaration, constant, procedure or function) with <code>EXPORT</code> to dump it into the current scope. There are no namespaces!</li>
<li>You can even include FFI modules with <code>INCLUDE_FFI</code>. They have to be the bundled modules, though.
<ul>
<li><code>beanray</code> is an incomplete set of raylib bindings that supports some <em>basic</em> examples.</li>
<li><code>beanstd</code> is a very small std library with some basic functinoality.</li>
<li><code>demo_ffimod</code> is just a demo FFI module as a proof-of-concept.</li>
</ul>
</li>
</ul>
</li>
<li>Mark custom scopes with <code>SCOPE</code> and <code>ENDSCOPE</code>. You can also export from them.</li>
<li>There are a bunch of added intrinsics. Check them out in the README on the <a href="https://github.com/ezntek/beancode">GitHub repo</a>.</li>
<li>You can type cast with <code>TYPE(expr)</code></li>
<li>You can declare and assign on the same line, if you really need to.
<ul>
<li>You don&rsquo;t even have to specify the type:</li>
</ul>
<pre tabindex="0"><code>DECLARE Num: INTEGER &lt;- 5
DECLARE Num &lt;- 4
</code></pre></li>
<li>I added a bunch of neat <em>type inference</em> features.
<ul>
<li><code>Num &lt;- 4</code> is equal to <code>DECLARE Num: INTEGER &lt;- 4</code>.</li>
<li>If you have a declared variable, it will be able to guess the type too.</li>
<li>You can <code>INPUT</code> into an undeclared variable, and it will &ldquo;insert a declaration&rdquo; and then store your input into the new variable.</li>
</ul>
</li>
<li>Array literals!
<ul>
<li><code>Arr &lt;- {1, 2, 3, 4, 5}</code> is an <code>ARRAY[1:5] OF INTEGER</code></li>
</ul>
</li>
<li>Matrix literals!
<ul>
<li><code>Mat &lt;- {{1, 2}, {4, 5}, {7, 6}}</code> is an <code>ARRAY[1:3,1:2]</code> OF INTEGER</li>
</ul>
</li>
<li>There is some introspection/reflection. You can get the type of any variable with <code>TYPE(value)</code> or <code>TYPEOF(value)</code> (case-insensitive)</li>
</ul>
<h2 id="quirks">Quirks</h2>
<ul>
<li>You cannot have multiple lines in a <code>CASE OF</code> statement. You have to put your code into a procedure.</li>
<li>File I/O does not work at all.</li>
<li>Some errors report as <code>unused expression</code> or <code>invalid statement or expression</code>.</li>
<li>Reported errors will look a little different on Windows (ASCII only chars), thanks to its horrible unicode support. F*** you, Micros*ft!</li>
</ul>
<h3 id="scope">Scope</h3>
<p><em><strong>All variables are global</strong></em>. They will infect all sub-scopes. For example:</p>
<pre tabindex="0"><code>DECLARE A: INTEGER
// In scope: A
SCOPE
    DECLARE B: INTEGER
    // In scope: A, B
    SCOPE
        DECLARE C: INTEGER
        // In scope: A, B, C
    ENDSCOPE
ENDSCOPE
</code></pre><p>When a new block is created, all the pointers to outside variables are copied to the current sub-interpreter&rsquo;s variable pool. This works the exact same for procedures. You cannot have truly local variables, and <em>you cannot shadow a declaration of an outside variable with a local variable.</em></p>
<h2 id="the-repl-1">The REPL</h2>
<p>The REPL is a <em>truly life-changing innovation</em>. Simply launch beancode without any arguments and you will be dropped in a REPL, and you can start typing code. Expressions will be printed.</p>
<p>The REPL also has special features which lets you see the state of the interpreter. You can talk to the REPL and not the interpreter with dot commands. Here are the very important ones.</p>
<ul>
<li><code>.var</code> gets information regarding an <em>existing variable</em>. It prints its name, type, and value.</li>
<li><code>.vars</code> prints information regarding <em>all variables</em>.</li>
<li><code>.func</code> gets information regarding <em>existing functions</em> <em><strong>or procedures</strong></em>.</li>
<li><code>.funcs</code> prints information regarding <em>all functions and procedures</em>.</li>
<li>Delete a variable if you need to with <code>.delete [name]</code>. (Version <code>0.3.4</code> and up)</li>
<li>Or, reset the entire interpreter&rsquo;s state with <code>.reset</code>.</li>
</ul>
<p>History is saved to <code>~/.beancode_history</code>.</p>
<h1 id="performance">Performance</h1>
<p>Performance is at the mercy of the Python implementation. From my GitHub page:</p>
<blockquote>
<p>It&rsquo;s really bad. However, PyPy makes it a lot better. Here&rsquo;s some data for the PrimeTorture benchmark in the examples, ran on an i7-14700KF with 32GB RAM on Arch Linux:</p>
<table>
  <thead>
      <tr>
          <th>Language</th>
          <th>Time Taken (s)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>beancode (CPython 3.13.5)</td>
          <td>148</td>
      </tr>
      <tr>
          <td>beancode (PyPy3 7.3.20)</td>
          <td>11</td>
      </tr>
      <tr>
          <td>beancode (CPython Nuitka)</td>
          <td>185</td>
      </tr>
      <tr>
          <td>Python (CPython 3.13.5)</td>
          <td>0.88</td>
      </tr>
      <tr>
          <td>Python (PyPy3)</td>
          <td>0.19</td>
      </tr>
      <tr>
          <td>C (gcc 15.2.1)</td>
          <td>0.1</td>
      </tr>
  </tbody>
</table></blockquote>
<h1 id="am-i-done">Am I done?</h1>
<p>NO! Of course not. This is just my foray into compiler engineering. I will not be making any more of these IGCSE Pseudocode interpreters; I will continue to maintain this one till the day I die. <strong>Please send bug reports!</strong> It will help all of us!</p>
<p>I will be working on a proper compiler that lowers an AST down to assembly, and a proper interpreter with a bytecode VM.</p>
<p>Oh also, my Computer Science teacher said that he&rsquo;ll use this to teach Pseudocode to his grade 10 class, and by extension, the cohort. Yay! I&rsquo;m not useless anymore, I guess. <em>Stay tuned for a part 3 where I actually roast my code&hellip;if I find the motivation to write it.</em></p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Eason's Life Chronicles Pt. 1: Disabled Pigeon</title><link>http://ezntek.com/personal/easons-life-chronicles-part-1-20250728t2007/</link><pubDate>Mon, 28 Jul 2025 20:07:33 +0800</pubDate><guid>http://ezntek.com/personal/easons-life-chronicles-part-1-20250728t2007/</guid><description>Poor pigeon...</description><content:encoded><![CDATA[<img src="/img/dogspecimen.jpg" width="50%" />
<p><em>(This dog has committed some naughty crimes!)</em></p>
<p>I aspire for this series of articles to be a lighthearted insight into the more interesting/noteworthy parts of my life&hellip;beginning with part 1, the tale of an injured pigeon.</p>
<p><em><strong>TW: injured animals</strong></em></p>
<p>I was walking my dog this evening (around 6:45). It&rsquo;s a small Bichon Frise, but I suspect it might be a bolognese. Anyway, when I was walking it, It came acrosss a pigeon walking around the residential areas near my house.</p>
<p>It seemed to walk slower than usual, but I didn&rsquo;t think much of it. My dog started chasing it, like usual, like any dog would when they see a bird (usually it likes to chase chickens, but a pigeon works). However, when it charged at the pigeon in an attempt to kill it, it actually managed to rip two feathers off its tail, which is very strange considering that pigeons are usually agile enough in these situations.</p>
<p>Yet despite how hard the pigeon tried to flap its wings after it got ambushed, it didn&rsquo;t manage to fly away. It helplessly and silently hopped and sprinted<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> for its life while my dog continued to chase it. I let my dog chase it, because if I didn&rsquo;t let it, it would go back to licking the floor and licking light poles with dog piss on it<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>It eventually tried to take a bite of the disabled pigeon once again, where the pigeon then stumbled (literally) into a very low corner with a staircase on top of it. There was a little bookcase in the corner, where it hid. My dog yanked its leash and went to corner, effectively putting the poor pigeon in a standoff.</p>
<p>The pigeon was glued to the wall; it stared out with all the fear a pigeon could express in its eyes, barely moving and ever so slightly shaking. I didn&rsquo;t let my dog attack it further.</p>
<p>I grabbed my dog and held it up as I went over to reach the pigeon to grab it. Pigeons&rsquo; wings are actually quite loose, they aren&rsquo;t really balls of cute fur. I gently picked it up to examine the damage and to investigate why it couldn&rsquo;t fly.</p>
<p><a href="/img/injuredpigeon1.jpg">Click here for image 1</a></p>
<p>On the surface it didn&rsquo;t look too injured, its wings were still loose and it was clearly able to flap. I petted the pigeon gently, but it didn&rsquo;t coo. It was just warm, still trembling slightly. I could hear its heartbeat quite loudly, which I didn&rsquo;t expect.</p>
<p>But when I turned it around&hellip;</p>
<p><a href="/img/injuredpigeon2.jpg">Click here for image 2</a></p>
<p>It was injured very badly. The skin and feathers below its heart and vital organs were all ripped off, somehow. It had smelly goo from a dumpster nearby stuck onto it too, it didn&rsquo;t smell very good. I could see its poor little heart beating at max speed, literally holding onto dear life.</p>
<p>I petted it a little bit more, and attempted to gently release it. However, it scrambled away from my hand before I could, and it fell to the ground whilst flapping its wings; it clearly couldn&rsquo;t fly.</p>
<p>I really didn&rsquo;t know what to do, I didn&rsquo;t consider calling a helpline or anything, this was just a street pigeon, and there are many, many more injured ones like this. It is however a shame I didn&rsquo;t constrain my dog enough while it chased it. It was just 2 feathers, however. Peace be upon this poor pigeon.</p>
<p>On the way home I realized my hands felt absolutely repulsively disgusting; that trash goo was legit.</p>
<p>My dog will not be allowed to chase any more pigeons after this.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This is actually the fastest I&rsquo;ve seen a pigeon walk.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>It&rsquo;s a dog, don&rsquo;t question it.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Memories</title><link>http://ezntek.com/personal/memories-20250711t2129/</link><pubDate>Fri, 11 Jul 2025 21:30:04 +0800</pubDate><guid>http://ezntek.com/personal/memories-20250711t2129/</guid><description>bittersweetness.</description><content:encoded><![CDATA[<p><img alt="The windowsill" loading="lazy" src="/img/windowsill.png"></p>
<p>I have been living in Singapore for 10 years by now. Eight long years ago, I moved into the house I am currently in.</p>
<p>I was assigned the second bedroom in my house, counting from the beginning of a long corridor that began in the living room. I had no desk, just a piano and a 90cm bed. I also had a large, long windowsill that spanned the width of my room; a large concrete slab with nothing on it.</p>
<p>I decided to put my computer there, the empty area between my piano and bed, and used it like a makeshift desk; I brought a little stool for me to sit on. My mac was an 11.6 inch, early 2014 MacBook Air, with an i7-4650U, 8gb of RAM, and a 512gb SSD. I still have it.</p>
<p>Months later, at the age of around 10 (my memory is very faint), I was gifted a custom PC that I was supposed to build&hellip;but I didn&rsquo;t know how to build one at all. I asked my dad to do it.</p>
<p>I put it on the windowsill, and that&rsquo;s where my computing journey really started to kick off. It was the windowsill where I did all the virtualization I did to run modern and ancient OSes. It was the windowsill where I had set up my first file servers (SMB, AFP when that was more common, and even FTP), and my first http server. It was the windowsill where I set up my first linux server, had all the emotional breakdowns I had as a young asian boy, and the windowsill where I taught myself simultaneous equations in 5th grade.</p>
<p>And as I grew up, the windowsill grew up with me. It wore off, the things on it got dusty, and I eventually grew out of it; I needed a better desk setup.</p>
<p>And years later&hellip;</p>
<p>I am now one of the best programmers<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> in my whole high school. I am now fluent in all the programming languages I&rsquo;ve dreamt of learning; C, Python, Rust<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, and exen x86_64 linux assembly; name whatever. I have written real software; many pieces of real software. I&rsquo;ve even made it onto hacker news, which made my dad pretty proud<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>And just 30 minutes ago, that same Eason who, many years ago, sat at that windowsill for hours exploring the world of computing; going from Eason Tech Reviews to ezntek, visited said room. There is now a new bed inside for my father, who&rsquo;s out of the country. I sat on the bed, and looked at the windowsill, where there were some books, some other assorted items, a fan and some plants.</p>
<p>And I realized. That was the exact same windowsill from all those years ago, where I explored computing as an elementary school computer enthusiast; where I ran my old YouTube channel. I am the same person as the Eason from all those years ago, still with that same passion for computing, just grown up a little, with a far more mature mind than the typical 16 year old<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. I am clearly changed, I am now ezntek.</p>
<p>But that windowsill will only be accessible to me for the next 2 years. In 2027, I graduate; I leave Singapore. In a mere two years, I will not be able to see the birthplace of the new me ever again. A slab of concrete with so many memories, like how I&rsquo;d used to put my MacBook underneath my bed next to run it as a file server, or where I miserably failed at learning Python.</p>
<p>I attribute all my success and my new passion to that windowsill, that isolated corner away from all the commotion in my family. It proved to me what I was really good at. And yet it will only be a faint memory, seen through just a few pictures taken on my iPhone SE in gold, one that I&rsquo;ll only tell my kids, because they will never be able to see it, and I&rsquo;ll never see it again very soon. Needless to say, I will miss my windowsill very much.</p>
<p>Cherish your time, cherish your memories; the things that you love will eventually become one too. Just a mere thought that will fade until your eventual demise.</p>
<p>Fortunately, I have an adulthood ahead of me. Unfortunately, I have an adulthood ahead of me. Sometimes I wish I could live as a teenager with free will a little longer, in this house a little longer, because I will miss this silly little apartment, and my silly little windowsill.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I still hope that someone secretly better than me shows up, but unfortunately, noone has. I am THE computer scientist in the high school. I am well known. Despite my contributions and work, I still do not like to think of myself as the best.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Tried to learn it in the seventh grade in mid 2022, that didn&rsquo;t go well.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>psst! check out my <a href="https://ezntek.com/posts/librebooting-the-thinkpad-t480-20241207t0933/">libreboot</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>I do not also like to claim I am wiser than everyone else. I have been told this my numerous people. Even judging by how the typical 16 year old kid in my high school behaves (similar to American High School students, exceptions of course exist but are not a major departure), this is a fair argument.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>A Review of the Sony WF-C510 Earbuds</title><link>http://ezntek.com/posts/a-review-of-the-sony-wf-c510-earbuds-20250524t1919/</link><pubDate>Sat, 24 May 2025 19:19:32 +0800</pubDate><guid>http://ezntek.com/posts/a-review-of-the-sony-wf-c510-earbuds-20250524t1919/</guid><description>Meh.</description><content:encoded><![CDATA[<h2 id="so">So&hellip;</h2>
<p><img alt="The earbuds" loading="lazy" src="/img/earbuds.jpg"></p>
<p>These earbuds are kinda meh. TL;DR: the mids are strong, the high end is muddy, the case feels kinda cheap but are actually decent for the price.</p>
<h2 id="the-good">The good</h2>
<ul>
<li>These are cheap. Despite how the label says S$79, these actually only costed S$65, which are actually a steal.</li>
<li>These are Sony earbuds, so you do get the Sony software and support, with all the EQ magic they have.</li>
<li>The noise cancellation is <strong>insanely good</strong>. Even without active noise cancellation, the ear tips themselves do absolute magic on any surrounding noise, whether if its wind from a fan, the air conditioner, or whatever else. Having used AirPods Pro gen 1&rsquo;s previously, I must say that these are 95-98% the strength, without all the head pain from the noise ANC generates.</li>
<li>The eartips fit very tightly within the ears, they do not fall out easily.</li>
<li>The range is great.</li>
<li>The mids are great. If you listen to genres like <a href="https://chillsynth.com">ChillSynth</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> these are perfect.</li>
<li>The &lsquo;ambient sound mode&rsquo; is incredible (the sound passthrough feature), it really sounds as if you are not wearing earbuds at all and even thhe little details like the volume your earbuds play the ambient sound back at matches the volume of your actual surrounding environment. These things are unfazed by wind.</li>
</ul>
<h2 id="the-bad">The bad</h2>
<ul>
<li>The case feels cheap.</li>
<li>The earbuds also feels cheap.</li>
<li>There is no ANC, which may annoy some.</li>
<li>The case does not actually have any magnets, making opening and closing feel much cheaper.</li>
</ul>
<h2 id="the-ugly">The ugly</h2>
<ul>
<li>The trebles are <strong>kinda muddy</strong>, if you have ever used better headphones you will know what I&rsquo;m talking about. Say goodbye to vocal clarity, they will be slightly muddy. The EQ only somewhat fixes it. In normal use, it isn&rsquo;t all that bad.</li>
<li>The drivers are only 6mm. Coming from my Pixel Buds A-series&rsquo;, it is a massive, massive downgrade.</li>
<li>The sound isn&rsquo;t particularly &ldquo;room filling&rdquo; nor &ldquo;full&rdquo;, it&rsquo;s just sound.</li>
</ul>
<h2 id="verdict">Verdict</h2>
<p>This was just a quick review, I&rsquo;m not in the mood of writing anything exciting. <strong>Avoid these</strong> if you have money, I tried to not burn my wallet this time around but man if I just splurged a bit more, would I have gotten much better sound. If these are the only budget option available, these are not bad, but coming from better audio devices, these aren&rsquo;t the best.</p>
<p>These are 90-95% as good as AirPods Pro 1&rsquo;s for EDM (complextro/electro house/bass house/future bass/future bass), Hardstyle sounds fine on these (not painful enough), and ChillSynth sounds pretty damn good (no it sounds <em>awesome</em>). I would still rather my Pixel Buds A-series, but oh well.</p>
<p>I also took them outside today (May 25), outdoor performance was excellent. The fit (again) is absolutely incredible, and sound quality was not sacrificed at all.</p>
<p>If you are looking for similarly priced earbuds in asia, look to <a href="https://sudio.com"><strong>sudio</strong></a>, I&rsquo;ve heard great things about them, and products similarly priced to these sonies (At least here) have at least bigger drivers, ANC, and a longer battery life.</p>
<p>I would describe these as <strong>utilitarian</strong>. Not too much flair, no BS, just-works earbuds that deliver good enough sound quality to make the typical bloke happy.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>shoutout to <a href="https://github.com/1seco">guyfrom</a> beacuse he really likes this genre and introduced chillsynth to me. Chillsynth is not lofi.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>WhatsApp is Bad, Move to Signal</title><link>http://ezntek.com/posts/whatsapp-is-bad-move-to-signal-20250514t1714/</link><pubDate>Wed, 14 May 2025 17:15:08 +0800</pubDate><guid>http://ezntek.com/posts/whatsapp-is-bad-move-to-signal-20250514t1714/</guid><description>And it took me just that long to realize.</description><content:encoded><![CDATA[<p>WhatsApp is bad. WhatsApp is evil, and they do not deserve a spot in anybody&rsquo;s lives. They hate their users.</p>
<p>Note that this article <em>will contain swear words. If you can&rsquo;t handle them, look away :)</em></p>
<h2 id="tldr">TL;DR</h2>
<p>WhatsApp is evil. Meta is evil. They steal your data, they have control over you. This is just a case study; for a change, use Signal.</p>
<h2 id="backstory">Backstory.</h2>
<p>Yesterday morning (13th May, around 9am GMT+8) I tried to create a new community on WhatsApp, on my desktop, for an up-and-coming mathematical society at my school.</p>
<p>This is completely fair. Nothing weird. Nothing suspicious. I create the community. No problem. But when I try to add an existing group chat, a feature given by WhatsApp, it says that there was <em>some mysterious error, and that I could try again</em>.</p>
<p>So I did. I tried again. And I got kicked back to the login screen on my computer. I check my phone, and see this:</p>
<img src="/img/reported_for_spam.png" width="40%"/>
<p><strong>WhatsApp suspended my account for doing absolutely nothing.</strong></p>
<h2 id="the-rabbit-hole">The rabbit hole</h2>
<p>Nothing got better after that. I requested for help on the form they had provided. It said that it would take upwards of 24 hours, but only after around 8 hours was it restored.</p>
<p>In the meantime, I could not communicate with anybody. Nobody used Signal. Some of my friends do not use Instagram<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, therefore the only way I could communicate with them was through&hellip;SMS. Good-ol&rsquo; SMS saved the day, I was able to notify my mom and dad about the issue.</p>
<p>It was really baffling. I didn&rsquo;t even do anything, what was WhatsApp going to say? What, I talked too much shit about them?</p>
<img src="/img/oopsie_woopsie_we_did_a_fuckie_wuckie.png" width="40%">
<p>After a while, my account was restored. <em>Apparently, they just fucked up! And that was good enough of an excuse to keep using their shitty bots to save costs, or something</em>.</p>
<p>Prominently standing on the homescreen was a button. Verify account.</p>
<p>It kicked me back to the login screen, the typical login screen, where I was told to enter my phone number and go through the typical verification steps. Okay. Nothing seemed off&hellip;until&hellip;</p>
<img src="/img/AAAAAAAAAAAAAAA.png" width="40%" /> 
<p>Apparently, I was using a custom ROM&hellip;or maybe a hacked version of WhatsApp&hellip;or had a rooted phone. <em><strong>I HAD NONE OF THOSE THINGS</strong></em>! It&rsquo;s baffling how horrible this proprietary piece of shitware is. I was on stock Android 16 Beta, on my Pixel 8a, without any software mods, with a locked bootloader.</p>
<h3 id="e-mails-to-support">E-mails to support</h3>
<p>This is the part that infuriates me the absolute most.</p>
<p>I went on WhatsApp&rsquo;s help page, in the app, accessible on the top right. I typed up my message, elaborating that I actually was NOT using a custom ROM or any modified software.</p>
<blockquote>
<p>After my account was restored after it was falsely flagged as spam, I am unable to log back in I am not using a custom ROM, and I have tried both the official and play store builds of WhatsApp. What steps should I take to logging back in?</p></blockquote>
<p>I sent it off, hoping I would get a reply within a few hours.</p>
<blockquote>
<p>Hi,</p>
<p>Your attempt to register your phone number failed because you appear to be using an <a href="https://faq.whatsapp.com/1217634902127718/">unofficial version of the WhatsApp app</a>. We don’t support unofficial apps because they put your privacy and security at risk. They also put your data and device at the risk of malware.</p>
<p>Custom ROMs and rooted phones also aren&rsquo;t supported by WhatsApp, learn more in <a href="https://faq.whatsapp.com/649203676836357/">this article</a>.</p>
<p><strong>What to do next</strong></p>
<ol>
<li>Save your chat history.</li>
<li>Uninstall the unofficial app.</li>
<li>Download <a href="https://www.whatsapp.com/download/">WhatsApp</a> or the <a href="https://business.whatsapp.com/">WhatsApp Business app</a> from official app stores or our website.</li>
<li><a href="https://faq.whatsapp.com/684051319521343/">Register</a> your phone number.</li>
</ol>
<p>If you see an error that the app isn’t available in your country, visit <a href="https://www.whatsapp.com/android">this page</a> to download WhatsApp.</p>
<p><strong>Need more help?</strong></p>
<ul>
<li>Try registering on a different phone.</li>
<li>Upgrade the <a href="https://faq.whatsapp.com/1150261202542208/">operating system</a> on your phone.</li>
<li>Troubleshoot why you <a href="https://faq.whatsapp.com/474624014728741/">can’t download or update WhatsApp</a>.</li>
</ul></blockquote>
<p>That&rsquo;s the reply I got. At first I thought,</p>
<blockquote>
<p>Damn. Why is this guy on the support side so insanely illiterate?</p></blockquote>
<p>Because they would have seemed very illiterate. I literally stated that I am not using a custom ROM, and whatnot. But no, this stupid person clearly did not read that I am not using one.</p>
<p>I sent a reply, like any sane person would, hoping that a person would answer.</p>
<blockquote>
<p>Hi,</p>
<p>I am definitely using the official WhatsApp App, I have downloaded it from the play store. I am not using a custom ROM. This is an unmodified, unrooted, Google Pixel 8a on Android 15 beta. The APK on your website does not work. My phone is running the latest update.</p>
<p>I believe my phone number may be falsely detected as a toll-free number. When I try to enter my phone number on the mobile app, it spaces it out as XXX XXX XX__, and not XXXX XXXX, as per Singapore standards. However, it is not a toll-free number, I purchased this number from my carrier.</p>
<p>May I please have more assistance? It would be much appreciated. This issue occurred from after my account was falsely flagged for spam. I am trying to re-register my phone number at this stage.</p>
<p>Sincerely,
Eason Qin</p></blockquote>
<p>I got,</p>
<blockquote>
<p>Hi,</p>
<p><strong>Please contact us in our app</strong></p>
<p>This enables us to collect information that’s necessary to understand and resolve your issue. <a href="https://faq.whatsapp.com/854037192262196">Learn more</a>.</p>
<p><a href="https://wa.me/support">Contact us</a></p>
<p>If you can’t contact us from the app, you can send us your question through our <a href="https://www.whatsapp.com/contact">support form</a> or browse our <a href="https://faq.whatsapp.com">Help Center</a>.</p>
<p>Thanks for your understanding and cooperation.</p></blockquote>
<p>Mind you, no data had to be collected. All data was present, I had sent it in the same E-mail thread, and any <em>sane human</em> would be able to open the file, and read it.</p>
<p>I got the first reply. <em><strong>FOUR TIMES</strong></em>. In a row. Yes. I was convinced by the third time that it was an LLM, it clearly was; on the contact form on WhatsApp&rsquo;s official website, it said that it <em>might</em> use Meta AI to answer my requests:</p>
<blockquote>
<p>By continuing, you allow WhatsApp to review technical information about your account to help answer your question. Messages from WhatsApp Support may be generated by AI using a secure technology from Meta. Your personal messages and calls remain end-to-end encrypted.</p></blockquote>
<p>Which it clearly did.</p>
<h2 id="the-crux-of-the-issue">The crux of the issue</h2>
<p>is that Meta is so damn <em>lazy</em> and <em>incompetent</em> that they cannot pull shit together to hire employees to respond to these E-mails. My situation clearly is unique. I am not using a custom ROM or anything, but I have been told that I am, and that meant that <em>my chats weren&rsquo;t secure anymore</em>, not like they really are in the first place. They can decrypt them anytime<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>In many of my requests I had included &ldquo;I would like to speak to a human about this matter&rdquo;, but to no avail. Instead, the same LLM, maybe even an Expert System<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, would answer my messages, with the same response, accusing me of doing something I don&rsquo;t do.</p>
<p>I tried every fix on the internet. On YouTube, on random tech websites; I even had to resort to random Indian/Indian-sounding tech guys who are known to post inaccurate content 50% of the time<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, to no avail. And <em><strong>there are no manuals. No piece of documentation exists on this specific case of this issue, on an app that over 3 billion users use, per month!<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></strong></em>. I am a programmer, I am <em>used to reading manuals)</em>, because they actually help. But instead, all I get are stupid YouTube videos, stupid articles that were written by a chatbot, or stupid LLM E-mails.</p>
<p>I tried to contact them over Twitter<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> (now known as X), and Instagram:</p>
<div style="display: grid; grid-gap: 0.5em">
    <img src="/img/instarequest.png" style="grid-column: 1; grid-row: 1"/>
    <img src="/img/twitterrequest.png" style="grid-column: 2; grid-row: 1;"/>
</div>
<p>But to no avail.</p>
<p>And look at the same thread I tweeted on! Verified influencers flood the top:</p>
<p><img alt="famous people" loading="lazy" src="/img/famouspeoplediarrhea.png"></p>
<p>and commoners like us, requesting for help, sit right below. No matter how hard Elon M*sk tries to hide it, we can see it.</p>
<p><img alt="people pleading" loading="lazy" src="/img/peoplepleading1.png">
<img alt="people pleading again" loading="lazy" src="/img/peoplepleading2.png"></p>
<p>There are more instances; I can&rsquo;t get it to fit on one screenshot.</p>
<p>I also got no response on Instagram.</p>
<h2 id="the-harsh-reality">The harsh reality.</h2>
<p><strong>All WhatsApp cares for is money, publicity, and users to milk data from.</strong> We are not users. <em><strong>We are useds</strong></em>. We do not in fact use the software.</p>
<p>Think about it, we are not given any instructions to recover our accounts. We cannot talk to the company. We have no transparency as to how the operate. <strong>We are not given the source code of the app;</strong> if we had the damn source code, maybe I would have been able to read it. And maybe I would have been able to do all the debugging work for them.</p>
<p>But instead, WhatsApp controls us. They can mess up by banning innocent users, and not talking about it. They can <strong>force people to plead, disrupt lives, and communications</strong>. What happens if somebody was discussing an important business deal on their accounts, an important school event, or anything of the likes?</p>
<p>I hate to bring him up due to his status of possibly liking children, but <strong>the wise man Richard Stallman once said in his <a href="https://www.youtube.com/watch?v=Ag1AKIl_2GM">TEDx speech</a> that having freedom over your software, i.e. you controlling it, is a human right</strong>. It indeed is, we should not be used like this by these companies, we should take control; our data is ours, the program deserves to be ours; nobody should be able to take it away from us.</p>
<p>I don&rsquo;t want to rant about Free Software all that much, free as in freedom, not free beer. It is software that you own; you have access to the source code, you can study it, you can change it, you can reshare it, and you can use it however you want, how software is supposed to be.</p>
<h3 id="dissecting-the-whatsapp-tos-terms-of-service">Dissecting the WhatsApp ToS (Terms of Service)</h3>
<p>It can be found at the following web link: <a href="https://www.whatsapp.com/legal/terms-of-service?lang=en">https://www.whatsapp.com/legal/terms-of-service?lang=en</a>, I may call it a EULA (you-lah), or end-user license agreement, too.</p>
<p>Here are some interesting things that it says:</p>
<ul>
<li>&ldquo;We collect device and connection-specific information when you install, access, or use our Services.&rdquo; This means that they can collect any information they want about your device, whenever the hell they feel like it. What phone you use, your operating system, your public IP, and even installed apps and such.</li>
<li>&ldquo;As part of the Facebook Companies, WhatsApp receives information from, and shares information with, the other Facebook Companies.&rdquo; This means that WhatsApp can share the data they collect to you to any other branch of facebook they want, whenever the hell they feel like it.</li>
<li>&ldquo;We may modify, suspend, or terminate your access to or use of our Services&hellip;&rdquo; This means that they can do whatever the hell they want to your account, like suspending it for unjust reasons.</li>
</ul>
<p>Basically, you don&rsquo;t even own your own account. How lovely! It&rsquo;s almost like you actually want to use it.</p>
<h3 id="other-privacy-concerns">Other privacy concerns</h3>
<p>WhatsApp also steals and sells your data whenever they feel like it. Of course, nobody will tell you this. Search engines have already been ce,nsored. <a href="https://medium.com/illumination/how-whatsapp-secretly-collects-and-shares-your-data-26b643e45928">This article is the tip of the iceberg</a>, you can access it on <a href="https://removepaywalls.com">https://removepaywalls.com</a> if you don&rsquo;t have a medium account.</p>
<p>No source will tell you the truth, that they could technically decrypt your chats and leak them. There is a reason that criminals don&rsquo;t actually use WhatsApp, or journalists; because they know their data will be compromised and that they can be tracked. I can&rsquo;t be assed to find 50 articles for you on this. However, a good starting point for your own discussion is that, <strong>if it&rsquo;s so private, why can&rsquo;t we prove it with cold, hard evidence, that being the code that WhatsApp runs on?</strong></p>
<h2 id="the-solution">The solution</h2>
<p>Use <a href="https://signal.org">Signal</a>.</p>
<p><img alt="Signal Logo" loading="lazy" src="/img/signallogo.png"></p>
<p>This is the real deal, a genuinely private messenger. Private Android variants like <a href="https://calyxos.org">CalyxOS</a> use it, and there&rsquo;s a reason. It was created <strong>by the original creator of WhatsApp,</strong> who left the company after it was bought by Facebook. Its encryption algorithms are identical to WhatsApp&rsquo;s, which is a great starting point; they don&rsquo;t actually collect your message metadata, and have better privacy practices.</p>
<p>Features are almost identical to WhatsApp&rsquo;s. The apps are equally as polished. Communication is equally as good. <strong>There is no reason why you shouldn&rsquo;t be using it</strong>. It is a truly private messenger that can&rsquo;t just randomly terminate your account for no reason.</p>
<p>I&rsquo;ve used Signal for years, and other privacy-respecting services. <em><strong>I don&rsquo;t even use Google Drive for serious purposes, nor Google Workspace</strong></em>, I self-host <a href="https://nextcloud.com">Nextcloud</a> and use <a href="https://libreoffice.org">LibreOffice</a> for basic document processing on Linux. If you want to know what I really use, you can comment below.</p>
<p>The truth is that <strong>I am not a criminal, in fact, I don&rsquo;t have anything major to hide</strong>. It&rsquo;s just that <strong>I do not want to feel watched and controlled; this is the perfect case study.</strong> I am exhausted. I sat a bunch of exams, and as of writing this article, I am both infuriated and sleepy. I got my account back, after <strong>FACTORY RESETTING MY PHONE 4 TIMES AND LOCKING THE BOOTLOADER</strong>, which is something I should not have had to do, but I had to, thanks to Meta.</p>
<p>Don&rsquo;t accuse me of being a cybercriminal, a hacker, or a terrorist; I&rsquo;ve been called suspicious since Grade 7 and I&rsquo;m tired of hearing it. <strong>Do you want your neighbors to know your exact activity and position, if you&rsquo;re wearing clothes, showering, or sleeping naked?</strong> They don&rsquo;t know all my secrets; but if Meta can point a telescope at my digital life that they promise is private (it&rsquo;s not), and my neighbors can&rsquo;t, this makes no sense. It is eerie knowing someone can detect your messaging patterns and what you say, at any time, with this data being sold to actual cybercriminals.</p>
<p><em><strong>Liberate yourselves. Start with a better chat app. Use Signal</strong></em>.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I use it because I want friends. Everyone else uses it, and with it, I can actually reach a certain demographic of people.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Don&rsquo;t worry, this will be addressed in later sections.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>igcse comp sci lore™&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>This is not racist. Many of them post fake videos demonstrating fixes that ruin your phone, or make you turn off your phone&rsquo;s security for no reason. This applies for many IT topics, half of them actually post real fixes, the other half scam you. I am not going to put up a citation, you can search &ldquo;you need the official whatsapp to log in fix&rdquo; on DuckDuckGo; all the initial responses are from sketchy YouTubers who don&rsquo;t actually help. If you think I&rsquo;m still being racist, you can stop reading.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://techcrunch.com/2025/05/01/whatsapp-now-has-more-than-3-billion-users/">https://techcrunch.com/2025/05/01/whatsapp-now-has-more-than-3-billion-users/</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>I made an account just for this!&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>IGCSE Computer Science (Theory) Revision Guide (Full)</title><link>http://ezntek.com/posts/igcse-computer-science-theory-revision-guide-20250509t2256/</link><pubDate>Fri, 09 May 2025 22:56:03 +0800</pubDate><guid>http://ezntek.com/posts/igcse-computer-science-theory-revision-guide-20250509t2256/</guid><description>A little bit of motivation later...</description><content:encoded><![CDATA[<h2 id="finally">Finally??</h2>
<p>Yes, this is a little bit delayed, but 3 days before the final computer science paper 1, I&rsquo;m releasing this guide to help you all study.</p>
<p><img alt="cover page" loading="lazy" src="/img/finalcsrgcover.png"></p>
<h2 id="what-is-this">What is this?</h2>
<p>This is essentially a bank of notes for IGCSE computer science, the theory part. I will chop up my CSRG G2 semester 1 guide, which has extensive coverage of logic gates and programming, and release it independently, later. These notes are very, very detailed, so it is also a guide to some extent.</p>
<p>For more info, read the Information section at the front of the guide.</p>
<p><em><strong>PLEASE CONTACT ME IF YOU SPOT ANY MISTAKES OR TYPOS!</strong></em></p>
<h2 id="download">DOWNLOAD</h2>
<ul>
<li><a href="/doc/CSRG_Full_Rev5.pdf">Revision 5 (May 13, 2025)</a></li>
</ul>
<p>Alternatively, <a href="https://github.com/ezntek/CSRG">view the source code on Github</a>.</p>
<h2 id="changelog">CHANGELOG</h2>
<ul>
<li><strong>Revision 2</strong>: Added Cloud Storage, fix spelling mistakes, improve wording across chapter 2.</li>
<li><strong>Revision 3</strong>: Add IPv6, fix minor errors in the intro, improve wording in chapter 5.</li>
<li><strong>Revision 4</strong>: Fix RAM access time, add ISBN-13/Modulo 11 syllabus omission notices, add SSL in chapter 5.</li>
<li><strong>Revision 5</strong>: Fix spelling errors, formatting errors, add note about possible inclusion of errors.</li>
</ul>
<h2 id="errata">ERRATA</h2>
<p>This details the <strong>factual, content-based errors</strong> that I may have made.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<ul>
<li>(fixed in <strong>revision 4</strong>): SRAM/DRAM access time is in <strong>nanoseconds, not milliseconds</strong> (pages 38, 39)</li>
<li>(fixed in <strong>revision 5</strong>): DRAM consumes less power (page 39) and not SRAM, the program counter holds the <strong>next instruction</strong> (page 50)</li>
</ul>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Idea suggested by Ved Jaggi. Added to the credits list.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>IGCSE Incomplete Computer Science Revision Guide (CSRG)</title><link>http://ezntek.com/posts/igcse-incomplete-computer-science-revision-guide-csrg-20250506t1130/</link><pubDate>Tue, 06 May 2025 11:30:05 +0800</pubDate><guid>http://ezntek.com/posts/igcse-incomplete-computer-science-revision-guide-csrg-20250506t1130/</guid><description>Sorry, I am NOT motivated to finish this bs.</description><content:encoded><![CDATA[<h2 id="ezntek-you-broke-a-promise">ezntek, you broke a promise?</h2>
<p>Yes! i said that would finish a full syllabus revision guide for all the chapters and for all the content covered, but I was not motivated to finish it all, below is a link to an incomplete coverless version with a bunch of inconsistencies, mid explanations and typos, but it surely is better than nothing.</p>
<p>Find it <a href="https://ezntek.com/doc/csrg_incomplete.pdf">here</a></p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Stressful Times, with a Math Society on The Side</title><link>http://ezntek.com/posts/stressful-times-20250505t2133/</link><pubDate>Mon, 05 May 2025 21:33:50 +0803</pubDate><guid>http://ezntek.com/posts/stressful-times-20250505t2133/</guid><description>As painful times come to an end, an exciting future awaits.</description><content:encoded><![CDATA[<h2 id="what">What?</h2>
<p>IGCSEs. Cambridge-assessed final examinations. As of writing this article, it is the 5th of May, the night before my IGCSE Coordinated Science (0654) paper 4<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. They are quite stressful, as you can tell by the title, but it really isn&rsquo;t all that much.</p>
<p>IGCSEs are simple! All you need to do is memorize the markschemes, do past papers and you&rsquo;re good. Even if you aren&rsquo;t comfortable with 5 entire topics, you can watch a few YouTube videos and cram all of it the night before! Such an easy life, no?</p>
<h2 id="some-spice">Some Spice</h2>
<p>There&rsquo;s a reason why I haven&rsquo;t updated the site in a while. Trust me, I am working on great things to post and to show off! Writing this article is already a testament to my procrastination; I have 5 (minor) topics to revise, and yet I am working on this article. I&rsquo;m an intelligent human being!</p>
<p>If I didn&rsquo;t just have to deal with IGCSEs, I would be cruising. Minimal studying, minimal effort; I&rsquo;d still get A*&rsquo;s anyway (as had happened with mocks). However, I am a programmer, I have external responsibilities and projects I want to work on, and a shiny new <em>mathematical society at my school!</em> My math teacher, who just came to our school after being dragged out of her previous school where she had legendary, math jesus<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>-like status, wanted to start a Math Society.</p>
<p>I told her that day, that our school had never seen a mathematical society ever before. The look on her face was shocking, but also amusing. I laughed on the inside, not at her, but at our school; our school&rsquo;s demographic consists of just medicine, law, bio/chem, and humanities students. Nobody here liked math enough to start a math society! And nor did anybody have the idea. But she brought it up, and I was slightly enlightened. <em>Being a computer scientist cosplaying as a mathematician</em>, it was hard to announce to this teacher so passionate about math that she started a massive math journal publication with many, many, many contributions (I don&rsquo;t have an exact page count, but the document was massive), the same person who openly said that she would marry math if it were a person<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, and dedicated her effort to serving the people and the math society at her old school; that I was not a true mathematician.</p>
<p>But I am ready to become one. (i still wanna do compiler engineering :P)</p>
<p>More details are to come; this article just announces my existence, but if you are in OFS (Singapore) and are in High School, look out for some amazing things next year. I just did a bunch of planning work, it was fun, but I really have to study for science now.</p>
<p>Bye, and good luck to all Cambridge IGCSE students sitting the May/June 2025 exam series!</p>
<h2 id="footnotes">Footnotes</h2>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>A long-answer paper, regarded as the hardest one and the longest.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>No offense intended.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I think she likes math.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>IGCSE Computer Science: Arrays and Iteration Workbook</title><link>http://ezntek.com/posts/igcse-computer-science-arrays-and-iteration-workbook-20250208t2108/</link><pubDate>Sat, 08 Feb 2025 21:08:06 +0800</pubDate><guid>http://ezntek.com/posts/igcse-computer-science-arrays-and-iteration-workbook-20250208t2108/</guid><description>&lt;h2 id="download">DOWNLOAD!&lt;/h2>
&lt;p>&lt;a href="https://ezntek.com/doc/igcse_cs_arrays_iteration_workbook.pdf">here&lt;/a>&lt;/p>
&lt;h2 id="long-time-no-see">Long time no see!&lt;/h2>
&lt;p>Sorry for not posting so much. My workloads been very very heavy; working on projects like this one. IGCSE CS students, look out for the comprehensive whole-syllabus comp sci revision guide soon! I will release it before mocks.&lt;/p>
&lt;h2 id="what-is-this-workbook">What is this workbook?&lt;/h2>
&lt;p>I wrote it because my comp sci teacher let me volunteer to teach. I happily took the offer due to everybody&amp;rsquo;s cluelessness (they still cannot assign variables). So far I taught 2d arrays in front of the class, and technically this is an assignment only I was set; to make a worksheet on iteration and arrays. It is now a workbook as people need explanations.&lt;/p></description><content:encoded><![CDATA[<h2 id="download">DOWNLOAD!</h2>
<p><a href="https://ezntek.com/doc/igcse_cs_arrays_iteration_workbook.pdf">here</a></p>
<h2 id="long-time-no-see">Long time no see!</h2>
<p>Sorry for not posting so much. My workloads been very very heavy; working on projects like this one. IGCSE CS students, look out for the comprehensive whole-syllabus comp sci revision guide soon! I will release it before mocks.</p>
<h2 id="what-is-this-workbook">What is this workbook?</h2>
<p>I wrote it because my comp sci teacher let me volunteer to teach. I happily took the offer due to everybody&rsquo;s cluelessness (they still cannot assign variables). So far I taught 2d arrays in front of the class, and technically this is an assignment only I was set; to make a worksheet on iteration and arrays. It is now a workbook as people need explanations.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Nothing's Sweet About 16</title><link>http://ezntek.com/personal/nothings-sweet-about-16-20250109t0730/</link><pubDate>Thu, 09 Jan 2025 07:30:15 +0800</pubDate><guid>http://ezntek.com/personal/nothings-sweet-about-16-20250109t0730/</guid><description>One step closer to death.</description><content:encoded><![CDATA[<h2 id="note">NOTE</h2>
<p>Am I a tech nerd who blogs about technical stuff? YES!</p>
<p>But do I have a life? yes. <em><strong>This post is not technical</strong></em>, and I will only continue writing this format when I feel like it.</p>
<p>Scroll down if you wish.</p>
<h2 id="a-long-time-ago">A long time ago&hellip;</h2>
<p>When I was 11 or 12 or so (back in 6th grade), I had one of the most amazing math teachers ever. She was great at her job, but most importantly, every time the class and her had an interaction everybody would burst out in happy laughter; there was never one negative moment in that room.</p>
<p>We talked about birthdays and such, and the term &ldquo;Sweet 16&rdquo; popped up somewhere. While we were laughing hysterically from something mentoned previously, I nicely tried to ask (I was very awkward at the time and was not as social as now), I asked what it was. Turns out that&hellip;it&rsquo;s just a special birthday&hellip;for some reason?</p>
<p>People get insane things (rich people, of course), like cars (why the hell), phones (you only need one when <a href="https://lineageos.org">LineageOS</a> stops supporting it, M*cBook Pro Max Ultra Extras or whatever that costs 6 grand&hellip;whatever. And I was told that everyone throws massive parties on that day, as if they were turning 18 or something.</p>
<p>To this day I still do not understand the appeal of calling this birthday so special. It doesn&rsquo;t grant you any more rights in the eyes of most western/western-influenced/sane governments (you must wait till 18 for an actually important birthday), and I also do not understand why it is such a major event for so many people. Lavish parties, crazy celebrations, but what for?</p>
<p>What&rsquo;s so sweet about 16?</p>
<h2 id="achievements">Achievements</h2>
<p>I&rsquo;d like to reminisce over the things I was able to pull off in the past year.</p>
<ol>
<li>Completely unrelated, but my age is now <code>0b1000</code>, it no longer fits in a 4 bit register.</li>
<li>I won second place in a hackathon, doing web development (something I never do) in a toolkit i have never touched previously (Vue).</li>
<li>I got very good at C, and convinced a good amount of my friends to either look into C or to learn C. I have made many projects in the language, one is in the works, namely <a href="https://github.com/ezntek/askthing">askthing</a>.</li>
<li>I taught C at school in the form of a club. 2 of my &ldquo;students&rdquo; are now programming in C for fun and enjoying it.</li>
<li>I also taught Python at school, 19 students fully understood my course and I am now able to teach them more complex topics this school semester :)</li>
<li>I wrote a grand total of 3 IGCSE comp sci guides (find them under the revision tab of my site), the Pseudocode Reference, the G1 Semester 2 guide I wrote last may, and the G2 semester 1 guide that I wrote this year, along with an abridged version with G1.</li>
<li>My ThinkPad addiction began in february. I bought way too many ThinkPads. Ended up corebooting all of them, learning BIOS programming was very fun.</li>
<li><a href="https://libreboot.org/contrib.html#eason-aka-ezntek">whatever this is.</a></li>
<li>I learned and actually got pretty good at the <a href="https://ziglang.org">zig programming language</a>. Although my skills have gotten rusty :(</li>
<li>I can confidently write a lexer for literally anything, although I still have to learn how to do recursive-descent parsing properly. Stay tuned.</li>
<li>I made so many friends :) They are all supportive, and are very sweet people (maybe not why 16 is sweet). I found community in the computer nerd group at my school, and in the Minecraft community I am in (Sitaku); I am relied on to build core infrastructure in our humongous city/nation.</li>
<li>I made some other silly projects, like <code>tpfanctl</code> to control ThinkPad fan speeds.</li>
<li>Got better at raylib.</li>
<li>Got better grades in school, slightly.</li>
<li>Other stuff. I can&rsquo;t remember, for me these achievements make me happy enough.</li>
</ol>
<p>Thank you for reading this post, this blog site actually expanding also makes me very happy. Thank you, all my readers and my supporters (there are a few that I am aware of), I wish you have a wonderful day.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Librebooting the ThinkPad T480</title><link>http://ezntek.com/posts/librebooting-the-thinkpad-t480-20241207t0933/</link><pubDate>Sat, 07 Dec 2024 09:33:13 +0800</pubDate><guid>http://ezntek.com/posts/librebooting-the-thinkpad-t480-20241207t0933/</guid><description>Muh freedumbs on muh hardware!</description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<img src="/img/lb_t480/glory.jpg" alt="glorious!" width="100%" />
<p><em>yes I really should clean it. shhh!</em></p>
<p>This is the story of how I librebooted my ThinkPad T480, less than 24 hours after support was added to <code>lbmk</code>&rsquo;s tree.</p>
<p>Note that <em><strong>this is not meant to be a comprehensive tutorial!</strong></em> Please always read the official documentation if you want to carry out this process for yourself, the steps for you and I will not necessarily be the same (as I had to build the ROM from source).</p>
<p>If you want to follow along with what I do, that is fine; I will provide generic instructions, but this is not a way of me saying that you should not check out the official docs <a href="https://libreboot.org/docs/install/t480.html">here!</a>.</p>
<p>For some of my own quirks while using libreboot, go <a href="/posts/librebooting-the-thinkpad-t480-20241207t0933/#some-libreboot-quirks">here</a></p>
<h2 id="backstory">Backstory</h2>
<p>Some legendary figure called Mate Kukri wrote <a href="https://codeberg.org/libreboot/deguard">this</a> to exploit a bug in the Intel Management Engine <a href="https://libreboot.org/">(you can read up on why it is evil here)</a>. Originally, it was just for a Dell OptiPlex desktop (if I remember correctly), and it abused a bug in the ME firmware to allow for arbitrary code execution (which allows one to disable Intel Boot Guard, therefore allowing <code>me_cleaner</code> and coreboot).</p>
<p>He ported it to the T480 after a little rewrite a few days ago, which is why he got this working (this was my genuine reaction I posted to my friends):</p>
<img src="/img/lb_t480/genuinereaction.jpg" alt="my geniune reaction to that information" width="70%" />
<p>However, <a href="https://www.reddit.com/r/libreboot/comments/1h4e8ym/comment/m007p56/">this commenter</a> revealed that it was already in the libreboot tree, only after a day or two. Maybe leah rowe is just magic.</p>
<h2 id="supplies">Supplies</h2>
<p><em><strong>you need:</strong></em></p>
<ol>
<li>a SOIC-8 clip.</li>
</ol>
<img src="/img/lb_t480/soic8.jpg" alt="a SOIC-8 clip" width="70%" />
<ol start="2">
<li>
<p>Some sort of thing that allows you to run a serial programmer on it. I used a Raspberry Pi Pico W for this, I just had it lying around one day.</p>
<div style="display: flex; column-gap: 0.7em;" >
    <img src="/img/lb_t480/picow_front.jpg" alt="Pico W front" width="35%" />
    <img src="/img/lb_t480/picow_back.jpg" alt="Pico W back" width="35%" />
</div>
</li>
<li>
<p>Any Philips-head screwdriver. Not too small, not too big, use what is sensible and available to you.</p>
<img src="/img/lb_t480/screwdriver.jpg" alt="a screwdriver" width="70%" />
</li>
<li>
<p>A spudger, it is generally good to have one, but you could use your nails if you are careful.</p>
<img src="/img/lb_t480/spudger.jpg" alt="a spudger" width="70%" />
</li>
</ol>
<h2 id="update-the-stock-bios">Update the stock BIOS</h2>
<p>The process is relatively simple. <em><strong>I assume that you are already on some Linux distribution or a BSD, if you are on Windows, you can do it via the EXE file</strong></em>.</p>
<p>Head over <a href="https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-t-series-laptops/thinkpad-t480-type-20l5-20l6/downloads/ds502355">here</a>, and choose the ISO download.</p>
<img src="/img/lb_t480/lenovosite.png" alt="the download entry" width="100%" />
<p>If you have a DVD/CD burner, just use something like <code>xorriso</code> or <code>cdrecord</code> to burn the ISO to a CD, and you&rsquo;re good</p>
<h3 id="optional-using-a-usb-stick">OPTIONAL: using a USB stick</h3>
<p>I assume that not many of you have blank DVDs/CDs lying around, along with a reader. To write the ISO to a USB, you must extract the El Torito image within the ISO, as it is not a hybrid ISO that can be <code>dd</code>ed to a USB stick.</p>
<p>Go to <a href="https://github.com/rainer042/geteltorito">this link</a> and fetch the <code>geteltorito.pl</code> script, and save it somewhere on your computer. Then, make it executable. The below commands does this:</p>
<pre tabindex="0"><code>curl -fL -o geteltorito https://raw.githubusercontent.com/rainer042/geteltorito/refs/heads/main/geteltorito.pl
chmod +x geteltorito
</code></pre><p>Then, extract the El Torito image by running the utility, like so:</p>
<pre tabindex="0"><code>./geteltorito -o t480_bios_update.img /path/to/your/downloaded.iso
</code></pre><p>Do replace the downloaded ISO path with the actual path to your ISO.</p>
<p>Write it to a USB stick however you want, I use dd:</p>
<p>(this command should be as root)</p>
<pre tabindex="0"><code>dd if=t480_bios_update.img of=/dev/sdX bs=4M conv=fsync status=progress
</code></pre><p>where sdX is your storage device.</p>
<h3 id="booting-the-bios-updater">Booting the BIOS updater</h3>
<p><em>Apologies for the lack of images!</em></p>
<p>Make sure that you allow the BIOS to be end-user-flashed in the stock BIOS, and also enable legacy boot.</p>
<p>Boot from the device stick by spamming <code>&lt;F12&gt;</code> and choose the appropriate entry. <em><strong>Make sure you have at least one battery in your system charged, and the AC connected! The program will not let you continue if you do not have both of those things satisfied.</strong></em></p>
<p>Select Option <code>2</code>, and follow the instructions. Your computer will reboot into a flash utility, make sure you do not unplug your BIOS update storage device!</p>
<p>It should look something like so:</p>
<img src="/img/lb_t480/stock_bios_update.jpg" alt="BIOS update screen" width="100%" />
<p>When you have rebooted your machine, you&rsquo;re good.</p>
<h2 id="dumping-the-stock-bios">Dumping the stock BIOS</h2>
<p>It generally is a good idea to have a backup of the stock BIOS, unless if things go seriously wrong. If you are somehow following this &ldquo;guide&rdquo; to <em>coreboot</em> your machine, you will need that as a part of the build process (but not for libreboot!)</p>
<h3 id="wiring-the-flasher">Wiring the flasher</h3>
<p>This will vary depending on the microcontroller/flashing device you use. For the Pi Pico and Pico W, you must have <a href="https://codeberg.org/libreboot/pico-serprog">pico-serprog</a> installed on it first.</p>
<p>To wire the SOIC-8 to the programmer, use the following diagram:</p>
<img src="/img/lb_t480/soic8_labelled.png" alt="SOIC-8 diagram" width="100%" />
<p>Pink corresponds to the pin on the Raspberry Pi, Bright red indicates the pin number on the chip and the dark red indicates what the pin is.</p>
<p>Here&rsquo;s a table:</p>
<table>
  <thead>
      <tr>
          <th>Chip Pin</th>
          <th>Chip Pin Number</th>
          <th>Raspi Pin</th>
          <th>Raspi GPIO Pin number</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CS</td>
          <td>1</td>
          <td>GP5</td>
          <td>7</td>
      </tr>
      <tr>
          <td>MISO</td>
          <td>2</td>
          <td>GP4</td>
          <td>7</td>
      </tr>
      <tr>
          <td>WP</td>
          <td>3</td>
          <td>Unused</td>
          <td>Unused</td>
      </tr>
      <tr>
          <td>GND</td>
          <td>4</td>
          <td>GND</td>
          <td>38</td>
      </tr>
      <tr>
          <td>MOSI</td>
          <td>5</td>
          <td>GP3</td>
          <td>5</td>
      </tr>
      <tr>
          <td>CLK/SCK</td>
          <td>6</td>
          <td>GP2</td>
          <td>4</td>
      </tr>
      <tr>
          <td>HOLD</td>
          <td>7</td>
          <td>Unused</td>
          <td>Unused</td>
      </tr>
      <tr>
          <td>VCC</td>
          <td>8</td>
          <td>3V3</td>
          <td>36</td>
      </tr>
  </tbody>
</table>
<p>After wiring, they form this, what I&rsquo;d like to call my <em>BIOS flashing jig.</em></p>
<img src="/img/lb_t480/biosjig_front.jpg" alt="my BIOS flashing jig (front)" width="35%" />
<p>The back sohuld look something like this:</p>
<img src="/img/lb_t480/biosjig_back.jpg" alt="my BIOS flashing jig (back)" width="35%" />
<h3 id="taking-some-dumps">Taking some dumps</h3>
<p><em>No, not excretion.</em></p>
<p><em>NOTE</em>: <code>flashrom</code>. Libreboot standardizes on <code>flashprog</code>, but I cannot find a good reason as to why leah does this (I was on IRC yesterday, somebody brought it up but to my knowledge, leah did not respond).</p>
<ol>
<li>
<p>Connect the flasher to the chip, simply press on the plastic portion of the SOIC-8 clip open, and make sure the bottom portion of the clip is completely flush with the board, with all pins contacting the chip.</p>
<img src="/img/lb_t480/soic8_seated.jpg" alt="the SOIC-8 clip, seated on the chip" width="70%" />
<p><em><strong>ONLY THEN, do you connect the power</strong></em>. NEVER CONNECT YOUR FLASHER TO YOUR COMPUTER BEFORE CONNECTING THE CLIP TO THE CHIP! <em><strong>YOU MIGHT FRY YOUR CHIP!</strong></em></p>
</li>
<li>
<p>Determine the model of flash chip you have. I have a Winbond <code>W25Q128.V</code>, but you may have a macronix chip. Use your phone&rsquo;s flash to take a zoomed in picture of the chip, and try to decipher the code.</p>
</li>
<li>
<p>Issue the flashrom command. As root:</p>
<pre tabindex="0"><code>flashrom -p yourprogrammer -c &#34;your chip model&#34; -r t480_stockbios_1.bin
</code></pre><p>Since I use a raspberry pi pico and <code>pico-serprog</code>, and I have a <code>W25Q128.V</code> chip, I issued</p>
<pre tabindex="0"><code>flashrom -p serprog:dev=/dev/ttyACM0 -c &#34;W25Q128.V&#34; -r t480_stockbios_1.bin
</code></pre></li>
<li>
<p>Repeat until you have 3 dumps. Change the filename between dumps, i.e. <code>-r t480_stockbios_1.bin</code> to <code>-r t480_stockbios_2.bin</code>, etc.</p>
</li>
<li>
<p>Run the following command:</p>
<pre tabindex="0"><code>sha256sum t480_stockbios*.bin
</code></pre><p>you should see some checksums, like so:</p>
<pre tabindex="0"><code>a7742cedf85706cafad984e555c0dc3dda9854855fcd131aadb4dd92a320ab00  t480_oldbios_a.bin
a7742cedf85706cafad984e555c0dc3dda9854855fcd131aadb4dd92a320ab00  t480_oldbios_b.bin
a7742cedf85706cafad984e555c0dc3dda9854855fcd131aadb4dd92a320ab00  t480_oldbios_c.bin
</code></pre><p><em><strong>If they are not all identical, remove all of your dumps and try steps 3-5 again until all your checksums match</strong></em>. this is to make sure you have 3 clean dumps.</p>
</li>
</ol>
<h2 id="compiling-with-lbmk">Compiling with lbmk</h2>
<p><em><strong>NOTE: you might as well use the libreboot docs for the T480 and inject vendor firmware into a prebuilt tarball. As of writing this, release 20241206 is already out, which has T480 binaries</strong></em>. Even if you want to build a rom with lbmk, you might as well use the docs. They are really good! <em>This section will highlight my unique experiences, becuase I compiled the rom before leah even released the tarballs.</em></p>
<p><em><strong>NOTE: go <a href="/posts/librebooting-the-thinkpad-t480-20241207t0933/#back-to-compiling-libreboot">here</a> to skip compilation to the part where I change the MAC address</strong></em></p>
<p>Things began simple. I just cloned lbmk:</p>
<pre tabindex="0"><code>git clone https://codeberg.org/libreboot/lbmk
cd lbmk
</code></pre><p>and followed the instructions on the site.</p>
<p>I had used lbmk before, so I did not need to <code>./mk dependencies arch</code> (I was building on my Artix god-PC, with an i7-14700KF. I really did not want to build it on one of my older ThinkPads).</p>
<p>Like a good and sane person who likes <em>reading the f*****g manuals™</em>, I <em>read the f*****g manuals™</em>.</p>
<p>This is what the guide said after I read all the other sections:</p>
<blockquote>
<h2 id="build-rom-image-from-source">Build ROM image from source</h2>
<p>The build target, when building from source, is thus:</p>
<pre tabindex="0"><code>./mk -b coreboot t480_fsp_16mb
./mk -b coreboot t480s_fsp_16mb
</code></pre><p>NOTE: The T480 and T480S may be similar, but they do have several critical differences in their wiring, so you MUST flash the correct image. Please choose one of the above build targets accordingly.</p></blockquote>
<p>I was aware of the pitfalls, like no thunderbolt, no brightness keys, etc. But due me having done all this prep (and me generally loving libreboot as a project), I continued anyways.</p>
<p>I issued</p>
<pre tabindex="0"><code>./mk -b coreboot t480_fsp_16mb
</code></pre><p>as I was told. Things went smoothly.</p>
<p>&hellip;until I hit this error.</p>
<pre tabindex="0"><code>--- TRUNCATED ---

Not a supported Inno Setup installer!
Done with 1 error.
Dell PFS Update Extractor v6.0_a16
 
*** df735a24242792bf4150f30bf0bd4fdbdc0fb6bf0f897ea533df32567be8e084006d692fb6351677f8cc976878c5018667901dbd407b0a77805754f7c101497c
 
    Extracting Dell PFS 1 &gt; df735a24242792bf4150f30bf0bd4fdbdc0fb6bf0f897ea533df32567be8e084006d692fb6351677f8cc976878c5018667901dbd407b0a77805754f7c101497c &gt; Firmware
 
Done!
config/data/deguard/mkhelper.cfg missing
Downloading project &#39;deguard&#39; to &#39;src/deguard&#39;
src/deguard missing
fatal: repository &#39;/home/ezntek/Sources/apps/lbmk/cache/repo/deguard&#39; does not exist
ERROR script/trees: !clone /home/ezntek/Sources/apps/lbmk/cache/repo/deguard /home/ezntek/Sources/apps/lbmk/tmp/gitclone
Cached clone failed; trying online.
Cloning into &#39;/home/ezntek/Sources/apps/lbmk/tmp/gitclone&#39;...
remote: Total 181 (delta 0), reused 181 (delta 0)
Receiving objects: 100% (181/181), 62.47 KiB | 120.00 KiB/s, done.
Resolving deltas: 100% (46/46), done.
HEAD is now at de176a7 Add note about DCI bit
Applying: t480s delta
Traceback (most recent call last):
  File &#34;/home/ezntek/Sources/apps/lbmk/src/deguard/./finalimage.py&#34;, line 76, in &lt;module&gt;
    with open(args.input, &#34;rb&#34;) as f:
         ^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: &#39;/home/ezntek/Sources/apps/lbmk/tmp/me.bin&#39;
ERROR ./vendor: Error running deguard for /home/ezntek/Sources/apps/lbmk/vendorfiles/t480/me.bin
ERROR ./vendor: Error running deguard for /home/ezntek/Sources/apps/lbmk/vendorfiles/t480/me.bin
ERROR script/trees: Unhandled non-zero exit: ./vendor download t480_fsp_16mb
ERROR ./mk: excmd: script/trees -b coreboot t480_fsp_16mb
</code></pre><p>Alright I guess. It complained about missing vendor files such as the ME. maybe I had to extract it with <code>ifdtool</code>?</p>
<p>&hellip;so I ran <code>ifdtool --platform sklkbl -x wherever/the/bios/was.bin</code></p>
<p>and copied <code>flashregion_2_intel_me.bin</code> to vendorfiles/t480. But then it gave me a new error.</p>
<img src="/img/lb_t480/error2.jpg" alt="the new error" width="70%" />
<p>(sorry for not having a proper text log for it, I couldn&rsquo;t find the pastebin paste I had it on :/)</p>
<p>The ME region was too big. At this point, I was completely confused and dumbfounded; which is why I took to IRC.</p>
<p><em><strong>NOTE: go <a href="/posts/librebooting-the-thinkpad-t480-20241207t0933/#back-to-compiling-libreboot">here</a> to skip my encounters on IRC</strong></em></p>
<h2 id="libreboot-on-liberachat-oh-dear"><code>#libreboot</code> on libera.chat: oh dear</h2>
<p>On the libreboot website, they recommended people to come to <code>#libreboot</code> on IRC for help&hellip;which is what I did. FYI, I almost never use IRC (although I am planning to become more active on IRC networks).</p>
<p>I expected at least a slightly nice greeting from the IRC people, but that was when I found out that these people are RTFMers. Mad respect for that, but I already read the manual(s) back to back for each applicable thing, and I was sure I knew what I was doing.</p>
<p>Here were the things they told me:</p>
<ol>
<li>RTFM (but I already did!)</li>
<li>Install dependencies (but I already installed them!)</li>
<li>Install dependencies again (but I just did a full system update?)</li>
</ol>
<p>I think at that point, everyone was clueless. I could tell that <code>fuel</code> (a mod) was a bit clueless and started asking <code>leah</code>. They did eventually discover that I used Artix and not arch, and they immediately denied further assistance (expected, they are 2 different distros, arch supported and Artix not).</p>
<p>But I was still skeptical. Those distros literally share the same repos (at least I made it do so), and I have successfully used <code>lbmk</code> on Artix once or twice before. Still, no luck.</p>
<h3 id="psa">PSA</h3>
<p>Don&rsquo;t paste raw output on IRC! horrible idea, you will get people barking at you. I did not know, but they could have reminded me a little better (because I am not an IRC user!!)</p>
<p>Just use pastebin—</p>
<h3 id="back-to-irc-shenanigans">Back to IRC shenanigans</h3>
<p>They began telling me to try it on debian. I said that I did not want to and I was certain that my distro was not the problem. They insisted. Alright, I would try on debian sid. No luck! I got missing candidate errors (my favorite!).</p>
<p>I then started bargaining, which really does make sense in this case. Sure, it is an unsupported distro, but why are the dependencies causing <code>me.bin</code> to not be present? the typical process for obtaining any ME dump would be</p>
<ol>
<li>somehow get an official BIOS image (in this case, download)</li>
<li>chop the rom up to extract the binary</li>
<li>copy it to <code>vendorfiles/t480</code></li>
</ol>
<p>Why would the dependencies (like GCC libraries and other coreboot dependencies) matter? The process would literally just be</p>
<pre tabindex="0"><code>curl -fL -o bios.bin https://some.shady/link
magicalextractionutility -o me.bin bios.bin
cp me.bin vendorfiles/t480
</code></pre><p>I suspected that the magical extraction utility would be bundled with coreboot. They did say no, however (which is true, but one utility that is used, <code>innoextract</code> was in the dependencies).</p>
<p>They then told me void was a supported distro, so I tried it again. <strong>Same error</strong>.</p>
<p>at that point, even leah became confused, so they decided it was a good idea to clone a fresh lbmk tree<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Turns out they made a commit that tried to do a shortcut to copy me.bin, but that didnt work! one local <code>git revert</code> later, it built!</p>
<h2 id="back-to-compiling-libreboot">Back to compiling libreboot</h2>
<p>Libreboot does not provide a MAC address for the Gigabit Ethernet (<code>gbe</code>) ROM, which means that you cannot have many librebooted machines on the same network. Therefore, it is advised to change it.</p>
<p><a href="https://libreboot.org/docs/install/nvmutil.html">Libreboot has documentation for this</a>.</p>
<p>This is the process:</p>
<ol>
<li>Have <code>ifdtool</code> and <code>nvmutil</code> compied somewhere</li>
<li><code>ifdtool --platform sklkbl -x libreboot.rom</code> to extract the gbe region</li>
<li><code>nvm flashregion_3_gbe.bin setmac aa:bb:cc:dd:ee:ff</code></li>
</ol>
<p>where <code>aa:bb:cc:dd:ee:ff</code>. I recommend you take the first 3 parts of the MAC address, but leave the rest as <code>??</code>, like so:</p>
<pre tabindex="0"><code>nvm flashregion_3_gbe.bin setmac aa:bb:cc:??:??:??
</code></pre><p>To keep the correct network card vendor, but change the device specific address. I did not do this, but I will the next time I flash the computer.</p>
<h2 id="flash">Flash!</h2>
<p>This process will take a while, as flashrom does not tell you when it starts writing the flash chip. Run the following as root:</p>
<p><code>flashrom -p yourprogrammer -c &quot;your chip model&quot; -w your_libreboot_variant.rom</code></p>
<p>And make sure your chip clip is sat on the chip <strong>tightly!</strong> If the bottom of the clip appears to not have crimped the chip at the very bottom anymore, but has slid up and crimpeed the top, that means your clip is about to die, and you have to hold the chip clip in place after you put it on, and press down with force to make sure it does not fly off.</p>
<h2 id="usage-report">Usage report</h2>
<p>That is about as simple as librebooting gets.</p>
<p>In terms of using the T480 (for buyers who want to try it out):</p>
<ul>
<li>
<p>CPU performance is good. I recommend the <code>i5-8350U</code>, as it has the best CPU performance to price ratio, if buying used. It will be a large jump from a haswell/ivy bridge M series dual core i5 and a relatively good jump from a dual core i7.</p>
<ul>
<li>If you are using a quad-core i7 (MQ or QM series chip from Ivy Bridge/Haswell), the performance boost is not very noticeable and is marginal.</li>
</ul>
<p>Either way, power efficiency will skyrocket, as these are U series chips after all.</p>
</li>
<li>
<p><strong>Get 16GB of RAM</strong>: tihs is basically a given, make sure you have at least 16. 8 will bottleneck the CPU, and 32 is too much for ost tasks.</p>
</li>
<li>
<p>If youre lucky and get a T480 with the 2280 SSD bracket, you will see a boost in disk speeds, but if you only do tasks such as office work/programming, a normal SATA SSD will have an identical performance. If you want, you can put an <em><strong>M.2 A+E key 2242 SSD</strong></em> into the WWAN slot for extra storage.</p>
</li>
<li>
<p><em><strong>AVOID TN PANELS LIKE THE PLAGUE</strong></em>: If buying used and you are not planning to upgrade the panel, please avoid the TN panels, because they will make your T480 experience horrendous and near unbearable. I recommend the following panels if you upgrade it:</p>
<ul>
<li>NE140FHM-N62: <strong>no mounting brackets</strong>, has enough clearance and is 96%DCI-P3 (1.07 billion colors!), 30 pin eDP, non-touch, IGZO low power, 500 nit and is 1920x1080. It should have 60 and 48hz support. I have not tested it, but in theory this should be the best panel.</li>
<li>NE140FHM-N61: <strong>no mounting brackets</strong>, has enough clearance and is 100%sRGB, 30pin eDP, non-touch, IGZO low power, 400 nit and is 1920x1080. Supports 60 and 48hz and is quite bright. I have tested this one.</li>
<li>N140HCG-GQ2/GR2: <strong>no mounting brackets</strong>, has enough clearance, 100%sRGB, 30pin eDP, non-touch, IGZO, has pretty damn bad backlight bleed, is 400 nit but quite dim, and is 1920x1080. I have tested this one.</li>
<li>B140HAN01.0/.1/.2/.3: has mounting brackets, has enough clearance, and is 45%NTSC, 30pin eDP, non-touch, not low power, 250~300 nits and is 1920x1080. Colors are quite washed out and is okay-bright. All &ldquo;compatible&rdquo; panels are acceptable, much better than a TN. I have tested the B140HAN01.3 and the N140HCE-EAA (a compatible).</li>
</ul>
<p>you could go for more exotic options, including 2.5k panels like the B140QAN02.0 (which I used, but swapped out, because it has clearance issues and creates 2 bulges on the bezel. Colors are amazing though) or 4k ones. Any panel higher than 1080p requires a 40 pin cable; and swapping the cable out is quite painful as you have to disassemble the hinge.</p>
</li>
<li>
<p>Do the glass trackpad mod if you like using the trackpad.</p>
</li>
<li>
<p><strong>Make sure you have the internal battery!</strong> the PowerBridge system is extremely useful, and even if you do not intend to hotswap batteries, the extra mileage you get is quite important at times. I have a 72wH (in reality, 68 due to it being aftermarket) external 6 cell and an internal 24Wh 3 cell, which gives me 9 total cells around ~90wH. My T480 did not even come with an internal battery because Lenovo did not ship these batteries in colder countries due to <em>chemistry™ reasons</em>.</p>
</li>
</ul>
<p>For daily tasks, the T480 smashes all of them, and most definitely can handle video editing, even with just the iGPU. Speaking of which, it gets over 80fps on 22 ren/22 sim chunks medium settings with sodium in a Minecraft Singleplayer world. It is portable, quite light, definitely 1-handable and is a great daily driver.</p>
<p>The keyboard does only have ~1.8-2mm of travel, which is unlike the classic keyboards and first gen island keyboards on Ivy Bridge ThinkPadswhich have anywhere between 2 and 2.3mm of travel. There are 3 T480 keyboard manufacturers:</p>
<ul>
<li>LiteOn (FRU 01HX459 for backlit): This one has the snappiest keyfeel, relatively short travel and slightly MacBook keyboard like, but still very comfortable to type on. The bottom-out is quite harsh.</li>
<li>Chicony (FRU 01HX419 for backlit): This one is soft but not too soft, has a shorter tactile peak that is more drawn out, and is not that heavy. Travel is definitely deeper than the LiteOn but still with a hard bottom-out.</li>
<li>Darfon (FRU 01HX449 for backlit): This one is the worst, mushy, not very tactile and keytravel is not that long either. I definitely do not recommend this one, either go with Chicony if you want the more balanced option or LiteOn if you want a snappier keyfeel.</li>
</ul>
<p>I personally use the LiteOn and have used the chicony, and do prefer the LiteOn simply because of the tactile bump. If you have ever tried to do the classic keyboard mod, LiteOn is the equivalent to the NMB keyboards, the best-regarded within the community; and LiteOn and NMB are related (I think one bought the other, or one NMB used LiteOn switches in their classic keyboards, because apart from the keytravel, they feel very similar).</p>
<h2 id="some-libreboot-quirks">Some libreboot quirks</h2>
<ul>
<li>
<p><em><strong><code>thinkpad_acpi</code> does not load correctly, just like on haswell ThinkPads</strong></em>. You must put the following:</p>
<pre tabindex="0"><code>options thinkpad_acpi force_load=1
</code></pre><p>inside a file named anything inside <code>/etc/modprobe.d</code> for the module to load correctly on boot. Else, you do not get temperature monitoring and fan control.</p>
</li>
<li>
<p>As per the <a href="https://libreboot.org/docs/install/t480.html">T480 libreboot page</a>, thunderbolt does not work as of now, and the brightness keys do not register keysyms within Linux. The settings, bluetooth, kekyboard and star keys on top of F9-F12 register a <code>NoSymbol</code> keysym within <code>wev</code>.</p>
</li>
<li>
<p>Suspend works as expected.</p>
</li>
<li>
<p>Bluetooth is slightly wonky, as it does not automatically turn on, at least for Void Linux; despite the service being enabled. Sometimes I have to restart the bluetooth service (with <code>doas sv restart bluetoothd</code>) to get things to work. Auto-pairing also does not work.</p>
</li>
<li>
<p>The installation process is far easier than compiling a custom coreboot payload (duh)</p>
</li>
<li>
<p><em><strong>leah added support for UEFI through U-boot</strong></em>, which means that you can do an EFI install of linux (I still use good-ol&rsquo; BIOS boot).</p>
</li>
</ul>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>not just normal confused, leah was quite pissed off because they were trying to port libreboot to Alder lake motherboards, but things were going well.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The OFS IGCSE Computer Science G1 Guide to Programming and Logic Gates DOWNLOAD</title><link>http://ezntek.com/posts/the-ofs-igcse-computer-science-g1-guide-to-programming-and-logic-gates-20241106t0845/</link><pubDate>Wed, 06 Nov 2024 08:48:02 +0800</pubDate><guid>http://ezntek.com/posts/the-ofs-igcse-computer-science-g1-guide-to-programming-and-logic-gates-20241106t0845/</guid><description>The G2 CSRG, abridged for G1</description><content:encoded><![CDATA[<h2 id="download">DOWNLOAD</h2>
<ul>
<li><a href="https://ezntek.com/doc/CSRG_G1_Rev3.pdf">The OFS IGCSE Computer Science G1 Guide to Programming and Logic Gates, Revision Three</a></li>
</ul>
<h2 id="what-is-this">What is this?</h2>
<p><img alt="Cover" loading="lazy" src="/img/csrgg1cover.png"></p>
<p>I have already released <a href="https://ezntek.com/posts/the-ofs-igcse-computer-science-g2-exam-revision-reference-guide-20241105t2152/">this</a> guide for the G2 students, and since your examination has so much overlap with mine, I have abridged my guide to make it so that you can use some content for your exam as well.</p>
<p>This guide covers:</p>
<ul>
<li>The programming stuff</li>
<li>The logic gate stuff</li>
</ul>
<p><em><strong>This guide doesn&rsquo;t cover:</strong></em></p>
<ul>
<li>Systems lifecycle stuff</li>
<li>The binary and data representation stuff</li>
</ul>
<p>I don&rsquo;t have time to write anything extra for you, <strong>please remember to use your own notes too!</strong> I am not liable.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>The OFS IGCSE Computer Science G2 Exam Revision Reference Guide DOWNLOAD</title><link>http://ezntek.com/posts/the-ofs-igcse-computer-science-g2-exam-revision-reference-guide-20241105t2152/</link><pubDate>Tue, 05 Nov 2024 21:52:09 +0800</pubDate><guid>http://ezntek.com/posts/the-ofs-igcse-computer-science-g2-exam-revision-reference-guide-20241105t2152/</guid><description>Did you all think that I would stop writing guides?</description><content:encoded><![CDATA[<h2 id="download">DOWNLOAD</h2>
<ul>
<li><a href="https://ezntek.com/doc/CSRG_G2_Rev4.pdf">The OFS IGCSE Computer Science G2 Exam Revision Reference Guide, Revision Four</a></li>
</ul>
<h2 id="after-much-anticipation">After much anticipation&hellip;</h2>
<p>It is finally ready, <em><strong>yes, the new CSRG</strong></em>!</p>
<img src="/img/newcsrgcover.png" width="100%" />
<p>The <em>TRUE</em> Sequel to the previous one (you can find it <a href="/posts/comp-sci-revision-20240509t0944">here</a>), where I wrote a ~40 page reference to all the topics that would be covered in the upcoming exam.</p>
<p>However, that only did so much; this semester we are being tested on a crap ton of stuff:</p>
<ul>
<li>Emerging Technologies <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
<ul>
<li>Expert Systems and machine learning and stuff</li>
<li>Automated systems and stuff</li>
<li>robots and stuff</li>
</ul>
</li>
<li>Logic gates
<ul>
<li>lotsa lotsa gates</li>
<li>lotsa lotsa diagrams</li>
<li>lotsa lotsa truth tables</li>
<li>lotsa lotsa expressions and stuff</li>
</ul>
</li>
<li><em><strong>programming</strong></em></li>
<li>some other stuff i think</li>
</ul>
<p>..which is why the new <strong>75 page guide</strong> aims to address all these topics. Read the PDF for more information!</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Check out <a href="/posts/dont-write-comments-write-better-code-20241105t2141">this</a> article for more info as to why I hate the IGCSE examiners so much. Of course they have to have a unit on emerging technologies; it is completely useless and bull&hellip;crap!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Don't Write Comments, Write Better Code!</title><link>http://ezntek.com/posts/dont-write-comments-write-better-code-20241105t2141/</link><pubDate>Tue, 05 Nov 2024 21:41:00 +0800</pubDate><guid>http://ezntek.com/posts/dont-write-comments-write-better-code-20241105t2141/</guid><description>Comments are not an excuse for bad code (look away, IGCSE students!)</description><content:encoded><![CDATA[<h2 id="important-information">Important Information!</h2>
<p>If you actually take IGCSE comp sci, ignore everything I say, because the examiners love seeing overcommented code for whatever reason, and they will even give you subjective marks based on if they think there are enough comments or not.</p>
<p>To them, &ldquo;well commented code&rdquo; is of higher quality, despite if 80% of the code is just paragraphs and paragraphs talking about the meaning of life.</p>
<p><em>Yes, I do have beef with these people. Write better code, comments are not an excuse to write bad code!</em></p>
<h2 id="the-problem">The Problem</h2>
<p>People are overcommenting their code nowadays; we are even taught to do this due to the stupid IGCSE Examiners <strong>giving marks for &ldquo;good&rdquo; commenting practices</strong> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Here&rsquo;s an example from our computer science teacher:</p>
<p><img alt="Some completely fine code" loading="lazy" src="/img/badcodefromslides.png"></p>
<p>To me, this looks like a completely fine set of code. I was asked by my teacher (because I think he knows that I&rsquo;m quite the <a href="https://github.com/ezntek">Good Programmer</a> and he wanted my input). I said that:</p>
<blockquote>
<p>The code looks completely fine to me, its good enough, readable, all I would do is run the code through a formatter and rename the variables.</p></blockquote>
<p>Instead, I was told that I was <em>Wrong</em> for saying that, and that this was a badly written program. The biggest problem that was highlighted to me was that there were no comments, and therefore the program was unmaintainable.</p>
<p><img alt="Code, but butchered™" loading="lazy" src="/img/evenworsecodefromslides.png"></p>
<p>(Written by my Comp Sci teacher)</p>
<p>I do agree that that having better variable names is objectively better, but this is clearly an example of overcommenting.</p>
<h2 id="why-not-comment">Why not comment?</h2>
<p>Consider this code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># grab the response after the message is sent</span>
</span></span><span style="display:flex;"><span>r <span style="color:#f92672">=</span> send_message(m)
</span></span><span style="display:flex;"><span><span style="color:#75715e"># check if the message sent correctly</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> r<span style="color:#f92672">.</span>status() <span style="color:#f92672">==</span> <span style="color:#ae81ff">9</span>:
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># mark as sent</span>
</span></span><span style="display:flex;"><span>	m<span style="color:#f92672">.</span>sent <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span></code></pre></div><p><em>Adapted from CodeAesthetic&rsquo;s Video</em></p>
<p>All these variable names, <code>m</code>, <code>r</code>, and the random <code>9</code> floating around isn&rsquo;t very legible to anyone else who may be maintaining your code.</p>
<p>This is why there are many comments explaining what my code does. Future maintainers of my code would ideally read those comments and understand what the code is doing.</p>
<p>But do we <em>really</em> need all this many comments? <strong>If your code is unreadable and you need comments to fix it, why not just adapt the code?</strong></p>
<p>We could easily rewrite this section like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># ...somewhere at the top of the file</span>
</span></span><span style="display:flex;"><span>MESSAGE_SENT <span style="color:#f92672">=</span> <span style="color:#ae81ff">9</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ...later</span>
</span></span><span style="display:flex;"><span>response <span style="color:#f92672">=</span> send_message(msg)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> response<span style="color:#f92672">.</span>status() <span style="color:#f92672">==</span> MESSAGE_SENT:
</span></span><span style="display:flex;"><span>	message<span style="color:#f92672">.</span>sent <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span></code></pre></div><p>Now the code reads itself. Everything is clear, and the code is self commenting; it reads like natural human language.</p>
<p>Here&rsquo;s another example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_time_from_server</span>(server):
</span></span><span style="display:flex;"><span>	response <span style="color:#f92672">=</span> request_for_time(server)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> response<span style="color:#f92672">.</span>status <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> response<span style="color:#f92672">.</span>result
</span></span></code></pre></div><p>Now what happens if you didn&rsquo;t have access to the contents of this function (if this were in a library); how would you know what the format of &ldquo;server&rdquo; should be, is it a list of values for an IPv4 address, is it an URL or URI, is it a string or a custom class? And how do you know what the return values mean?</p>
<p>Here&rsquo;s the naïve solution implemented with some ✨comments✨</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># grabs the time from a server.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># the server argument takes in a tuple with 4 integers,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># representing an IPv4 address.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_time_from_server</span>(server):
</span></span><span style="display:flex;"><span>	response <span style="color:#f92672">=</span> request_for_time(server)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># If the value is 1, return -1 to mean an error</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> response<span style="color:#f92672">.</span>status <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># return the actual result</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> response<span style="color:#f92672">.</span>result
</span></span></code></pre></div><p>Unclean, right? Here&rsquo;s some issues:</p>
<ul>
<li>Now what happens if the server actually sends -1 as some useful value, like representing a server-side error; would the caller of the function even know?</li>
<li>What happens if we want to support IPv6? The parameter type would completely change, but if we forget to update the comment in a rush, that wouldn&rsquo;t be very nice for the caller, right?</li>
<li>We are, again, rewriting the code itself in English, which provides no extra worth to the reader.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
</ul>
<p>Here&rsquo;s how I would fix it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>IPv4Address <span style="color:#f92672">=</span> tuple[int, int, int, int]
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_time_from_server</span>(server: IPv4Address) <span style="color:#f92672">-&gt;</span> int <span style="color:#f92672">|</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>	response <span style="color:#f92672">=</span> request_for_time(server)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> response<span style="color:#f92672">.</span>status <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> response<span style="color:#f92672">.</span>result
</span></span></code></pre></div><p>Now the function is a lot clearer, even without comments. Here, we use <strong>types</strong> to make it clear to the caller what data is supposed to come in and out, without ever needing to consult documentation (although that&rsquo;s a bad idea!)</p>
<p>The only other thing that I would do in this case would be to annotate what None represents, i.e.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>IPv4Address <span style="color:#f92672">=</span> tuple[int, int, int, int]
</span></span><span style="display:flex;"><span><span style="color:#75715e"># returns None if there&#39;s an error</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_time_from_server</span>(server: IPv4Address) <span style="color:#f92672">-&gt;</span> int <span style="color:#f92672">|</span> <span style="color:#66d9ef">None</span>:
</span></span><span style="display:flex;"><span>	response <span style="color:#f92672">=</span> request_for_time(server)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> response<span style="color:#f92672">.</span>status <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> response<span style="color:#f92672">.</span>result
</span></span></code></pre></div><p>but again, we could just make a type alias.</p>
<h2 id="example-refactor">Example Refactor</h2>
<p>Looking at the above example from my Computer Science Teacher, here&rsquo;s the before:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>x <span style="color:#f92672">=</span> int(input(<span style="color:#e6db74">&#34;Enter a number: &#34;</span>))
</span></span><span style="display:flex;"><span>y <span style="color:#f92672">=</span> int(input(<span style="color:#e6db74">&#34;Enter a number: &#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>f1 <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(x, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>):
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> x <span style="color:#f92672">%</span> i <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>		f1<span style="color:#f92672">.</span>append(i)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>f2 <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(y, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>):
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> y <span style="color:#f92672">%</span> i <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>		f2<span style="color:#f92672">.</span>append(y)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> f1[i] <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> f2:
</span></span><span style="display:flex;"><span>	i <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;The GCF of&#34;</span>, x, <span style="color:#e6db74">&#34;and&#34;</span>, y, <span style="color:#e6db74">&#34;is&#34;</span>, f1[i])
</span></span></code></pre></div><p>Messy, right?</p>
<p>You could tell which part of the program does that, though:</p>
<ul>
<li>the first portion of the code that deals with f1 gets all the factors of x.</li>
<li>the second portion of the code that deals with f2 gets all the factors of y</li>
<li>the last portion of the code checks each factor of f1 and sees if it&rsquo;s also a factor of f2.
<ul>
<li>Since the list is in reverse order, i.e. largest factor goes first, the first common factor it finds should be the greatest.</li>
</ul>
</li>
</ul>
<p>But this is quite hard to decipher for a person that doesn&rsquo;t know the algorithm; let&rsquo;s refactor it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>x <span style="color:#f92672">=</span> int(input(<span style="color:#e6db74">&#34;Enter a number: &#34;</span>))
</span></span><span style="display:flex;"><span>y <span style="color:#f92672">=</span> int(input(<span style="color:#e6db74">&#34;Enter a number: &#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>x_factors <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> possible_factor <span style="color:#f92672">in</span> range(x, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>):
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> x <span style="color:#f92672">%</span> possible_factor <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>		x_factors<span style="color:#f92672">.</span>append(i)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>y_factors <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> possible_factor <span style="color:#f92672">in</span> range(y, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>):
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> y <span style="color:#f92672">%</span> possible_factor <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>		y_factors<span style="color:#f92672">.</span>append(y)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>index <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> x_factors[i] <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> y_factors:
</span></span><span style="display:flex;"><span>	index <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">&#34;The GCF of&#34;</span>, x, <span style="color:#e6db74">&#34;and&#34;</span>, y, <span style="color:#e6db74">&#34;is&#34;</span>, x_factors[index])
</span></span></code></pre></div><p>The identifier names make it a bit easier to understand. Now the reader of the code can understand that the first part is at least related to finding the factors of a number.</p>
<p>However, we can do better; since these are all repeatable and generic sections of code, we might as well extrapolate them into functions and use an f-string:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>x <span style="color:#f92672">=</span> int(input(<span style="color:#e6db74">&#34;Enter a number: &#34;</span>))
</span></span><span style="display:flex;"><span>y <span style="color:#f92672">=</span> int(input(<span style="color:#e6db74">&#34;Enter a number: &#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_factors</span>(num: int) <span style="color:#f92672">-&gt;</span> list[int]:
</span></span><span style="display:flex;"><span>	result <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> possible_factor <span style="color:#f92672">in</span> range(num, <span style="color:#ae81ff">0</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>):
</span></span><span style="display:flex;"><span>		result<span style="color:#f92672">.</span>append(possible_factor)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> result
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_gcf</span>(num1: int, num2: int) <span style="color:#f92672">-&gt;</span> int:
</span></span><span style="display:flex;"><span>	factors_num1 <span style="color:#f92672">=</span> get_factors(num1)
</span></span><span style="display:flex;"><span>	factors_num2 <span style="color:#f92672">=</span> get_factors(num2)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	index <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">while</span> factors_num1[index] <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> factors_num2:
</span></span><span style="display:flex;"><span>		index <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	result <span style="color:#f92672">=</span> factors_num1[index]
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> result
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gcf <span style="color:#f92672">=</span> get_gcf(x, y)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;the GCF of </span><span style="color:#e6db74">{</span>x<span style="color:#e6db74">}</span><span style="color:#e6db74"> and </span><span style="color:#e6db74">{</span>y<span style="color:#e6db74">}</span><span style="color:#e6db74"> is </span><span style="color:#e6db74">{</span>GCF<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span></code></pre></div><p>Now what makes this code so much better than before?</p>
<ul>
<li><strong>Types:</strong> It is so much easier to track the flow of data simply by noting down the types. It is also easier to run type-specific operations on parameters as you know for sure what the type is and don&rsquo;t need to backtrack.</li>
<li><strong>Good identifier names</strong>: Now the code reads like human language! There are no weird array indexes, all lines have some sort of human-legible meaning to them.</li>
<li><strong>No redundant comments:</strong> There are no comments, as the code simply reads like English, given that you know how Python Syntax works.</li>
</ul>
<p>Notice how there&rsquo;s no need for comments to explain <strong>what</strong> the code does?</p>
<ul>
<li>Now, let&rsquo;s say if we changed the algorithm in the future. If we had used comments, they may slowly become outdated and not reflect what the code says.</li>
<li>If we write our code in more human-friendly form with nicer identifiers, the code immediately gets clearer, just by reading function names.</li>
</ul>
<h2 id="when-should-i-comment">When should I comment?</h2>
<p>In the previous example, I (or my teacher) uses a set algorithm to get the greatest common factor, some widely-known set algorithm that doesn&rsquo;t need to be changed. If we were trying to teach how that algorithm worked;  this would be a  <strong>sub-optimal</strong> example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># Grab the greatest common factor between 2 integers</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_gcf</span>(num1: int, num2: int) <span style="color:#f92672">-&gt;</span> int:
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># Get the factors of both numbers</span>
</span></span><span style="display:flex;"><span>	factors_num1 <span style="color:#f92672">=</span> get_factors(num1)
</span></span><span style="display:flex;"><span>	factors_num2 <span style="color:#f92672">=</span> get_factors(num2)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># Define an index</span>
</span></span><span style="display:flex;"><span>	index <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># Keep iterating through the first list while there a given</span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># factor is not in the other list</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">while</span> factors_num1[index] <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> factors_num2:
</span></span><span style="display:flex;"><span>		index <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># when we find the common factor, return it to the call</span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># site</span>
</span></span><span style="display:flex;"><span>	result <span style="color:#f92672">=</span> factors_num1[index]
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> result
</span></span></code></pre></div><p>We are annotating as to <strong>what</strong> the code does. That&rsquo;s bad; as programmers we know the syntax of the language we program in, why is there a need to rewrite the syntax?</p>
<p>This still doesn&rsquo;t help your understanding of the algorithm; one could ask:</p>
<blockquote>
<p>Why are we iterating through one list and checking it with the other; wouldn&rsquo;t the condition be met on the first iteration because both lists are in ascending order?</p></blockquote>
<p>And other such questions. A better way to comment your code could be:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">get_gcf</span>(num1: int, num2: int) <span style="color:#f92672">-&gt;</span> int:
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># both lists are in descending order</span>
</span></span><span style="display:flex;"><span>	factors_num1 <span style="color:#f92672">=</span> get_factors(num1)
</span></span><span style="display:flex;"><span>	factors_num2 <span style="color:#f92672">=</span> get_factors(num2)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	index <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># the lists are in reverse, biggest goes first, so the</span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e"># first hit would be the greatest common factor.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">while</span> factors_num1[index] <span style="color:#f92672">not</span> <span style="color:#f92672">in</span> factors_num2:
</span></span><span style="display:flex;"><span>		index <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	result <span style="color:#f92672">=</span> factor_num1[index]
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> result
</span></span></code></pre></div><p>Instead, we use comments here <strong>to provide missing information</strong> that is not immediately clear; it says <strong>why</strong> the code is written in a certain way.</p>
<h2 id="the-takeaway">The takeaway</h2>
<p>Commenting is bad; there is always some way to make your code cleaner and more organized without comments. The only times where you <em>should</em> comment is to say <strong>why</strong> the code is there, not <strong>what it does</strong>. Commenting is not an excuse to write bad code, write good code that reads itself later on.</p>
<p>Comments can lie, any compiler/interpreter or even linter will not statically analyze your comments to see if they match your code. However, code doesn&rsquo;t lie, because it has to follow rules. There is no use for commenting if there is a tool that can tell if your code is correct or not.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>One of the many reasons as to why I have beef with the examiners.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Before you bring up how beginner programmers who are not good at programming will not be able to read this, <strong>that is their problem</strong>. Code is meant to be read by programmers, they should learn how to code well before they try to decipher other people&rsquo;s code as in trying to work with them.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The IGCSE Pseudocode to Python Reference Guide (for IGCSE G1 and G2 students)</title><link>http://ezntek.com/posts/the-igcse-pseudocode-to-python-reference-guide-for-g1-and-g2-computer-science-20241018t2049/</link><pubDate>Fri, 18 Oct 2024 20:49:28 +0800</pubDate><guid>http://ezntek.com/posts/the-igcse-pseudocode-to-python-reference-guide-for-g1-and-g2-computer-science-20241018t2049/</guid><description>Another guide for the suffering people!</description><content:encoded><![CDATA[<p><img src="/img/pseudoguidecover.png" width="100%"></img></p>
<p><em>LibreOffice does not know my full name. What a shame!</em></p>
<h2 id="download">DOWNLOAD</h2>
<p><a href="https://ezntek.com/doc/pseudocodereference_rev7.pdf">Revision Seven</a></p>
<h3 id="changelog">Changelog</h3>
<ul>
<li><em>Revision two fixed minor syntax highlighting errors, and added details to the Python half of the functions section for consistency.</em></li>
<li><em>Revision three added a License, the CC-BY-SA-ND 4.0 International License. <strong>all previous versions are also licensed under the same license, if you have it</strong></em>.</li>
<li><em>Revision four fixed inconsistencies in the notes, and fixed minor syntax highlighting errors</em>.</li>
<li><em>Revision five fixed an error in the for loops section where I iterate from a list beginning from 1 not 0</em>.</li>
<li><em>Revision six fixed errors relating to if statements and arrays</em>.</li>
<li><em>Revision seven contains final fix-ups and rephrasing</em>.</li>
</ul>
<h2 id="what">What???</h2>
<p>Yes, count this as a sequel to my <a href="https://ezntek.com/posts/comp-sci-revision-20240509t0944/">last epic guide</a>, which was for the suffering G1 students under <em>magic man</em>&rsquo;s leadership.</p>
<p>This one is for everyone! a <strong>FULLY SYNTAX HIGHLIGHTED</strong> (manually done with blood sweat and tears!) reference with side-by-side examples of common programming tasks in both Pseudocode and Python. Revision One covers:</p>
<ul>
<li>comments</li>
<li>values</li>
<li>conditional branching</li>
<li>loops</li>
<li>procedures</li>
<li>functions</li>
<li>arrays</li>
<li>file handling</li>
</ul>
<p>and more to come if you nicely ask!</p>
<p>This is <em><strong>NOT</strong></em> A comprehensive programming guide! Please do not assume that  this will actually teach you how to code in-depth! When exam season rolls around, I will be authoring a programming tutorial and a theory reference guide just like last year.</p>
<p>stay tuned!!!</p>
<p>(Update as of August 09, 2025: I ended up not finishing the programming guide.)</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Creating an IGCSE Pseudocode Interpreter (part 1)</title><link>http://ezntek.com/posts/creating-an-igcse-pseudocode-interpreter-pt-1-20240918t1045/</link><pubDate>Wed, 18 Sep 2024 10:45:48 +0800</pubDate><guid>http://ezntek.com/posts/creating-an-igcse-pseudocode-interpreter-pt-1-20240918t1045/</guid><description>&lt;h2 id="time-for-the-compiler-engineer-to-manifest">Time for the compiler engineer to manifest&amp;hellip;&lt;/h2>
&lt;p>For the longest time, I have loved compiler engineering.&lt;/p>
&lt;img alt="A compiler's structure, represented by a cow" src="http://ezntek.com/img/cowcompiler.png" width="100%" />
&lt;p>It just sounded so mesmerizing. Being able to take code and analyze it in a multitude of ways to generate something based off of logical reasoning&amp;hellip;all done through an &lt;em>automated program&lt;/em>.&lt;/p>
&lt;p>The possibilities of a pseudocode runtime is endless, I can even self host a compiler on it, eventually!&lt;/p></description><content:encoded><![CDATA[<h2 id="time-for-the-compiler-engineer-to-manifest">Time for the compiler engineer to manifest&hellip;</h2>
<p>For the longest time, I have loved compiler engineering.</p>
<img alt="A compiler's structure, represented by a cow" src="/img/cowcompiler.png" width="100%" />
<p>It just sounded so mesmerizing. Being able to take code and analyze it in a multitude of ways to generate something based off of logical reasoning&hellip;all done through an <em>automated program</em>.</p>
<p>The possibilities of a pseudocode runtime is endless, I can even self host a compiler on it, eventually!</p>
<h2 id="origins-of-the-idea--rationale">Origins of the idea + Rationale</h2>
<p>I take 0478 CS at my school.</p>
<p>My teacher, Let&rsquo;s call him Magic man. Magic man is quite inexperienced for his profession, he is quite good at teaching all topics but actual programming. He is alright at pseudocode, which is why from day 1, he <em>absolutely obliterated</em> our innocent minds with this mess:</p>
<pre tabindex="0"><code>PROCEDURE CanDrinkBeer(Age : INTEGER)
  IF Age &gt; 18
    THEN
      OUTPUT &#34;you can drink beer!&#34;
    ELSE
      OUTPUT &#34;do not drink beer!
  ENDIF
ENDPROCEDURE

DECLARE Age : INTEGER
OUTPUT &#34;what is your age? &#34;
INPUT Age
CALL CanDrinkBeer(Age)
</code></pre><p>(Without types of course. Let us say that he cannot comprehend those very well).</p>
<h3 id="puzzle-piece-1">Puzzle Piece 1</h3>
<p>You can&rsquo;t run pseudocode. That is a given, pseudo is in the name.</p>
<p>Sketchy web interpreters and even desktop interpreters exist, such as <a href="https://cs.coursemo.com/igpc/">this</a> one and another one called <a href="https://github.com/virchau13/pcse">pcse</a>. However there are some issues.</p>
<ol>
<li>Normal programming concenpts cannot even be implemented. Since not all data types are first class citizens, code like so won&rsquo;t even work:</li>
</ol>
<pre tabindex="0"><code>PROCEDURE BubbleSort(Data : ARRAY[1:5] OF INTEGER)
  FOR i &lt;- 1 TO LENGTH(Data) - 1
    FOR j &lt;- 1 TO LENGTH(Data) - i - 1
      IF Data[j] &gt; Data[j+1]
        THEN
          temp &lt;- Data[j+1]
          Data[j+1] &lt;- Data[j]
          Data[j] &lt;- temp
      ENDIF
    NEXT j
  NEXT i
ENDPROCEDURE

DECLARE Data : ARRAY[1:5] OF INTEGER
Data[1] &lt;- 5
Data[2] &lt;- 1
Data[3] &lt;- 6
Data[4] &lt;- 9
Data[5] &lt;- 4

CALL BubbleSort(Data)
OUTPUT Data
</code></pre><p>In the case of pcse, This code simply complains about a missing assignment operator. Strange.</p>
<ol start="2">
<li>I am not even sure if the specification was implemented properly, as omitting the parameter list in a procedure or function doesn&rsquo;t even parse properly.</li>
<li>There is a big lack of convenience features. Array initialization syntax, i.e. <code>Array &lt;- [1, 2, 3, 4, 5]</code> Doesn&rsquo;t even exist, one must spell out each index and each value.</li>
</ol>
<p>There are a few more I could come up with, but at the time of writing (I am in class), I can&rsquo;t think of any more reasons.</p>
<h3 id="puzzle-piece-2">Puzzle Piece 2</h3>
<p>Cambridge actually has quite a <a href="/doc/2023_2025_cs_syllabus.pdf">detailed syllabus</a>, where in the 4th section of the document there exists a concretely defined but loosely phrased grammar for the language. This would actually be quite convenient, I would be able to have a guaranteed-to-be-correct and accurate reference to write a parser for.</p>
<h2 id="the-end">The end?</h2>
<p>This is just the rationale, and the beginning steps. I really want to get an article out, and I have no clue how this project will grow and progress over time.</p>
<p>I have already commenced the creation of a more formal grammar for the language. Stay tuned.</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>The GIIS Hackathon X - My Experience (the finale)</title><link>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-4-20240803t1857/</link><pubDate>Sat, 03 Aug 2024 18:57:43 +0800</pubDate><guid>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-4-20240803t1857/</guid><description>Was everything all for nothing?</description><content:encoded><![CDATA[<h2 id="foreword">Foreword</h2>
<p>Yes, this will take you very long to read because this really should
have been 5 parts. These reading time calculations are also quite
messed up. Not my fault, click away if you don&rsquo;t have patience :p</p>
<h2 id="recap">Recap</h2>
<p>As of the last post, I was on day 2, right before the lunch break. I had just
(almost) finished writing my article display, and I am now ready to start
connecting the API. Everything else works just fine.</p>
<h2 id="lunch">Lunch</h2>
<p>Lunch first before work. However this time, I didnt realyl have too much time
on me to properly enjoy lunch sat down sipping my drink, etc. I really had to
cook.</p>
<p>&hellip;well the lunch suited this perfectly. A depressing chicken wrap!</p>
<p><img alt="The food" loading="lazy" src="/img/hackathon/dickwrap.jpg"></p>
<p>It was not far less the length of my <em>schlong</em> when it is <em>active</em>;
in layman&rsquo;s terms, around 15 centimeters. The diameter was also miniscule,
coming in at around 3-5 centimeters. Overall, a very depressing lunch, but
the good thing would be that I was able to shove it in my mouth within ~3 bites,
which is great.</p>
<h2 id="the-final-coding-session">The final coding session</h2>
<p>This is where I really felt the energy in the room rising. Tensions were high
amongst <em>people who came here to chase excellence, not the complementary Mario
Kart stations</em>, myself included.</p>
<p>We were to submit our work at 2:15 PM, it was around 1:20 when I returned,
Keep that in mind.</p>
<p>I didn&rsquo;t really have time to deal with any sort of API errors, which majorly
backfired later on. I just worked on my recipe cards just a little further.</p>
<p>As mentioned in the previous article, the paragraph would cut off outside
the recipe bubble, like so:</p>
<p><img alt="The card overflowing" loading="lazy" src="/img/hackathon/cardoverflow.png"></p>
<p>Therefore I had to figure out how to chop it off. <code>overflow: hidden;</code> didn&rsquo;t
really work that well, so I had to set the <code>max-height</code>, meaning that the page
resizing would break, too.</p>
<p><img alt="Page resizing" loading="lazy" src="/img/hackathon/pageresizing.png"></p>
<p>After that, API hell commenced.</p>
<h3 id="api-hell">API hell</h3>
<p>I started by making a basic request to</p>
<p><code>https://api.spoonacular.com/recipes/complexSearch?apiKey=&lt;API Key Goes Here&gt;&amp;ranking=2&amp;number=50&amp;addRecipeInstructions=true&amp;addRecipeInformation=true</code></p>
<p>to get a bunch of recipes in JSON format. They displaed just fine on the homepage.</p>
<p>Then, I got to compiling the API options. List arguments were separated by <code>,</code>,
and each option would be in the format <code>&amp;key=value</code>.</p>
<p>I started with an empty string:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">options</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span></code></pre></div><p>I began with the dietary restrictions, which was simple:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">diet</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#39;diet&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">diet</span> <span style="color:#f92672">===</span> <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">diet</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Non-Vegetarian&#39;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">options</span> <span style="color:#f92672">+=</span> <span style="color:#e6db74">`&amp;diet=</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">diet</span>.<span style="color:#a6e22e">replace</span>(<span style="color:#e6db74">&#39;-&#39;</span>, <span style="color:#e6db74">&#39; &#39;</span>).<span style="color:#a6e22e">toLowerCase</span>()<span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span></code></pre></div><p>Note that I have to replace <code>-</code> with <code> </code>, which is what the API requires.
I set the enum members to enumerate strings in the second post, with dashes
for display purposes. But for querying, I had to do the above. I do also
set a default just in case the user did not set anything prior, but a settings
class would streamline this process of having a default, along with other
null-safety things. I didn&rsquo;t have time to mess with TypeScript (a completely
foreign language to me) at the time, which is why I just stuck with it.</p>
<p>I then did the allergies:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">intolerances</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#39;allergies&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">intolerances</span> <span style="color:#f92672">!==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">intolerances</span> <span style="color:#f92672">!==</span> <span style="color:#e6db74">&#39;&#39;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">newOption</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`&amp;intolerances=</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">intolerances</span>.<span style="color:#a6e22e">replace</span>(<span style="color:#66d9ef">new</span> RegExp(<span style="color:#e6db74">&#39;;&#39;</span>, <span style="color:#e6db74">&#39;g&#39;</span>), <span style="color:#e6db74">&#39;,&#39;</span>).<span style="color:#a6e22e">toLowerCase</span>()<span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">options</span> <span style="color:#f92672">+=</span> <span style="color:#a6e22e">newOption</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Note the <code>RegExp</code> constructor. I had to construct a RegExp class to
properly replace every occurence of <code>;</code> with <code>g</code> which stood for global,
meaning to replace every occurence in a line, not just the first one.
<em>(vim users would know).</em></p>
<p>I also only do the find and replace only if the <code>intolerances</code> variable&rsquo;s
value is set, which honestly could only happen with JavaScript (and therefore
TypeScript). Oh well.</p>
<p>I did the same with the pantry:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#75715e">// get pantry from local storage
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">includeIngredients</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#39;pantry&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">includeIngredients</span> <span style="color:#f92672">!==</span> <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">newOption</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`&amp;includeIngredients=</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">includeIngredients</span>.<span style="color:#a6e22e">replace</span>(<span style="color:#66d9ef">new</span> RegExp(<span style="color:#e6db74">&#39;;&#39;</span>, <span style="color:#e6db74">&#39;g&#39;</span>), <span style="color:#e6db74">&#39;,&#39;</span>).<span style="color:#a6e22e">toLowerCase</span>()<span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">options</span> <span style="color:#f92672">+=</span> <span style="color:#a6e22e">newOption</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I was then ready to construct the URL:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">url</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`https://api.spoonacular.com/recipes/complexSearch?apiKey=</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">apikey</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&amp;ranking=2&amp;number=200&amp;addRecipeInstructions=true&amp;addRecipeInformation=true</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">options</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span></code></pre></div><p>Simple enough. I then wrote some basic code with the fetch API I just learned
(XMLHttpRequest was too old and complicated for me):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">response</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">url</span>, { <span style="color:#a6e22e">method</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;GET&#39;</span> })
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">text</span>()
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">error</span>(<span style="color:#a6e22e">error</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>which would save the text to the <code>result</code>. Response would return a promise
(a future because I&rsquo;m a rust dev), and I&rsquo;d then parse the text and append
every new <code>Recipe</code> class to a reactive list (in the try block).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">results</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">AnyObject</span>&gt; <span style="color:#f92672">=</span> <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">parse</span>(<span style="color:#a6e22e">result</span>)[<span style="color:#e6db74">&#39;results&#39;</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">results</span>.<span style="color:#a6e22e">forEach</span>((<span style="color:#a6e22e">recipeObj</span>: <span style="color:#66d9ef">AnyObject</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">recipes</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">push</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Recipe</span>(<span style="color:#a6e22e">recipeObj</span>))
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>All of this would be in an async closure, keep that in mind.</p>
<p>Now, the spoonacular API doesn&rsquo;t actually randomize the recipes that it gets,
only by relevance. I didn&rsquo;t know that, so I set the load more button on the
bottom of the page to just run this callback again, which would result in
the recipes duplicating.</p>
<p>Not good. So I decided to actually clear the array of recipes when the user
hit the load more button, and load, say, 20 more recipes than the previous
number of recipes.</p>
<p>The code should have looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">results</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">AnyObject</span>&gt; <span style="color:#f92672">=</span> <span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">parse</span>(<span style="color:#a6e22e">result</span>)[<span style="color:#e6db74">&#39;results&#39;</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">recipes</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">results</span>.<span style="color:#a6e22e">forEach</span>((<span style="color:#a6e22e">recipeObj</span>: <span style="color:#66d9ef">AnyObject</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">recipes</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">push</span>(<span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Recipe</span>(<span style="color:#a6e22e">recipeObj</span>))
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>but instead, on my initial run, I got ratelimited. I scrambled to make another
account to get a fresh API key, but I got insta-rate-limited once again. One
more key? Ratelimited again.</p>
<p>See, the spoonacular API has this points limit. Not requests, but points.
To this day, I still do not know how these points are calculated, but basically,
if you try to make too many requests, the API would rate limit you and refuse
to give you more recipes, instead asking you to pay some cash to get the more
premium tiers.</p>
<p><img alt="API rate limit" loading="lazy" src="/img/hackathon/apiratelimit.png"></p>
<p>At this point, it was too much for me to handle. It was around 2:13 PM when I
began testing and I really couldn&rsquo;t hit the submission deadline of 2:15.</p>
<p>I approached a volunteer for help. Turns out his name was Tagore, keep that name
in mind. He suggested using tempoary e-mails which would be fair, if only if
spoonacular allowed you to spam keys with tempoary e-mails. Things were
looking grim, meaning that I had to revert the commit that I worked on back to
the version with duplicating entries.</p>
<p>It was only after the event that I realized that there was some sort of recursion
without a predicate happening, but I scanned through the code not to find anything useful.</p>
<p>My hands were stone cold, I was shaking, jittering and thinking aobut everything
that could go wrong in the next 3 hours, wondering if I had done all this for
nothing. I could barely even type on my keyboard while rushing my submission
into the Google Form because I was so stressed yet pissed that spoonacular really
rate-limited me at this time. Oh well.</p>
<h2 id="the-aftermath-of-the-coding-sessions">The aftermath (of the coding sessions)</h2>
<p>So what have I made? A recipe scroller. You can tailor search results to your
dietary needs and the ingredients you have at home to get a curated list of
food that you could make.</p>
<video controls width="100%">
    <source src="/img/hackathon/fulldemo.mp4" type="video/mp4">
</video>
<p>Cool enough for a small side project, but genuinely, in a professional and
more mature setting in front of more experienced programmers, this would not be
anything. If anything, I genuinely thought I was going to go dead last.</p>
<p>But I kept thinking; maybe all those small children weren&rsquo;t secretly senior developers.</p>
<p>Either way, there was nothing to lose, nothing to give up on the table. I am competing
against small children and a few professional developers, what could they do to me?</p>
<p>These two conflicting thoughts flooded my mind as I went back into ICT lab S3-06,
the very first room I entered in this competition; a day ago I had nothing but now
I have a semi-broken app, and my fate were to be decided.</p>
<p>It was like 2:30, I was ready for the worst.</p>
<h2 id="the-room">The room</h2>
<p>The not cold enough air coming out of the air conditioner settled on me as I slowly
inched my way into the room, to pull a red chair with an armrest and a headrest,
the only one of its kind in the lab.</p>
<video controls width="50%">
    <source src="/img/hackathon/pitchroom.mp4" type="video/mp4">
</video>
<p>As I sat there and waited for the judges to discuss something (which I still do not
know), I decided to put on some Wii Shop Channel music on through my right earbud
to try to make the situation less tense.</p>
<p><strong>You may even join me in listening as I describe the rest of the event.</strong></p>
<audio controls width="100%" loop>
    <source src="/img/hackathon/shopchannel.mp3" type="audio/mp3">
</audio>
<p>Oh, and before presentations actually started, of course there had to be a false
fire alarm. Only in the Global International Indian School :)</p>
<video controls width="50%">
    <source src="/img/hackathon/firealarm.mp4" type="video/mp4">
</video>
<p>But wait. Take a look at the first video.</p>
<p>You may see a man, a figure wearing a tan shirt with an IdeaPad (ew why not a ThinkPad)
with a slightly goofy face.</p>
<p>Let&rsquo;s zoom in.</p>
<img src="/img/hackathon/judgetagore.png" width="30%" />
<p>This guy would play a pivotal part in my journey, nothing would have ever happened
without the presence of Judge Tagore. More on him later.</p>
<h2 id="the-judges-and-the-pitches">The Judges and the Pitches</h2>
<p>There were 2 judges. One had a clean shave and one still had facial hair. Let as call the
one with facial hair &ldquo;facial hair&rdquo;. The clean shaved one I know the name of, which is why
he will be referred to as Judge Tagore.</p>
<p>Revisiting part 1 and the judging system, this is what it was:</p>
<table>
  <thead>
      <tr>
          <th>criteria</th>
          <th>marks</th>
          <th>description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Creativity</td>
          <td>25%</td>
          <td>How innovative it is, and</td>
      </tr>
      <tr>
          <td>Technical complexity</td>
          <td>25%</td>
          <td>The complexity, with comments and documentation</td>
      </tr>
      <tr>
          <td>Functionality and usability</td>
          <td>25%</td>
          <td>User-friedliness and real-life applications</td>
      </tr>
      <tr>
          <td>Pitching</td>
          <td>15%</td>
          <td>Being able to answer questions regading the code</td>
      </tr>
      <tr>
          <td>Software-dependent marks</td>
          <td>10%</td>
          <td>AI would be data representation, Game development would be game design, app development would be the UI, and web dev would be the layout.</td>
      </tr>
  </tbody>
</table>
<p>all I had to do is make my product as presentable as possible to the judges and just rely on
trusting myself with the quality of my app in order to not lose that boldness.</p>
<p>I was quite far down the list, but here we go~</p>
<h3 id="pitches">Pitches</h3>
<p>Before every pitch, the judges would always tell the room to shut their mouths,
else they would deduct points from the team for being too noisy and disrespectful.</p>
<p>Huge respect to those guys, they tried their best to keep the kids in check and
they kinda did it.</p>
<p>Pitches were done via Apple&rsquo;s AirPlay (proprietary wifi screen casting between
iDevices, basically) to an Apple TV mounted on the back of a massive television,
connected by HDMI.</p>
<p><img alt="Diagram" loading="lazy" src="/img/hackathon/airplay.png"></p>
<p>They didn&rsquo;t have an HDMI cable available for other devices, so we kind of had
to use the school computer at the desk to pitch to the whole room, else we had
to just present to the judges and two supervising teachers. As I&rsquo;d soon find
out, everybody who pitched to the audience got to use their public speaking
skills more and draw attention from the whole room. This, along with other
usually leads to people who can AirPlay to get higher scores.</p>
<p>Basically, we <em>had</em> to have some sort of iDevice in order to pitch like a
champ. I genuinely thought the iCult of iDevices didn&rsquo;t extend to my fate
in a hackathon, but it did. I love you Apple, and your overly loyal
customers! You should suck my d-</p>
<h3 id="not-so-honorable-mention-1">Not-so-honorable mention #1</h3>
<p><em><strong>a scratch game</strong></em>. I almost laughed out loud when I saw a scratch game. At myself,
I stressed so much about this competition but this scratch game completely got
rid of all my stress. It was a Junior team and they were literally just getting
started, but my mood immediately rose as I came to see a glorious green flag on
the screen with the 3 team members, who basically just dragged blocks around
and relied on how simple scratch is to make a game. Wonderful, I wish I could do that ;(</p>
<p>I posted this on my story:</p>
<img src="/img/hackathon/juniors.jpg" width="50%">
<p>as I waited my turn.</p>
<p>It was some kind of game, I forgot what it was. I just kept listening to Wii
Shop Channel music while waiting my turn, waiting for all the teams&rsquo; submissions
to fly by.</p>
<p>As I both eagerly and scarily stared at the presenting order, slowly seeing the
contestants present their scratch and thunkable apps, I only became slightly
more hopeful.</p>
<h3 id="honorable-mention-1">Honorable Mention #1</h3>
<p>First honorable mention. I forgot this guy&rsquo;s team name (I think Ubistrong? not sure)
but he made a completely bespoke website in HTML, with a little bit of JS
and a lot of CSS. It was not custom and there was no name prefixing (LMAO IMAGINE),
he just copied a lot from past projects, but it was still cool.</p>
<p>It was about water conservation and it was meant to raise awareness on
a core element of life (life on all elements), and he had quite a lot
of information jam-packed inside. The layout and style was very pretty to
look at, and the programmer art was respectable on the background.</p>
<p>Unfortunately I don&rsquo;t have any photos (I have this terrible habit of not
having any) but this one was still very cool.</p>
<h3 id="not-so-honorable-mention-2">Not-so-honorable mention #2</h3>
<p>I forgot how many pitches went by after the first one, but this one was also
from a Junior team. I really do not want to bash them, but this one was a
very cursed scratch flappy bird clone. The sprites were (of course) random
PNGs and Emojis (not even programmer art, what a shame), and all the obstacles
were on the floor.</p>
<p>This one barely related to the theme at all, the pitch was terrible, and I
still will not ever understand the appeal of playing flappy bird when all the
obstacles are on the floor. Oh well, not my fault-</p>
<p>Everything up to this point were Junior teams if I remember correctly, nothing
was cool and my confidence was quite high. A multitude of teams were missing
for no reason whatsoever; the order went absolute bonkers as teams randomly
spawned in.</p>
<p>Eventually it was my turn.</p>
<h3 id="my-pitch">My Pitch</h3>
<p>I got up from my seat and approached the school computer. It had failed a
little while ago, it lost power during a pitch and it had been really unstable.</p>
<p>They did warn me that this could very much happen again, but I persisted.</p>
<p>After a few reboots, it appeared to be stable enough for a presentation.</p>
<p>My plan was to run <code>vite --host</code> to host my app on the local network, and then
make the iMac visit my server on the local network to get the site. Simple enough.
After a simple WiFi Network change (WHY NOT ETHERNET) it worked like a charm.</p>
<p>My heart was pounding, but I was ready. As Tagore and Facial Hair Man proceeded
to tell everyone to shut their mouths up, I quickly changed the iMac to dark mode
to ensure the color scheme would load properly, and when they were done, I began.</p>
<p>With a bold voice,</p>
<blockquote>
<p>See, there are many elements required for any living organism including humans
to survive. They include air, (pause) water (pause) and food.
However, we are not cows or sheep. We cannot simply lie down on the floor and
munch on grass and call it a day, we require something more than that. We must
prepare our food in a way that it will taste good to us and also provide
adequate nutrition to meet our dietary needs.</p></blockquote>
<p>I opened the minimized browser, and I begain, while worrying about me getting
ratelimited.</p>
<p>I gave them a demo similar to this (I didn&rsquo;t rehearse this one, but I did on
pitch day):</p>
<video controls width="100%">
    <source src="/img/hackathon/fulldemo.mp4">
</video>
<p>Of course I had a commentary going on, with large hand gestures and pointing
to the display to highlight important features, and EVERYBODY seemed to love it.</p>
<p>And about Judge Tagore. His face lit up in awe after I showed him the dynamism
of my program. His eyes broadened line a blooming flower, he attempted to standd up
while in awe. He froze mid-stand with his hand covered and decided to sit back down.</p>
<p>Turns out, he went to call Aditya Bairy, the head of the tech club. While the commotion
in the room grew, my legs started becoming soft due to shock and confusion- was all this
custom CSS that cool? I started making some of the weirdest screams that sounded more
like whines as I stood there shook, Magic Mouse in hand.</p>
<p>Despite how I listed out all the app&rsquo;s shortcomings, like the lack of a search and filter
feature, everybody was still in utter shock.</p>
<p>A loud round of applause filled the air to the brim, as Aditya approached. Tagore
demanded him give me a demo. Everybody wanted me to play with the app more. I felt
like all the knots tied up in my heart were undoing themselves as I let out
a huge, massive sigh of relief; even. I couldn&rsquo;t believe my eyes.</p>
<p>This had to be one of the highest energy moments in my programming life. Everybody was
convinced I was gonna win; a Junior team of all girls had even started a conversation with me.
They complimented me and said that my app was so cool, and they really were rooting for me,
and they insisted that I would win first place. They even recognized me as the person
who had his computer out coding in the opening ceremony; IT WAS ME writing my blog post,
the first one, the very one that I published a couple of days ago.</p>
<p>The teachers even asked me which school I went to after I sat back down.</p>
<p><em><strong>HAHAHAHAHA.</strong></em></p>
<p>I go to OFS, I said, and they were convinced that the teachers would teach us all this at
grade 9. All the OFSers should know about the tragedies that we encounter. I really should
write about Mr. Hubbard&rsquo;s atrocities. A man who doesn&rsquo;t even know what a function is
who plaigarizes his teaching material from random books who taught us SQL a year early due
to lack of skill was our course leader, one who I had to constantly correct because of
<em><strong>idiotic</strong></em> mistakes like saying that you can <strong>cast</strong> a function to run the code inside,
<em><strong>not call</strong></em>.</p>
<p>I really couldn&rsquo;t let Mr. Hubbard&rsquo;s lack of skill ruin this moment. I just told the teachers
that our school does not teach this at all and I had to learn all this myself from the start
of the hackathon. I said that I did programming since 6th grade.</p>
<p>They asked which curriculum we did, I said we did IGCSE and MYP together. They then asked
if I took computer science, I said I did just for the academic credit because I already knew
most of the syllabus. They asked if this was my first hackathon, I said yes, because I
did more niche things and that I prefer to stay in my own cave and code instead of coding
outside.</p>
<p>My energy level was high, but I couldn&rsquo;t be too hopeful. There were still pitches left.</p>
<p>I actually think that they were much cooler than mine.</p>
<h3 id="honorable-mention-2">Honorable Mention #2</h3>
<p>Immediately after my pitch, came core.</p>
<p>This team is <em><strong>cracked</strong></em>, to say the least.</p>
<p>A group of two quite dark skinned people who go to Singapore Polytechnic. This was their
52nd hackathon. <em><strong>FIFTY-SECOND</strong></em>.</p>
<p>They took gardening, which is the act of maintaining living plants&rsquo; health (theme) to
a whole new level, They used an esp32 as their µController, loaded <strong>6</strong> AI models on it
and hooked up a camera to detect if a plant were healthy or not. Depending on that, along
with a humidity and temperature sensor all mounted below the plant pot that they 3D printed,
it will water the plant and also send the plant&rsquo;s state to the user via Telegram and
a Telegram bot, all running on the esp32 with network access.</p>
<p><img alt="The product" loading="lazy" src="/img/hackathon/core.jpg"></p>
<p>This project was crazy; Judge Tagore had already been fazed and exposed to skill, so
he wasn&rsquo;t as shocked, but this one was even crazier than mine because Core incorporated
so many fields of Computer Science and even some biology into this hackathon project.</p>
<p>Almost all of this was written in C++, also very impressive.</p>
<h3 id="honorable-mention-3">Honorable Mention #3</h3>
<p>This one is from the Destiute Dingbats, a team actually from our school, those very kids
from places far and wide. They decided to not AirPlay for whatever reason despite having a
MacBook Air on hand, but I digress.</p>
<p>They took the theme and adapted it to a mood tracker/calendar thingy. They interpreted
elements as in elements of life, and made it very simple and minimalistic, stating
life on its core elements. A very neat concept for sure.</p>
<p><img alt="Blurry picture" loading="lazy" src="/img/hackathon/fromplacesfarandwide.jpg"></p>
<p>Again, I forgot what they did exactly, but it definitely had elements of dynamism,
it was very clean and minimal and it actualy functioned quite well. There were no CSS
animations, which was understandable because it isn&rsquo;t easy anyways.</p>
<p>Coming from students who were only taught IGCSE ICT at school (HTML4 and CSS2) and
without knowledge of component based design or any more advanced frontend design concepts,
this was very impressive. They should have done programming outside of ICT before though,
if I remember correctly.</p>
<h3 id="honorable-mention-4">Honorable Mention #4</h3>
<p>This team of 4 made a whole role-playing game, simulating the contsruction of a civilization,
including the process of taking control of the native people, along with other
concepts such as the construction of housing and sustainable fishing and farming
all in this small package. They have had experience doing game jams before as I asked,
but it was still quite cool.</p>
<p>The animations were wonky. The characters just rotated around a pivot situated around
the middle-ish of their body at a smooth rate of 10° ish each side, which did not look
very good. The programmer art was nice though.</p>
<p><img alt="The game" loading="lazy" src="/img/hackathon/godotgame.jpg"></p>
<p>The controls were quite interesting as I saw. The judges interacted with the game a bit
(which was written in Godot unfortunately, I&rsquo;m using SDL and OpenGL with C next year haha.),
and the demo was quite cool although I did forget many aspects of it. They did also preach
Fish and Chips as a very sustainable food source though.</p>
<p>I dont want to eat bland food despite how sustainable it could be. <em>Let me eat my
red braised pork and samosas.</em></p>
<h3 id="honorable-mention-5">Honorable Mention #5</h3>
<p>This team of pure Asian talent cooked together an extremely complicated algorithm to
figure out sleep timings.</p>
<p><img alt="The people" loading="lazy" src="/img/hackathon/asiantalent.jpg"></p>
<p>Given a schedule, the algorithm would find out which sleep pattern (monophasic, biphasic
or polyphasic sleep) the user is best suited to, and a new schedule would be denegrated
with optimal sleep times and with regard to their activities.</p>
<p>They had an insanely long presentation, which toured each part of the codebase. Unfortunately,
the judges did not really want to look at it for some reason despite all the effort
they put into the presentation, incuding all the math needed to figure out the time complexity.
The C++ they wrote was not the best at all (not expecting anything crazy, no formatter
and no C++ features were even used, it could have easily been done in C), but its still very cool
what they pulled off.</p>
<p>They also wrote a schedule parser which would let you write a custom syntax to describe
your schedule (which would be very annoying to test and implement being a compiler engineer
wannabe), and they even trained a custom GPT-4 to optimize the results of their algorithm
further. Unfortunately, it did not translate too well to their website, as it was mostly
informative, the UI did not look very good and it was quite shotty overall.</p>
<h2 id="the-end">The End?</h2>
<p>I nervously waited for the closing ceremony to commence while eating some mystery
potato and chaat in yogurt sauce with ketchup.</p>
<p><img alt="Funny tasting food" loading="lazy" src="/img/hackathon/funnyfood.jpg"></p>
<p>I really didn&rsquo;t think I would win first place because Core did such a good job with
their plant, but maybe I&rsquo;d win second, or third, or maybe nothing. I didn&rsquo;t know.</p>
<p>The closing ceremony began with speech after speech as I nervously watched as the Juniors
claimed their awards.</p>
<p>They did have 2 honorable mentions which I both also shouted out, but I forgot which
teams exactly. I waited anxiously as they announced at third place was Code Lovers
(i forgor what they made)</p>
<p><img alt="Third Place" loading="lazy" src="/img/hackathon/thirdplace.jpg"></p>
<p>hats off to them.</p>
<p>And after a little more anxious waiting, came</p>
<blockquote>
<p>And at second place, we have&hellip;ezntek!</p></blockquote>
<p><em><strong>oh yes</strong></em>.</p>
<p><em><strong>I won</strong></em>.</p>
<p><img alt="i won" loading="lazy" src="/img/hackathon/iwon.jpg"></p>
<p>Usually I look like a scarecrow. Dead serious face with perpetually tired looking
eyes and zero smile, but here I couldn&rsquo;t hide it. I won second place while at
my FIRST hackathon! Very nice.</p>
<p>Second place was fair, which meant at first place there had to be&hellip;</p>
<p><em><strong>CORE!</strong></em></p>
<p>Those guys were actually cracked, and they definitely deserved it. Considering
how I rawdogged TypeScript and Vue as a low level systems enthusiast/dev who uses
C and Rust more than anything, and how Core had attended over 50 hackathons
prior to this event, I really did not expect anybody but them to win.</p>
<p><img alt="first place" loading="lazy" src="/img/hackathon/firstplace.jpg"></p>
<p>This is the end of a journey; of random ass roadblocks, 2 rides on the PGLRT that
I am looking forward to not riding again, an API rate limit and Judge Tagore completely
saving me and flabbergasting me with that face. God, you did NOT have to open your mouth
that wide, you could have gotten lockjaw ;)</p>
<p>I thank everyone who made this event possible, including the president Aditya Bairy,
designer Ashwath who demanded an appearance (make sure your friends dont bully
you for demanding for an appearance on this site who noone visits), the VP of the club
whose name I do not know, Judge Tagore and Mr. Facial hair who yelled at the kids,
Akshat and all the other people.</p>
<p><img alt="Ashwath" loading="lazy" src="/img/hackathon/ashwath.png"></p>
<p>Not to mention the MC&rsquo;s which I believe were Twisha Metha and some other guy. Too many
names, SORRY IF I GET ANY WRONG (please correct me in the comments)</p>
<p>(You really should have put your names in some credits sheet so people can shout you out smh)</p>
<h2 id="random-ass-other-shit">Random ass other shit</h2>
<p>To the photographer who took this photo, please take a look at this:</p>
<div style="display: flex;">
    <img src="/img/hackathon/secondplacewalking.jpg" alt="Me" width="50%" />
    <img src="/img/hackathon/communistbunny.jpg" alt="Not Me" width="50%" />
</div>
<p>(do not meme me)</p>
<h2 id="my-username">MY USERNAME</h2>
<p>ITS EZNTEK. yeah right.</p>
<p>E as in Feed.
ZN as in Zin, with an I. Similar to the Sin in Singapore.
TEK as in Tech.</p>
<p>No capitalization required. Pronouncing it</p>
<p>/ɛznt̪ɛk/ is a bit of a crime. sorry-</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>The GIIS Hackathon X - My Experience (part 3)</title><link>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-3-20240801t1539/</link><pubDate>Thu, 01 Aug 2024 15:39:11 +0800</pubDate><guid>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-3-20240801t1539/</guid><description>More vue.</description><content:encoded><![CDATA[<h2 id="intro--recap">Intro &amp; Recap</h2>
<p>As of August 1st, I have been quite busy over the past few days. I went out
with friends, and a lot of new hardware parts arrived, such as SOIC-8 clips
(that I had to assemble), my T440p keyboard (finally!!), and other such things.
This explains why I haven&rsquo;t uploaded part 3 yet, but here it finally is!</p>
<p>Some source code examples might deviate from the actual source code at
<a href="https://github.com/ezntek/foodscroller">https://github.com/ezntek/foodscroller</a> for clarity sake.</p>
<p>If you haven&rsquo;t really read the first and second parts, I urge you to do so
<a href="https://ezntek.com/posts/the-giis-hackathon-x-a-review-part-1-20240726t1322/">here</a>
and <a href="https://ezntek.com/posts/the-giis-hackathon-x-a-review-part-2-20240729t1317/">here</a>.
To summarize the second article, I met some pretty cool people and almost
teamed up with them, but ended up not in the end. I didn&rsquo;t have any ideas until
I had a samosa during breaktime, and I started implementing my app in Vue.js.</p>
<p>I finished the navbar, and worked on the settings page.</p>
<h2 id="going-home">Going home</h2>
<p>This took about an hour. I had 3 choices for a bus, bus 3, 39 and 81. 81 would
take the longest, as I&rsquo;d have to ride the <em>wonderfully engineered</em> Punggol
LRT (which is more like an Airport shuttle train, not a tram. It&rsquo;s called an
Automated People Mover System) with wonderful acceleration curves that are not
jarring (sarcasm); for 2 stops, then only walking for another 10 minutes to get to the
bus stop after crossing 4 crossings to board the bus. The Bus 3 bus would have taken
over 10 minutes to arrive at the bus stop I had to go to, so I decided on 39.</p>
<p>Turns out that was quite a bad idea, I had to walk the distance of 2 LRT stops,
and Google Maps routed my journey wrong. it told me to cross at a crossing that
doesn&rsquo;t even exist! (I had to walk back and take 2 sets of crossings, don&rsquo;t ask.)</p>
<p>It took me an hour to get home, double that than if bus 3 came earlier. Oh well!</p>
<h2 id="the-nighttime-programming-session">The nighttime programming session</h2>
<p>This is when I got the majority of my work done. I slept at 1:30 and stopped
programming at 1 and only at 1 (I came back at 9, which meant a 5 hour programming/
procrastination session), but oh well.</p>
<h3 id="finishing-the-settings-page">Finishing the settings page</h3>
<p>I lied in the previous article. I actually only finished the settings bubble
up to that point, and I did not have any of the settings stuff working.</p>
<p>I worked on the component props to decide the color of the bubble and whether or
not to show the plus icon or not, which took a while.</p>
<p>To get the color to change, I had to conditionally switch out a CSS class based
on JS (TypeScript) state. Luckily, Vue.js has this thing called the <code>v-bind</code> directive,
where you can write something like</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">v-bind:href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.activeLink&#34;</span>&gt;My Link&lt;/<span style="color:#f92672">a</span>&gt;
</span></span></code></pre></div><p>to actually bind JS state to an HTML prop. Its short form looks as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">a</span> <span style="color:#a6e22e">:href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.activeLink&#34;</span>&gt;My Link&lt;/<span style="color:#f92672">a</span>&gt;
</span></span></code></pre></div><p>which is what I used to conditionally swap CSS.</p>
<h4 id="interlude-css-naming-conventions">Interlude: CSS Naming Conventions</h4>
<p>Despite how you may have a scoped CSS block in Vue, like</p>
<pre tabindex="0"><code>&lt;style scoped&gt;
.title {
    font-weight: 800;
}
&lt;/style&gt;
</code></pre><p>For some reason, on my end, If I defined this CSS class in a child SFC,
it would still apply the CSS class in the parent, which was very annoying.</p>
<p>Therefore, I had to prefix everything the C style which basically meant</p>
<p><code>component-name-class-name</code></p>
<p>just like how you have to prefix functions and <code>typedef</code>s in C for
&ldquo;namespacing&rdquo;.</p>
<h4 id="more-settings-page">More settings page</h4>
<p>I had defined a base class for the div, which is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span>.<span style="color:#a6e22e">diet-bubble-container</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">border-radius</span>: <span style="color:#ae81ff">8</span><span style="color:#66d9ef">px</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">padding</span>: <span style="color:#ae81ff">0.3</span><span style="color:#66d9ef">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">margin</span>: <span style="color:#ae81ff">0.3</span><span style="color:#66d9ef">em</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">diet-bubble-container-blue</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">background-color</span>: <span style="color:#a6e22e">var</span>(<span style="color:#f92672">--</span>vt<span style="color:#f92672">-</span>c<span style="color:#f92672">-</span><span style="color:#66d9ef">blue</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">diet-bubble-container-green</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">background-color</span>: <span style="color:#a6e22e">var</span>(<span style="color:#f92672">--</span>vt<span style="color:#f92672">-</span>c<span style="color:#f92672">-</span><span style="color:#66d9ef">green</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>and in the template section, I used the v-bind directive to conditionally
change the color by doing something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;diet-bubble-container&#34;</span> <span style="color:#a6e22e">:class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.showPlus ? &#39;diet-bubble-container-green&#39; : &#39;diet-bubble-container-blue&#39;&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">&lt;!-- contents --&gt;</span>
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p>where every DietBubble container would have the base CSS class assigned to it,
and a prop would change what the second class would be (either a green bubble or a blue bubble).</p>
<p>And to show the icon, I did</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">FontAwesomeIcon</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.showPlus&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;far.faSquarePlus&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;diet-bubble-container-tick-icon&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color: var(--vt-c-white-soft)&#34;</span>
</span></span><span style="display:flex;"><span>/&gt;
</span></span></code></pre></div><p>to conditionally render the FA icon. I then assigned a CSS class to it that
would set <code>display: none;</code> but <code>display: inline-block;</code> when the parent div
was hovered like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span>.<span style="color:#a6e22e">diet-bubble-container-tick-icon</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">none</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* other styles */</span> 
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span>.<span style="color:#a6e22e">diet-bubble-container</span>:<span style="color:#a6e22e">hover</span> .<span style="color:#a6e22e">diet-bubble-container-tick-icon</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">inline-block</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="allergies">Allergies</h3>
<p>The story is basically the same, the components are also identical. However, I
also display a Font Awesome cross icon when hovering the green bubble, as one
should be able to freely remove their allergies.</p>
<p>I just used the <code>v-else</code> directive, like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">FontAwesomeIcon</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.showXmark&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;far.faCircleXmark&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;allergy-bubble-container-x-icon&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color: var(--vt-c-white-soft)&#34;</span>
</span></span><span style="display:flex;"><span>/&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">FontAwesomeIcon</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">v-else</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;far.faSquarePlus&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;allergy-bubble-container-x-icon&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color: var(--vt-c-white-soft)&#34;</span>
</span></span><span style="display:flex;"><span>/&gt;
</span></span></code></pre></div><p>Showing the icon for each allergy was also trivial, I just used <code>v-if</code> and
<code>v-else-if</code> chaining to get it done:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;allergy-bubble-container-icon&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Dairy&#34;</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faCow&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Egg&#34;</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faEgg&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">FontAwesomeIcon</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Gluten&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faBreadSlice&#34;</span>
</span></span><span style="display:flex;"><span>    /&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Seafood&#34;</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faFish&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Shellfish&#34;</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faShrimp&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Soy&#34;</span>&gt;🫘&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Peanut&#34;</span>&gt;🥜&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">v-else-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.allergy == Allergy.Wheat&#34;</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faWheatAwn&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">span</span>&gt;
</span></span></code></pre></div><p>In the end, it looks like this:</p>
<video controls width="100%">
    <source src="/img/hackathon/allergydemo.mp4" type="video/mp4" />
</video>
<p>Storing the state was not too hard. Local storage does only permit you to store
basic JavaScript primitives and not arrays, meaning that I had to concatenate all
elements into a string and seperate them with a separator. Originally I picked <code>;</code>,
but I didn&rsquo;t account for how the API requires <code>,</code>, meaning that I had to do a find
and replace operation later on.</p>
<p>It was as simple as</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">addAllergy</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">allergy</span>: <span style="color:#66d9ef">Allergy</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">selectedAllergies</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">allergy</span>)
</span></span><span style="display:flex;"><span>    window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#e6db74">&#39;allergies&#39;</span>, <span style="color:#a6e22e">selectedAllergies</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;;&#39;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>to concatenate all elements into a string and store it.</p>
<p>I also wrote the <code>onBeforeMount</code> hook to do some work before a widget is mounted onto
the DOM to load in the settings, which looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#a6e22e">onBeforeMount</span>(() <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">diet</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#39;diet&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">allergies</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#39;allergies&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">allergies</span> <span style="color:#f92672">===</span> <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">allergies</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">allergies_arr</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">allergies</span>.<span style="color:#a6e22e">split</span>(<span style="color:#e6db74">&#39;;&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;loaded&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">selectedDiet</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">diet</span> <span style="color:#f92672">!==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">?</span> (<span style="color:#a6e22e">diet</span> <span style="color:#66d9ef">as</span> <span style="color:#a6e22e">Diet</span>) <span style="color:#f92672">:</span> <span style="color:#a6e22e">Diet</span>.<span style="color:#a6e22e">Vegetarian</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">selectedAllergies</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">allergies</span> <span style="color:#f92672">!==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">?</span> (<span style="color:#a6e22e">allergies_arr</span> <span style="color:#66d9ef">as</span> Array&lt;<span style="color:#f92672">Allergy</span>&gt;) <span style="color:#f92672">:</span> []
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>All in all, this gives us a working settings page, with proper storage in local storage.</p>
<p>Woohoo!</p>
<h3 id="the-pantry">The Pantry</h3>
<p>Again, the pantry bubble was also trivial. I didn&rsquo;t even need to store an icon (as implementing
icons through basic string matching would take FAR TOO LONG), which results in a simple SFC:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vue" data-lang="vue"><span style="display:flex;"><span>&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ts&#34;</span> <span style="color:#a6e22e">setup</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">far</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;@fortawesome/free-regular-svg-icons&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">FontAwesomeIcon</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;@fortawesome/vue-fontawesome&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">Props</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">itemName</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">defineProps</span>&lt;<span style="color:#f92672">Props</span>&gt;()
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pantry-item-container&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pantry-item-container-item-name&#34;</span>&gt;{{ <span style="color:#a6e22e">$props</span>.<span style="color:#a6e22e">itemName</span> }}&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">FontAwesomeIcon</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;far.faCircleXmark&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pantry-item-container-x-icon&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color: var(--vt-c-white-soft)&#34;</span>
</span></span><span style="display:flex;"><span>        /&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">style</span> <span style="color:#a6e22e">scoped</span>&gt;
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">display</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">flex</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">border</span><span style="color:#f92672">-</span><span style="color:#a6e22e">radius</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">8</span><span style="color:#a6e22e">px</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">padding</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0.3</span><span style="color:#a6e22e">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">margin</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0.3</span><span style="color:#a6e22e">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">background</span><span style="color:#f92672">-</span><span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">var</span>(<span style="color:#f92672">--</span><span style="color:#a6e22e">vt</span><span style="color:#f92672">-</span><span style="color:#a6e22e">c</span><span style="color:#f92672">-</span><span style="color:#a6e22e">blue</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">name</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">font</span><span style="color:#f92672">-</span><span style="color:#a6e22e">weight</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">700</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">var</span>(<span style="color:#f92672">--</span><span style="color:#a6e22e">vt</span><span style="color:#f92672">-</span><span style="color:#a6e22e">c</span><span style="color:#f92672">-</span><span style="color:#a6e22e">white</span><span style="color:#f92672">-</span><span style="color:#a6e22e">soft</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span><span style="color:#f92672">-</span><span style="color:#a6e22e">icon</span> <span style="color:#a6e22e">FontAwesomeIcon</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">margin</span><span style="color:#f92672">-</span><span style="color:#a6e22e">top</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0.3</span><span style="color:#a6e22e">em</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span> .<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span><span style="color:#f92672">-</span><span style="color:#a6e22e">x</span><span style="color:#f92672">-</span><span style="color:#a6e22e">icon</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">display</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">none</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">margin</span><span style="color:#f92672">-</span><span style="color:#a6e22e">top</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0.3</span><span style="color:#a6e22e">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">margin</span><span style="color:#f92672">-</span><span style="color:#a6e22e">left</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">0.5</span><span style="color:#a6e22e">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">var</span>(<span style="color:#f92672">--</span><span style="color:#a6e22e">vt</span><span style="color:#f92672">-</span><span style="color:#a6e22e">c</span><span style="color:#f92672">-</span><span style="color:#a6e22e">red</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span><span style="color:#f92672">:</span><span style="color:#a6e22e">hover</span> .<span style="color:#a6e22e">pantry</span><span style="color:#f92672">-</span><span style="color:#a6e22e">item</span><span style="color:#f92672">-</span><span style="color:#a6e22e">container</span><span style="color:#f92672">-</span><span style="color:#a6e22e">x</span><span style="color:#f92672">-</span><span style="color:#a6e22e">icon</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">display</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">inline</span><span style="color:#f92672">-</span><span style="color:#a6e22e">block</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">style</span>&gt;
</span></span></code></pre></div><p>For the user input box, I went with a simple HTML <code>&lt;input&gt;</code> tag. Like every other
clickable &ldquo;bubble&rdquo;, I went with the wrap-it-in-a-<code>null-button</code> technique. The
template is trivial:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">button</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;null-button&#34;</span> <span style="color:#960050;background-color:#1e0010">@</span><span style="color:#a6e22e">click</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;addPantryItem(inputBoxModel)&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pantry-add-item-container&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">input</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;textInput&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pantry-view-input-box&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">v-model</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;inputBoxModel&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#960050;background-color:#1e0010">@</span><span style="color:#a6e22e">keyup</span><span style="color:#960050;background-color:#1e0010">.</span><span style="color:#a6e22e">enter</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;addPantryItem(inputBoxModel)&#34;</span>
</span></span><span style="display:flex;"><span>        /&gt;<span style="color:#75715e">&lt;!-- Add a pantry item when the enter key is pressed (and therefore released) --&gt;</span>
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;pantry-add-item-container-plus-icon&#34;</span>
</span></span><span style="display:flex;"><span>            &gt;&lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;far.faSquarePlus&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;color: var(--vt-c-white-soft)&#34;</span>
</span></span><span style="display:flex;"><span>        /&gt;&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">button</span>&gt;
</span></span></code></pre></div><p>Notice the <code>v-model</code> directive. That allows me to set a piece of reactive state
in TS:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">inputBoxModel</span>: <span style="color:#66d9ef">Ref</span>&lt;<span style="color:#f92672">string</span>&gt; <span style="color:#f92672">=</span> <span style="color:#a6e22e">ref</span>(<span style="color:#e6db74">&#39;&#39;</span>)
</span></span></code></pre></div><p>and sync the contents of the Input Box to the variable. I also use a <code>@keyup.enter</code>
event handler (I don&rsquo;t know the name, its the <code>@</code> thing) to add a pantry item
to a reactive list when enter is pressed. Of course, I had to make a hoverable
<code>div</code> with rounded corners to tie it all together.</p>
<p>I keep a reactive list <code>pantry</code> around like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">pantry</span>: <span style="color:#66d9ef">Ref</span>&lt;<span style="color:#f92672">Array</span>&lt;<span style="color:#f92672">string</span>&gt;&gt; <span style="color:#f92672">=</span> <span style="color:#a6e22e">ref</span>([])
</span></span></code></pre></div><p>and I also set a few callbacks for this page:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">onBeforeMount</span>(() <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">pantry_s</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">getItem</span>(<span style="color:#e6db74">&#39;pantry&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">pantry_s</span> <span style="color:#f92672">===</span> <span style="color:#66d9ef">null</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">pantry_s</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">pantry_arr</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">pantry_s</span>.<span style="color:#a6e22e">split</span>(<span style="color:#e6db74">&#39;;&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">pantry_arr</span>
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">addPantryItem</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">item</span>: <span style="color:#66d9ef">string</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">includes</span>(<span style="color:#a6e22e">item</span>)) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">item</span>.<span style="color:#a6e22e">toLowerCase</span>())
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">inputBoxModel</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#e6db74">&#39;pantry&#39;</span>, <span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;;&#39;</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">removePantryItem</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">item</span>: <span style="color:#66d9ef">string</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">filter</span>((<span style="color:#a6e22e">v</span>) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">v</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">item</span>)
</span></span><span style="display:flex;"><span>    window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#e6db74">&#39;pantry&#39;</span>, <span style="color:#a6e22e">pantry</span>.<span style="color:#a6e22e">value</span>.<span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;;&#39;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>which also deal with concatenating all pantry items and mounting stuff, which
yields one this:</p>
<video controls width="100%">
    <source src="/img/hackathon/pantrydemo.mp4" type="video/mp4" />
</video>
<p>After that, I promptly went to sleep, dreaming about all the things that would
go wrong the next day&hellip;</p>
<h2 id="day-2">Day 2</h2>
<p><img alt="the Punggol LRT APM" loading="lazy" src="/img/hackathon/apm.jpg"></p>
<p>I took a ride on this wonderful Singaporean creation again (rubber tires on
concrete disguised as a train with dogshit acceleration) to come to Day 2. It was
mostly as expected, I got there at 8:45 AM and got straight to the library to
lock in for the day. The previous night I also fiddled with swapping out components
based on JS state while keeping state across those components, and that worked.</p>
<p>The reason why I tested it is becuase when I fetch the data from the spoonacular API,
I will only display a few recipe cards. However, when one clicks on a recipe card, data
from the clicked card should transfer over to another component and be displayed
as a full article. That actually worked, meaning that I could get strated.</p>
<h2 id="coding-session-1">Coding session 1</h2>
<p><em><strong>NOTE</strong>: I am not sponsored by anyone, and I don&rsquo;t really like this API either.
I used it because it was the first one I found. Oh well.</em></p>
<h3 id="getting-the-api-key">Getting the API key</h3>
<p>It was as simple as creating a spoonacular account and getting it from the dashboard.</p>
<p>After that, I used curl to grab some JSON from
<code>https://api.spoonacular.com/recipes/complexSearch?apiKey=&lt;THE API KEY WAS HERE&gt;</code></p>
<p>I then saved the JSON to a file. This is so that I don&rsquo;t rate-limit myself (foreshadowing)
when I have to submit my code.</p>
<p>I hardcoded the JSON to a file for testing in the frontend and I used node&rsquo;s fs module&rsquo;s
<code>readFileSync</code> to test the class constructor.</p>
<p>I made a simple TypeScript class like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Recipe</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">vegetarian</span>: <span style="color:#66d9ef">boolean</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">vegan</span>: <span style="color:#66d9ef">boolean</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">glutenFree</span>: <span style="color:#66d9ef">boolean</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">dairyFree</span>: <span style="color:#66d9ef">boolean</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timeToCook</span>: <span style="color:#66d9ef">number</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">summary</span>: <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">sourceURL</span>: <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">imageURL</span>: <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ingredients</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">equipment</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">instructions</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt;
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* more code here */</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The constructor should take an object where the key is a String. However,
<code>tsserver</code> decided it was cool that I used a string to query the parsed
JSON object, so I had to write an interface:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">AnyObject</span> {
</span></span><span style="display:flex;"><span>    [<span style="color:#a6e22e">key</span>: <span style="color:#66d9ef">string</span>]<span style="color:#f92672">:</span> <span style="color:#66d9ef">any</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>with funky syntax that I definitely did not copy from a certain LLM called
ChatGPT. I then made the constructor take this in (the object happily
coerced, <em>unlike in other languages where types are real and are not
imaginary constructs created by a transpiler to make developers feel better
about using a toy dynamic language</em>) ANYWAYS.</p>
<p>The overall idea was:</p>
<ul>
<li>Get the applicable diets (Veg, Vegan, Non-veg etc) from boolean values given</li>
<li>Save all needed string values without change</li>
<li>Loop over all the steps and save the string values and required ingredients
<ul>
<li>Store all the ingredients in a set (ideally, I used an array because I thought
the response would have no duplicates).</li>
</ul>
</li>
</ul>
<p>This is what the constructor looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">constructor</span>(<span style="color:#a6e22e">recipeObj</span>: <span style="color:#66d9ef">AnyObject</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// type annotating the whole JSON response would be too tedious
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">vegetarian</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;vegetarian&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">vegan</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;vegan&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">glutenFree</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;glutenFree&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">dairyFree</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;dairyFree&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">timeToCook</span> <span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;preparationMinutes&#39;</span>] <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">number</span>) <span style="color:#f92672">+</span> (<span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;cookingMinutes&#39;</span>] <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">number</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">title</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;title&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">summary</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;summary&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">sourceURL</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;sourceUrl&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">imageURL</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;image&#39;</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">timeToCook</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">timeToCook</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;readyInMinutes&#39;</span>] <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">number</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">ingredients</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt; <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">equipment</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt; <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">instructions</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt; <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">steps</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">AnyObject</span>&gt; <span style="color:#f92672">=</span> <span style="color:#a6e22e">recipeObj</span>[<span style="color:#e6db74">&#39;analyzedInstructions&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;steps&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">steps</span>.<span style="color:#a6e22e">forEach</span>((<span style="color:#a6e22e">obj</span>: <span style="color:#66d9ef">AnyObject</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// create a list of all new ingredients to add
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">ingredientNames</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">obj</span>[<span style="color:#e6db74">&#39;ingredients&#39;</span>] <span style="color:#66d9ef">as</span> Array&lt;<span style="color:#f92672">AnyObject</span>&gt;).<span style="color:#a6e22e">map</span>(
</span></span><span style="display:flex;"><span>            (<span style="color:#a6e22e">o</span>: <span style="color:#66d9ef">AnyObject</span>) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">o</span>[<span style="color:#e6db74">&#39;name&#39;</span>] <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">equipmentNames</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">obj</span>[<span style="color:#e6db74">&#39;equipment&#39;</span>] <span style="color:#66d9ef">as</span> Array&lt;<span style="color:#f92672">AnyObject</span>&gt;).<span style="color:#a6e22e">map</span>(
</span></span><span style="display:flex;"><span>            (<span style="color:#a6e22e">o</span>: <span style="color:#66d9ef">AnyObject</span>) <span style="color:#f92672">=&gt;</span> <span style="color:#a6e22e">o</span>[<span style="color:#e6db74">&#39;name&#39;</span>] <span style="color:#66d9ef">as</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ingredients</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ingredients</span>.<span style="color:#a6e22e">concat</span>(<span style="color:#a6e22e">ingredientNames</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">equipment</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">equipment</span>.<span style="color:#a6e22e">concat</span>(<span style="color:#a6e22e">equipmentNames</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">instructions</span>.<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">obj</span>[<span style="color:#e6db74">&#39;step&#39;</span>])
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">ingredients</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ingredients</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">equipment</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">equipment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">instructions</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">instructions</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Making the recipe card itself was trivial. The template itself is quite simple:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-card-container&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">img</span> <span style="color:#a6e22e">:src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;props.recipeInfo.imageURL&#34;</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-card-image&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-card-info&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-card-title&#34;</span>&gt;{{ title }}&lt;/<span style="color:#f92672">h1</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">RecipeInfoDisplay</span> <span style="color:#a6e22e">:recipe-info</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.recipeInfo&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">p</span> <span style="color:#a6e22e">v-html</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.recipeInfo.summary&#34;</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-card-description&#34;</span>&gt;&lt;/<span style="color:#f92672">p</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><em>(Notice the interpolation syntax with <code>{{ title }}</code>, that is to interpolate JS state into a template.)</em></p>
<p>The CSS was not so easy, I had to display the image and the recipe card inline,
and then everything else as block.</p>
<p>The Recipe Info Display detailed the information about the recipe. It kinda looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-container recipe-info-display-container-green&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.recipeInfo.vegetarian&#34;</span>
</span></span><span style="display:flex;"><span>    &gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faLeaf&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-span&#34;</span>&gt;Vegetarian&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-container recipe-info-display-container-green&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.recipeInfo.vegan&#34;</span>
</span></span><span style="display:flex;"><span>    &gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faSeedling&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-span&#34;</span>&gt;Vegan&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-container recipe-info-display-container-green&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.recipeInfo.glutenFree&#34;</span>
</span></span><span style="display:flex;"><span>    &gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faBreadSlice&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-span&#34;</span>&gt;Gluten Free&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-container recipe-info-display-container-green&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;$props.recipeInfo.dairyFree&#34;</span>
</span></span><span style="display:flex;"><span>    &gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faCow&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-span&#34;</span>&gt;Dairy Free&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-container recipe-info-display-container-blue&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;far.faClock&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">span</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-info-display-span&#34;</span>&gt;{{ $props.recipeInfo.timeToCook }}min&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span></code></pre></div><p>Yes, it most definitely looks long, but it really is just a bunch of <code>v-if</code> directives.</p>
<p>In the end, you get a recipe card:</p>
<p><img alt="image of the recipe card" loading="lazy" src="/img/hackathon/recipecard.png"></p>
<p>Simple. The CSS is bog-standard:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span>.<span style="color:#a6e22e">recipe-card-container</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">padding</span>: <span style="color:#ae81ff">1</span><span style="color:#66d9ef">rem</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">margin</span>: <span style="color:#ae81ff">0.5</span><span style="color:#66d9ef">rem</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">margin-bottom</span>: <span style="color:#ae81ff">1</span><span style="color:#66d9ef">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">border-radius</span>: <span style="color:#ae81ff">12</span><span style="color:#66d9ef">px</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">transition</span>: <span style="color:#ae81ff">300</span><span style="color:#66d9ef">ms</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">max-height</span>: <span style="color:#ae81ff">12.5</span><span style="color:#66d9ef">rem</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">background-color</span>: <span style="color:#a6e22e">var</span>(<span style="color:#f92672">--</span>vt<span style="color:#f92672">-</span>c<span style="color:#f92672">-</span>divider<span style="color:#f92672">-</span>dark<span style="color:#ae81ff">-2</span>);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">recipe-card-description</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">max-height</span>: <span style="color:#ae81ff">4.5</span><span style="color:#66d9ef">rem</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">overflow</span>: <span style="color:#66d9ef">hidden</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">recipe-card-title</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">font-weight</span>: <span style="color:#ae81ff">600</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">recipe-card-image</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/* scaled 70%, all images from spoonacular are of equal dimension */</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">width</span>: <span style="color:#ae81ff">218</span><span style="color:#66d9ef">px</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">height</span>: <span style="color:#ae81ff">162</span><span style="color:#66d9ef">px</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">margin-right</span>: <span style="color:#ae81ff">1</span><span style="color:#66d9ef">em</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I also really wanted a hover effect, which is as simple as adding a box
shadow:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-css" data-lang="css"><span style="display:flex;"><span>.<span style="color:#a6e22e">recipe-card-container</span>:<span style="color:#a6e22e">hover</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">box-shadow</span>: <span style="color:#ae81ff">0</span> <span style="color:#ae81ff">4</span><span style="color:#66d9ef">px</span> <span style="color:#ae81ff">20</span><span style="color:#66d9ef">px</span> <span style="color:#a6e22e">var</span>(<span style="color:#f92672">--</span>vt<span style="color:#f92672">-</span>c<span style="color:#f92672">-</span><span style="color:#66d9ef">indigo</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="snack-1">Snack #1</h2>
<p>It really was not too interesting. There was a free donut, but it was miniscule
and I put it all in my mouth in one go. That wasn&rsquo;t so good.</p>
<p>I then decided to go downstairs to the café once again, to get a Vadai and
Samosa. I thought about how well my idea developed into my final program
while eating my food.</p>
<p><img alt="The vadai" loading="lazy" src="/img/hackathon/vadai.jpg"></p>
<p><em>If you don&rsquo;t know what a vadai is, it&rsquo;s basically a salty fermented donut.
It isn&rsquo;t really made from a dough but from something more similar to a batter,
and it is a lot more aerated. It also tastes Indian, unlike a Donut.</em></p>
<p>I really had 0 time to waste, though. I spent basically no time eating my food
and I immediately went back up to work on the article display.</p>
<h2 id="coding-session-ii">Coding session II</h2>
<p>By this point, my whole body was freezing cold from the Air Conditioning unit
constantly blowing on me, but I really couldn&rsquo;t do much about it at this point.
I really just had to push through.</p>
<p>At that point, I only had one object with the recipe data, meaning that it was
really easy to test the description view.</p>
<p>The overall layout was to be:</p>
<ul>
<li>The navbar (stays intact)</li>
<li>The title (heading 1, centered)</li>
<li>A divider</li>
<li>The image (centered)</li>
<li>Recipe Info</li>
<li>The ingredients</li>
<li>The Equipment</li>
<li>The summary</li>
<li>The steps</li>
<li>A go back button</li>
</ul>
<p>A diagram may help:</p>
<p><img alt="The page layout" loading="lazy" src="/img/hackathon/pagelayout.png"></p>
<p>Putting everything together was quite simple. I had just made a component that
contains a label that you specify and a separator which looked like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">p</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;divider-item-label&#34;</span>&gt;&lt;<span style="color:#f92672">slot</span>&gt;&lt;/<span style="color:#f92672">slot</span>&gt;&lt;/<span style="color:#f92672">p</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">hr</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;margin-bottom: 0.3em&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">style</span> <span style="color:#a6e22e">scoped</span>&gt;
</span></span><span style="display:flex;"><span>.<span style="color:#a6e22e">divider-item-label</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">margin-bottom</span>: <span style="color:#ae81ff">0.3</span><span style="color:#66d9ef">em</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">display</span>: <span style="color:#66d9ef">flex</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">justify-content</span>: <span style="color:#66d9ef">center</span>;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">style</span>&gt;
</span></span></code></pre></div><h3 id="an-interlude">An interlude</h3>
<p>At this point, I was really, really, really cold (my hands were about to
freeze), so I decided to go downstairs to the area right below the 2nd to 3rd
floor escalator.</p>
<p><img alt="Coding while exposed to nature" loading="lazy" src="/img/hackathon/codingoutside.jpg"></p>
<p>That is where I finished the article display page.</p>
<p>I pass through the array of recipes from the home page to the widget via a prop,
and the index of the recipe. The component then only needs to do a simple array
index.</p>
<p>Here I render the list of recipes in a for loop and keep the index:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">v-if</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipes.length &gt; 0&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">v-for</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;(recipe, index) in recipes&#34;</span> <span style="color:#a6e22e">:key</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;index&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">button</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;null-button&#34;</span> <span style="color:#960050;background-color:#1e0010">@</span><span style="color:#a6e22e">click</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;openRecipe(index)&#34;</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">RecipeCard</span> <span style="color:#a6e22e">:recipe-info</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe&#34;</span>&gt;&lt;/<span style="color:#f92672">RecipeCard</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">button</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p><code>openRecipe</code> then swaps a conditional value which then controls which page
is active, and sets the index:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">showMain</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ref</span>(<span style="color:#66d9ef">true</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">/* later */</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">openRecipe</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">index</span>: <span style="color:#66d9ef">number</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">selectedRecipeIndex</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">index</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">showMain</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#f92672">!</span><span style="color:#a6e22e">showMain</span>.<span style="color:#a6e22e">value</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The component only takes in 2 props:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">Props</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">recipeList</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">Recipe</span>&gt;
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">index</span>: <span style="color:#66d9ef">number</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">props</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">defineProps</span>&lt;<span style="color:#f92672">Props</span>&gt;()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">recipe</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">props</span>.<span style="color:#a6e22e">recipeList</span>[<span style="color:#a6e22e">props</span>.<span style="color:#a6e22e">index</span>]
</span></span></code></pre></div><p>the recipe list and then the index, and it is rendered like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">v-else</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">RecipeView</span> <span style="color:#a6e22e">:recipe-list</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipes&#34;</span> <span style="color:#a6e22e">:index</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;selectedRecipeIndex&#34;</span>&gt;&lt;/<span style="color:#f92672">RecipeView</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">button</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;back-home-button&#34;</span> <span style="color:#960050;background-color:#1e0010">@</span><span style="color:#a6e22e">click</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;showMain = !showMain&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">FontAwesomeIcon</span> <span style="color:#a6e22e">:icon</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;fas.faCircleArrowLeft&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">span</span>&gt;GO BACK&lt;/<span style="color:#f92672">span</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">button</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span></code></pre></div><p>The template is (again) quite simple:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">main</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;title&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-align: center; font-size: 2.5rem&#34;</span>&gt;{{ recipe.title }}&lt;/<span style="color:#f92672">h1</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">br</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">hr</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">br</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;display: flex; justify-content: center&#34;</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">img</span> <span style="color:#a6e22e">:src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe.imageURL&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">br</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;display: flex; justify-content: center&#34;</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">RecipeInfoDisplay</span> <span style="color:#a6e22e">:recipe-info</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">h2</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;subtitle&#34;</span>&gt;INGREDIENTS&lt;/<span style="color:#f92672">h2</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">BubbleListView</span> <span style="color:#a6e22e">:items</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe.ingredients&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">h2</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;subtitle&#34;</span>&gt;EQUIPMENT&lt;/<span style="color:#f92672">h2</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">BubbleListView</span> <span style="color:#a6e22e">:items</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe.equipment&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">h2</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;subtitle&#34;</span>&gt;SUMMARY&lt;/<span style="color:#f92672">h2</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe-body-container&#34;</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">p</span> <span style="color:#a6e22e">v-html</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;recipe.summary&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">br</span> /&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">h2</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;subtitle&#34;</span>&gt;INSTRUCTIONS&lt;/<span style="color:#f92672">h2</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">ol</span>&gt;
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#f92672">li</span> <span style="color:#a6e22e">v-for</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;(instruction, index) in recipe.instructions&#34;</span> <span style="color:#a6e22e">:key</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;index&#34;</span>&gt;
</span></span><span style="display:flex;"><span>                {{ instruction }}
</span></span><span style="display:flex;"><span>            &lt;/<span style="color:#f92672">li</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;/<span style="color:#f92672">ol</span>&gt;
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">br</span> /&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">main</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span></code></pre></div><p>After going back up and finishing all the code I showed you, here&rsquo;s what the display looks like.</p>
<video controls width="100%">
    <source src="/img/hackathon/carddemo.mp4" type="video/mp4" />
</video>
<p>Since this article is getting too long once again, I will post a fourth and final part, which
details my troubles connecting the API to my app and some final remarks. So until then, cheers!</p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>The GIIS Hackathon X - My Experience (part 2)</title><link>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-2-20240729t1317/</link><pubDate>Mon, 29 Jul 2024 13:17:09 +0800</pubDate><guid>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-2-20240729t1317/</guid><description>The Vue rush begins...</description><content:encoded><![CDATA[<h2 id="recap">Recap</h2>
<p>If you haven&rsquo;t checked the first article out, please do <a href="https://ezntek.com/posts/the-giis-hackathon-x-a-review-part-1-20240726t1322/">here</a>.</p>
<p>This article will cover mainly the events on day one, along other appropriate
stories fit for this article. Source code shown is in the state that it is
in right now (i.e. the complete app). I only set up git later on in the
development process due to time reasons. Some components may not be explained,
that is to be expected.</p>
<p>In summary, I thought the campus that the event was held in was pretty cool in
all, and gave off some heavy hospital vibes. Despite the very late start and
multiple delays given without much explanation, initial impressions were very
good.</p>
<h2 id="structure-for-the-first-day">Structure for the first day</h2>
<p>It was very simple, we would have 2 coding sessions that&rsquo;d last for 1h30m and
2h respectively, however due to the delay before prior to the opeing ceremony,
the times were shifted a bit. We were to have our coding session 1 end at
5:00PM, and and coding session 2 to begin at 5:30. That means that we would get
1h30mins for the last coding session. Oh well.</p>
<h2 id="coding-session-1">Coding session #1</h2>
<p>As addressed in my first article, I went to the library to get work done.</p>
<p><img alt="my workspace" loading="lazy" src="/img/hackathon/workspace.jpg"></p>
<p><em>(That&rsquo;s actually a custom BIOS, my own Coreboot GRUB+SeaBIOS payload! Article
coming soon).</em></p>
<p>I ultimately decided against using <a href="https://gtk.org">GTK</a>+<a href="https://rust-lang.org">Rust</a>+<a href="https://gnome.pages.gitlab.gnome.org/libadwaita/">Libadwaita</a> due to additional complications that I did not want to deal with later.
I also really didn&rsquo;t  want to deal with the whole async rust shebang, which
is a pure pain in the arse.</p>
<p>I did find a few tablemates, however. A man by the name of <a href="https://github.com/jiwookki">Jiwoo</a>
was sitting at a table and I decided to join him. We talked for a bit, and
I found out that he went to Saint John&rsquo;s International in Singapore, and that
he&rsquo;s actually a pretty good Python developer. I was still brainstorming when
I sat next to him because I was still so clueless on the theme &ldquo;Life on all
elements&rdquo; (I still don&rsquo;t know what that means <em>exactly</em>).</p>
<p>I met a few of his friends who just had a Comp Sci exam, and had come late.
We talked about project ideas and a possibility of a team merger. My idea
was to use raylib to make a bug killing simulator in a living room, as bugs
are elements along with other things in a room with life, and they really
seemed to like the idea of raylib, but we all mutually agree that doing
a team merger would be dumb. Our prior experiences are quite different
(as I learnt C as my first language for some reason, and still mainly use
it along with Rust). I also didn&rsquo;t resonate with their idea of a chemical
reaction simulator.</p>
<p>This is how I ultimately decided on using Vue.js and TS as described in the
first article. But where did my idea come from?</p>
<h2 id="snack-time-an-interlude">Snack time (an interlude)</h2>
<p>I really wanted a break from this stuff, so I decied to go to the second
floor to get snack, as I saw a café there. It was actually a huge mistake,
as I soon found out that all food at this vent, including the snack was
completely complimentary. But it really was a blessing in disguise.</p>
<p>I got a Samosa (like a <strong>true</strong> <a href="/posts/the-giis-hackathon-x-a-review-part-2-20240729t1317/#footnotes">Indian food enjoyer [1]</a>).</p>
<p><img alt="The very samosa" loading="lazy" src="/img/hackathon/samosa.jpg"></p>
<p>But while I enjoyed that piping hot samosa for 2 dollars (I think)
(I almost burned myself) an idea popped in my head.</p>
<p><em>See, this samosa was made by following a set of instructions, a recipe.
Written down or not, it was processed by somebody&rsquo;s brain and those
instructions were followed to make delicious food.</em></p>
<p>Yet, I thought, we aren&rsquo;t cows or sheep. We must prepare our ingredients in
such a way that we can taste it, such that we can enjoy it and use its
nutrients effectively. We <em>must cook food, and a recipe is crucial to that</em>.</p>
<p>Therefore the idea came up. <em>Make a recipe scroller.</em> One can scroll in an
infinite and never-ending list of recipes, and continuously discover new ones.
They should also be able to specify their dietary needs and have their recipes
in their feed tailored to them.</p>
<p><em><strong>so basically I came up with my idea over a samosa</strong></em>.</p>
<p>Instead of quickly running up like a cartoon innovator, however, I decided to
get a bottle of water and a mystery orange chicken sandwich nuked in the
microwave (which was nice too), and I waited till the end of the break to really
get my idea baked into my mind so I wouldn&rsquo;t contemplate when I&rsquo;d be productive</p>
<h2 id="the-second-coding-session">The second coding session</h2>
<p>I told my rough (at the time) idea to my new-found friends Jiwoo and the other
people (I don&rsquo;t know their names, I should have asked LMAO), and they seemed
to love it.</p>
<h3 id="the-overall-architecture">The overall architecture</h3>
<p>The idea was to use Vue.js, and TypeScript. I would make a single-page
application (an SPA) and use Vue Router as a way to swap out components naturally
to emulate a multipage application, but with absolutely no lag when switching
between pages. I&rsquo;d also have a navbar just like a proper web app.</p>
<p><img alt="the navbar" loading="lazy" src="/img/hackathon/navbar.png"></p>
<p>In this session, I didn&rsquo;t really do much. I brainstormed potential features I
wanted more, and I also browsed on RapidAPI to see what my API options were.
I decided on using <a href="https://spoonacular.com/food-api">spoonacular</a>, which would
actually be my downfall (foreshadowing), and I got right to work on the settings.</p>
<h3 id="the-settings-page">The settings page</h3>
<p>My idea was to use the browser local storage. In JS, you can simply access it with
this simple expression:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span>window.<span style="color:#a6e22e">localStorage</span>
</span></span></code></pre></div><p>This is a basic key-value store. You can store JavaScript primitives in it by simply
issuing <code>.setItem(key, value)</code>. This way, I was able to cook up a basic way to save
persistent application state without a server. With that, I was able to make a simple
diet setting item, which would save if you were vegan, vegetarian, non-veg, pescetarian
and the like.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#75715e">// types.ts
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">enum</span> <span style="color:#a6e22e">Diet</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">NonVegetarian</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Non-Vegetarian&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Vegetarian</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Vegetarian&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Vegan</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Vegan&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">LactoVegetarian</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Lacto-Vegetarian&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">OvoVegetarian</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Ovo-Vegetarian&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Pescetarian</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;Pescetarian&#39;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I used a simple TypeScript enum for this. Then I used some some reactive state to
save the currently selected diet option.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Diet</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@/types.ts&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">selectedDiet</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">ref</span>&lt;<span style="color:#f92672">Diet</span>&gt;(<span style="color:#a6e22e">Diet</span>.<span style="color:#a6e22e">Vegetarian</span>)
</span></span></code></pre></div><p>I made a diet bubble component that would display a div and a font awesome icon.
Oh Wait-</p>
<h3 id="font-awesome">Font Awesome</h3>
<p>my good friend. As I would soon find out, there are actually 3 ways that one can go
about displaying FA icons.</p>
<ol>
<li>Use the JS <code>&lt;script /&gt;</code> tag method,</li>
<li>Download a bunch of CSS and import that</li>
<li>Use a Node.js package and Vue components</li>
</ol>
<p>The first one didn&rsquo;t work. A typical Vue SPA looks like this (not a complete directory tree):</p>
<pre tabindex="0"><code>|
+- node_modules
+- src
|  |
|  +- main.ts
|  +- App.vue
|  +- router
|     |
|     +- index.ts
+- package.json
+- tsconfig.json
+- vite.config.ts
+- index.html
+- env.d.ts
</code></pre><p>I decided to put the script tag in the index.html where all the Vue code is
eventually injected. That didn&rsquo;t really work.</p>
<p>I also tried downloading a bundle including CSS and all necessary fonts and
SVGs, but that also didn&rsquo;t work.</p>
<p><img alt="font awesome&rsquo;s download widget" loading="lazy" src="/img/hackathon/fontawesome.png"></p>
<p>In the end, I decided to use the third method. I followed <a href="https://docs.fontawesome.com/web/use-with/vue">this guide</a>,
and icons were displaying just fine.</p>
<p><img alt="font awesome works!" loading="lazy" src="/img/hackathon/fontawesomeworks.png"></p>
<p>..and it only took me till around 6:45 to finish. What an unproductive first day.</p>
<p>By the end of session 2, I had finished what the bubble was supposed to look
like, but only one (the one that says Vegetarian). Unfortunately I do not
have screenshots or a commit from when I finished that. Sorry ._.</p>
<h3 id="more-vue">More Vue</h3>
<p>The component is very simple. It looks like this:</p>
<pre tabindex="0"><code>&lt;script lang=&#34;ts&#34; setup&gt;
import type { Diet as DietT } from &#39;@/types&#39;
import { Diet } from &#39;@/types&#39;
import { far } from &#39;@fortawesome/free-regular-svg-icons&#39;
import { fas } from &#39;@fortawesome/free-solid-svg-icons&#39;
import { FontAwesomeIcon } from &#39;@fortawesome/vue-fontawesome&#39;

// Component properties: TS+Vue allows one to pass an iface in as a generic type argument to define props.
interface Props {
    diet: DietT
    showTick: boolean
}

defineProps&lt;Props&gt;()

// Reactive state
&lt;/script&gt;

&lt;template&gt;
    &lt;div
        class=&#34;diet-bubble-container&#34;
        :class=&#34;$props.showTick ? &#39;diet-bubble-container-green&#39; : &#39;diet-bubble-container-blue&#39;&#34;
    &gt;
        &lt;span class=&#34;diet-bubble-container-icon&#34;&gt;
            &lt;!-- using Vue&#39;s v-if/v-else-if/v-else directives to do conditional rendering --&gt;
            &lt;FontAwesomeIcon v-if=&#34;$props.diet == Diet.Vegetarian&#34; :icon=&#34;fas.faLeaf&#34; /&gt;
            &lt;FontAwesomeIcon v-else-if=&#34;$props.diet == Diet.LactoVegetarian&#34; :icon=&#34;fas.faCow&#34; /&gt;
            &lt;FontAwesomeIcon v-else-if=&#34;$props.diet == Diet.OvoVegetarian&#34; :icon=&#34;fas.faEgg&#34; /&gt;
            &lt;FontAwesomeIcon v-else-if=&#34;$props.diet == Diet.Vegan&#34; :icon=&#34;fas.faSeedling&#34; /&gt;
            &lt;FontAwesomeIcon v-else-if=&#34;$props.diet == Diet.NonVegetarian&#34; :icon=&#34;fas.faBacon&#34; /&gt;
            &lt;FontAwesomeIcon v-else-if=&#34;$props.diet == Diet.Pescetarian&#34; :icon=&#34;fas.faFish&#34; /&gt;
        &lt;/span&gt;
        &lt;span class=&#34;diet-bubble-container-diet-name&#34;&gt;{{ $props.diet }}&lt;/span&gt;
        &lt;FontAwesomeIcon
            v-if=&#34;$props.showTick&#34;
            :icon=&#34;far.faSquarePlus&#34;
            class=&#34;diet-bubble-container-x-icon&#34;
            style=&#34;color: var(--vt-c-white-soft)&#34;
        /&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre><p>This is excluding the CSS, of course (covered in the netx article).
It simply renders 2 span elements, one with a font awesome icon that is
conditionally rendered based on the enum&rsquo;s state, and the other is just the
enum name. I also included an icon that would pop up on hover, if a property
<code>showTick</code> is set.</p>
<pre tabindex="0"><code>&lt;DividerItem&gt;Your diet is:&lt;/DividerItem&gt;
&lt;div class=&#34;diet-container&#34;&gt;
    &lt;DietBubble :diet=&#34;selectedDiet&#34; :showTick=&#34;false&#34; /&gt;
&lt;/div&gt;

&lt;br /&gt;

&lt;DividerItem&gt;Or set your diet:&lt;/DividerItem&gt;
&lt;div class=&#34;all-diets-container&#34; v-for=&#34;(diet, index) in diets&#34; :key=&#34;index&#34;&gt;
    &lt;button class=&#34;null-button&#34; @click=&#34;setCurrentDiet(diet)&#34;&gt;
        &lt;DietBubble :diet=&#34;diet&#34; :showTick=&#34;true&#34; v-if=&#34;diet != selectedDiet&#34; /&gt;
    &lt;/button&gt;
&lt;/div&gt;
</code></pre><p>I made a class <code>null-button</code> which is basically a button with all the properties
unset so I could put a div in it AND use the <code>@click</code> event handler just fine.</p>
<p>I would render every possible diet (which is preset in the <code>diets</code> array) with the
<code>v-for</code> directive, and if the <code>DietBubble</code> is clicked I would call <code>setCurrentDiet</code>
with the diet the button is displaying. It is a closure that looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ts" data-lang="ts"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">setCurrentDiet</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">diet</span>: <span style="color:#66d9ef">Diet</span>) <span style="color:#f92672">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">selectedDiet</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">diet</span>
</span></span><span style="display:flex;"><span>    window.<span style="color:#a6e22e">localStorage</span>.<span style="color:#a6e22e">setItem</span>(<span style="color:#e6db74">&#39;diet&#39;</span>, <span style="color:#a6e22e">diet</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I used an arrow function (a closure) because you can capture the surrounding values
just fine. In the end, clicking a diet bubble saves its state in local storage and
displays it (through the reactive variable <code>selectedDiet</code>) Easy!</p>
<video controls width="100%">
    <source src="/img/hackathon/settingsdemo.mp4" type="video/mp4">
</video>
<h2 id="dinner">Dinner</h2>
<p>We were called to dinner at 7. Again, it was served on level 4. This time, I
actually cared to go up (because I knew that it was going to be served there),
and I was greeted by a wonderfully long line that stretched all the way from
the counter into the corridor; luckily they had 2 lines for crowd control.</p>
<p>(unfortunately I dont have pics. whoops-)</p>
<p>After I claimed my food, I was actually not surprised that served us&hellip;</p>
<p><img alt="Pizza Hut" loading="lazy" src="/img/hackathon/pizzahut.jpg"></p>
<p><em>Pizza Hut</em>. Cheaper than catering I guess&hellip;and it&rsquo;s free! Can&rsquo;t complain too
much I guess-</p>
<p>As you probably already figured, this school does have 2 canteens, the level 3
and the level 4 one. Here&rsquo;s the latter:</p>
<video width="50%" controls>
    <source src="/img/hackathon/canteen-compressed.mp4" type="video/mp4">
</video>
<p><em>Video compressed to save space</em></p>
<p>I&rsquo;m quite surprised that GIIS has 2 canteens with proper seating and real tables.
As an individual from places far and wide who must live with generic gray plastic
chairs without armrests and perpetually stained glossy plastic tables, this was
a real breath of fresh air. <em>Don&rsquo;t take your tables and chairs for granted!</em></p>
<h2 id="miscellaneous">Miscellaneous</h2>
<p>I forgot to talk about the event&rsquo;s design. Designer Ashwath and his team really put
a lot of effort designing everything within the event!</p>
<p>At the registration, everybody was given merchandise. A band, lanyard and T-shirt.
Not only that, every room had cool designs stuck on outside that almost aggressively
shoves the fact that you&rsquo;re at Hackathon X and not any other event but almost in
a kind way. Even the slides were really carefully designed; there was quite a bit
of attention to detail.</p>
<p>Heres how the merch looks:</p>
<div style="display: grid; grid-gap: 0.5em">
    <img src="/img/hackathon/lanyardfront.jpg" style="grid-column: 1; grid-row: 1;"/>
    <img src="/img/hackathon/lanyardback.jpg" style="grid-column: 2; grid-row: 1"/>
    <img src="/img/hackathon/shirtfront.jpg" style="grid-column: 1; grid-row: 2"/>
    <img src="/img/hackathon/shirtback.jpg" style="grid-column: 2; grid-row: 2"/>
</div>
<p><em>(I lost my bracelet :skull:)</em></p>
<p>Unfortunately, I don&rsquo;t have any photos of the rooms, or any of the other designs;
It really was in my subconscious until now. I do still think it still deserves
a mention.</p>
<p>I&rsquo;m also ending this article early because there&rsquo;s still a lot to talk about. However,
I have already gotten to &gt;1900 words (according to neovim at least), so I&rsquo;ll discuss
the second day and other such things in the next and most likely last article.</p>
<p>Stay tuned.</p>
<p><em><strong>UPDATE:</strong></em> <a href="https://ezntek.com/posts/the-giis-hackathon-x-a-review-part-3-20240801t1539/">Here&rsquo;s</a> the next part.</p>
<h2 id="footnotes">footnotes</h2>
<ol>
<li>I really like Indian food. I really dont care if its north or south,
because I&rsquo;ve tried it all. And I love it. <em>Millions must consume Indian
food.</em></li>
</ol>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>The GIIS Hackathon X - My Experience (part 1)</title><link>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-1-20240726t1322/</link><pubDate>Fri, 26 Jul 2024 13:22:35 +0800</pubDate><guid>http://ezntek.com/posts/the-giis-hackathon-x-a-review-part-1-20240726t1322/</guid><description>Have I ascended or descended?</description><content:encoded><![CDATA[<h2 id="why">Why?</h2>
<p>In the student homebase representative group chat, there arrived a message. A
certain message leading people to visit a certain site.</p>
<p><img alt="Announcement message" loading="lazy" src="/img/hackathon/announcement.png"></p>
<p>That would be <a href="https://hackathonx.net">this</a>.</p>
<p>This would be a hackathon. An event where programmers (or non programmers)
(foreshadowing) would come together and code in teams while competing with other
teams to win prizes and recognition from other programmers in the event.
Usually, one would have to pay for these kinds of events, but this one was
actually free. And held close to my home. So I decided, why not? I&rsquo;d just have
to go to the <em>Global International Indian School</em> that my peers who came from
that school had said <em>great things</em> about. Could anyting go wrong?</p>
<h2 id="first-impressions">First impressions</h2>
<p>Upon arrival, there was nobody at the front door to greet anybody. Nobody, just
security guards and and two large buildings in front of me (One world
international school was also right next door).</p>
<p><img alt="Image of the campus" loading="lazy" src="/img/hackathon/campus.jpg"></p>
<p>I actually had to look in the distance to what looked like the atrium area, to
see some dudes changing into their hackathon X jerseys. Naturally, I entered
through the side gate to find out that the aforementioned &ldquo;MPH&rdquo; area was
actually just the entrance area.</p>
<p><img alt="Image of chat" loading="lazy" src="/img/hackathon/mphchat.png"></p>
<p>Entering the building, it felt more like a hospital than anything. An oddly
geometric wooden panel greeted me with a softly lit set of hexagonal ceiling
lights, angular edges on the walls, the shebang. It was clear to me that some
sort of big corporation built this campus (which is correct, this school
actually has various campuses scattered all across the globe) and the designers
obviously paid A LOT of attention to designing this place well.</p>
<p><img alt="Image of the interior" loading="lazy" src="/img/hackathon/mainarea.jpg"></p>
<h2 id="registration">Registration</h2>
<p>This is where I hit the first pothole. There was actually a lot of people
&ldquo;lining&rdquo; up, surprisingly. I expected much fewer participants but obviously I
was wrong.</p>
<p><img alt="The crowd" loading="lazy" src="/img/hackathon/crowd.jpg"></p>
<p>I wouldn&rsquo;t really call this a line, but I&rsquo;d actually call this a crowd.</p>
<p>A really packed one. Nobody seemed to know what line up meant (I mean, they were
all small children, but that&rsquo;s besides the point). The workers and even some
adult had to yell at the crowd collectively and tell them to get into 3 lines.
What formed were three distinct enough crowds, loosely bunched up. Everybody
seemed to cut the queue, and nothing was done. Finally, some organizer decided
to ask a bunch of people if they had registered yet, and directed them to
counters and helped the line move along. I was one of those people and I was
told to go to the leftmost counter, with a group already doing their
registration.</p>
<p>Annoyingly, at the counter, they actually couldn&rsquo;t find my name in their
<em>wonderfully structured</em> spreadsheet. I had to show them my whole inbox for them
to see the two acceptance letters (those who were rejected got one acceptance
and one rejection letter due to some system error), and they somehow didn&rsquo;t have
me on the list.</p>
<p>Okay, I thought, so I told them all my details, but as they were filling out my
full name, my name autocompleted. That means that my name was already on the
system. I was index 3, and they somehow seemed to miss that :p</p>
<h2 id="the-opening-ceremony">The opening ceremony</h2>
<p>Oh dear this did not go well.</p>
<p>According to the itinerary given to us on our tags and on the slack group, it
was supposed to begin at 2:00 PM sharp. Everything seemed to be on track, until
it hit 2PM. When we first all did our registration, we were told to go back to
the first floor to do the opening ceremony, but instead, people were in chaos.
The organizers and/or volunteers had told us to go to the third floor canteen
area, but the volunteers on the third floor told us to go to the first floor.
After two laps, we were told to go to the third floor and stay there. Instead,
only a small group of people went there to get cotton candy, and they all seemed
to just go back to the second floor lounging area, where everybody seemed to be.</p>
<p>As I am writing this paragraph, it is already 2:21PM, and no ceremony was
announced yet. It has been an hour and twenty minutes, with absolutely no
progress done to the event. What a bummer.</p>
<h3 id="later">Later</h3>
<p>It got delayed again. We were all allowed in at around 14:32PM; but turns out,
all the &ldquo;internal&rdquo; students from the campus that the hackathon was hosted at
didnt know how to sit properly, and they all sat too high up in the booths.
Everyone was told to move as far down as possible, which took a while. There was
also an organizer yelling at all the students on the microphone. How fun.</p>
<p>It took until around 14:46 until the event really got around to strating. It
took multiple people, including all the execs of the school coming to get the
crowd to quiet down. Surprisingly took very very long.</p>
<h2 id="announcements">Announcements</h2>
<p>(note: this section is unpolished as fuck because I wrote while they were speaking)</p>
<p>Before he (the announcer, &ldquo;Karthik&rdquo;) got around to the actual announcement, He
decided to talk about his experience in 2018, where his brand new laptop was
destroyed because some teammate of his tripped over the charging cable :skull:</p>
<h3 id="speaker-1">Speaker 1</h3>
<p>One Nishant Patil came onto the stage, and&hellip;No theme for a while. He shared his
experiences after he got elected as the VP of the tech club, including how he
witnessed the prices going up. Apparently, he found hack club, a 501(c)(3)
nonprofit organization which oversees multiple hackathons globally. The VP and
his friends also apparently began crying when they found out that DBS (the bank)
ended up being able to ssponsor them in the event.</p>
<p>No theme, though.</p>
<h3 id="speaker-2">Speaker 2</h3>
<p>Aditya Bairy, the president of the Tech club came up to the stage, and annouced
the theme is&hellip;</p>
<p>Not yet. He talked about how he was happy about how multiple student groups came
from multiple countries even (oh), came to GIIS participate in the hackathon. He
also talked about how the sacrifices he had to make of rejecting young
programmers hurted him and made him &ldquo;sad&rdquo;. He also told us more talented and
older programmers to form our own events, if it suited us ;)</p>
<h3 id="the-theme">The Theme</h3>
<p>Life on all elements. More on my brainstorming later.</p>
<h3 id="the-rubric">The Rubric</h3>
<table>
  <thead>
      <tr>
          <th>criteria</th>
          <th>marks</th>
          <th>description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Creativity</td>
          <td>25%</td>
          <td>How innovative it is, and</td>
      </tr>
      <tr>
          <td>Technical complexity</td>
          <td>25%</td>
          <td>The complexity, with comments and documentation</td>
      </tr>
      <tr>
          <td>Functionality and usability</td>
          <td>25%</td>
          <td>User-friedliness and real-life applications</td>
      </tr>
      <tr>
          <td>Pitching</td>
          <td>15%</td>
          <td>Being able to answer questions regading the code</td>
      </tr>
      <tr>
          <td>Software-dependent marks</td>
          <td>10%</td>
          <td>AI would be data representation, Game development would be game design, app development would be the UI, and web dev would be the layout.</td>
      </tr>
  </tbody>
</table>
<h2 id="the-event-is-declared-open">The event is declared open!</h2>
<p>At 15:12:50 (ish), the pricipal declared the event open. yay.</p>
<h2 id="how-it-works">How it works</h2>
<p>Contestants are split into two major categories, seniors and juniors. Juniors
are in grades 6 through 8, and seniors 9 through 12. The main difference are the
skill levels between the two major categories, and due to the Juniors most
likely having less exeprience with programming in general, they are be allowed
to use tools like <a href="https://scratch.mit.edu">Scratch</a> and
<a href="https://thunkable.com">Thunkable</a>. The seniors would have more skill in
general, so they weren&rsquo;t allowed scratch, but by the end, nobody actually used
it (which is a good thing.)</p>
<p>I was placed into the senior team. I saw my fellow students <a href="/posts/the-giis-hackathon-x-a-review-part-1-20240726t1322/#footnotes">from places far and
wide there</a>, which were also placed into the senior team. I genuinely believed
that they, or the older kids would do better than me, as my self-confidence
wasn&rsquo;t too high at the time of me joining. I did not know any sort of toolkit or
framework that I could use to create a product, so I kinda had to raw-dog it.</p>
<h2 id="first-steps">First steps</h2>
<p>We were made to go to the Third floor coding rooms. After 2 flights worth of
escalator, we would have to make a U turn from the canteen into an auto-opening
door, which would lead us to the coding rooms.</p>
<p>At first, I went into S3-07 (keep this one in mind), which was actually supposed
to be a Junior coding room, as I&rsquo;d soon find out. I had already placed my bag
down on an empty space on a table mounted to a pillar, and have already grabbed
a chair. Oh well, <em>I guess this would be the last time I&rsquo;d be in a room like
this.</em></p>
<p>I went to the library instead, where we were told to go. At this point, I had
absolutely no idea as to what I would build. I wanted to use Rust+GTK+Libadwaita
at first, but due to the theme being so incredibly vague, it would be completely
useless to build something with it. Additionally, this framework is considerably
harder than doing web development, because it uses completely foreign concepts.
GTK usually recommends you to write XML to describe your components, and include
it in your Rust project as a resource. However, styling and positioning would
also be difficult due to GTK CSS being limited and components (widgets) being a
pain in general. One could also not subclass a GObject and therefore a
<code>gtk::Widget</code> easily without 2 modules per subclass and about 30,000 macros, so
I ultimately decided against it.</p>
<p>I was back to square one. At this point, I really didn&rsquo;t know what I wanted to
do. I could make a game in C and <a href="https://raylib.com">raylib</a>, but game development would be my
downfall because I&rsquo;d have to make art. A graphics library would also be a pain
overall, and if I wanted any chance of winning, I would have to learn and
therefore master a game engine.</p>
<p>There was only really one option left for me, which is web development. I had
actually feared this before the event, so I did some practice beforehand with
<a href="vuejs.org">Vue.js</a>, but not nearly enough for a full app. That means that if I didn&rsquo;t want
to be a disappointment, I would have to basically master the basics of Vue while
in the Hackathon.</p>
<p>I&rsquo;ll address my experience with Vue and my overall experience in this 2 day
event, along with all the other fun things offered alongside it in the next
article. At around 1,900 words, this is already a really long article. I
promise, the next one will be just as good if not miles ahead of this one.</p>
<p><em><strong>UPDATE:</strong></em> <a href="https://ezntek.com/posts/the-giis-hackathon-x-a-review-part-2-20240729t1317/">here</a> is the
next post.</p>
<h2 id="random-things-i-heard">Random things I heard</h2>
<blockquote>
<p>Some adult: Oh, you joined the hackathon as a participant?</p>
<p>Some child: yes! I did! this would be a great chance for me to demonstrate and
develop my technical skills-</p>
<p>That same adult: Why didn&rsquo;t you join as an organizer?</p></blockquote>
<blockquote>
<p>Some random tall guy who obviously is an outsider to the school: WOW THIS
CAMPUS IS AMAZING</p></blockquote>
<blockquote>
<p>The guest speaker mentioned the AI buzzword. ha ha ha ha ha-</p></blockquote>
<h2 id="some-random-things-i-saw">Some random things I saw</h2>
<ul>
<li><em><strong>why are there students <a href="https://ofs.edu.sg">from places far and wide™</a>
<a href="/posts/the-giis-hackathon-x-a-review-part-1-20240726t1322/#footnotes">[1]</a></strong></em></li>
<li>the canteen is actually high tech lmao why can&rsquo;t our school not force a
monopoly on us and force us to pay absurd prices for food? this school has
different stalls with different food options. oh wow.</li>
<li>the slide authors coldn&rsquo;t spell &ldquo;principal&rdquo; (they spelt it &ldquo;principle&rdquo;).</li>
</ul>
<h2 id="footnotes">footnotes</h2>
<ol>
<li>Our school song begins with &ldquo;From places far and wide, we come to make
friends and live here side by side&rdquo;. You may see references later throughout the
post, I am not sure.</li>
</ol>
<script src="https://utteranc.es/client.js" repo="ezntek/ezntek.github.io"
issue-term="title" label="comments" theme="github-dark" crossorigin="anonymous"
async> </script>
]]></content:encoded></item><item><title>Econs Revision (20240509T110029)</title><link>http://ezntek.com/posts/econs-revision-20240509t110029/</link><pubDate>Thu, 09 May 2024 11:00:44 +0800</pubDate><guid>http://ezntek.com/posts/econs-revision-20240509t110029/</guid><description>for the suffering econs students</description><content:encoded><![CDATA[<h2 id="economics-revision-guide">Economics revision guide</h2>
<p><em><strong>NOTE:</strong></em> This guide was NOT WRITTEN BY ME, but by Markus Clausen. I do not take the economics course.</p>
<p><a href="https://ezntek.com/revision/econs_g1_s2.pdf">you can find the PDF here.</a></p>
<script src="https://utteranc.es/client.js"
        repo="ezntek/ezntek.github.io"
        issue-term="title"
        label="comments"
        theme="github-dark"
        crossorigin="anonymous"
        async>
</script>
]]></content:encoded></item><item><title>Computer Science G1 revision guide</title><link>http://ezntek.com/posts/comp-sci-revision-20240509t0944/</link><pubDate>Thu, 09 May 2024 09:44:54 +0800</pubDate><guid>http://ezntek.com/posts/comp-sci-revision-20240509t0944/</guid><description>I need help with the Comp Sci exam!</description><content:encoded><![CDATA[<h2 id="da-link">Da link</h2>
<p><a href="https://ezntek.com/revision/comp_sci_g1_s2.html">here</a></p>
<h2 id="for-the-uneducated">For the uneducated:</h2>
<p>Our school provides the IGCSE Comp Sci 0478 course (instead of ICT now), but the school&rsquo;s selection of
teachers are <em>suboptimal</em>. Despite their best efforts to teach us as much as they can, computer science
is a hard subject for many and they may need a revision guide to help them.</p>
<h2 id="tested-topics">Tested topics</h2>
<p>From the homework post,</p>
<blockquote>
<h3 id="chapter-2-data-transmission">Chapter 2 Data transmission</h3>
<ul>
<li>structure of data packets &amp; contents</li>
<li>packet switching</li>
<li>methods of transmission (serial, parallel, HD &amp; FD)</li>
<li>USB</li>
<li>error detection - parity check, checksum, echocheck</li>
<li>check digits - ISBN 13 and Modulo 11 (know how GENERATE and RECALCULATE) NO CALCULATOR ALLOWED</li>
<li>ARQ</li>
<li>Encryption</li>
</ul>
<h3 id="chapter-3-hardware">Chapter 3 Hardware</h3>
<ul>
<li>CPU Dual core &amp; quad core processors</li>
<li>Von neumann architecture</li>
<li>control bus, address bus, data bus</li>
<li>fetch-decode-execute cycle</li>
<li>instruction sets</li>
<li>embedded systems</li>
<li>qr codes</li>
<li>RAM, ROM, DRAM, SRAM</li>
<li>virtual memory</li>
<li>MAC addresses</li>
<li>router</li>
</ul>
<h3 id="chapter-9-databases">Chapter 9 Databases</h3>
<ul>
<li>structure of flat file - fields, records, validation</li>
<li>data types</li>
<li>use of primary keys</li>
<li>SQL scripts to query data</li>
</ul></blockquote>
<script src="https://utteranc.es/client.js"
     repo="ezntek/ezntek.github.io"
     issue-term="title"
     label="comments"
     theme="github-dark"
     crossorigin="anonymous"
     async>
</script>
]]></content:encoded></item><item><title/><link>http://ezntek.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://ezntek.com/about/</guid><description>&lt;h1 id="about-me">About Me&lt;/h1>
&lt;h2 id="who">who&lt;/h2>
&lt;p>Hey, I&amp;rsquo;m Eason, otherwise known as eggman or ezntek. I&amp;rsquo;m a 16-year-old programmer living in Singapore.&lt;/p>
&lt;p>&lt;em>&lt;strong>NOTE: I barely update this page. If you want proper introductions, might as well e-mail me at &lt;a href="mailto:eason@ezntek.com">eason@ezntek.com&lt;/a>&lt;/strong>&lt;/em>&lt;/p>
&lt;h2 id="technologieslanguages">technologies/languages&lt;/h2>
&lt;ul>
&lt;li>C&lt;/li>
&lt;li>Rust&lt;/li>
&lt;li>Python&lt;/li>
&lt;li>Vue.js and TypeScript (to an extent)&lt;/li>
&lt;li>C++ (to an extent)&lt;/li>
&lt;li>Java (to an extent)&lt;/li>
&lt;li>zig (to an extent)&lt;/li>
&lt;/ul>
&lt;h2 id="hobbies">hobbies&lt;/h2>
&lt;ul>
&lt;li>coding&lt;/li>
&lt;li>computer hardware/bios mods&lt;/li>
&lt;li>playing on &lt;a href="https://ezntek.com/sitaku">sitaku&lt;/a> &lt;a href="https://sitaku.miraheze.org">(wiki)&lt;/a>&lt;/li>
&lt;li>researching transport networks/transit nerd activities&lt;/li>
&lt;li>cooking (surprisingly)&lt;/li>
&lt;li>cycling&lt;/li>
&lt;/ul>
&lt;h2 id="why-cant-i-code-a-lot">why can&amp;rsquo;t i code a lot?&lt;/h2>
&lt;ul>
&lt;li>I&amp;rsquo;m a student.&lt;/li>
&lt;li>No inspiration&lt;/li>
&lt;li>No project ideas&lt;/li>
&lt;li>No motivation&lt;/li>
&lt;/ul>
&lt;h2 id="what-do-i-use">what do i use&lt;/h2>
&lt;p>I use (ranked in order of preference/usage frequency):&lt;/p></description><content:encoded><![CDATA[<h1 id="about-me">About Me</h1>
<h2 id="who">who</h2>
<p>Hey, I&rsquo;m Eason, otherwise known as eggman or ezntek. I&rsquo;m a 16-year-old programmer living in Singapore.</p>
<p><em><strong>NOTE: I barely update this page. If you want proper introductions, might as well e-mail me at <a href="mailto:eason@ezntek.com">eason@ezntek.com</a></strong></em></p>
<h2 id="technologieslanguages">technologies/languages</h2>
<ul>
<li>C</li>
<li>Rust</li>
<li>Python</li>
<li>Vue.js and TypeScript (to an extent)</li>
<li>C++ (to an extent)</li>
<li>Java (to an extent)</li>
<li>zig (to an extent)</li>
</ul>
<h2 id="hobbies">hobbies</h2>
<ul>
<li>coding</li>
<li>computer hardware/bios mods</li>
<li>playing on <a href="https://ezntek.com/sitaku">sitaku</a> <a href="https://sitaku.miraheze.org">(wiki)</a></li>
<li>researching transport networks/transit nerd activities</li>
<li>cooking (surprisingly)</li>
<li>cycling</li>
</ul>
<h2 id="why-cant-i-code-a-lot">why can&rsquo;t i code a lot?</h2>
<ul>
<li>I&rsquo;m a student.</li>
<li>No inspiration</li>
<li>No project ideas</li>
<li>No motivation</li>
</ul>
<h2 id="what-do-i-use">what do i use</h2>
<p>I use (ranked in order of preference/usage frequency):</p>
<table>
  <thead>
      <tr>
          <th>computer</th>
          <th>operating system</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ThinkPad T420 (i7-3632QM)</td>
          <td>Arch Linux+niri (libreboot)</td>
      </tr>
      <tr>
          <td>ThinkPad T480 (i7-8650U)</td>
          <td>Arch Linux+GONME</td>
      </tr>
      <tr>
          <td>Workstation (ASUS Z790-p+i7-14700KF+RX5600XT)</td>
          <td>Artix Linux (dinit)</td>
      </tr>
      <tr>
          <td>ThinkPad X230 (i7-3520M)</td>
          <td>Arch Linux+XFCE (coreboot tianocore)</td>
      </tr>
      <tr>
          <td>ThinKPad L460 (i5-6200U)</td>
          <td>No OS currently</td>
      </tr>
      <tr>
          <td>ThinkPad T440p (i7-4712MQ)</td>
          <td>Arch Linux+KDE Plasma (libreboot)</td>
      </tr>
      <tr>
          <td>ThinkPad X200/X201 FrankenPad (i5-560M)</td>
          <td>Arch Linux+niri (coreboot)</td>
      </tr>
      <tr>
          <td>ThinkPad X230 (i5-3320M)</td>
          <td>Linux Mint (lent to others) (coreboot)</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item><item><title/><link>http://ezntek.com/revision/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://ezntek.com/revision/</guid><description>&lt;h1 id="revision-resources-mostly-for-ofs">Revision Resources (mostly for OFS)&lt;/h1>
&lt;ul>
&lt;li>&lt;a href="http://ezntek.com/doc/CSRG_Full_Rev5.pdf">The IGCSE Computer Science (Theory) Revision Guide (Full), Revision 5&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ezntek.com/doc/igcse_cs_arrays_iteration_workbook.pdf">IGCSE Computer Science: Arrays and Iteration Workbook(booklet?)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ezntek.com/doc/CSRG_G2_Rev4.pdf">The OFS IGCSE Computer Science G2 Exam Revision Reference Guide (2025-2027), Revision Three&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ezntek.com/doc/CSRG_G1_Rev4.pdf">The OFS IGCSE Computer Science G1 Guide to Programming and Logic Gates (2026-2028), Revision Four&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://ezntek.com/doc/pseudocodereference_rev7.pdf">The IGCSE Pseudocode to Python Reference Guide, Revision Seven&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<h1 id="revision-resources-mostly-for-ofs">Revision Resources (mostly for OFS)</h1>
<ul>
<li><a href="/doc/CSRG_Full_Rev5.pdf">The IGCSE Computer Science (Theory) Revision Guide (Full), Revision 5</a></li>
<li><a href="https://ezntek.com/doc/igcse_cs_arrays_iteration_workbook.pdf">IGCSE Computer Science: Arrays and Iteration Workbook(booklet?)</a></li>
<li><a href="https://ezntek.com/doc/CSRG_G2_Rev4.pdf">The OFS IGCSE Computer Science G2 Exam Revision Reference Guide (2025-2027), Revision Three</a></li>
<li><a href="https://ezntek.com/doc/CSRG_G1_Rev4.pdf">The OFS IGCSE Computer Science G1 Guide to Programming and Logic Gates (2026-2028), Revision Four</a></li>
<li><a href="https://ezntek.com/doc/pseudocodereference_rev7.pdf">The IGCSE Pseudocode to Python Reference Guide, Revision Seven</a></li>
</ul>
]]></content:encoded></item><item><title/><link>http://ezntek.com/sitaku/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://ezntek.com/sitaku/</guid><description>&lt;h1 id="sitaku">Sitaku&lt;/h1>
&lt;p>&lt;a href="https://sitaku.miraheze.org">Sitaku&lt;/a> is a minecraft server I play on (a lot).&lt;/p>
&lt;img src="http://ezntek.com/img/japan2c.png" alt="A render of Dechora-dera, Distaye on 2C" width="100%" />
&lt;p>&lt;em>Above: A render of Dechora-dera, Distaye on 2C.&lt;/em>&lt;/p>
&lt;p>&amp;hellip;It is not really a minecraft server in the classic sense. Instead, it is more like a
collection of minecraft worlds with a community overlaid on top of it. Sitaku always will have a
survival and creative world active, and map resets are done whenever they are necessary.&lt;/p></description><content:encoded><![CDATA[<h1 id="sitaku">Sitaku</h1>
<p><a href="https://sitaku.miraheze.org">Sitaku</a> is a minecraft server I play on (a lot).</p>
<img src="/img/japan2c.png" alt="A render of Dechora-dera, Distaye on 2C" width="100%" />
<p><em>Above: A render of Dechora-dera, Distaye on 2C.</em></p>
<p>&hellip;It is not really a minecraft server in the classic sense. Instead, it is more like a
collection of minecraft worlds with a community overlaid on top of it. Sitaku always will have a
survival and creative world active, and map resets are done whenever they are necessary.</p>
<p>The current maps that we play on (active) are 9s (the ninth survival) and 2c (the second creative).
I am most active on 2c (because it is objectively better).</p>
<h2 id="what-do-i-do">What do I do?</h2>
<p>Rails.</p>
<img src="/img/sitaku/comprehensive_map.png" alt="The comprehensive railway map of the Republic of Sitaku" width="100%" />
<p><em>Above: The comprehensive (planning) map of the Republic of Sitaku</em></p>
<p>A lot of this is under construction, especially the heavy rail. But that is my job. I drew the
map and planned the rail network, and it is my job, along with my friend <a href="https://kasreti.github.io">Kasreti</a> to build.</p>
<p>The capital region railway (the Sitaku City metro) is the dense area with station bubbles, and station distances ar
anywhere from 500 to 800 blocks.</p>
<p>Anything without station bubbles are out-of-capital, meaning that those are <em>heavy rail</em> stations. This means that they
can span much longer distances, the shortest distance between stations on the network being about 1750 blocks (I think).</p>
<p>I also do graphic design; I draw all the maps and station art for the subway stations, with the exemption of heavy rail, which
Kasreti does.</p>
]]></content:encoded></item><item><title/><link>http://ezntek.com/stuff/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://ezntek.com/stuff/</guid><description>&lt;h1 id="what-ive-built">What I&amp;rsquo;ve Built&lt;/h1>
&lt;p>&lt;em>&lt;strong>NOTE: this is extremely out of date! might as well check out my &lt;a href="https://github.com/ezntek">GitHub.&lt;/a>&lt;/strong>&lt;/em>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/ezntek/brickout">brickout&lt;/a> (wip ig)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/beancode">beancode&lt;/a> (wip)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/tpfanspeed">tpfanspeed&lt;/a> (will rewrite in C)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/dictionaryparser">dictionaryparser&lt;/a> (will write web app)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/whowlang">whowlang&lt;/a> (will definitely rewrite and rename)&lt;/li>
&lt;li>&lt;a href="https://github.com/BeanwareHQ/beanbattery">beanbattery&lt;/a> (stale)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/youareanidiot">youareanidiot&lt;/a> (stale)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/mathparser">mathparser&lt;/a> (unfinished)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/cursython">cursython&lt;/a> (bullshit)&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/n64romconvert">n64romconvert&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/ezntek/dotfiles">dotfiles&lt;/a> my dotfiles&lt;/li>
&lt;/ul>
&lt;h2 id="sitaku">SITAKU!!&lt;/h2>
&lt;p>Not mine, technically, but I&amp;rsquo;ll put it &lt;a href="http://ezntek.com/sitaku/">here&lt;/a> for now.&lt;/p></description><content:encoded><![CDATA[<h1 id="what-ive-built">What I&rsquo;ve Built</h1>
<p><em><strong>NOTE: this is extremely out of date! might as well check out my <a href="https://github.com/ezntek">GitHub.</a></strong></em></p>
<ul>
<li><a href="https://github.com/ezntek/brickout">brickout</a> (wip ig)</li>
<li><a href="https://github.com/ezntek/beancode">beancode</a> (wip)</li>
<li><a href="https://github.com/ezntek/tpfanspeed">tpfanspeed</a> (will rewrite in C)</li>
<li><a href="https://github.com/ezntek/dictionaryparser">dictionaryparser</a> (will write web app)</li>
<li><a href="https://github.com/ezntek/whowlang">whowlang</a> (will definitely rewrite and rename)</li>
<li><a href="https://github.com/BeanwareHQ/beanbattery">beanbattery</a> (stale)</li>
<li><a href="https://github.com/ezntek/youareanidiot">youareanidiot</a> (stale)</li>
<li><a href="https://github.com/ezntek/mathparser">mathparser</a> (unfinished)</li>
<li><a href="https://github.com/ezntek/cursython">cursython</a> (bullshit)</li>
<li><a href="https://github.com/ezntek/n64romconvert">n64romconvert</a></li>
<li><a href="https://github.com/ezntek/dotfiles">dotfiles</a> my dotfiles</li>
</ul>
<h2 id="sitaku">SITAKU!!</h2>
<p>Not mine, technically, but I&rsquo;ll put it <a href="/sitaku/">here</a> for now.</p>
]]></content:encoded></item><item><title/><link>http://ezntek.com/tuluwasa/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://ezntek.com/tuluwasa/</guid><description>&lt;h1 id="lol-you-found-it">lol you found it&lt;/h1>
&lt;p>Here are some tools for my conlang, Tuluwasa, that I speak with friends.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://ezntek.com/tuluwasa/kaithifier.html">Kaithifier&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<h1 id="lol-you-found-it">lol you found it</h1>
<p>Here are some tools for my conlang, Tuluwasa, that I speak with friends.</p>
<ul>
<li><a href="/tuluwasa/kaithifier.html">Kaithifier</a></li>
</ul>
]]></content:encoded></item></channel></rss>