<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
  <channel>
    <title>Memory Leaks</title>
    <link>https://zachmanson.com/blog/feed.xml</link>
    <description>Artisanally crafted text dumps</description>
    <atom:link href="https://zachmanson.com/blog/feed.xml" rel="self"/>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>python-feedgen</generator>
    <image>
      <url>https://zachmanson.com/icons/android-chrome-256x256.png</url>
      <title>Memory Leaks</title>
      <link>https://zachmanson.com/blog/feed.xml</link>
    </image>
    <language>en</language>
    <lastBuildDate>Fri, 26 Sep 2025 08:10:35 +0000</lastBuildDate>
    <item>
      <title>This feed has moved and GitHub Pages won't let me redirect you automatically</title>
      <link>https://zachmanson.com/blog/new-feed</link>
      <description>Sorry for the inconvenience</description>
      <content:encoded><![CDATA[<p>I haven't written on here in a while, but I have been writing on my <a href="https://notes.zachmanson.com">public notes</a>. At this point I've got over 300 pages on there, far more than I ever wrote on here but in a format that looks a lot more like a wiki. This is mostly a factor of convenience, the format of the notes means I can iteratively add whatever I am thinking about, without the expectation of the writing being complete and canonical. The writing is organised into a heirarchy of folders, with tags and backlinks. I find this format much better for wiki-style writing but doesn't lend itself well to chronological feeds like this one.</p>
<p>Occasionally I would write a note on there that would fit well into the blog format, or was long and more story based. Some of my posts on this blog worked much better as wiki pages than as blog posts. I have wanted to unify these two sites for a long time now. Last week I finally got around to it!</p>
<p>The <a href="https://notes.zachmanson.com/ochrs">Ochrs</a> site generator that I wrote to build the wiki can now generate chronological feeds of posts and RSS feeds. Feeds are generated based on tags, where each tag has its own RSS feed. A note can be added to a chronological feed by adding a publish date (<code>date</code> field in the frontmatter), which will then be used as the publish date in all feeds that the note is a part of. This approach is great since it allows each note to exist anywhere in the wiki hierarchy while also being a part of many feeds. The RSS feed will be at <code>notes.zachmanson.com/&lt;tagname&gt;.xml</code>, and the list of notes in a chronological feed can be shown on a page using the <code>ochrs:chrono:&lt;tagname&gt;</code> build function (see <a href="https://notes.zachmanson.com/ochrs-syntax">Ochrs Syntax</a> for more on how this works), which will results in all the posts being displayed in blog-style HTML.</p>
<p>To succeed <code>zachmanson.com/blog/</code> I have created the posts tag. The feeds can be seen at <a href="https://notes.zachmanson.com/posts">notes.zachmanson.com/posts</a> and using <a href="https://notes.zachmanson.com/posts.xml">RSS</a>. I have moved the existing posts here over to the this new feed.</p>
<p>If you are reading this using RSS then I would like to apologize that you will have to subscribe to a new feed. I would have like to automatically redirect your feed reader, but <strong>GitHub Pages doesn't let you create 301 redirects</strong>. The <code>redirect-from</code> plugin is capable of meta tag redirects, but those are soft and not followed by feed readers. This is very frustrating.</p>
<p>I've marked a bunch of longer notes I've made as posts, so the new feed already has 7 new posts. Hopefully more will come soon!</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/new-feed</guid>
      <pubDate>Fri, 29 Mar 2024 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>The H Chord</title>
      <link>https://zachmanson.com/blog/h-chord</link>
      <description>How German monks broke my website</description>
      <content:encoded><![CDATA[<p>I received a <a href="https://github.com/pavo-etc/penultimate-guitar/issues/41">strange bug report</a> on <a href="https://pg.zachmanson.com">Penultimate Guitar</a> where the Next.js rendering would fail completely on certain songs.</p>
<h2>The Error</h2>
<p>The type error was being triggered by a chord component, where the key was undefined. The chords are pulled from Ultimate Guitar, so I inspected the JSON payload from the <a href="https://tabs.ultimate-guitar.com/tab/1684995">original source</a>.</p>
<p><img alt="" src="https://zachmanson.com/blog/h-chord/ug.png" /></p>
<p>Why the hell is there a <code>[ch]H[/ch]</code> chord in there? That's definitely the cause as I never accounted for non-existent keys. Looking at Ultimate Guitar, it renders as a B chord.</p>
<p><img alt="" src="https://zachmanson.com/blog/h-chord/ug-rendered.png" /></p>
<p>Huh?</p>
<details>
<summary>An aside on Ultimate Guitar HTML</summary>
<p>The way Ultimate Guitar handles data is bizarre. It passes a static dehydrated HTML page to the client.  The data payload is in the HTML as well, but instead of being contained in a <code>script</code> tag it's a giant JSON payload in an escaped string within an attribute of a random <code>div</code></p>
</details>
<h2>One Google Search Later</h2>
<p>Germany and the Netherlands ha(d/ve) their own musical key notation that include(d/s) a H chord. <a href="https://www.guitarsite.com/newsletters/010122/12.shtml">This site</a> claims it ended in "1994/1995", though I've seen <a href="https://github.com/pavo-etc/penultimate-guitar/issues/41#issuecomment-1538452351">other</a> <a href="https://www.reddit.com/r/musictheory/comments/8rn0ve">sources</a> claim its still taught this way. This comes as an artifact of the bizarre history Western music notation, which is a problem I seem to keep running into recently in my attempts to learn more about music theory.</p>
<blockquote>
<p>I learned in music school that the chord is called "B" (like in the rest of the world), but the note is called "H" (eg. the C Major scale would be C, D, E, F, G, A, H, C).</p>
</blockquote>
<p><cite>TobTobXX, who reported the bug</cite></p>
<h2>Western Music's Stupid Origins</h2>
<p>Western music is based on ecclesiastic modes used in church in the early Middle Ages, which only used the diatonic notes of the C scale (<em>natural</em> notes). The musical notation systems of the time reflected this, not accounting for notes outside of the C scale. When sharps and flats later came into more common use, the existing notation systems needed a way to distinguish them from the natural notes they sat between.</p>
<p>This problem first arose with B natural and B flat, according to the <a href="https://www.britannica.com/art/musical-expression">Encyclopedia Britannica</a>. The first method of distinguishing B from B flat was using two different forms of the lowercase "b" character:</p>
<p><img alt="" src="https://zachmanson.com/blog/h-chord/The-Flat-Sharp-And-Natural-A-Historical-Sketch.png" /></p>
<p>Niecks, Frederick. “The Flat, Sharp, and Natural. A Historical Sketch.” <em>Proceedings of the Musical Association</em>, vol. 16, 1889, pp. 79–100.</p>
<p>Somewhere along the line in Germany, monks transcribing these square and round "b" characters confused the squared "b" for "h", and this was later assumed to be intentional. "H" became a convention for writing B natural, while the "b" character remained convention for writing B flat. "B" and "b" became amalgamated, both coming to represent modern B flat.</p>
<p>In the rest of the world, this "H" note didn't catch on.</p>
<p>In time, notation for sharp and flat notes other than B flat was needed. This use of square and round "b" to denote B and B flat eventually evolved into our modern notation for indicating natural and non-natural, <em>accidentals</em>. ♭ comes from the round "b", while ♯ and ♮ come from the square "b".</p>
<p>Somehow, Germany still hasn't fully corrected this mistake, continuing to use "H" to represent B natural and many places. Including Ultimate Guitar. Ugh.</p>
<h2>Resolution</h2>
<p>I don't love dealing with problems caused by the whims of millenia dead monks, but this was an interesting rabbit hole to fall into. The issue has since been patched, and I look forward to my mistakes ruining someone's day in 3023.</p>
<p><img alt="" src="https://zachmanson.com/blog/h-chord/commit.png" /></p>
<details>
<summary> An aside on The Flat, Sharp, and Natural. A Historical Sketch </summary>
<p>When I first copied the text from the article, it copied that "square b" as a "h". Funny that modern OCR technology makes the same mistakes at 1000 year old monks.</p>
<blockquote>
<p>The first known writer who distinguished between b natural and b flat was Odo of Clugny, who died in 942 ; the b natural being indicated by a square b (h), the b flat by a round b (b)</p>
</blockquote>
<p>It was also a pain in the ass to find a copy of that article. It's mostly found on paywalled academic sites despite the article definitely being out of copyright. Luckily the Internet Archive <a href="https://scholar.archive.org/work/3jdud373effq3e376gqtlkxqvq">has a copy</a>.</p>
</details>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/h-chord</guid>
      <pubDate>Mon, 08 May 2023 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>LinkedIn's Ghost Text</title>
      <link>https://zachmanson.com/blog/linkedin-duplication</link>
      <description>Why does LinkedIn copy text twice</description>
      <content:encoded><![CDATA[<p>Why does LinkedIn do this?</p>
<video autoplay loop muted>
   <source src="./duplication.webm" type="video/webm">
</video>

<p>If you look into the source of a LinkedIn profile page (at your own personal risk) you'll find a text element like this</p>
<p><img alt="" src="https://zachmanson.com/blog/linkedin-duplication/element.png" /></p>
<p>will have HTML like this</p>
<pre><code class="language-html">&lt;div class=&quot;display-flex flex-row justify-space-between&quot;&gt;
    &lt;div class=&quot;
          display-flex flex-column full-width&quot;&gt;

        &lt;div class=&quot;display-flex flex-wrap align-items-center full-height&quot;&gt;
            &lt;span class=&quot;mr1 t-bold&quot;&gt;
                &lt;span aria-hidden=&quot;true&quot;&gt;&lt;!----&gt;Product manager, Creative coder&lt;!----&gt;&lt;/span&gt;
                &lt;span class=&quot;visually-hidden&quot;&gt;&lt;!----&gt;Product manager, Creative coder&lt;!----&gt;&lt;/span&gt;
            &lt;/span&gt;
            &lt;!----&gt;&lt;!----&gt;&lt;!----&gt;
        &lt;/div&gt;
        &lt;span class=&quot;t-14 t-normal&quot;&gt;
            &lt;span aria-hidden=&quot;true&quot;&gt;&lt;!----&gt;Freelancer/Consultant&lt;!----&gt;&lt;/span&gt;
            &lt;span class=&quot;visually-hidden&quot;&gt;&lt;!----&gt;Freelancer/Consultant&lt;!----&gt;&lt;/span&gt;
        &lt;/span&gt;
        &lt;span class=&quot;t-14 t-normal t-black--light&quot;&gt;
            &lt;span aria-hidden=&quot;true&quot;&gt;&lt;!----&gt;2009 - 2018 · 9 yrs&lt;!----&gt;&lt;/span&gt;
            &lt;span class=&quot;visually-hidden&quot;&gt;&lt;!----&gt;2009 - 2018 · 9 yrs&lt;!----&gt;&lt;/span&gt;
        &lt;/span&gt;
        &lt;span class=&quot;t-14 t-normal t-black--light&quot;&gt;
            &lt;span aria-hidden=&quot;true&quot;&gt;&lt;!----&gt;Europe&lt;!----&gt;&lt;/span&gt;
            &lt;span class=&quot;visually-hidden&quot;&gt;&lt;!----&gt;Europe&lt;!----&gt;&lt;/span&gt;
        &lt;/span&gt;

    &lt;/div&gt;

    &lt;!----&gt;
    &lt;div class=&quot;pvs-entity__action-container&quot;&gt;
        &lt;!----&gt;
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Gross.  Why the hidden duplicated text?  Why the empty comments?  Why seperate aria versions of text?  Is this related to using Ember?  LinkedIn has so many little unpleasantries like this.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/linkedin-duplication</guid>
      <pubDate>Sun, 16 Apr 2023 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Planting a Flag in Wikipedia</title>
      <link>https://zachmanson.com/blog/libkey-nomad</link>
      <description>LibKey Nomad's Stolen Valour</description>
      <content:encoded><![CDATA[<p>There is a wonderful browser extension with an awful name made by Third Iron called <a href="https://thirdiron.com/products/libkey-nomad/">LibKey Nomad</a>. It allows university staff and students gain access to academic journals that your univesity is subscribed to without logging in or requesting access. You install the extension, sign in with your university credentials, and then it just works. Whenever you are on a site that LibKey Nomad is automatically allowing you access to, it displays this little banner in the corner of the page.</p>
<p><img alt="" src="https://zachmanson.com/blog/libkey-nomad/proper-popup.png" /></p>
<p>You click it and you are taken direct to the paper. It really streamlines the process for a lot of sites.</p>
<p><img alt="" src="https://zachmanson.com/blog/libkey-nomad/wikipedia-popup.png" /></p>
<p>It hilariously appears on Wikipedia articles, as if the University of Western Australia has been incredibly gracious by allowing me to see Wikipedia. The reasoning here is that access to (at least one of) the citations is being provided through LibKey Nomad, but this makes the banner no less obnoxious.</p>
<p>The banner doesn't even have a function like it does on other journals, clicking it just takes you to the references list.</p>
<p><img alt="" src="https://zachmanson.com/blog/libkey-nomad/citations.png" /></p>
<p>Wikipedia is one of the most impressive things humans have created. Keep your banners to yourself, Third Iron.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/libkey-nomad</guid>
      <pubDate>Mon, 06 Feb 2023 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>The Curse of the Last Good Day</title>
      <link>https://zachmanson.com/blog/last-good-day</link>
      <description>Spoilers for &lt;i&gt;The Fault in Our Stars&lt;/i&gt;, I guess</description>
      <content:encoded><![CDATA[<p><em>The Fault in Our Stars</em> describes the phenomenon of the Last Good Day, a concept that has been echoing in my head ever since I read the book in high school. In the novel, the Last Good Day is the final time a person who is slowly deteriorating has a peaceful and pleasant moment with those around them.</p>
<blockquote>
<p>There's no way of knowing that your last good day is Your Last Good Day. At the time, it is just another good day.</p>
</blockquote>
<p>The story presents this in the context of Augustus' worsening condition and eventual death.</p>
<p>So far, I've been lucky enough to be free of facing slow health declines in myself and those close to me, but the concept of the Last Good Day has sat quietly in the back of my mind since first encountering it. Whenever I have some kind of impending loss, no matter how small, my thoughts tend to orbit around it.</p>
<p>With my bachelor's degree coming to a close soon, lots of my friends are moving away for careers or further study in other countries. The groups that have formed won't continue to exist in the way the have for the last 3 years. Not everyone is leaving of course and no one person is single load-bearing pillar of the group, but buildings tend not to handle losing half their supports.</p>
<p>I've been aware that this will happen for months, and at every group event in my head I'm constantly thinking <em>Is this going to be the Last Good Day? Is this Good enough? Can I make this better?</em> This is definitely not healthy and definitely doesn't help me enjoy the limited time left with my friends. This line of thinking is something I hesitate to even talk about with my friends in fear of inspiring the same kind of thought spirals in their minds.</p>
<p>Time has gone on and the erosion has begun. As I write this, another will be flying to Sydney tomorrow morning. Enough friends have left now that the Last Good Day has certainly passed, and yet I'm still not 100% sure which day it was. There's some strong contenders though.</p>
<p>That's life, I know I'll make more friends and there's no need to be melodramatic. Regardless, I'm going to miss this period and the people I've spent it with.</p>
<p>Cheers and auld lang syne and so forth.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/last-good-day</guid>
      <pubDate>Fri, 27 Jan 2023 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Minecraftle v1.1</title>
      <link>https://zachmanson.com/blog/minecraftle-v1.1</link>
      <description>Enumerating some small usability changes</description>
      <content:encoded><![CDATA[<p><a href="https://minecraftle.zachmanson.com">Minecraftle</a> is a simple game written for my introductory web development unit (CITS3403 with Tim French, would recommend) in 2022 Semester 1. The project was submitted (and completed) on May 23, but since then I have been periodically updating it with "nice to have" features that we didn't manage to get in before the deadline. After 8 months I am happy to label this Minecraftle version 1.1, with changes stemming from three main motivations: usability, optimisation, and revenge.</p>
<p><img alt="" src="https://zachmanson.com/blog/minecraftle-v1.1/git-log.png" /></p>
<h2>Usability</h2>
<p><code>537c928</code>: Made the emoji summary more vague, so a correct guess shows up as 9 green squares no matter what, rather than giving away the shape of the final solution.</p>
<details>
<summary>Before and after comparison</summary>
Before:


<pre><code>Minecraftle 24/05/2022

⬜⬜⬜
⬜⬜⬜
⬜⬜⬜

🟩🟩🟨
⬜🟩⬜
⬜🟩⬜

🟩🟩⬜
🟩🟩⬜
⬜🟩⬜
</code></pre>


After:


<pre><code>Minecraftle 25/05/2022 3/10
⬜🟩⬜
⬜⬜⬜
⬜⬜⬜

⬜⬜⬜
🟩🟩⬜
🟨⬜⬜

🟩🟩🟩
🟩🟩🟩
🟩🟩🟩

</code></pre>


</details>

<p><code>23dc225</code>: Added check so that only valid Minecraft recipes could be submitted as guesses.</p>
<p><code>bc40b98</code>: Added proper support for horizontal reflections of recipes.</p>
<p><code>f3295d7</code> &amp; <code>36386df</code>: Made end-of-game popup display different text for Random games.</p>
<p><code>fef189d</code>: Changed the given items for a more interesting set of possible recipes.</p>
<p><code>30a3829</code>: Made end-of-game popup togglable.</p>
<p><code>f43b062</code>: Added loading indicator to initial pageload.</p>
<p><code>8a842d9</code>: Added slot highlighting to the ingredients panel to help keep track of which items have been used before.</p>
<p><img alt="" src="https://zachmanson.com/blog/minecraftle-v1.1/panel.png" /></p>
<h2>Optimisation</h2>
<p><code>c502eb2</code>: Rewrote the entire script that turned the 600+ Minecraft crafting recipe JSON files into single file with a usable schema. Previous implementation was very messy, future modifications will be much easier.</p>
<p><code>172b3f2</code>: Optimised recipes validation, still relatively inefficient though.</p>
<p><code>cd0c61e</code>: Made game submission API a seperate endpoint to user stats endpoint, rather than being a hacky overload of the stats endpoint.</p>
<p><code>007865b</code>: Significantly reduced size of <code>recipes.json</code> to only include recipes that are actually possible to create with the given ingredients. This reduces initial pageload time and reduces load on my server.</p>
<p><code>007865b</code>: Set <code>init.js</code> to store <code>recipes.json</code> and <code>items.json</code> in local storage, and only request them if they don't exist there. This makes pageloading much faster, and reduces load on my server.</p>
<h2>Revenge</h2>
<p>Since initial release 2500+ people have played the game. From what I can tell this primary originated from popularity at a local primary school and the now-defunct <a href="https://web.archive.org/web/20220927035303/https://likewordle.com/">LikeWordle.com</a>. Most of these players haven't become regular users though, only playing one or two games. Really its just me and my ex competing for the number one spot on the leaderboard. After months of hard work she overtook me as number 1 and I have been clawing my way back ever since. Obviously I could cheat since I'm the admin but where's the fun in that? Despite that, I'm not opposed to adding features to help me take back the crown.</p>
<p><code>309a557</code>: Added indicators to the stats page to show users how far behind and ahead they are of their neighbours on the leaderboard.</p>
<p><code>5dca9d2</code>: Added an account recovery button since I accidentally deleted my account the day after taking back the crown (I swear). This does expose a prexisting security flaw, but does enable it to be used in a positive way. For reference, clearing your browser data will make your device forget which account is it associated with.</p>
<h2>Bugfixes</h2>
<p><code>23dc225</code>: Fixed bug caused by the use of item tags in Minecraft source files. Item tags represent groups of items that are effectively interchangable in crafting recipes, such as coal and charcoal. Minecraftle conversion scripts have been updated to expect this, and will replace these with the preferred variant e.g. coal/charcoal group will be replaced with coal.</p>
<p><code>d98e83a</code>: Forced game to be set to Perth timezone. Deployment VPS is in North America, so the server-side calculation of the current recipes wasn't changing at midnight in Perth, where most of the player base is.</p>
<p><code>c0ce8c2</code>: Fixed stick recipe being overwritten by second stick recipes that uses bamboo.</p>
<hr />
<p>It was some of the first JavaScript I ever wrote and it reflects that, much of the old code makes me want to cringe. With the benefit of 6 months professionally working on large TypeScript projects, Minecraftle seems quaint (and somewhat shocking that it even works).</p>
<p>Regardless, it holds a place in my heart as my first webdev project with any complexity and is likely responsible for getting my foot in the door at my current job. For this reason I am willing to continue tending the garden it occupies.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/minecraftle-v1.1</guid>
      <pubDate>Thu, 26 Jan 2023 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Vercel Image Optimisation</title>
      <link>https://zachmanson.com/blog/vercel-images</link>
      <description>A small footgun for beginners using Vercel</description>
      <content:encoded><![CDATA[<p>TLDR: If you are using Vercel and display a lot of images hosted on someone else's server, you probably want to turn off <a href="https://vercel.com/docs/concepts/image-optimization">Image Optimisation</a>.</p>
<p>Over the last week I built <a href="https://alculator.zachmanson.com">Alculator</a> using Next.js and hosted on Vercel. The site uses data lovingly freebooted from the Dan Murphy's public API to rank products by the ratio of price to standard drinks, and displaying this data in a card.</p>
<p><img alt="" src="https://zachmanson.com/blog/vercel-images/cards.png" /></p>
<p>The images for these cards are hosted on a Dan Murphy's server (media.danmurphys.com.au), and I am simply using Next.js <code>&lt;Image&gt;</code> tags to point to them.</p>
<pre><code class="language-tsx">...
    &lt;div className=&quot;card center&quot;&gt;
      &lt;div
        className=&quot;flex center-aligned&quot;
        onClick={() =&gt; (window.location.href = `https://www.danmurphys.com.au/product/${item.stockcode}`)}
      &gt;
        &lt;Image
          alt=&quot;Image of drink&quot;
          height=&quot;100&quot;
          width=&quot;80&quot;
          src={`https://media.danmurphys.com.au/dmo/product/${item.stockcode}-1.png`}
        /&gt;
        &lt;div className=&quot;fill-width&quot;&gt;
          &lt;div className=&quot;flex space-between align-center&quot;&gt;
            &lt;h3&gt;{item.name}&lt;/h3&gt;
...
</code></pre>
<p>I'm using Vercel's free tier to serve the application which grants 100GB of bandwidth, which is magnitudes higher than any amount of users I expected to serve. I put the finishing touches on the site on December 30th, sent the link to a few people and went to the pub.</p>
<p>Over the next few hours I started receiving strongly worded alerts from Vercel:</p>
<p><img alt="" src="https://zachmanson.com/blog/vercel-images/warnings.png" /></p>
<p>Either I had gone viral or my code was a lot more inefficient than I thought. At 1AM I returned home, having reached the Ballmer Peak to diagnose the problem.</p>
<p>On the Vercel/Dashboard/Usage page, right at the bottom is the harmless looking graph titled "Image Optimisations" which turned out to be the culprit. By default, Vercel takes all images hosted in <code>&lt;Image&gt;</code> tags and caches them all at the Edge. Vercel had optimised 1400 of these images before I discovered this and at the free tier this is capped at 1000 images. Whoops.</p>
<p>The solution is simple:</p>
<p class="filename">next-config.js</p>

<pre><code class="language-js">/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    unoptimized: true,
  },
};

module.exports = nextConfig;
</code></pre>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/vercel-images</guid>
      <pubDate>Mon, 02 Jan 2023 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Plain HTML Components</title>
      <link>https://zachmanson.com/blog/jinja-components</link>
      <description>Building resuable components in plain HTML using Jinja</description>
      <content:encoded><![CDATA[<p>Building sites with vanilla HTML can be a pain in the ass if you want to redesign an element that appears in every page, like a navigation bar or footer. Static site generators solve this problem with components, but I like reinventing the wheel. Jinja templating can be used to ape components since it is capable of passing arbitrary functions into its DOM templates.</p>
<p class="filename">ssg.py</p>

<pre><code class="language-python">def blog_footer():
    return &quot;&quot;&quot;
    &lt;footer&gt;
      &lt;p&gt;
        &lt;a href=&quot;/blog/feed.xml&quot;&gt;Feed&lt;/a&gt; -
        &lt;a
          href=&quot;https://github.com/pavo-etc/pavo-etc.github.io/tree/master/generator&quot;
          &gt;ironprof&lt;/a
        &gt;
      &lt;/p&gt;
    &lt;/footer&gt;&quot;&quot;&quot;

page_template = jinja2.Template(open(&quot;template.html&quot;, &quot;r&quot;).read())
with open(&quot;output.html&quot;), &quot;w&quot;) as f:
    f.write(page_template.render({
        &quot;blog_footer&quot;:blog_footer
    }))
</code></pre>
<p class="filename">template.html</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    {{ blog_footer() }}
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p class="filename">output.html</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;footer&gt;
      &lt;p&gt;
        &lt;a href=&quot;/blog/feed.xml&quot;&gt;Feed&lt;/a&gt; -
        &lt;a
          href=&quot;https://github.com/pavo-etc/pavo-etc.github.io/tree/master/generator&quot;
          &gt;ironprof&lt;/a
        &gt;
      &lt;/p&gt;
    &lt;/footer&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>This follows normal Python rules, so inheritance is followed and parameters can be passed into functions in the template.</p>
<p class="filename">components.py</p>

<pre><code class="language-python">def nav(activated_link):
    links = [&quot;projects&quot;, &quot;blog&quot;]
    link_elements = []
    for link in links:
        is_active = ' activated-link' if activated_link == link else ''
        link_elements.append(f'        &lt;a href=&quot;/{link}/&quot; class=&quot;discrete-link{is_active}&quot;&gt;{link.capitalize()}&lt;/a&gt;')

    right_links_str = '\n'.join(link_elements)

    is_root_active = ' activated-link' if activated_link == '.' else ''
    return f&quot;&quot;&quot;
    &lt;nav&gt;
        &lt;div class=&quot;flex&quot;&gt;
        &lt;div class=&quot;flex-left&quot;&gt;
            &lt;a href=&quot;/&quot; class=&quot;discrete-link{is_root_active}&quot;&gt;Zach Manson&lt;/a&gt;
        &lt;/div&gt;
        &lt;div class=&quot;flex-right&quot;&gt;
{right_links_str}
        &lt;/div&gt;
        &lt;/div&gt;
    &lt;/nav&gt;&quot;&quot;&quot;

def blog_footer():
    return &quot;&quot;&quot;
    &lt;footer&gt;
      &lt;p&gt;
        &lt;a href=&quot;/blog/feed.xml&quot;&gt;Feed&lt;/a&gt; -
        &lt;a
          href=&quot;https://github.com/pavo-etc/pavo-etc.github.io/tree/master/generator&quot;
          &gt;ironprof&lt;/a
        &gt;
      &lt;/p&gt;
    &lt;/footer&gt;&quot;&quot;&quot;
</code></pre>
<p class="filename">ssg.py</p>

<pre><code class="language-python">import components

page_template = jinja2.Template(open(&quot;template.html&quot;, &quot;r&quot;).read())
with open(&quot;output.html&quot;), &quot;w&quot;) as f:
    f.write(page_template.render({
        &quot;components&quot;:components
    }))
</code></pre>
<p class="filename">template.html</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    {{ components.nav(&quot;blog&quot;)}}

    &lt;p&gt;Lorem ipsum amirite&lt;/p&gt;

    {{ components.blog_footer() }}
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p class="filename">output.html</p>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;nav&gt;
      &lt;div class=&quot;flex&quot;&gt;
        &lt;div class=&quot;flex-left&quot;&gt;
          &lt;a href=&quot;/&quot; class=&quot;discrete-link&quot;&gt;Zach Manson&lt;/a&gt;
        &lt;/div&gt;
        &lt;div class=&quot;flex-right&quot;&gt;
          &lt;a href=&quot;/projects/&quot; class=&quot;discrete-link&quot;&gt;Projects&lt;/a&gt;
          &lt;a href=&quot;/blog/&quot; class=&quot;discrete-link activated-link&quot;&gt;Blog&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/nav&gt;

    &lt;p&gt;Lorem ipsum amirite&lt;/p&gt;

    &lt;footer&gt;
      &lt;p&gt;
        &lt;a href=&quot;/blog/feed.xml&quot;&gt;Feed&lt;/a&gt; -
        &lt;a
          href=&quot;https://github.com/pavo-etc/pavo-etc.github.io/tree/master/generator&quot;
          &gt;ironprof&lt;/a
        &gt;
      &lt;/p&gt;
    &lt;/footer&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/jinja-components</guid>
      <pubDate>Thu, 17 Nov 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Antisocial Media</title>
      <link>https://zachmanson.com/blog/github-feed</link>
      <description>An ode to the GitHub following feed</description>
      <content:encoded><![CDATA[<p>Without me even noticing, the GitHub following feed become the healthiest form of social media in my life. It's a feature that feels so incidental the platform that I forget it exists, leaving me pleasantly surprised every time I see it. As far as I can tell, most GitHub users rarely even see the feed since it's only visible on the main <a href="https://github.com"><code>https://github.com</code></a> page, which I imagine isn't really a hot destination for most users.</p>
<p>I have the standard complement of social media platforms for someone my age, but I wouldn't say I use any of them they way I would like to. For me, social media ideally is a tool I use to occasionally and asynchronously keep up with what's going on in my friends' lives. My actual use of social media platforms is one of the following:</p>
<ul>
<li>Barely use at all (Facebook)</li>
<li>Be angry that barriers are put between me and my friends posts (Instagram)</li>
</ul>
<p>Instagram once supported my ideal relationship with social media, but has incrementally moved further and further away. For years each update has made it harder to see my friends posts, cramming more things into my feed I have no interest in. The main feed has been so polluted that they recently added the "feature" to see a feed consisting exclusively of your friends posts. What was once the primary point has become a footnote in a changelog, hidden behind an awkwardly placed dropdown menu. Snapchat, while a very different beast, has suffered the same gentle decline in usability.</p>
<p>In contrast to this, the GitHub feed has been nothing but pure (though compsci slanted) embodiment of my ideal social media. Over the last 3 years of university, I have followed a total of 21 people followed people on GitHub, either friends or people I admire. This is tiny compared to my social graphs on other networks, but I found seeing updates from these 21 people more rewarding than hundreds of people on Facebook, and significantly less rage inducing than using Instagram.</p>
<p>Obviously these platforms are extremely different and selection bias plays a massive role in my experience using these feeds, but I seeing the projects that are being worked on by people I am interested in is always a lovely respite from the rest of the social internet.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/github-feed</guid>
      <pubDate>Fri, 09 Sep 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Collaborative Awe</title>
      <link>https://zachmanson.com/blog/collaborative-awe</link>
      <description>Minor but impactful differences between DALL·E and Midjourney</description>
      <content:encoded><![CDATA[<p>I recently received access to DALL·E and played around with it for a while. The interface is simple, just a textbox and a few curated images to give you some inspiration.</p>
<p><img alt="" src="https://zachmanson.com/blog/collaborative-awe/dall-e.jpg" /></p>
<p>Once you submit a prompt to DALL·E, you are shown more curated images and their corresponding prompts while your request is processed.</p>
<p>DALL·E is capable of producing some amazing images. It takes a bit of warming up, but after little practice I was able to create wonderful and bizarre generative art. Despite this, I felt I didn't know how to produce something as interesting or engaging as their curated images the presented to me when using DALL·E. I could logically understand that the tool had potential, but I felt seperated from it.</p>
<p>Using Midjourney is completely a different experience. Midjourney currently runs its generative art AI through a bot on their company Discord server. You generate art by typing in a command in certain channels, and all the art produced is visible to everyone on the server simultaneously. Before you can even type something you are assaulted by an array of incredible images, diffusing right before your eyes.</p>
<video autoplay loop muted>
   <source src="./firehose.webm" type="video/webm">
</video>

<p>While both generators are impressive, using Midjourney was a visceral experience for me in a way that DALL·E was not. Where DALL·E is sipping a glass of water, Midjourney is a firehose blasting your face. I was immediately in awe.</p>
<p>The design of Midjourney makes it abundantly clear how big an impact this technology will have on the world. Watching hundreds of strangers generate incredible images in real-time is jaw dropping. The unfiltered, uncurated stream of regular people using the software is a lot more impressive than the sanitised, slick, and isolated interface of DALL·E. I was mesmerised, and within minutes saw images that moved me, despite being created by an AI without any intention behind them.</p>
<p>While I am sure both of these are the future, only one made me feel it.</p>
<p><img alt="" src="https://zachmanson.com/blog/collaborative-awe/soldiers.png" /></p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/collaborative-awe</guid>
      <pubDate>Sat, 06 Aug 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>The Poor Man's Static Site Generator</title>
      <link>https://zachmanson.com/blog/poor-mans-site</link>
      <description>Using Pandoc to well below its fullest potential!</description>
      <content:encoded><![CDATA[<p><a href="https://pandoc.org/">Pandoc</a> is a Swiss Army knife that I exclusively use to cut cheese.</p>
<p>Every time I begin to write for this site, I open Codium and create a new file in my repo. Then I realise I meant to create a new folder, call myself an idiot and start over. This is the first step of my process, which currently concludes with running my anemic static site generator. I hope to soon replace with this a bespoke and iron proficient(?) site generator, but until then I will continue using Pandoc to fulfill my static site generator requirements.</p>
<p>My static site generator requirements:</p>
<ul>
<li>Markdown input</li>
<li>Code highlighting</li>
<li>with theming?</li>
<li>Simple templating</li>
<li>Minimum (ideally 0) dependencies</li>
<li>Automated?</li>
</ul>
<h2>Markdown Input</h2>
<p>It can convert documents between myriad formats, most relevantly Markdown to HTML.</p>
<pre><code class="language-bash">pandoc inputfile.md -o output.html --to=html5
</code></pre>
<h2>Code Highlighting</h2>
<p>There are plenty of tools that can do this, but Pandoc was the first I found that had code highlighting without having any depedencies like <a href="https://highlightjs.org/">highlight.js</a>, just saving the colour information in inline CSS</p>
<p>Any fenced Markdown code blocks with a language specified (like the following) will have the syntax automatically highlighted by Pandoc.</p>
<pre><code class="language-Markdown"># Title

## Subtitle

Lorem ipsum amirite.  Hey look a fenced code block:

```Python
if x == 5:
    print(&quot;x is equal to 5&quot;)
```
</code></pre>
<p>Pandoc will turn this Markdown into the following HTML:</p>
<pre><code class="language-HTML">&lt;h1 id=&quot;title&quot;&gt;Title&lt;/h1&gt;
&lt;h2 id=&quot;subtitle&quot;&gt;Subtitle&lt;/h2&gt;
&lt;p&gt;Lorem ipsum amirite. Hey look a fenced code block:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode python&quot;&gt;&lt;code class=&quot;sourceCode python&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;#cb3-1&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; x &lt;span class=&quot;op&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dv&quot;&gt;5&lt;/span&gt;:&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;#cb3-2&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;bu&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;st&quot;&gt;&quot;x is equal to 5&quot;&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</code></pre>
<p>The various HTML tag class spaghetti are used to highlight the code, though the CSS required to actually make this work is not included in the standard Pandoc command invocation. To actually generate the CSS that makes these classes work you need to use the <code>--standalone</code> flag, à la:</p>
<pre><code class="language-bash">pandoc inputfile.md -o output.html --to=html5 --standalone
</code></pre>
<p>This will include some inline CSS, along the lines of:</p>
<pre><code class="language-CSS">code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</code></pre>
<p>This CSS is great, but only actually needs to be generated once. I copied this CSS in a single <a href="https://github.com/pavo-etc/pavo-etc.github.io/blob/94d59de43188660d625d12b5df33894841f99922/styles/code.css">file</a>. After copying this text, the <code>--standalone</code> flag can be omitted.</p>
<h3>with theming?</h3>
<p>The colours used in the highlighting can be customised! As with everything else in my life at the moment, I use <a href="https://draculatheme.com/">Dracula</a>, which thankfully <a href="https://draculatheme.com/pandoc">already has a theme for Pandoc</a>.</p>
<p>Once the theme is installed, Pandoc can highlight using the theme with the <code>--defaults</code> flag:</p>
<pre><code class="language-bash">pandoc inputfile.md -o output.html --to=html5 --standalone --defaults path/to/theme/dracula.yaml
</code></pre>
<p>The HTML classes will be the same names regardless of the theme, but the inline CSS will be dependent on the theme. Again, this only needs to be run once, and the inline CSS can be copied and saved to be used later.</p>
<h2>Simple Templating</h2>
<p>Pandoc makes it simple, as long as you only need simple template insertions! HTML templates can be created easily. A basic template could look like the following:</p>
<pre><code class="language-HTML">&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;/styles/code.css&quot;&gt;
    &lt;title&gt;$title$&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;header&gt;
        &lt;h1&gt;$title$&lt;/h1&gt;
        &lt;p&gt;$subtitle$&lt;/p&gt;
        &lt;p&gt;$date$&lt;/p&gt;
    &lt;/header&gt;

    &lt;article&gt;
        $body$
    &lt;/article&gt;
&lt;/body&gt;
</code></pre>
<p>Note the inclusion of the stylesheet. <code>code.css</code> includes the CSS for code highlighting.</p>
<p>The <code>body</code> variable is automatically assigned to the content of the Markdown file, and the rest of the variables can be assigned manually using a YAML header in the Markdown file.</p>
<pre><code class="language-Markdown">---
title: A Titular Title
subtitle: A subtitular subtitle
date: 2022-06-12
---

## Chapter 1: Where it all began

Lorem ipsum amirite.  Hey look a fenced code block:

```Python
if x == 5:
    print(&quot;x is equal to 5&quot;)
```
</code></pre>
<p>To use the template, you use the <code>--template</code> flag in the Pandoc invocation:</p>
<pre><code class="language-bash">pandoc inputfile.md -o outputfile.html --to=html5 --template=path/to/template.html
</code></pre>
<h2>Dependencies</h2>
<p>None! The outputted HTML has no external dependencies.</p>
<h2>Automated?</h2>
<p>It's a pain typing out that command for each post - so don't.</p>
<pre><code class="language-bash">#!/usr/bin/env bash

nonposts=(&quot;styles/&quot;, &quot;templates/&quot;)

for d in */; do
    echo &quot;$d&quot;
    if ! [[ ${nonposts[*]} =~ $d ]]; then
        echo &quot;is post&quot;
        echo &quot;generating index.html&quot;
        name=$(echo &quot;$d&quot; | sed 's/.$//')
        echo &quot;pandoc ${d}${name}.md -o ${d}index.html --to=html5 --template=templates/template.html&quot;
        pandoc ${d}${name}.md -o ${d}index.html --to=html5 --template=templates/template.html
    fi
done
echo &quot;done&quot;
</code></pre>
<p>This script is designed to work on the following directory structure:</p>
<pre><code class="language-bash">blog
├── basic-seleniumbasic
│   ├── adding-reference.png
│   ├── basic-seleniumbasic.md
│   ├── dotnet-frameworks.png
│   ├── index.html
│   └── seleniumbasic-installer.png
├── covid-updates
│   ├── covid-updates.md
│   ├── index.html
│   └── media_release.png
├── crafting-recipes
│   ├── crafting-recipes.md
│   └── index.html
├── file-descriptors
│   ├── file-descriptors.md
│   └── index.html
├── generate_posts.sh
├── hep-001
│   ├── hep-001.md
│   └── index.html
├── index.html
├── poor-mans-site
│   ├── index.html
│   └── poor-mans-site.md
├── templates
│   └── template.html
└── usernames
    ├── index.html
    └── usernames.md
</code></pre>
<p>It opens every directory (except those in <code>nonposts</code>), runs pandoc on a Markdown file with the same name as the directory, and outputs <code>index.html</code> based on <code>templates/template.html</code>.</p>
<p>The top level <code>index.html</code> is not modified by the script at all. This page presumably would link to the subdirectories and must be manually modified. Ugh.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/poor-mans-site</guid>
      <pubDate>Sat, 11 Jun 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Sneaky File Descriptors in Popen()</title>
      <link>https://zachmanson.com/blog/file-descriptors</link>
      <description>Always be closing</description>
      <content:encoded><![CDATA[<p>In the last month I had three projects due dates converge, on top of full-time work I accidentally signed up for and on top of standard full-time uni commitments.  Of these projects, <a href="https://minecraftle.zachmanson.com">Minecraftle</a> was the most fun to put together, but <a href="https://github.com/pavo-etc/rake">rake</a> was the most <em>educational</em>.  I'd never worked with sockets or TCP protocols before so this project was out of my comfort zone, and barring a few hiccups, we managed to build decent (and fragile) client and server programs.</p>
<p>The assignment required the design and implementation of protocol to enable distributed compilation of files and remote command execution across multiple servers (r(emote) + (m)ake = rake).  We needed to write a server program, and two client programs (one in C, the other in Python).  Between Beej's guide and the coursework, we fumbled our way to working prototypes, able to pass files, messages, and any other arbitrary data between our clients and servers.  The commands and files needing to be passed to servers were defined in a <code>Rakefile</code>, a strictly written parody of makefiles.</p>
<p>It was extremely satisfying to see the programs work, with files being sent from my partner's machine to servers at my house and back again in perfect-ish synchronicity.  Just for our own gratification we tested it by compiling larger and larger C programs, which the servers handled by creating subprocesses to execute in parallel threads.</p>
<pre><code class="language-py"># Executes a given command
def run_command(cmd_str, execution_path):
    if cmd_str.startswith('remote-'):
        cmd_str = cmd_str[7:]

    if verbose: print(&quot;\tExecuting&quot;, cmd_str)
    proc = subprocess.Popen(cmd_str, cwd=execution_path, shell = True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    global n_active_procs
    n_active_procs+=1
    return proc
</code></pre>
<p>(Obviously this is a security nightmare, we literally open the servers to execute arbitrary commands without any santising or checks.  Please don't abuse.  Or better yet, don't use.)</p>
<p>In our satisfaction-based testing, we noticed that the server would regularly crash when it received its 1010th to 1020th connection from the clients.</p>
<pre><code class="language-bash">Got connection 1012 from ('192.168.0.28', 56193)
    &lt;-- cost-query
    --&gt; cost 2
Got connection 1013 from ('192.168.0.28', 56194)
    &lt;-- 2 1 cc -c func3.c
    &lt;-- filename: func3.c
    &lt;-- file (size 39)
    Saved file /tmp/rs-5c1c832d-a18f-4189-b039-f6335af03aeb/func3.c
    Saved 1 files to dir: /tmp/rs-5c1c832d-a18f-4189-b039-f6335af03aeb
    Executing cc -c func3.c
Traceback (most recent call last):
  File &quot;rakeserver.py&quot;, line 151, in &lt;module&gt;
    receive_command(received_data, connection)
  File &quot;rakeserver.py&quot;, line 88, in receive_command
    proc = run_command(cmd_str, execution_path)
  File &quot;rakeserver.py&quot;, line 41, in run_command
    proc = subprocess.Popen(cmd_str, cwd=execution_path, shell = True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  File &quot;/usr/lib/python3.8/subprocess.py&quot;, line 858, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File &quot;/usr/lib/python3.8/subprocess.py&quot;, line 1605, in _execute_child
    errpipe_read, errpipe_write = os.pipe()
OSError: [Errno 24] Too many open files
</code></pre>
<p>1020 is suspiciously close to 1024, which immediately seemed like a red flag.  Though <code>select()</code> wasn't being called in the server, I was aware of the existence of limitations around socket/file descriptors over 1024 in some functions.</p>
<p>As far as I could tell all sockets were being closed properly by the server and all files that were opened were closed.  Adding some debug output revealed that the sockets were leaking in pairs‽</p>
<p>It was also noteworthy that many file descriptors <em>were</em> being reused by the server, so clearly some of them were being handled properly.</p>
<p>After hours of StackOverflow and dubious imitations of it, I resigned myself to hoping the markers never ran the server for long enough to get 1010 connections and notice the leak.  The rest of the assignment was functional and we'd get a good mark regardless of this bug.  The only thing at risk was my pride.</p>
<p>We fixed up the other remaining bugs, generally cleaned up formatting and completed our write-up, readied for submission the next day.  On the morning it was due, I hoped fresh eyes would help me squash this tantalisingly simple bug.</p>
<p>Fresh eyes prevailed and I found the leak.  To allow the stdout and stderr to be passed back to the client, we set the proc object to capture the command's outputs with the arguments <code>stdout=subprocess.PIPE, stderr=subprocess.PIPE</code>.  These were retrieved later in the program:</p>
<pre><code class="language-py">msg2 = str(proc.stdout.read().decode(&quot;utf-8&quot;)) 
msg3 = str(proc.stderr.read().decode(&quot;utf-8&quot;))
</code></pre>
<p><code>read()</code> should have been the giveaway.  Unbeknownst to me, <code>subprocess.Popen()</code> opens files to capture the outputs, which I was never closing.  </p>
<pre><code class="language-py">msg2 = str(proc.stdout.read().decode(&quot;utf-8&quot;)) 
msg3 = str(proc.stderr.read().decode(&quot;utf-8&quot;))

proc.stdout.close()
proc.stderr.close()
</code></pre>
<p>Leak plugged!</p>
<hr />
<p><strong>Update 2022-06-20:</strong></p>
<p>Just learnt about popen() in C, now I feel really stupid.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/file-descriptors</guid>
      <pubDate>Thu, 09 Jun 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Web Scraping vs. Government Whimsy</title>
      <link>https://zachmanson.com/blog/covid-updates</link>
      <description>"another spaghetto in my spaghetti"</description>
      <content:encoded><![CDATA[<p>The West Australian State Government has overall handled COVID-19 quite well, but in the arena of making my regex elegent they are have failed completely.  (Almost) every day since March 2020 they have published a media release detailing the number in overnight cases identified and other updates about the pandemic in the state.</p>
<p><img alt="" src="https://zachmanson.com/blog/covid-updates/media_release.png" /></p>
<p>For the first 2 years, this was sufficient for me, since for the most part WA was both physically isolated and had restrictions on who could enter the state.  This led us to have very little community transmission, only having minor and shortlived clusters of cases every few months.</p>
<p>All good things must come to an end, and COVID-19 v5.0 Omicron debuted in WA on Christmas Eve 2021.  Since then I've (somewhat unhealthily) been compulsively checking the listing of <a href="https://ww2.health.wa.gov.au/News/Media-releases-listing-page">media releases</a> every day to find the number of local cases. </p>
<h2>Why not automate this?</h2>
<p><img alt="xkcd: Automation" src="https://imgs.xkcd.com/comics/automation.png" title="'Automating' comes from the roots 'auto-' meaning 'self-', and 'mating', meaning 'screwing'." /></p>
<p>Web scraping can't be that hard, right?  It's not, but person writing the media releases clearly is trying to spite me. The <a href="https://github.com/pavo-etc/wa-covid-tracker/blob/main/scraper.py">scraping script I wrote</a> first generates a list of every update since a given date by traversing the DOM with Beautiful Soup and parsing the article titles.  This would be fine but on a whim the release titles will change format:</p>
<pre><code>COVID-19 update 18 March 2020
COVID-19 update – 19 March 2020 (em dash)
COVID-19 update - 17 October 2020 (hyphen)
COVID-19 update – 1 October 2020 (em dash &amp; no leading 0)
COVID-19 update – 01 January 2021 (em dash &amp; leading 0)
COVID-19 update - 05 January 2021 (hyphen &amp; leading 0)
COVID 19 update 2 December 2020 (COVID 19)
COVID19 update – 13 July 2020 (em dash &amp; COVID19)
COVID-19 update –  4 October 2020 (em dash &amp; 2-space gap)
COVID-19 update: 6 March 2022 (colon)
COVID-19 update  7 March 2022 (2-space gap)
COVID-19 update: 27th March 2022 (colon and ORDINAL)
</code></pre>
<p>Gross.  The somewhat warty regex I resigned myself to:</p>
<pre><code class="language-python">match_text = &quot;COVID.?19( )+(U|u)pdate.{0,6}[0-9]+(st|nd|rd|th)?( )+[a-zA-Z]+( )+[0-9]{4}&quot;
</code></pre>
<p>I part of this I'm least proud of is <code>.{0,6}</code>, to just match up to 6 arbitrary characters.  It's hacky but it does allow for the media releases to have more inexplicable spacing errors or random characters to use as seperators.</p>
<p>Ordinals only appeared in March 2022 and are what motivated my to whine about this on the internet.  They are particularly annoying to my program since it only scrapes articles since the previous run.  This is calculated by parsing the date in the media release title which is frustrating when they change the format of their dates on a whim.  It's not a major change to the codebase, but it's just another spaghetto in my spaghetti.</p>
<h2>Spaghetto 2: Actual Data</h2>
<p>The titles are just the first issue.  Once a list of COVID-19 updates has been assembled, each link is opened by Beautiful Soup, and the content of all <code>&lt;p&gt;</code> tags are concatenated.  The case numbers are written in normal and again inconsistent sentences.  The concatenated string is searched with more inelegant (and shockingly functional) regex to attempt to find the number of total new cases and locally transmitted cases.</p>
<pre><code class="language-python"># For local cases
match_text = &quot;(total )?(of )?(([0-9]|,)+) new(?! local)( COVID-19)?( case(s?))?&quot;
# For all cases
match_text = &quot;(([0-9]|,)+)( are)?( new)? local( COVID-19)?( cases)?&quot;
</code></pre>
<p>This took a surmountable and annoying amount of trial and error to find, and I'm sure will need updating in the future when the Government changes their phrasing on their next whim.</p>
<h1>Ongoing Development...</h1>
<p>For now <a href="https://tracker.zachmanson.com">this setup</a> works without much any effort from me, barring the occasional regex update.</p>
<p>Unfortunately, this experience hasn't turned me off web scraping so I feel a niggling desire to add more functionality to the site.  Daily hospitalisations, deaths, and other morbidities are all tempingly trivial to add, right?  It's hard to call a project complete, even when you've already done everything you set out to do.  I have a sneaking suspicious that the xkcd comic will be more prophetic than I'd like.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/covid-updates</guid>
      <pubDate>Fri, 01 Apr 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>How Minecraft Stores Crafting Recipes</title>
      <link>https://zachmanson.com/blog/crafting-recipes</link>
      <description>Some (extremely) mildly interesting quirks in Minecraft.jar</description>
      <content:encoded><![CDATA[<p>As of Minecraft version 1.18.1, there are 739 crafting recipes in the game...depending on how you count. This little rabbithole is something I fell into during a meeting for a group project this week. The project tasks us with building a "daily puzzle game" in vein of Wordle, as my web development unit likes the idea of looking anacronistic in a few years.</p>
<p>The meeting was very preliminary, essentially just getting to know one another and bounce some ideas off each other. One idea I had been mulling over was "Wordle but with for Minecraft crafting recipes", which requires no explanation for players of these two games (which among my peers includes everyone). My group immediately latched onto the idea, but acknowledged the scope of including all Minecraft recipes would be challenge, both in terms of game design and actually acquiring them.</p>
<p>Transcribing by hand was possible but likely impractical, as was automatically scraping the <a href="https://minecraft.fandom.com/wiki/Minecraft_Wiki">wiki</a>. Luckily, the thousands of modders before us had poured through Minecraft's source file and through <a href="https://gaming.stackexchange.com/questions/382431/list-of-all-minecraft-crafting-recipes-for-crafting-web">cursory googling</a> we were able to get to <code>.minecraft/versions/1.18.1.jar/data/minecraft/recipes/</code>, where all crafting recipes in the game are stored as lovely little JSON files. Perfect!</p>
<p>Upon further inspection there are some mildly interesting quirks within these JSON files. The majority of recipes vaguely follow this format:</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;minecraft:crafting_shaped&quot;,
  &quot;pattern&quot;: [&quot;###&quot;, &quot;#X#&quot;, &quot;#R#&quot;],
  &quot;key&quot;: {
    &quot;R&quot;: {
      &quot;item&quot;: &quot;minecraft:redstone&quot;
    },
    &quot;#&quot;: {
      &quot;item&quot;: &quot;minecraft:cobblestone&quot;
    },
    &quot;X&quot;: {
      &quot;item&quot;: &quot;minecraft:bow&quot;
    }
  },
  &quot;result&quot;: {
    &quot;item&quot;: &quot;minecraft:dispenser&quot;
  }
}
</code></pre>
<p>Seems like a straightforward and logical way of laying it all out. Presumably the "type" being "crafting_shaped" is to denote recipes with particular shapes (like a dispenser) as different from recipes where block arrangment doesn't matter (like mushroom stew).</p>
<p>Shapeless crafting recipes follow a simpler format:</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;minecraft:crafting_shapeless&quot;,
  &quot;ingredients&quot;: [
    {
      &quot;item&quot;: &quot;minecraft:brown_mushroom&quot;
    },
    {
      &quot;item&quot;: &quot;minecraft:red_mushroom&quot;
    },
    {
      &quot;item&quot;: &quot;minecraft:bowl&quot;
    }
  ],
  &quot;result&quot;: {
    &quot;item&quot;: &quot;minecraft:mushroom_stew&quot;
  }
}
</code></pre>
<p>The recipes folder contains a lot more than two types though:</p>
<pre><code class="language-zsh">zach@grannysmith recipes % awk 'FNR == 2' *.json | sort | uniq -c
  24   &quot;type&quot;: &quot;minecraft:blasting&quot;,
   9   &quot;type&quot;: &quot;minecraft:campfire_cooking&quot;,
 543   &quot;type&quot;: &quot;minecraft:crafting_shaped&quot;,
 183   &quot;type&quot;: &quot;minecraft:crafting_shapeless&quot;,
   1   &quot;type&quot;: &quot;minecraft:crafting_special_armordye&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_bannerduplicate&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_bookcloning&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_firework_rocket&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_firework_star&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_firework_star_fade&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_mapcloning&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_mapextending&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_repairitem&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_shielddecoration&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_shulkerboxcoloring&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_suspiciousstew&quot;
   1   &quot;type&quot;: &quot;minecraft:crafting_special_tippedarrow&quot;
  70   &quot;type&quot;: &quot;minecraft:smelting&quot;,
   9   &quot;type&quot;: &quot;minecraft:smithing&quot;,
   9   &quot;type&quot;: &quot;minecraft:smoking&quot;,
 198   &quot;type&quot;: &quot;minecraft:stonecutting&quot;,
</code></pre>
<p>Hold on, that's not just crafting recipes! All kinds "recipes" are stored in this directory, from normal smoking to smithing. This explains the repeated entries for items like cooked porkchop.</p>
<pre><code class="language-zsh">zach@grannysmith recipes % ls cooked*.json
cooked_beef.json                           cooked_mutton_from_smoking.json
cooked_beef_from_campfire_cooking.json     cooked_porkchop.json
cooked_beef_from_smoking.json              cooked_porkchop_from_campfire_cooking.json
cooked_chicken.json                        cooked_porkchop_from_smoking.json
cooked_chicken_from_campfire_cooking.json  cooked_rabbit.json
cooked_chicken_from_smoking.json           cooked_rabbit_from_campfire_cooking.json
cooked_cod.json                            cooked_rabbit_from_smoking.json
cooked_cod_from_campfire_cooking.json      cooked_salmon.json
cooked_cod_from_smoking.json               cooked_salmon_from_campfire_cooking.json
cooked_mutton.json                         cooked_salmon_from_smoking.json
cooked_mutton_from_campfire_cooking.json
</code></pre>
<p>Smelted items have an even simpler format:</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;minecraft:smelting&quot;,
  &quot;ingredient&quot;: {
    &quot;item&quot;: &quot;minecraft:porkchop&quot;
  },
  &quot;result&quot;: &quot;minecraft:cooked_porkchop&quot;,
  &quot;experience&quot;: 0.35,
  &quot;cookingtime&quot;: 200
}
</code></pre>
<p>Puzzingly there is a value for cooking time for each item, though smelting time is dependent on the type of furnace used rather than the item being smelted.</p>
<p>For example, all items cooked in a blast furnace have the same value for cooking time:</p>
<pre><code class="language-zsh">zach@grannysmith recipes % cat *blast* | grep &quot;cookingtime&quot;
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
  &quot;cookingtime&quot;: 100
</code></pre>
<p>It could be to accommodate varying smelting times in the future, but for the moment this seems redundant.</p>
<p>All of the special crafting recipes aren't stored at all, which makes sense since these are more complex than what should be written in a single JSON file.</p>
<pre><code class="language-zsh">zach@grannysmith recipes % cat $(grep -lr . -e &quot;special&quot;)
{
  &quot;type&quot;: &quot;minecraft:crafting_special_bookcloning&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_firework_star_fade&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_firework_rocket&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_shulkerboxcoloring&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_mapcloning&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_repairitem&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_bannerduplicate&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_armordye&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_tippedarrow&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_firework_star&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_mapextending&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_shielddecoration&quot;
}{
  &quot;type&quot;: &quot;minecraft:crafting_special_suspiciousstew&quot;
}
</code></pre>
<p>Tools smithed with netherite get a different and simple format as well:</p>
<pre><code class="language-sh">&gt; cat netherite_sword_smithing.json
{
  &quot;type&quot;: &quot;minecraft:smithing&quot;,
  &quot;base&quot;: {
    &quot;item&quot;: &quot;minecraft:diamond_sword&quot;
  },
  &quot;addition&quot;: {
    &quot;item&quot;: &quot;minecraft:netherite_ingot&quot;
  },
  &quot;result&quot;: {
    &quot;item&quot;: &quot;minecraft:netherite_sword&quot;
  }
}
</code></pre>
<p>Stonecutting unveiled an interesting quirk:</p>
<pre><code class="language-sh">&gt; cat chiseled_stone_bricks_stone_from_stonecutting.json
{
  &quot;type&quot;: &quot;minecraft:stonecutting&quot;,
  &quot;ingredient&quot;: {
    &quot;item&quot;: &quot;minecraft:stone&quot;
  },
  &quot;result&quot;: &quot;minecraft:chiseled_stone_bricks&quot;,
  &quot;count&quot;: 1
}
</code></pre>
<p>The count value determine how many items are output by the recipe, and appears in over 579 total recipes. For the standard crafting recipes this value only appears for recipes that output more than 1 item, but curiously stonecutter recipes always include this value.</p>
<p>All this elicited a nose exhale and an "oh neat" from me as I poked around the files. Most of these details aren't important to the project, but the parts that are important will be a huge time save. Web scraping in my experience is a pain (though somehow I think the maintainers of the Minecraft wiki would be more consistent than the <a href="https://github.com/pavo-etc/wa-covid-tracker/blob/1e1e8e317084e22aba6c143afbe9a8249e6231dc/scraper.py#L29">WA Government</a>).</p>
<p>Now we can focus on the real trouble - every other aspect of puzzle game design and web development. I am particularly not looking forward to dealing with the interaction between Wordle-style position hints and different crafting orientation/positions.</p>
<hr />
<p><strong>Update 2022-06-05:</strong></p>
<p>Minecraftle is <a href="https://minecraftle.zachmanson.com">playable</a> and nearly fully finished. We submitted two weeks ago but there are a few things I want to implement for my own interest. The JSON files discussed in this post were used, but their format was inconvenient for querying so needed to be restructured. Yesterday I rewrote the script to restructure the recipe JSON files since the initial version was a mess, and finally implemented the functions to validate recipes on object placement. These changes revealed a few more quirks in the recipe JSON files.</p>
<p>Some recipes have multiple forms, such as torches which use coal or charcoal. I had assumed this would just result in two seperate recipe files, but:</p>
<pre><code class="language-sh">&gt; cat torch.json
{
  &quot;type&quot;: &quot;minecraft:crafting_shaped&quot;,
  &quot;pattern&quot;: [
    &quot;X&quot;,
    &quot;#&quot;
  ],
  &quot;key&quot;: {
    &quot;#&quot;: {
      &quot;item&quot;: &quot;minecraft:stick&quot;
    },
    &quot;X&quot;: [
      {
        &quot;item&quot;: &quot;minecraft:coal&quot;
      },
      {
        &quot;item&quot;: &quot;minecraft:charcoal&quot;
      }
    ]
  },
  &quot;result&quot;: {
    &quot;item&quot;: &quot;minecraft:torch&quot;,
    &quot;count&quot;: 4
  }
}
</code></pre>
<p>This actually caused a mild issue in Minecraftle that I currently have a hacky solution for. As far as I can tell this doesn't appear in many recipes (though I'm too lazy to write the regex to check). When we restructure the JSON files, we simplify the format so the <code>pattern</code> field contains the actual item names rather than character representations. This doesn't leave much room alternate forms, short of just repeating the whole recipe with the differing items. While totally possible, I just forced it to select the first item whenever there are multiple options. This means that you would be unable to craft a torch with charcoal.</p>
<p>As far as I can tell this poses no issues since in Minecraftle's current form you don't receive charcoal as a crafting ingredient, and I am yet to find another appearance of this format that uses the items in <code>given_ingredients.json</code>. I'm still uncomfortable with this though, and I hope to come back and fix it after exams.</p>
<p>Another oddity reared its head with stone tools:</p>
<pre><code class="language-sh">&gt; cat stone_axe.json
{
  &quot;type&quot;: &quot;minecraft:crafting_shaped&quot;,
  &quot;pattern&quot;: [
    &quot;XX&quot;,
    &quot;X#&quot;,
    &quot; #&quot;
  ],
  &quot;key&quot;: {
    &quot;#&quot;: {
      &quot;item&quot;: &quot;minecraft:stick&quot;
    },
    &quot;X&quot;: {
      &quot;tag&quot;: &quot;minecraft:stone_tool_materials&quot;
    }
  },
  &quot;result&quot;: {
    &quot;item&quot;: &quot;minecraft:stone_axe&quot;
  }
}
</code></pre>
<p>See it? <code>tag</code> replaces <code>item</code> in a number of cases, such as stone tools. Blackstone and cobbled deepslate have been added fairly recently to the game, and can be used for interchangably for cobblestone in a number of recipes. This kind of notation appears in a number of recipes:</p>
<pre><code class="language-sh">&gt; grep  &quot;\&quot;tag\&quot;:&quot; *
acacia_planks.json:      &quot;tag&quot;: &quot;minecraft:acacia_logs&quot;
barrel.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
barrel.json:      &quot;tag&quot;: &quot;minecraft:wooden_slabs&quot;
beehive.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
birch_planks.json:      &quot;tag&quot;: &quot;minecraft:birch_logs&quot;
black_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
blue_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
bookshelf.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
bowl.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
brewing_stand.json:      &quot;tag&quot;: &quot;minecraft:stone_crafting_materials&quot;
brown_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
campfire.json:      &quot;tag&quot;: &quot;minecraft:logs&quot;
campfire.json:      &quot;tag&quot;: &quot;minecraft:coals&quot;
cartography_table.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
chest.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
composter.json:      &quot;tag&quot;: &quot;minecraft:wooden_slabs&quot;
crafting_table.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
crimson_planks.json:      &quot;tag&quot;: &quot;minecraft:crimson_stems&quot;
cyan_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
dark_oak_planks.json:      &quot;tag&quot;: &quot;minecraft:dark_oak_logs&quot;
daylight_detector.json:      &quot;tag&quot;: &quot;minecraft:wooden_slabs&quot;
fletching_table.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
furnace.json:      &quot;tag&quot;: &quot;minecraft:stone_crafting_materials&quot;
gray_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
green_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
grindstone.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
jukebox.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
jungle_planks.json:      &quot;tag&quot;: &quot;minecraft:jungle_logs&quot;
lectern.json:      &quot;tag&quot;: &quot;minecraft:wooden_slabs&quot;
light_blue_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
light_gray_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
lime_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
loom.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
magenta_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
note_block.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
oak_planks.json:      &quot;tag&quot;: &quot;minecraft:oak_logs&quot;
orange_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
painting.json:      &quot;tag&quot;: &quot;minecraft:wool&quot;
pink_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
piston.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
purple_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
red_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
shield.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
smithing_table.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
smoker.json:      &quot;tag&quot;: &quot;minecraft:logs&quot;
soul_campfire.json:      &quot;tag&quot;: &quot;minecraft:logs&quot;
soul_campfire.json:      &quot;tag&quot;: &quot;minecraft:soul_fire_base_blocks&quot;
soul_torch.json:      &quot;tag&quot;: &quot;minecraft:soul_fire_base_blocks&quot;
spruce_planks.json:      &quot;tag&quot;: &quot;minecraft:spruce_logs&quot;
stick.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
stone_axe.json:      &quot;tag&quot;: &quot;minecraft:stone_tool_materials&quot;
stone_hoe.json:      &quot;tag&quot;: &quot;minecraft:stone_tool_materials&quot;
stone_pickaxe.json:      &quot;tag&quot;: &quot;minecraft:stone_tool_materials&quot;
stone_shovel.json:      &quot;tag&quot;: &quot;minecraft:stone_tool_materials&quot;
stone_sword.json:      &quot;tag&quot;: &quot;minecraft:stone_tool_materials&quot;
tripwire_hook.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
warped_planks.json:      &quot;tag&quot;: &quot;minecraft:warped_stems&quot;
white_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
wooden_axe.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
wooden_hoe.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
wooden_pickaxe.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
wooden_shovel.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
wooden_sword.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
yellow_bed.json:      &quot;tag&quot;: &quot;minecraft:planks&quot;
</code></pre>
<p>These tags are referring to <code>.minecraft/versions/1.18.1.jar/data/minecraft/tags/items/</code>, which holds yet more JSON files with lists of all items that belong to each tag. For example:</p>
<pre><code class="language-sh">&gt; cat stone_crafting_materials.json
{
  &quot;replace&quot;: false,
  &quot;values&quot;: [
    &quot;minecraft:cobblestone&quot;,
    &quot;minecraft:blackstone&quot;,
    &quot;minecraft:cobbled_deepslate&quot;
  ]
}
</code></pre>
<p>To deal with these I've currently just put edge cases in to handle these tags manually, though I would like to have a more robust solution in the future that can be determined by <code>given_ingredients.json</code>.</p>
<p>Both of these appear to be solving the same problem, and it's strange that coal doesn't use the <code>tag</code> field, especially since there is <code>tag</code> JSON file that seems made for this:</p>
<pre><code class="language-sh">&gt; cat coals.json
{
  &quot;replace&quot;: false,
  &quot;values&quot;: [
    &quot;minecraft:coal&quot;,
    &quot;minecraft:charcoal&quot;
  ]
}
</code></pre>
<p>I theorize that the torch was older method of achieving this goal and just hasn't been updated to use the new system (though I am again too lazy and won't check old versions), unless there is some other difference I am not aware of between the two methods. Maybe I should send Mojang a pull request.</p>
<p>Recipe validation was the final feature I really felt was necessary for my own sense of completion. That said, we never added shapeless recipe support... or varying difficulties... or hardcore mode. Oh well.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/crafting-recipes</guid>
      <pubDate>Thu, 31 Mar 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>hEP 001 – Mathematical 𝐡  in the Standard Implementation</title>
      <link>https://zachmanson.com/blog/hep-001</link>
      <description>Enhancement Proposal for the h Programming Language</description>
      <content:encoded><![CDATA[<table>
    <tr><td>hEP</td><td>001</td></tr>
    <tr><td>Title</td><td>Mathematical 𝐡 in the Standard Implementation</td></tr>
    <tr><td>Author</td><td>Zach Manson</td></tr>
    <tr><td>Status</td><td>Proposed</td></tr>
    <tr><td>Type</td><td>Feature</td></tr>
    <tr><td>hlang-version</td><td>1.0</td></tr>
</table>

<h2>Abstract</h2>
<p>This hEP proposes a framework for adding first-class support for additional forms of h used in mathematical contexts, and that "𝐡" be added to the h programming language standard implementation. As of version 1.0, the only supported output characters are "h" and "'".</p>
<h2>Motivation</h2>
<p>The h Programming language is borne from <a href="https://christine.website/blog/formal-grammar-of-h-2019-05-19">the conlang h</a> which was formalised in 2019 by Xe Iaso.</p>
<p>In their unveiling of the language they exhibited how the conlang's simple syntax could be used for complex conversation, all within the use of a single written character and phone:</p>
<pre><code class="language-h">&lt;Cadey&gt; h
&lt;DoesntGetIt&gt; h h h h
* Cadey facepalms
</code></pre>
<p>As stated in h's formal specification, it can be used to represent all possible communications within the confines of the letter h.</p>
<p>They later developed a syntax, compiler and runtime for <a href="https://christine.website/blog/h-language-2019-06-30">the h Programming Language</a> (hlang). This programming language was designed for the output of the h conlang, and expanded the character set in use to include both the Romanic form "h" and its Lojbanic equivalent "'".</p>
<p>A long-standing difficulty for the programming language is expressing mathematical notation in its output. While it is entirely possible to express any and all equations in the language, for this purpose it doesn't utiltise the "h" character to its maximum capacity and as such is unnecessarily verbose.</p>
<h2>Goals</h2>
<p>The primary goals of this proposal are to provide a specification for implementing additional forms of "h" in hlang to enable more terse output for use in mathematical contexts. The only additional form of "h" proposed in this hEP is "𝐡" (U+1D421).</p>
<p>The proposal would enable programs such as this:</p>
<pre><code class="language-sh">$ cat new_equation.h
𝐡 𝐡 h '
</code></pre>
<p>AST this would generate during compilation:</p>
<pre><code class="language-h">H(&quot;𝐡 𝐡 h '&quot;)
</code></pre>
<p>Program output:</p>
<pre><code class="language-h">𝐡 𝐡 h '
</code></pre>
<p>This is an improvement over the existing implementation, which would require the much longer and more cumbersome hlang 1.0 source to produce the equivalent equation:</p>
<pre><code class="language-sh">$ cat old_equation.h
h h h ' ' h ' h h h h h h h h h ' h
</code></pre>
<p>Translating this from h (conlang) to English:</p>
<pre><code class="language-h">a^2 + b^2 = c^2
</code></pre>
<h2>Non-Goals</h2>
<p>This hEP is not an attempt to sully hlang with characters outside of the formal grammar of the h conlang. The scope of the specification, while allowing for additional forms of "h" to be supported, is exclusively for forms of "h". It is not applicable to other characters, such as "g".</p>
<p>This hEP only explicitly proposes the addition of "𝐡" (U+1D421) though is designed to allow other forms of "h" to be supported by hlang in the future, such as "𝒉" (U+1D489), "𝓱" (U+1D4F1), or "𝕙" (U+1D559). While these forms could be implemented in a near identical manner to "𝐡", that is outside of the scope of this hEP. All of these forms of "h" are UTF-8 characters designed for use in mathematical equations.</p>
<p>The hEP is also explicitly not intending to enable support for "h" with diacritics, such as "ḧ" (U+1E27). Etymologically related forms of modern "h" are also outside the scope of this hEP.</p>
<h2>Semantics</h2>
<p>Under this proposal, "𝐡" would receive first class support by hlang. To output it would be the same as the standard "h" character:</p>
<pre><code class="language-sh">$ cat math_h.h
𝐡
</code></pre>
<p>Produced AST:</p>
<pre><code class="language-h">H(𝐡)
</code></pre>
<p>Output:</p>
<pre><code class="language-h">𝐡
</code></pre>
<p>Represented with a node tree:</p>
<pre><code class="language-h">&amp;peg.Node{
    Name: &quot;H&quot;,
    Text: &quot;𝐡&quot;,
    Kids: nil,
}
</code></pre>
<p>The node tree for the earlier Pythagorean example would be:</p>
<pre><code class="language-h">&amp;peg.Node{
    Name: &quot;H&quot;,
    Text: &quot;𝐡 𝐡 h '&quot;,
    Kids: {
        &amp;peg.Node{
            Name: &quot;&quot;,
            Text: &quot;𝐡&quot;,
            Kids: nil,
        },
        &amp;peg.Node{
            Name: &quot;&quot;,
            Text: &quot;𝐡&quot;,
            Kids: nil,
        },
        &amp;peg.Node{
            Name: &quot;&quot;,
            Text: &quot;h&quot;,
            Kids: nil,
        },
        &amp;peg.Node{
            Name: &quot;&quot;,
            Text: &quot;'&quot;,
            Kids: nil,
        },
    },
}
</code></pre>
<p>The "𝐡" character should be usable in all contexts where "h" and "'" are currently usable. Grammatically it should follow all the same principles and restrictions as "h" and "'".</p>
<h2>Dependencies</h2>
<p>As with hlang v1.0, this proposal not require any dependencies, other than UTF-8 formatted source files.</p>
<h2>Acknowledgements</h2>
<p>Thanks to Xe Iaso for creating h and hlang, as well as all their other incredible projects.</p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/hep-001</guid>
      <pubDate>Fri, 18 Mar 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Usernames</title>
      <link>https://zachmanson.com/blog/usernames</link>
      <description>An experience I have had many times and should be numb to by now.</description>
      <content:encoded><![CDATA[<p>I spend an inordinate amount of time reading arguments on the internet. Every now and then I'll find one that is actually thoughtful and in-depth. One with more kindness than usual, with genuine attempts to understand the perspectives of each other.</p>
<p>And after the arguing has concluded, the fighting parties express that they respect the other.</p>
<p>These are rare. But it isn't rare that upon finding one of these I will glance at the usernames.</p>
<p>Today I glanced at the usernames of two people having a beautiful argument on a forum:</p>
<p><code>"euronforpresident"</code></p>
<p>Fine, nice little Game of Thrones reference.</p>
<p>And their opponent:</p>
<p><code>"Hitleresque"</code></p>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/usernames</guid>
      <pubDate>Fri, 11 Mar 2022 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>Basic SeleniumBasic</title>
      <link>https://zachmanson.com/blog/basic-seleniumbasic</link>
      <description>Writing down everything that's required to get VBA and Selenium to work together before link rot sets in.</description>
      <content:encoded><![CDATA[<p>The library most commonly recommended for web automation is Selenium, which supports driving a variety of browsers and officially provides bindings for a number of languages. Unfortunately it doesn't officially support the ultimate programming language, Visual Basic. For VB, this is a single blemish on the otherwise immaculate language, and for Selenium, it's a unforgivable crime.</p>
<p>Luckily there is a glimmer of hope. SeleniumBasic is a Selenium based framework for the VB family. Now you may look at it and think "this is unmaintained and hasn't been updated since 2016", but it was actually perfected in 2016 and no update could ever possibly improve it.</p>
<p>So it is only natural that these two exquisite pieces of software (that are in no way whatsoever out of date) work together. Unfortunately there's a few...underdocumented hiccups that arise from integrating these two. This post is written in the hopes of making the hiccups between VBA (in Excel) and SeleniumBasic slightly more documented.</p>
<h2>Installation</h2>
<ul>
<li>Excel (duh)</li>
<li><a href="https://github.com/florentbr/SeleniumBasic/releases">SeleniumBasic</a></li>
<li>A WebDriver</li>
<li><a href="https://www.microsoft.com/en-au/download/details.aspx?id=21">.NET Framework 3.5</a></li>
</ul>
<p>Given that the VBA interface in Excel hasn't been updated since...1832...I am going to assume Excel version will not have any major impacts, but for reference I used Version 2201.</p>
<p>Each of the major web browsers provides a WebDriver, which is a piece of software that allows a browser to be controlled and automated by external programs. The SeleniumBasic installer includes a number of WebDrivers which you can choose to include, but these all appear to be wildly out of date and will cause Excel to crash if you have a recent browser installed.</p>
<p><img alt="" src="https://zachmanson.com/blog/basic-seleniumbasic/seleniumbasic-installer.png" /></p>
<p>To fix this you will need to manually replace the WebDrivers you intend to use. Each of the major browser vendors distribute up to date versions of their own WebDrivers (<a href="https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/">Edge</a>, <a href="https://chromedriver.chromium.org/downloads">Chrome</a>, <a href="https://github.com/mozilla/geckodriver/releases">Firefox</a>). Within each of these zip archive you'll find an executable, e.g. <code>msedgedriver.exe</code> for everyone's fifth favourite browser. To replace driver included with SeleniumBasic, you need to place the up to date executable in <code>C:\Users\&lt;username&gt;\AppData\Local\SeleniumBasic</code>. The up to date WebDriver will need to have the same name as the original outdated WebDriver, so <code>msedgedriver.exe</code> needs to be renamed <code>edgedriver.exe</code>. Irritatingly, it's probable that eventually automatic browser updates will render even these updated WebDrivers outdated, and these executables will need to be replaced again.</p>
<p>.NET Framework versions annoyingly don't show up as installed programs, so if you want to find out what versions you already have installed, you must use one of several annoying ways. The least annoying of these is to look in the <code>C:\Windows\Microsoft.NET\Framework</code> directory.</p>
<p><img alt="" src="https://zachmanson.com/blog/basic-seleniumbasic/dotnet-frameworks.png" /></p>
<h2>Actual Use</h2>
<p>Now you can actually use it as you'd expect. Open the VBA IDE in Excel start using some of the gorgeous syntax and best in-class tools that Microsoft so kindly allows you to purchase.</p>
<p>To enable SeleniumBasic in Excel you'll need to navigate to <code>Tools &gt; References</code> and check <code>Selenium Type Library</code>.</p>
<p><img alt="" src="https://zachmanson.com/blog/basic-seleniumbasic/adding-reference.png" /></p>
<p>From here you can write fairly standard Selenium code in VBA.</p>
<pre><code class="language-VB">Sub AutoSearch()

Dim driver As Selenium.EdgeDriver
Set driver = New Selenium.EdgeDriver

driver.Start
driver.Get (&quot;https://old.reddit.com/&quot;)

Dim searchbox As WebElement
Set searchbox = driver.FindElementByName(&quot;q&quot;, -1, True)
searchbox.SendKeys (&quot;selenium&quot;)

Dim submit As WebElement
Set submit = driver.FindElementById(&quot;search&quot;, -1, True)
submit.submit

driver.Quit

End Sub
</code></pre>
<h2>Footnotes</h2>
<ol>
<li>Software versions used: Windows 10 10.0.19043, Excel 2201, SeleniumBasic 2.0.9.0, Edge 98.0.1108.50, EdgeDriver 98.0.1108.51</li>
<li><a href="https://www.selenium.dev/documentation/overview/">Official Selenium Documentation</a></li>
<li>A fantastic resource for VBA+Selenium is <a href="https://youtube.com/playlist?list=PLNIs-AWhQzcl3xKvF8sVL4sWRWICj_clM">WiseOwlTutorials' series</a> on this exact thing.</li>
</ol>]]></content:encoded>
      <guid isPermaLink="false">https://zachmanson.com/blog/basic-seleniumbasic</guid>
      <pubDate>Tue, 15 Feb 2022 00:00:00 +0800</pubDate>
    </item>
  </channel>
</rss>
