<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://tarasyk.ca/feed.xml" rel="self" type="application/atom+xml" /><link href="https://tarasyk.ca/" rel="alternate" type="text/html" /><updated>2026-08-14T05:35:34+00:00</updated><id>https://tarasyk.ca/feed.xml</id><title type="html">Taras Gritsenko - Software Engineer</title><subtitle>The personal website of Taras Gritsenko.</subtitle><entry><title type="html">ZSA Voyager Review</title><link href="https://tarasyk.ca/2025/11/01/zsa-voyager-review.html" rel="alternate" type="text/html" title="ZSA Voyager Review" /><published>2025-11-01T18:31:11+00:00</published><updated>2025-11-01T18:31:11+00:00</updated><id>https://tarasyk.ca/2025/11/01/zsa-voyager-review</id><content type="html" xml:base="https://tarasyk.ca/2025/11/01/zsa-voyager-review.html"><![CDATA[<p>Recently I got into split keyboards after trying out a friend’s Kinesis Freestyle 2 and getting used to split keyboard layouts.</p>

<p>I decided to pick up a ZSA Voyager after justifying the price with potential ergonomic benefits. The decision was more an ergonomic one and excuse to learn a slightly better way of typing. That said I don’t get RSI from typing on anything except mac keyboards and using the mac trackpad which is pretty awful ergonomically.</p>

<h1 id="my-experience">My experience</h1>

<p>The first thing I noticed was that typing on it was surprisingly difficult to initially get adjusted to. It felt like I might give up after the first 2 days of training because I was so used to row staggered keyboards and kept on making typos. I was averaging 30-60WPM on the first few days with very high peaks that were mostly luck. My average speed was low and my accuracy was worse.</p>

<p>The reason for this is because the ZSA voyager has a columnar layout which is fundamentally different to regular keyboards. A columnar layout places the keys in alignment with the columns as opposed to the rows. I had heard from a coworker with who had tried a columnar keyboard that typing on columnar was extremely frustrating and difficult so I was anticipating a long onboarding process in figuring out how to actually use the thing.</p>

<p><img src="/assets/zsa2.png" alt="" class="center-image" /></p>

<p>With training I found that that once you stick through it for a day or two it becomes significantly easier. I was able to pick up columnar pretty quickly strangely enough. Learning columnar is easier than learning an entirely different layout.</p>

<p>I type normally at 170-200WPM on row staggered keyboards on typing tests (avg 155 wpm with grammar) and was able to reach this in 10 days with 1 hour of daily practice on keybr.com.</p>

<p>Typing symbols or non alphabetical characters is noticeably difficult since you only have 52 keys and have to remap them, so I spent some time figuring out a layout that worked for me. I did not stick with the default layout. I still don’t consider myself super fast at typing the symbols as my layout isn’t fully optimal, but the symbols which I press frequently are easier to press with my layout and feel more comfortable than a regular keyboard.</p>

<p>Remapping your own keys opens you up to decision fatigue which is something I think a lot of people would find annoying. There’s even stuff like homerow mods to keep most of your important keys on the homerhow, which I haven’t learned. To get over this decision fatigue hump I accepted that my layout would have tradeoffs that make it suboptimal.</p>

<p>I frequently changed my layout to things that made more sense and with the help of Oryx (the official software ZSA makes to do this) it felt really natural and easy to do. By far the least frustrating experience I’ve had with keyboards since I didn’t have to use QMK or flash firmware manually without the help of a nice interface. <a href="https://configure.zsa.io/voyager/layouts/6yE4R/latest/0">If you are curious, here is the latest layout I am using</a>.</p>

<h1 id="speed-results">Speed results</h1>

<p>My speed results on keybr.com show that the first few days of typing I was messing up 1/10 words and not going very fast, but as time went on my accuracy and average speed improved. Right now without any grammar being typed I average around 175wpm with the ZSA</p>

<ul>
  <li>Day 1: top speed 168wpm, avg 60wpm, acc 91.7%</li>
  <li>Day 2: top speed 168wpm, avg 102wpm, acc 90.18%</li>
  <li>Day 3: top speed 157wpm, avg 115wpm, acc 92.05%</li>
  <li>Day 4: top speed 156wpm, avg 127wpm, acc 94.6%</li>
  <li>Day 5: top speed 170wpm, avg 132wpm, acc 95.38%</li>
  <li>Day 6: top speed 180wpm, avg 145wpm, acc 95.81%</li>
  <li>Day 7: top speed 178wpm, avg 146wpm, acc 96.7%</li>
  <li>Day 8: top speed 180wpm, avg 155wpm, acc 96.89%</li>
  <li>Day 9: top speed 180wpm, avg 156wpm, acc 95.67%</li>
  <li>Day 10: top speed 169wpm, avg 150wpm, acc 96.22%</li>
  <li>Day 11: top speed 183wpm, avg 160wpm, acc 96.68%</li>
  <li>Day 12: top speed 189wpm, avg 165wpm, acc 96.87%</li>
</ul>

<p>I highly recommend using keybr for training since it lets you practice typing case insensitive words, which practically is the vast majority of your typing experience anyway</p>

<p><img src="/assets/zsa.png" alt="" class="center-image" /></p>

<p>By day 20 I hit 203wpm. It’s possible I’m the fastest ZSA Voyager typist in the world!</p>

<h1 id="the-thumb-cluster">The thumb cluster</h1>

<p>I was very surprised how pleasant it is to use your thumbs for space, backspace, enter and a layer modifier. This is currently how I use the keyboard and it is very intuitive to type on and doesn’t cause me any issues.</p>

<p>I don’t think I can go back to regular keyboards after having experienced the ergonomic benefits of having my fingers not have to move around as much.</p>

<h1 id="the-downsides">The downsides</h1>

<p>The main things I find that are downsides</p>

<ul>
  <li>All of the switches that come with the board are loud, and the keyboard is noticeably louder than a regular keyboard. You have to buy silent ones online separately. I have bought separate silent switches to help with this.</li>
  <li>Not a whole lot of tenting options</li>
  <li>Price is a bit too steep for a keyboard ($365)</li>
  <li>Very easy to misstype on if you are used to switches that require more actuation force</li>
  <li>Cost, price point is almost double what it should be</li>
</ul>

<p>Anecdotally I’ve also heard that the lack of palm support combined with your hand size may impact how comfortable you feel using the keyboard. For every 9 users that have a good experience with the board there is 1 that doesn’t really like it. Personally I found it very comfortable to type on even without palm support (using a desk mat).</p>

<h1 id="trrs-port">TRRS port</h1>

<p>There is a known defect with the TRRS ports where either both ports will break or only one port will break, but it results in the keyboard not receiving enough power to work. The board I received started having this issue shortly after use. I sent it in under warranty, and ZSA’s support was incredible. I received a replacement keyboard within a week under no cost.</p>

<p>My main recommendation here is to just be aware of the warranty, but I don’t think most people will encounter any problems.</p>

<p>Still, it’s not ideal that this defect exists at all so keep this in mind if you are considering buying the keyboard.</p>

<h1 id="the-upsides">The upsides</h1>

<p>In no particular order</p>

<ul>
  <li>Super portable</li>
  <li>Ergonomic. It can’t be overstated how much better it feels to type on a split keyboard with a flat profile like this if you used regular keyboards</li>
  <li>Best in class customization software, entirely in browser (Oryx)</li>
  <li>2 year warranty</li>
  <li>Comes with a portable case</li>
</ul>

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

<p>I highly recommend this keyboard if you are already used to split keyboards and want something portable/don’t mind the steep price point. Would I recommend it for people beginning to learn split keyboards though? No. There’s probably better options out there to start with like a Kinesis or even a Glove80.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Recently I got into split keyboards after trying out a friend’s Kinesis Freestyle 2 and getting used to split keyboard layouts.]]></summary></entry><entry><title type="html">Applying GENEVE encapsulation (flow hashing, VPC&amp;lt;-&amp;gt;VPC NAT)</title><link href="https://tarasyk.ca/2024/10/18/networking-at-aws-and-geneve-encapsulation.html" rel="alternate" type="text/html" title="Applying GENEVE encapsulation (flow hashing, VPC&amp;lt;-&amp;gt;VPC NAT)" /><published>2024-10-18T18:33:36+00:00</published><updated>2024-10-18T18:33:36+00:00</updated><id>https://tarasyk.ca/2024/10/18/networking-at-aws-and-geneve-encapsulation</id><content type="html" xml:base="https://tarasyk.ca/2024/10/18/networking-at-aws-and-geneve-encapsulation.html"><![CDATA[<p>This post is going to be about one of the technologies that powers networking at AWS: an application of Geneve encapsulation to do VPC to VPC NAT, or how you get packets from an ENI in one VPC to an ENI in another VPC.</p>

<p>This post describes the encapsulation that is used and explains some of the motivations and a blurb on flow hashing.</p>

<h1 id="what-is-the-networking-stack">What is the networking stack</h1>

<p>When you hear the “networking stack” you need understand it in terms of layers of abstraction that build on top of each other. At the lowest level you have physical data transmission, the next level you have ethernet frames (data link), the next level contains IP frames (network layer) and then finally a TCP or UDP frame. Colloquially we just refer to these as layers 1-4.</p>

<p>One way to understand the usefulness of the networking stack is in terms of what constraints you have: when you go up
the networking stack you have less constraints. TCP on the transport layer enforces a socket per connection and has
strict ordering rules, but when you get to layer 3 (IP aka TUN layer) you can inspect TCP, as well as UDP packets, as well as everything else (eg ICMP packets).</p>

<p>To oversimplify, understanding these layers is important for understanding how you can process packets quickly for something like sending packets from one partition/network to another, because intuitively you need additional layers to store meaningful routing metadata.</p>

<h1 id="geneve-encapsulation">Geneve encapsulation</h1>

<p><a href="https://www.rfc-editor.org/rfc/rfc8926.html#name-geneve-packet-format-over-i">RFC 8926</a> describes Geneve encapsulation.</p>

<p>Geneve encapsulation is most relevant from sections 3.1 to 3.3. From a technical perspective the main thing you need to know to understand GENEVE is the following: it exists as a layer 4 (transport layer) UDP way of encapsulating layer 2 - layer 4 information for network virtualization. So you can send these GENEVE encapsulated packets using a user space socket but it communicates a lot more information because it has both an inner and outer packet frame. The inner frame can contain an entire ethernet frame which can be intercepted by a TUN/TAP device running on a different machine (a NIC). Any time you have a need to do network virtualization it is most likely you would have to use GENEVE encapsulation as it is the standard (supercedes VXLAN).</p>

<p><img src="/assets/geneve.png" alt="" class="center-image" /></p>

<p>The included figure of GENEVE on IPv4 shows that your payload can be anything you want it to be or what the receiver expects. If you’re encapsulating a regular TCP packet you would just write the IPv4 header and TCP frame here.</p>

<p>Importantly the details of how these packets are used are up to the network you’re connecting to</p>

<p>Since GENEVE operates within the context of UDP it has a natural analogue to the connectionless semantics of Ethernet and IP which is why it lends itself so well to be used to directly pipe in data from a TUN device (as opposed to something like TCP).</p>

<p><img src="https://www.digitaltut.com/images/Knowledge/VXLAN/VXLAN_VTEP.jpg" alt="" class="center-image" /></p>

<p>GENEVE encapsulation is super important whenever you want to build networks (overlay networks) on top of another network (an underlay) which is why it’s widely adopted among cloud computing companies. They have the raw hardware (underlay) and the overlay (to oversimplify a bit) is the virtualized network that they create for you (eg a VPC with subnets). The following section will describe this in more detail</p>

<h1 id="real-world-application-with-flow-hashing">Real world application with flow hashing</h1>

<p>A lot of the time you’ll have to deal with some form of encapsulation during which there’s a network boundary transition. I dealt with this on my team where our traffic transitions the network boundary from our service VPC (IPv6) into a customer VPC (IPv4). A large part of our service offering is tunneling traffic into customer’s VPCs.</p>

<p>In this case the outer packet was IPv6 going over UDP and the inner (GENEVE encapsulated) packet was IPv4.</p>

<p>This meant that any reverse traffic was always IPv4 and had to be translated to IPv6 to be sent back to the originating client.</p>

<p>You cannot simply encode the client’s IPv6 address in an IPv4 space because it won’t fit, so instead you have to perform translation by hashing the originating flow 5-tuple, mapping this to an IPv6 address and use this information to map back onto the originating flow for reverse traffic.</p>

<p>For example, if your service operates in an IPv6 space, that is to say every client that ingresses to your service sends it with an IPv6 header and you want to egress this traffic to a machine that operates exclusively in IPv4 space (over GENEVE or not) this becomes a significant hurdle. On the ingress path for return traffic you must identify the correct forward initiating IPv6 flow from an IPv4 packet, and are forced to store additional information (state) to identify originating flows for reverse traffic. This can get quite complicated the more flows you have, or if your system is multi tenant, and it has to be done per packet without degrading performance. Additionally, since flow hashing is generally the way we identify what flows are which, we have to make sure that whatever form of hashing that is being used is resistant to collisions and/or have adequate solutions for dealing with them. This is a real problem that services have to solve when sending traffic from one machine to another when transitioning over network boundaries.</p>

<p>Flow hashing isn’t perfect and definitely has some tradeoffs, including handling cache misses/latency, but if you constrain the number of flows your system can support and have a large enough hash space all of these problems are solvable.</p>

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

<p>This post was brief but something that I felt was necessary to capture as I haven’t found any other articles about it online.</p>

<p>Thanks for reading!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This post is going to be about one of the technologies that powers networking at AWS: an application of Geneve encapsulation to do VPC to VPC NAT, or how you get packets from an ENI in one VPC to an ENI in another VPC.]]></summary></entry><entry><title type="html">Porting disk intensive Kubernetes services to AWS Lambda</title><link href="https://tarasyk.ca/2023/02/16/porting-disk-intensive-to-lambda.html" rel="alternate" type="text/html" title="Porting disk intensive Kubernetes services to AWS Lambda" /><published>2023-02-16T18:33:36+00:00</published><updated>2023-02-16T18:33:36+00:00</updated><id>https://tarasyk.ca/2023/02/16/porting-disk-intensive-to-lambda</id><content type="html" xml:base="https://tarasyk.ca/2023/02/16/porting-disk-intensive-to-lambda.html"><![CDATA[<p>Recently I was working on porting a terraform drift detector to AWS Lambda. This is a service I developed a while back, still is in use today and has been responsible for preventing incidents due to unmerged/unapplied infrastructure in terraform.</p>

<p>I would like to write about the main constraint I encountered trying to accomplish this and how this constraint can be circumvented around while <a href="https://aws.amazon.com/lambda/">porting this service to Lambda</a>.</p>

<h1 id="context">Context</h1>

<p>The short and simple of it is that the drift detector just clones a bunch of repositories, finds all of its terraform directories with some tree searching, and then invokes <code class="language-plaintext highlighter-rouge">terraform plan</code> a bunch (after doing some massaging of some module paths) to figure out whether or not that service has drift.</p>

<p><img src="/assets/pd.png" alt="" class="center-image" /></p>

<p>This service is really interesting from a technical perspective because there’s some cool things it does to get this work since</p>

<ul>
  <li>it doesn’t actually use the git cli to clone repos (it does super shallow cloning using the GHE api and only gets <code class="language-plaintext highlighter-rouge">terraform</code> directories)</li>
  <li>it rewrites custom <a href="https://developer.hashicorp.com/terraform/language/modules/sources">terraform module URIs</a> that go over HTTPs/ssh unauthed by rewriting them to strictly HTTPs ones with your personal access token, to allow you to pull modules from other internal repos</li>
</ul>

<p>Before moving it to lambda the drift detector was running in an EKS cluster and doing perfectly fine until it eventually would consume all of the disk on the node it was scheduled on. It was only ran as a single pod in our cluster and would result in a lot of disk pressure errors, eventually resulting <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/">in a whole node being evicted from the cluster</a> which would trigger a variety of fun alarms that were hard to diagnose.</p>

<p>It’s not clear exactly why there was a disk resourcing issue with the service and I even tried limiting its ability to write to a limited-capacity <a href="https://kubernetes.io/docs/concepts/storage/volumes/">Kubernetes volume</a> but it didn’t resolve the issue :). My guess is <code class="language-plaintext highlighter-rouge">terraform</code> commands generate artifacts that aren’t being cleaned up, possibly <a href="https://github.com/hashicorp/terraform/issues/26144">something related to plugins</a>.</p>

<p>Either way, in the meantime the problem would be solved if we just moved the service to lambda. The other reason besides the node evictions was that it would be part of a wider initiative to reduce the number of services running in our legacy EKS infrastructure which we were offboarding. Fixing the disk leak in the first place would’ve been a great solution too, but it was proving more difficult than simply porting it to lambda. Plus, even if we fixed the disk issue we couldn’t guarantee that it simply wouldn’t re-emerge or have to write a lot of code that did manual cleaning.</p>

<h1 id="porting-the-drift-detector-to-lambda">Porting the drift detector to lambda</h1>

<p>Since the drift detector was designed to run as a Go service in a docker container and use all of the binaries in that container (<code class="language-plaintext highlighter-rouge">terraform</code>, <code class="language-plaintext highlighter-rouge">tfenv</code> (for terraform versioning) and <code class="language-plaintext highlighter-rouge">git</code>) it made the most sense to use AWS Lambda with ECR for containerization. Previously the service had been running in an EKS cluster.</p>

<p>The setup for this change was pretty simple. All that was necessary was to stand up an ECR repo, set up a build process script (or whatever you prefer) for building the main service binary locally, then create a Dockerfile that pulls that locally built binary and once the image is build pushes it to ECR so that the image can execute that binary.</p>

<p>The only real “gotcha” here is that you have to make sure you are configuring the lambda’s network configuration properly, host machine (in this case x86_64 which is amd64, aka not ARM) and that it has enough disk space/memory and you should be good to go.</p>

<p>The vast majority of the source code could stay the same but with minor modifications to some of the alarms (we can use the <a href="https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html">cloudwatch lambda invocation metric</a> as a heartbeat check as opposed to emitting one ourselves like before).</p>

<p>Difficulty arose whenever I tried to actually get binaries to run on the machine. The AWS documentation isn’t great about this but the only writable directory on a dockerized lambda is <code class="language-plaintext highlighter-rouge">/tmp</code>. This becomes an issue when you run a binary and it stores local data in the current working directory of where the binary is, or <code class="language-plaintext highlighter-rouge">/usr/local/bin</code> on <code class="language-plaintext highlighter-rouge">amazonlinux</code> with a lambda function.</p>

<p>It turns out that <code class="language-plaintext highlighter-rouge">tfenv</code> writes its current version state in <code class="language-plaintext highlighter-rouge">$PATH/tfenv/version</code> which isn’t writable if it’s not in <code class="language-plaintext highlighter-rouge">/tmp</code> so you have to make sure that your <code class="language-plaintext highlighter-rouge">tfenv</code> is installed with respect to <code class="language-plaintext highlighter-rouge">/tmp</code>.</p>

<p>You also can’t just move everything on container launch to <code class="language-plaintext highlighter-rouge">/tmp</code> and point the <code class="language-plaintext highlighter-rouge">$PATH</code> to <code class="language-plaintext highlighter-rouge">/tmp</code>–it won’t work because <code class="language-plaintext highlighter-rouge">/tmp</code> is an ephemeral volume that only gets mounted when the lambda container loads.</p>

<p>The solution for this ends up to be setting an environment variable called <code class="language-plaintext highlighter-rouge">TFENV_CONFIG_DIR</code> to <code class="language-plaintext highlighter-rouge">/tmp/tfenv/</code> so that <code class="language-plaintext highlighter-rouge">tfenv</code> thinks its going to configure itself in <code class="language-plaintext highlighter-rouge">/tmp</code>.</p>

<p>Fortunately, this workaround was very simple.</p>

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

<p>So that’s how I ended up getting everything working on lambda as it did before despite the constraint that <code class="language-plaintext highlighter-rouge">/tmp</code> is the only writable volume in a lambda container.</p>

<p><img src="/assets/scan.png" alt="" class="center-image" /></p>

<p>After invoking the lambda with the <code class="language-plaintext highlighter-rouge">repository</code> name and setting up EventBridge to invoke it repeatedly everything works just as before. The only other thing that is notable is that EventBridge rules are limited to at most 5 target per rule, so if you have more than 5 repos you need to keep that in mind (I ended up making 1 rule per repo).</p>

<p><img src="/assets/eventbridge.png" alt="" class="center-image" /></p>

<p>After deploying the lambda our graphs started to look a lot healthier, specifically the disk usage and the number of node evictions. The disk usage dropped to from 80% to 60% and the number of evictions to 0.</p>

<p><img src="/assets/kubegraph.png" alt="" class="center-image" /></p>

<p>I was happy with this result. Since making this change on-call has been a lot less noisy and we’ve seen less alarms go off overall which is a result of having zero node evictions.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Recently I was working on porting a terraform drift detector to AWS Lambda. This is a service I developed a while back, still is in use today and has been responsible for preventing incidents due to unmerged/unapplied infrastructure in terraform.]]></summary></entry><entry><title type="html">Using a SAT solver to do Day 24 of Advent of Code</title><link href="https://tarasyk.ca/2021/12/23/day-24-aoc.html" rel="alternate" type="text/html" title="Using a SAT solver to do Day 24 of Advent of Code" /><published>2021-12-23T18:33:36+00:00</published><updated>2021-12-23T18:33:36+00:00</updated><id>https://tarasyk.ca/2021/12/23/day-24-aoc</id><content type="html" xml:base="https://tarasyk.ca/2021/12/23/day-24-aoc.html"><![CDATA[<p>I’ve been doing Advent of Code this year with coworkers (did it later year too) and day 24 was actually funny enough to write about.</p>

<p>So on <a href="https://adventofcode.com/2021/day/24">Day 24</a> of Advent of Code they gave us this problem where it’s basically: write a program that takes in a program as assembly and runs it against a 14 digit number (with no zeros) and finds the answer where <code class="language-plaintext highlighter-rouge">z=0</code>.</p>

<p>The assembly is pretty straight forward, there’s just ops for multiplying, adding, dividing, modulo, equality and taking in input that set <code class="language-plaintext highlighter-rouge">x</code>,<code class="language-plaintext highlighter-rouge">z</code>,<code class="language-plaintext highlighter-rouge">y</code>,<code class="language-plaintext highlighter-rouge">w</code> registers.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">inp</span> <span class="n">w</span>
<span class="n">mul</span> <span class="n">x</span> <span class="mi">0</span>
<span class="n">add</span> <span class="n">x</span> <span class="n">z</span>
<span class="n">mod</span> <span class="n">x</span> <span class="mi">26</span>
<span class="n">div</span> <span class="n">z</span> <span class="mi">1</span>
<span class="n">add</span> <span class="n">x</span> <span class="mi">12</span>
<span class="n">eql</span> <span class="n">x</span> <span class="n">w</span>
<span class="n">eql</span> <span class="n">x</span> <span class="mi">0</span>
<span class="n">mul</span> <span class="n">y</span> <span class="mi">0</span>
<span class="n">add</span> <span class="n">y</span> <span class="mi">25</span>
<span class="n">mul</span> <span class="n">y</span> <span class="n">x</span>
<span class="n">add</span> <span class="n">y</span> <span class="mi">1</span>
<span class="n">mul</span> <span class="n">z</span> <span class="n">y</span>
<span class="n">mul</span> <span class="n">y</span> <span class="mi">0</span>
<span class="n">add</span> <span class="n">y</span> <span class="n">w</span>
<span class="n">add</span> <span class="n">y</span> <span class="mi">7</span>
<span class="n">mul</span> <span class="n">y</span> <span class="n">x</span>
<span class="n">add</span> <span class="n">z</span> <span class="n">y</span>
<span class="n">inp</span> <span class="n">w</span>
<span class="n">mul</span> <span class="n">x</span> <span class="mi">0</span>
<span class="n">add</span> <span class="n">x</span> <span class="n">z</span>
<span class="n">mod</span> <span class="n">x</span> <span class="mi">26</span>
<span class="n">div</span> <span class="n">z</span> <span class="mi">1</span>
<span class="n">add</span> <span class="n">x</span> <span class="mi">13</span>
<span class="n">eql</span> <span class="n">x</span> <span class="n">w</span>
<span class="n">eql</span> <span class="n">x</span> <span class="mi">0</span>
<span class="n">mul</span> <span class="n">y</span> <span class="mi">0</span>
<span class="n">add</span> <span class="n">y</span> <span class="mi">25</span>
<span class="n">mul</span> <span class="n">y</span> <span class="n">x</span>
<span class="n">add</span> <span class="n">y</span> <span class="mi">1</span>
<span class="n">mul</span> <span class="n">z</span> <span class="n">y</span>
<span class="p">...</span>
</code></pre></div></div>

<p>You can try to write a bruteforcer that does this (like I did) but it’s pretty impractical since the input <code class="language-plaintext highlighter-rouge">w</code> is a 14 digit number and that’s too big for all the possible values.</p>

<p>So what I found out (perusing reddit lol) was you can just parse the input, convert it into C code and then compile it with as many compiler flags as possible (I guess) and then put it into Ghidra, get the simplified source code and then put it into <a href="https://github.com/Z3Prover/z3">z3</a>. You could probably skip the “put it into Ghidra step” but I did this anyway.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">z3</span>

<span class="k">def</span> <span class="nf">to_val</span><span class="p">(</span><span class="n">inp</span><span class="p">):</span>
    <span class="n">res</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">inp</span><span class="p">:</span>
        <span class="n">res</span> <span class="o">=</span> <span class="n">res</span><span class="o">*</span><span class="mi">10</span> <span class="o">+</span> <span class="n">d</span>
    <span class="k">return</span> <span class="n">res</span>

<span class="k">def</span> <span class="nf">add_constraints</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">inp</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">digit</span> <span class="ow">in</span> <span class="n">inp</span><span class="p">:</span>
        <span class="n">s</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">digit</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">s</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">digit</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">)</span>

    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">12</span><span class="p">)</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="mi">10</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mh">0x19</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">10</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">4</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mh">0x19</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">5</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">+</span> <span class="mi">6</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">14</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">6</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mh">0x19</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">+</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">7</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">+</span> <span class="mh">0xd</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">15</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">8</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">15</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">9</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">14</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">10</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mi">26</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">10</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">11</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">+</span> <span class="mh">0xd</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">14</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">12</span><span class="p">])</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mi">26</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">12</span><span class="p">]</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>
    <span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">!=</span> <span class="n">inp</span><span class="p">[</span><span class="mi">13</span><span class="p">]);</span>
    <span class="n">res</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">inp</span><span class="p">[</span><span class="mi">13</span><span class="p">]</span> <span class="o">+</span> <span class="mi">14</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span>

    <span class="n">s</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">res</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">to_val</span><span class="p">(</span><span class="n">inp</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">inp</span><span class="p">):</span>
    <span class="n">s</span> <span class="o">=</span> <span class="n">z3</span><span class="p">.</span><span class="n">Optimize</span><span class="p">()</span>
    <span class="n">value</span> <span class="o">=</span> <span class="n">add_constraints</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">inp</span><span class="p">)</span>

    <span class="n">s</span><span class="p">.</span><span class="n">maximize</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="c1"># maximize, you can change this for part 2
</span>
    <span class="k">assert</span> <span class="n">s</span><span class="p">.</span><span class="n">check</span><span class="p">()</span> <span class="o">==</span> <span class="n">z3</span><span class="p">.</span><span class="n">sat</span>
    <span class="n">m</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">model</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">m</span><span class="p">.</span><span class="nb">eval</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

<span class="n">inp</span> <span class="o">=</span> <span class="p">[</span><span class="n">z3</span><span class="p">.</span><span class="n">Int</span><span class="p">(</span><span class="s">'x{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">14</span><span class="p">)]</span>

<span class="k">print</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">inp</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">prog_to_python</span><span class="p">(</span><span class="n">prog</span><span class="p">):</span>
    <span class="n">s</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">prog</span><span class="p">:</span>
        <span class="n">instr</span><span class="p">,</span> <span class="n">ops</span> <span class="o">=</span> <span class="n">p</span>
        <span class="k">if</span> <span class="n">instr</span> <span class="o">==</span> <span class="s">"add"</span><span class="p">:</span>
            <span class="n">s</span> <span class="o">+=</span> <span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"+="</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s">";"</span>
        <span class="k">elif</span> <span class="n">instr</span> <span class="o">==</span> <span class="s">"mod"</span><span class="p">:</span>
            <span class="n">s</span> <span class="o">+=</span> <span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"%="</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s">";"</span>
        <span class="k">elif</span> <span class="n">instr</span> <span class="o">==</span> <span class="s">"div"</span><span class="p">:</span>
            <span class="n">s</span> <span class="o">+=</span> <span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"/="</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s">";"</span>
        <span class="k">elif</span> <span class="n">instr</span> <span class="o">==</span> <span class="s">"mul"</span><span class="p">:</span>
            <span class="n">s</span> <span class="o">+=</span> <span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"*="</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s">";"</span>
        <span class="k">elif</span> <span class="n">instr</span> <span class="o">==</span> <span class="s">"eql"</span><span class="p">:</span>
            <span class="n">s</span> <span class="o">+=</span> <span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"="</span> <span class="o">+</span> <span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="s">"=="</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s">"?1:0;"</span>
        <span class="k">elif</span> <span class="n">instr</span> <span class="o">==</span> <span class="s">"inp"</span><span class="p">:</span>
            <span class="n">s</span> <span class="o">+=</span> <span class="s">"w=bla;"</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">instr</span><span class="p">,</span><span class="n">ops</span><span class="p">)</span>
            <span class="k">assert</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
        <span class="n">s</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span>
    <span class="k">print</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>


<span class="n">prog</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
    <span class="n">sp</span> <span class="o">=</span> <span class="n">line</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">" "</span><span class="p">)</span>
    <span class="n">instr</span> <span class="o">=</span> <span class="n">sp</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">ops</span> <span class="o">=</span> <span class="n">sp</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">ops</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="s">"x"</span><span class="p">,</span><span class="s">"y"</span><span class="p">,</span><span class="s">"z"</span><span class="p">,</span><span class="s">"w"</span><span class="p">]:</span>
        <span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">ops</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">prog</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">instr</span><span class="p">,</span><span class="n">ops</span><span class="p">))</span>
</code></pre></div></div>

<p>Note that the above SAT code is taken from my input which I took from Ghidra to form the z3 eqns</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">!=</span> <span class="mi">12</span><span class="p">);</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">!=</span> <span class="n">param_2</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_2</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">!=</span> <span class="n">param_3</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_3</span> <span class="o">+</span> <span class="mi">10</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">!=</span> <span class="n">param_4</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mh">0x19</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_4</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">10</span> <span class="o">!=</span> <span class="n">param_5</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mh">0x19</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_5</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">!=</span> <span class="n">param_6</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_6</span> <span class="o">+</span> <span class="mi">6</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">14</span> <span class="o">!=</span> <span class="n">param_7</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mh">0x19</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_7</span> <span class="o">+</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">!=</span> <span class="n">param_8</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_8</span> <span class="o">+</span> <span class="mh">0xd</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">15</span> <span class="o">!=</span> <span class="n">param_9</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_9</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">15</span> <span class="o">!=</span> <span class="n">param_10</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_10</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">14</span> <span class="o">!=</span> <span class="n">param_11</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mi">26</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_11</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">+</span> <span class="mi">10</span> <span class="o">!=</span> <span class="n">param_12</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_12</span> <span class="o">+</span> <span class="mh">0xd</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">14</span> <span class="o">!=</span> <span class="n">param_13</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mi">26</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_13</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span>
<span class="n">uVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">uVar1</span> <span class="o">%</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">!=</span> <span class="n">param_14</span><span class="p">);</span>
<span class="k">return</span> <span class="p">(</span><span class="n">uVar1</span> <span class="o">/</span> <span class="mh">0x1a</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">uVar2</span> <span class="o">*</span> <span class="mi">25</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">param_14</span> <span class="o">+</span> <span class="mi">14</span><span class="p">)</span> <span class="o">*</span> <span class="n">uVar2</span><span class="p">;</span> <span class="c1">// z value</span>
</code></pre></div></div>
<p>And that’s about it. I got <code class="language-plaintext highlighter-rouge">79197919993985</code> for part 1 and <code class="language-plaintext highlighter-rouge">13191913571211</code> for part 2, best of all the solver can do this within 20-30 seconds on a 2019 mbp.</p>

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

<p>I think this was a really clever way of solving this problem that is totally reasonable since you may not want to reverse engineer all of that C code into figuring out what the max/min value you can give the input is. Definitely keep knowledge of z3/SAT in your arsenal. Plus, this is in theory a solution that could be done SUPER quickly if you were fast enough.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’ve been doing Advent of Code this year with coworkers (did it later year too) and day 24 was actually funny enough to write about.]]></summary></entry><entry><title type="html">Regexing fun</title><link href="https://tarasyk.ca/2021/10/31/regexing.html" rel="alternate" type="text/html" title="Regexing fun" /><published>2021-10-31T18:33:36+00:00</published><updated>2021-10-31T18:33:36+00:00</updated><id>https://tarasyk.ca/2021/10/31/regexing</id><content type="html" xml:base="https://tarasyk.ca/2021/10/31/regexing.html"><![CDATA[<p>Hi all, yeah it’s been a long time. I got a job 1.5 years ago and haven’t really posted since I’ve been “really busy”, etcetera, and <a href="https://youtu.be/LxZfScuOR0I">tldr</a>.</p>

<p>I figured I’d post about something I did today that I thought was interesting/fun. This post probably won’t make sense unless you have a basic understanding of regex.</p>

<h1 id="problem-description">Problem description</h1>

<p>Let’s say you have a bunch of old urls that are hosting resources which follow an old format and point to an old s3 bucket/have a different uniform resource locator.</p>

<p>In the case of s3, there’s two ways of representing urls: <code class="language-plaintext highlighter-rouge">http://s3.amazonaws.com/[bucket_name]-accountnumber/</code> or <code class="language-plaintext highlighter-rouge">http://[bucket_name].s3.amazonaws.com/</code>.</p>

<p>Your task is to convert these to a consistent format so when they point to a “consistent” structure–you get something like <code class="language-plaintext highlighter-rouge">s3://bucketname/dir/dir2/resource.ext</code> that you can convert to the proper url for locating the resource.</p>

<p>It sounds convoluted, I know, because it sort of is. The reason this is desirable is because if you can convert all these urls
to a different format you can serve a different format that’s easier to look at/parse and is more “consistent”.</p>

<p>In my case I have two url formats similar to above <code class="language-plaintext highlighter-rouge">https://coolhost.s3.us-west-2.amazonaws.com/files/bucketname/dir1/dir3/file.jpeg</code> and <code class="language-plaintext highlighter-rouge">https://s3-us-west-2.amazonaws.com/bucket2/dir/filename.png</code> that I wanted to pass into a function to get a consistent resource location scheme. For the first case it would be <code class="language-plaintext highlighter-rouge">s3://coolhost/files/bucket1/dir3/file.jpeg</code> and for the second case <code class="language-plaintext highlighter-rouge">s3://otherbucket/dir/filenamepng</code>. Essentially the bucket name has to follow before all the key information.</p>

<h1 id="regexing">Regexing</h1>

<p>The way I decided to do this was to use groups and <code class="language-plaintext highlighter-rouge">MatchAll</code> in go’s <code class="language-plaintext highlighter-rouge">regexp</code> standard library.</p>

<p>Here’s the code which includes the regex</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Convert converts the s3 url into a digestible s3 path</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">S3URLConverter</span><span class="p">)</span> <span class="n">Convert</span><span class="p">(</span><span class="n">url</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">s</span><span class="o">.</span><span class="n">re</span><span class="o">.</span><span class="n">ReplaceAllString</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="s">"s3://$2$3$4/$7"</span><span class="p">)</span>
<span class="p">}</span>

<span class="c">// NewS3URLConverter creates a new s3 url converter</span>
<span class="k">func</span> <span class="n">NewS3URLConverter</span><span class="p">()</span> <span class="n">S3URLConverter</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">S3URLConverter</span> <span class="p">{</span> 
        <span class="n">re</span><span class="o">:</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompile</span><span class="p">(</span><span class="s">`https:\/\/((coolhost)\.s3\.us-west-2\.amazonaws\.com(\/)|s3-us-west-2\.amazonaws\.com\/)(bucket1|bucket2)(-([0-9]+)|)\/([\w\/\-?=%.]+)`</span><span class="p">)</span>
    <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Working backwards, you can view the regex <a href="https://regex101.com/r/NUrHsH/1">here</a> you’ll notice that both URLs get matched, and groups 2, 3, 4
and 7 can be appended to form a consistent url (hence <code class="language-plaintext highlighter-rouge">Convert</code>).</p>

<p><img src="/assets/Screen Shot 2021-11-02 at 12.47.30 AM.png" alt="" class="center-image" /></p>

<p>The idea is to just generate groups that can be appended from left to right no matter what the url is so that
they form a consistent url. Groups are all the stuff that are included in the <code class="language-plaintext highlighter-rouge">()</code>.</p>

<p>A few things:</p>

<ul>
  <li>the problem boils down to extracting the bucket’s name in both cases and appending it with its subdirectories</li>
  <li>the <code class="language-plaintext highlighter-rouge">(-([0-9]+)|)</code> is a group that just says “a hyphen appended with multiple numbers or nothing at all” (I added this to handle the <code class="language-plaintext highlighter-rouge">bucketname-accountnumber</code> format. You can probably make this <code class="language-plaintext highlighter-rouge">\d+</code> if you want to be more correct (since this catches account numbers that start with 0)</li>
  <li>the group <code class="language-plaintext highlighter-rouge">((coolhost)\.s3\.us-west-2\.amazonaws\.com(\/)|s3-us-west-2\.amazonaws\.com\/)</code> is the most
important.  note how it catches the backslash in the first case, but not the second case after the OR. this is needed so that the
next group is a backslash when we match <code class="language-plaintext highlighter-rouge">coolhost</code> in group 2, but not in the other url format. with the <code class="language-plaintext highlighter-rouge">coolhost</code> url format where the host is a subdomain of <code class="language-plaintext highlighter-rouge">s3.us-west-2.amazonaws.com</code> the value <code class="language-plaintext highlighter-rouge">coolhost</code> is actually the bucketname, so it should prepend everything. in the second format it’s not and the first subdirectory is the bucketname, thus we <em>do</em> want to capture a slash in the other case hence why the OR branch is so large to evaluate these two these cases separately.</li>
  <li>the <code class="language-plaintext highlighter-rouge">\/([\w\/\-?=%.]+)</code> group just represents “everything after the backslash” aka the bucket’s subdirectories</li>
  <li>the converter is wrapped in a struct because it wraps around the <code class="language-plaintext highlighter-rouge">regexp.Regexp</code> object and we don’t want to re-compile
it (that’s slow)</li>
  <li>the unit tests aren’t included in the above code, but they’re incredibly important for verifying the converter behavior and do exist/are exhaustive</li>
</ul>

<p>And that’s about it! Hopefully this gave some people some new ideas for how to do “string conversion” because I feel
like it’s rare that we actually get to “use regex” to solve problems. 
Keep in mind that this is a basic problem and isn’t meant to highlight anything other than the premise behind “conversion problems” which require you to use information in the
string to build new strings.</p>

<p>You can catch me streaming at <a href="https://twitch.tv/cub">twitch.tv/cub</a>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Hi all, yeah it’s been a long time. I got a job 1.5 years ago and haven’t really posted since I’ve been “really busy”, etcetera, and tldr.]]></summary></entry><entry><title type="html">The future of mcidle</title><link href="https://tarasyk.ca/2020/06/02/mcidle-cpp-update.html" rel="alternate" type="text/html" title="The future of mcidle" /><published>2020-06-02T18:33:36+00:00</published><updated>2020-06-02T18:33:36+00:00</updated><id>https://tarasyk.ca/2020/06/02/mcidle-cpp-update</id><content type="html" xml:base="https://tarasyk.ca/2020/06/02/mcidle-cpp-update.html"><![CDATA[<p>For the last 2 weeks I’ve been mostly working on <a href="https://github.com/qubard/mcidle-python">mcidle</a> and updating it so that it’s perfectly usable by players. Even though I don’t play Minecraft and haven’t played in a long while, I just wanted to work on something that wasn’t web related. There’s a lot to go over, but in essence I just made everything extremely thread safe, added the ability for the program to terminate with daemon threads, abstracted away communication to sockets with “upstreams”, removed all the worker thread packet processing which actually slowed down <code class="language-plaintext highlighter-rouge">mcidle</code> due to threads deadlocking while they wait for access to the mutex, and store all relevant to-be-serialized packet data in a <code class="language-plaintext highlighter-rouge">game_state</code> object.</p>

<h1 id="the-future-of-mcidle">The future of mcidle</h1>

<p>Currently mcidle can actually imitate the game’s state fairly well, that is almost every relevant packet is processed (inventory, gamemode, health, position, packets, loaded entities) except block/chunk state. The reason the old version of mcidle didn’t have chunk processing originally is because I was too lazy to read the docs on how to deserialize chunks.</p>

<p>The major problems are</p>

<ul>
  <li>We can’t easily implement protocol agnostic packet serialization/deserialization</li>
  <li>Processing chunks is extremely slow and cannot be done in Python (blocks the entire program, too much CPU usage)</li>
  <li>Packaging python is brittle, even with <code class="language-plaintext highlighter-rouge">pipx</code> you have to compile the <code class="language-plaintext highlighter-rouge">cryptography</code> library with <code class="language-plaintext highlighter-rouge">mcidle</code> which doesn’t work on a lot of Windows machines for some reason even with VS tools installed</li>
</ul>

<h1 id="mcidle-cpp">mcidle-cpp</h1>

<p>Remember <a href="https://tarasyk.ca/2019/11/10/mcidle-performance.html">mcidle-cpp</a>? That post was a little misleading. I basically ended up realizing that there’s no way to design mcidle without virtual inheritance/relying purely on templates and not pull my hair out. So instead I decided to borrow an implementation of protocol agnostic packet processing like <a href="https://github.com/plushmonkey/mclib/blob/3750f3b7f915f7206fb956c6177d8fa800de6703/mclib/src/mclib/protocol/Protocol.cpp">mclib does</a>.</p>

<p>The basic idea is to take a packet id and then call a lambda function on a protocol object which returns the correct instance of the packet we want for that id then call <code class="language-plaintext highlighter-rouge">serialize</code> or <code class="language-plaintext highlighter-rouge">deserialize</code> functions respectively which are also implemented using virtual inheritance. Sure there are some complications since we still have to be careful about protocol versions during the serialization process, but in general it works.</p>

<p>So far <code class="language-plaintext highlighter-rouge">mcidle-cpp</code> contains a full <code class="language-plaintext highlighter-rouge">cmake</code> build system, tests for all the primitive data types, working client-&gt;server minecraft connection, byte buffers, all the primitive data types, zlib compression/decompression, and packet encryption/decryption. The only code that I did not write myself was the Yggdrasil auth API which is basically just boilerplate from <code class="language-plaintext highlighter-rouge">mclib</code>.</p>

<h1 id="results-so-far">Results so far</h1>

<p>Today I was able to test chunk section processing. When I controlled for how slow print statements are in C++ if you use streams and being especially careful of different optimizations and avoiding excessive copies I was able to get a <code class="language-plaintext highlighter-rouge">15-20x</code> speed improvement for chunk processing (0.37 seconds c++ versus 7.1 seconds in python for 14mb of compressed chunk data or 441 chunk packets). <a href="https://wiki.vg/Chunk_Format#Data_structure">Chunk processing means deserializing the entire chunk sections, palette information, etc</a>. I think this is sufficient for my port to be successful. Honestly, testing is sort of a nightmare but I can definitely say the C++ version is at least one order of magnitude faster.</p>

<p>In fact, when I tried to measure how long it took to process chunks it had zero impact on the speed of the program (it was fast). It seems like my actual bottleneck is the rate at which the server could send packets (latency), which works out to chunks being read and processed in 0.36-0.4 seconds upon initially connecting! Blazing fast. These chunks when uncompressed and deserialized amount to ~50mb of data in memory.</p>

<p>The following code, written in Python, to read chunk sections drops the performance of <code class="language-plaintext highlighter-rouge">mcidle-python</code> to 20x slower than it’s C++ equivalent</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="nb">staticmethod</span>
<span class="k">def</span> <span class="nf">read</span><span class="p">(</span><span class="n">stream</span><span class="p">):</span>
    <span class="c1"># In the latest protocol we have to read a short here (block count)
</span>    <span class="c1"># block_count = Short.read(stream)
</span>    <span class="n">bits_per_block</span> <span class="o">=</span> <span class="n">UnsignedByte</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>

    <span class="n">palette_len</span> <span class="o">=</span> <span class="n">VarInt</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">bits_per_block</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">:</span>
        <span class="c1"># Indirect palette
</span>        <span class="n">bits_per_block</span> <span class="o">=</span> <span class="mi">4</span>
    <span class="k">if</span> <span class="n">bits_per_block</span> <span class="o">&gt;</span> <span class="mi">8</span><span class="p">:</span>
        <span class="c1"># Direct palette, ignore
</span>        <span class="n">bits_per_block</span> <span class="o">=</span> <span class="mi">13</span>

    <span class="k">if</span> <span class="n">palette_len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">while</span> <span class="n">palette_len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">VarInt</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
            <span class="n">palette_len</span> <span class="o">-=</span> <span class="mi">1</span>

    <span class="n">mask</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">bits_per_block</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>

    <span class="n">data_len</span> <span class="o">=</span> <span class="n">VarInt</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">num_read</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">while</span> <span class="n">num_read</span> <span class="o">&lt;</span> <span class="n">data_len</span><span class="p">:</span>
        <span class="n">data</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">UnsignedLong</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">stream</span><span class="p">))</span>
        <span class="n">num_read</span> <span class="o">=</span> <span class="n">num_read</span> <span class="o">+</span> <span class="mi">1</span>

    <span class="n">SECTION_HEIGHT</span> <span class="o">=</span> <span class="mi">16</span>
    <span class="n">SECTION_WIDTH</span> <span class="o">=</span> <span class="mi">16</span>

    <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">SECTION_HEIGHT</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">z</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">SECTION_WIDTH</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">SECTION_WIDTH</span><span class="p">):</span>
                <span class="n">block_number</span> <span class="o">=</span> <span class="p">((</span><span class="n">y</span> <span class="o">*</span> <span class="n">SECTION_HEIGHT</span><span class="p">)</span> <span class="o">+</span> <span class="n">z</span><span class="p">)</span> <span class="o">*</span> <span class="n">SECTION_WIDTH</span> <span class="o">+</span> <span class="n">x</span>
                <span class="n">start_long</span> <span class="o">=</span> <span class="p">(</span><span class="n">block_number</span> <span class="o">*</span> <span class="n">bits_per_block</span><span class="p">)</span> <span class="o">//</span> <span class="mi">64</span>
                <span class="n">start_offset</span> <span class="o">=</span> <span class="p">(</span><span class="n">block_number</span> <span class="o">*</span> <span class="n">bits_per_block</span><span class="p">)</span> <span class="o">%</span> <span class="mi">64</span>
                <span class="n">end_long</span> <span class="o">=</span> <span class="p">((</span><span class="n">block_number</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">bits_per_block</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">//</span> <span class="mi">64</span>

                <span class="n">val</span> <span class="o">=</span> <span class="mi">0</span>
                <span class="k">if</span> <span class="n">start_long</span> <span class="o">==</span> <span class="n">end_long</span><span class="p">:</span>
                    <span class="n">val</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">start_long</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">start_offset</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">end_offset</span> <span class="o">=</span> <span class="mi">64</span> <span class="o">-</span> <span class="n">start_offset</span>
                    <span class="n">val</span> <span class="o">=</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">start_long</span><span class="p">]</span> <span class="o">&gt;&gt;</span> <span class="n">start_offset</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">end_long</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="n">end_offset</span><span class="p">)</span>
                <span class="n">val</span> <span class="o">&amp;=</span> <span class="n">mask</span>

    <span class="n">block_light</span> <span class="o">=</span> <span class="n">ByteArray</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="mi">4096</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<p>When I write the exact same thing in C++ with some niceties like using <code class="language-plaintext highlighter-rouge">std::copy</code> or <code class="language-plaintext highlighter-rouge">memcpy</code> to read a bunch of 64 bit longs it’s very fast. The loop over each block in the chunk section is extremely slow in Python and dominates the time (removing this loop makes the python code run in max 1 second). <a href="https://stackoverflow.com/questions/8097408/why-python-is-so-slow-for-a-simple-for-loop">It boils down to how slow loops are in python due to it being an interpreted language and not being able to optimize to down to bare metal very well</a>.</p>

<p>This was a major win for me and the main issue that blocked mcidle-python from working performantly: per chunk processing was bottlenecked by Python itself and not hardware.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[For the last 2 weeks I’ve been mostly working on mcidle and updating it so that it’s perfectly usable by players. Even though I don’t play Minecraft and haven’t played in a long while, I just wanted to work on something that wasn’t web related. There’s a lot to go over, but in essence I just made everything extremely thread safe, added the ability for the program to terminate with daemon threads, abstracted away communication to sockets with “upstreams”, removed all the worker thread packet processing which actually slowed down mcidle due to threads deadlocking while they wait for access to the mutex, and store all relevant to-be-serialized packet data in a game_state object.]]></summary></entry><entry><title type="html">How I Broke Graal in 2 Days</title><link href="https://tarasyk.ca/2020/04/24/graal.html" rel="alternate" type="text/html" title="How I Broke Graal in 2 Days" /><published>2020-04-24T18:33:36+00:00</published><updated>2020-04-24T18:33:36+00:00</updated><id>https://tarasyk.ca/2020/04/24/graal</id><content type="html" xml:base="https://tarasyk.ca/2020/04/24/graal.html"><![CDATA[<p>For those of you who don’t know, <a href="https://www.graalonline.com/">Graal</a> is an old popular MMO from the early 2000s on the PC that lets players create servers, design levels and otherwise create mmo experiences much like <a href="http://www.byond.com/?">BYOND</a> or <a href="https://cms.furcadia.com/">Furcadia</a>. The game has 4 main servers that people can play on each with a different genre of gameplay. One is fantasy (Zodiac), one is mafia/gunshooting (Era) and the other 2 aren’t super relevant since they have &lt; 5 players. The most popular gamemode is Era which averages ~50-100 players for the last 15 years. Graal itself is just an engine based off of Zelda, and there are tons of developer tools for adding stuff that loads at runtime to create an actual game.</p>

<p>Ultimately I was able to break the game and accomplish feats like alter my player’s stats, get infinite ammo, make my punches instantly kill and buy items from stores for free (items that take weeks if not months to grind for). I would like to say that I had no malicious intent in doing this and didn’t abuse this power at all. It was merely out of curiosity. I did notify the administrators of all the vulnerabilities I found.</p>

<h1 id="how-i-broke-it">How I broke it</h1>

<p>Graal is actually not that easy to hack on if you want to do anything complicated. It’s packed with <a href="https://www.oreans.com/Themida.php">Themida</a> and makes it basically impossible to do static analysis. If you try to attach a debugger with CheatEngine the game will automatically close. I actually unsuccessfully tried to get anything better than a teleport hack out of this game for 4 years where every year I’d make some attempt at hacking it but then fail because of how difficult it was to get a debugger attached (the TP hack is EXTREMELY easy to do). The game purposefully makes it very difficult to cheat in and does take a lot of precautions to dissuade hacking. However, there is no real anti-cheat except preventing the game from being easily reversed.</p>

<p>The way I got around both these security protections is by just by using VEH debugging and never really doing much static analysis. But still, this is only the beginning of an attempt at breaking this game. We need to understand how exactly the game works because otherwise it’s going to be very difficult to attack.</p>

<p>Graal contains many layers of abstraction than in your traditional game. That is to say, the core aspects of a server you’re connected to aren’t actually contained in the binary when you install the game but are loaded once the game is started up and sit on top of the actual engine. In Graal there’s not a lot of hard coded stuff in the binary outside of accomplishing this. All the game logic can be found in clientside scripts written in something called gscript which interact with the game server.</p>

<p>I believe the files are <code class="language-plaintext highlighter-rouge">.code</code> files in the <code class="language-plaintext highlighter-rouge">weblevels/</code> directory, but I have no idea how exactly the clientside script <code class="language-plaintext highlighter-rouge">.code</code> file format works because I can’t find the routines that handle this in the binary–nor am I exactly certain that there is a way to access this information. Give me a few more days and I bet I could do it though.</p>

<p>This lead me to explore attacking Graal by getting into all the data that the client/server send back and forth to each other. Most of the testing was done on Era but I also found just as many bugs on Zodiac.</p>

<h1 id="attacking-zlib">Attacking zlib</h1>

<p>The big weakness of Graal is that it relies on <code class="language-plaintext highlighter-rouge">zlib</code> for compressing a lot of its data. I figured the game was doing some compression/decompression of data using it, so I attached breakpoints on <code class="language-plaintext highlighter-rouge">Graal.inflate</code> and <code class="language-plaintext highlighter-rouge">Graal.deflate</code>.</p>

<p>I was able to figure out that this was <code class="language-plaintext highlighter-rouge">zlib</code> inflate because when I looked at the first argument in <code class="language-plaintext highlighter-rouge">eax</code> and compared it to the first argument of <code class="language-plaintext highlighter-rouge">zlib.inflate</code> I saw that it was basically a form of <code class="language-plaintext highlighter-rouge">zstream*</code> except with <code class="language-plaintext highlighter-rouge">int</code>s instead of <code class="language-plaintext highlighter-rouge">long</code>s for some of the data types. So, all the data that gets sent to the server is in <code class="language-plaintext highlighter-rouge">deflate</code> (compress) and otherwise in <code class="language-plaintext highlighter-rouge">inflate</code> if it’s coming to us.</p>

<p>I was able to find data like this in the <code class="language-plaintext highlighter-rouge">inflate</code> output buffer, so I knew I was on the right track</p>

<p><img src="/assets/grel1.png" alt="" class="center-image" /></p>

<p>which surprises me because it appears the game sends its data in plaintext more or less!</p>

<h1 id="writing-a-hook">Writing a hook</h1>

<p>To attack the game’s netcode I had to write hooks for <code class="language-plaintext highlighter-rouge">inflate</code> and <code class="language-plaintext highlighter-rouge">deflate</code>. <a href="https://www.codeproject.com/Articles/30140/API-Hooking-with-MS-Detours">Basically used the technique described in this article except I had to use cdecl__ which took me 3 hours to figure out</a>. The only difference between the hooking technique I used here and my previous hooks is that it involves writing a <code class="language-plaintext highlighter-rouge">jmp</code> to your hook, writing back the original bytes, executing the original function, writing back the jump and returning the original function output. Before I was trying to some nightmare mode thing where I was writing raw assembly but then I couldn’t call functions that threw exceptions, had to manually allocate stack space for variables and though it worked it was too messy. To this day I can’t figure out how to build detours on Windows, so this would do.</p>

<p>In the end the code wasn’t super interesting, but it’s crucial for being able to interact with the input/output bytes of the buffers during compression.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span>  <span class="n">_cdecl</span> <span class="nf">HookedDeflate</span><span class="p">(</span><span class="n">z_stream</span><span class="o">*</span> <span class="n">stream</span><span class="p">,</span> <span class="kt">int</span> <span class="n">flush</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">memcpy</span><span class="p">(</span><span class="n">origDeflate</span><span class="p">,</span> <span class="n">oldBytesDeflate</span><span class="p">,</span> <span class="n">SIZE</span><span class="p">);</span>

	<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span><span class="p">;</span>
	<span class="c1">// Too big of messages are just blobs of large data and not packet related</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">stream</span><span class="o">-&gt;</span><span class="n">avail_in</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">stream</span><span class="o">-&gt;</span><span class="n">avail_in</span> <span class="o">&lt;</span> <span class="mi">1048</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
		<span class="k">while</span> <span class="p">(</span><span class="n">j</span> <span class="o">&lt;</span> <span class="n">stream</span><span class="o">-&gt;</span><span class="n">avail_in</span> <span class="o">&amp;&amp;</span> <span class="n">stream</span><span class="o">-&gt;</span><span class="n">avail_in</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
			<span class="kt">char</span> <span class="n">c</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)(</span><span class="n">stream</span><span class="o">-&gt;</span><span class="n">next_in</span> <span class="o">+</span> <span class="n">j</span><span class="p">);</span>
			<span class="n">str</span> <span class="o">+=</span> <span class="n">c</span><span class="p">;</span>
			<span class="n">j</span><span class="o">++</span><span class="p">;</span>
		<span class="p">}</span>

		<span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">"HitNoPk,Punch"</span><span class="p">)</span> <span class="o">!=</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">::</span><span class="n">npos</span><span class="p">)</span> <span class="p">{</span>
			<span class="c1">// ...</span>
		<span class="p">}</span>
		<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">"removeweapon,1"</span><span class="p">)</span> <span class="o">!=</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">::</span><span class="n">npos</span><span class="p">)</span> <span class="p">{</span>
            <span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">str</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">"removeweapon,1"</span><span class="p">);</span>
            <span class="n">stream</span><span class="o">-&gt;</span><span class="n">next_in</span><span class="p">[</span><span class="n">idx</span> <span class="o">+</span> <span class="mi">13</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'0'</span><span class="p">;</span>
        <span class="p">}</span>

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

<p>In the above excerpt preventing the removal of an item from the player’s inventory in some cases is as simple as changing a <code class="language-plaintext highlighter-rouge">1</code> to a <code class="language-plaintext highlighter-rouge">0</code>.</p>

<p>If we look at the messages we send to the server it’s pretty interesting</p>

<p><img src="/assets/grel2.png" alt="" class="center-image" /></p>

<p>The game uses a weird string-like format that isn’t exactly string like because there are some binary parts, but most of the message/RPC-like protocols just involve sending data like this as strings. Eventually I stumbled upon the way items are bought, for instance it’s typically comma delimited and contains <code class="language-plaintext highlighter-rouge">store name, action, quantity, cost</code> and just set the price to <code class="language-plaintext highlighter-rouge">0</code> (someone didn’t do bounds checks).</p>

<p><img src="/assets/grel3.png" alt="" class="center-image" /></p>

<p>I noticed that each shop seems to use a different store script, so I would guess that some shops are vulnerable while others aren’t with different scripts. It was pretty cool being able to spend 0 spar tickets on a gun instead of 3500, and quite the dopamine rush! I couldn’t believe that worked.</p>

<p>The other thing I tried doing was making my punches instantly kill, which was as easy as changing the damage parameter. I also tried getting infinite money by sending floating points in, but it just rounded up the values. At no point could I overflow any data though or send negative values (some scripts did do bounds checking). I’m sure there are TONS more vulnerable scripts that I didn’t get time to test, but I could only inspect a small amount of messages.</p>

<p><img src="/assets/grel5.png" alt="" class="center-image" /></p>

<p>On Zodiac (the above image) I was able to make my fireballs do MAX damage very easily since the damage of a spell is controlled by the player for some weird reason. Normally they only do 251 or so at level 1.</p>

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

<p>That about wraps it up. I left out some of the stuff about what the client receives since I didn’t think it was super interesting, but you can’t really prevent yourself from taking damage in the game or anything. All in all I had an insane amount of fun doing this!</p>

<p>I believe the game tried security through obscurity since they didn’t think anyone could actually change the data that gets sent to the server.</p>

<p>Future work would include figuring out the <code class="language-plaintext highlighter-rouge">.code</code> file format and being able to not just packet edit but construct packets.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[For those of you who don’t know, Graal is an old popular MMO from the early 2000s on the PC that lets players create servers, design levels and otherwise create mmo experiences much like BYOND or Furcadia. The game has 4 main servers that people can play on each with a different genre of gameplay. One is fantasy (Zodiac), one is mafia/gunshooting (Era) and the other 2 aren’t super relevant since they have &lt; 5 players. The most popular gamemode is Era which averages ~50-100 players for the last 15 years. Graal itself is just an engine based off of Zelda, and there are tons of developer tools for adding stuff that loads at runtime to create an actual game.]]></summary></entry><entry><title type="html">Fun Decision Tree Question</title><link href="https://tarasyk.ca/2020/03/30/evaluate-rules.html" rel="alternate" type="text/html" title="Fun Decision Tree Question" /><published>2020-03-30T18:33:36+00:00</published><updated>2020-03-30T18:33:36+00:00</updated><id>https://tarasyk.ca/2020/03/30/evaluate-rules</id><content type="html" xml:base="https://tarasyk.ca/2020/03/30/evaluate-rules.html"><![CDATA[<p>Yesterday I solved an interesting question that went as follows: given two players with a list of integers evaluate whether or not a player has won based on 3 rules. Note that the question was not EXACTLY worded like this but this is the gist of it.</p>

<p>The first rule is if player 1 has a fibonacci sequence player 1 wins, otherwise player 2 wins and if there’s a tie we evalute the second rule. If need be, we evaluate the third rule if the second rule results in a tie also.</p>

<p>The second rule is the player with the highest # wins.</p>

<p>And the third rule is the player with a higher count of prime numbers wins.</p>

<h1 id="the-solution">The solution</h1>

<p>Initially my solution was to simply write a function that evaluated each of the 3 rules one by one, resolving ties if needed. This solution basically breaks down into a tree and a sort of messy if/else branch. We can think of evaluating a rule as returning 3 values <code class="language-plaintext highlighter-rouge">PLAYER1_WON</code>, <code class="language-plaintext highlighter-rouge">PLAYER2_WON</code>, or <code class="language-plaintext highlighter-rouge">TIE</code>. We can use separate functions like <code class="language-plaintext highlighter-rouge">count_primes(list)</code>, <code class="language-plaintext highlighter-rouge">isFibonacci(list)</code> and <code class="language-plaintext highlighter-rouge">max_card(list)</code> to avoid redundant code and simplify everything. I coded it and then my interviewer asked me to do better for arbitrary <code class="language-plaintext highlighter-rouge">N</code> rules.</p>

<p>As I was coding the worse solution that worked, I realized we could just simplify each rule into a list of them and evaluate them one by one top down much like you would in a directed-acylic graph of dependencies or a decision tree. This approach is nice because in essence we only have to write code for each rule, then evaluate them sequentially instead of writing very branchy/bug-prone and difficult to read code. With this approach we think of our rules as a pipeline and just evaluate the next rule if the current rule evaluates to a tie.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Rule</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="n">RULE_RESULT</span> <span class="n">evaluate</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list2</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>and then have each rule override accordingly using virtual inheritance</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">FibonacciRule</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Rule</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="n">RULE_RESULT</span> <span class="n">evaluate</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list2</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// implement logic for determining who won the fib sequence rule</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">class</span> <span class="nc">MaxHandrule</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Rule</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="n">RULE_RESULT</span> <span class="n">evaluate</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list2</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// implement this</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">class</span> <span class="nc">PrimeRule</span> <span class="o">:</span> <span class="k">public</span> <span class="n">Rule</span> <span class="p">{</span>
    <span class="k">virtual</span> <span class="n">RULE_RESULT</span> <span class="n">evaluate</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list2</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// implement this</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When we want to evaluate any number of <code class="language-plaintext highlighter-rouge">N</code> rules we can just pass in a list of rules and go through to evaluate a hand like so, continuing if any ties occur</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">RULE_RESULT</span> <span class="nf">evaluateLists</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list1</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">list2</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">Rule</span><span class="o">&gt;&amp;</span> <span class="n">rules</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">Rule</span><span class="o">&amp;</span> <span class="n">rule</span><span class="o">:</span> <span class="n">rules</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">RULE_RESULT</span> <span class="n">result</span> <span class="o">=</span> <span class="n">rule</span><span class="p">.</span><span class="n">evaluate</span><span class="p">(</span><span class="n">list1</span><span class="p">,</span> <span class="n">list2</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">!=</span> <span class="n">TIE</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="c1">// All the rules were ties</span>
    <span class="k">return</span> <span class="n">TIE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So we simplified the problem, reduced the amount of work to do and improved the maintainability of our code by taking this approach to solve this problem.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Yesterday I solved an interesting question that went as follows: given two players with a list of integers evaluate whether or not a player has won based on 3 rules. Note that the question was not EXACTLY worded like this but this is the gist of it.]]></summary></entry><entry><title type="html">Adding BTTV Emotes to claack + youtube videos</title><link href="https://tarasyk.ca/2020/03/04/adding-bttv-emotes-to-claack.html" rel="alternate" type="text/html" title="Adding BTTV Emotes to claack + youtube videos" /><published>2020-03-04T18:33:36+00:00</published><updated>2020-03-04T18:33:36+00:00</updated><id>https://tarasyk.ca/2020/03/04/adding-bttv-emotes-to-claack</id><content type="html" xml:base="https://tarasyk.ca/2020/03/04/adding-bttv-emotes-to-claack.html"><![CDATA[<p>Recently I added <a href="https://betterttv.com">Better TwitchTV</a> emotes to claack. Below you can see them in action. Currently there are over 50k emotes supported. I’m making a post on this because it’s surprisingly easy to do but a lot of websites DON’T do this for some reason, despite it being actually very easy to rely on BTTV’s api much like a lot of the Twitch userbase does.</p>

<p><img src="/assets/emotes.png" alt="" class="center-image" /></p>

<p>Best of all, it took only 30 minutes to do. I simply went to the <a href="https://betterttv.com">bttv</a> website and monitored the XHR requests to get the URI to their API and then wrote a very simple script to dump the emotes to a dictionary for me.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">json</span>

<span class="n">emoteTable</span> <span class="o">=</span> <span class="p">{}</span>

<span class="k">def</span> <span class="nf">updateEmoteTable</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">limit</span><span class="p">):</span>
    <span class="n">url</span> <span class="o">=</span> <span class="s">"https://api.betterttv.net/3/emotes/shared/top?offset=%d&amp;limit=%d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">limit</span><span class="p">)</span>

    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    
    <span class="n">js</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">js</span><span class="p">)):</span>
        <span class="n">emote</span> <span class="o">=</span> <span class="n">js</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">'emote'</span><span class="p">]</span>
        <span class="n">code</span> <span class="o">=</span> <span class="n">emote</span><span class="p">[</span><span class="s">'code'</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">code</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">emoteTable</span><span class="p">:</span>
            <span class="nb">id</span> <span class="o">=</span> <span class="n">emote</span><span class="p">[</span><span class="s">'id'</span><span class="p">]</span>
            <span class="n">emoteTable</span><span class="p">[</span><span class="n">code</span><span class="p">]</span> <span class="o">=</span> <span class="nb">id</span>
        
        
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1000</span><span class="p">):</span>
    <span class="n">updateEmoteTable</span><span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span>
        
<span class="k">print</span><span class="p">(</span><span class="n">emoteTable</span><span class="p">)</span>
</code></pre></div></div>

<p>The way we do something like output DOM elements for a string of text in react is pretty simple too</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">renderTextLine</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">tokens</span> <span class="o">=</span> <span class="nx">message</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">)</span>
    <span class="nx">tokens</span> <span class="o">=</span> <span class="nx">tokens</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">token</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">token</span> <span class="k">in</span> <span class="nx">emoteTable</span><span class="p">)</span>  <span class="p">{</span>
        <span class="k">return</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;&lt;</span><span class="nx">img</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">emote</span><span class="dl">"</span> <span class="nx">src</span><span class="o">=</span><span class="p">{</span> <span class="dl">"</span><span class="s2">https://cdn.betterttv.net/emote/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">emoteTable</span><span class="p">[</span><span class="nx">token</span><span class="p">]</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">/1x</span><span class="dl">"</span><span class="p">}</span><span class="sr">/&gt; &lt;/</span><span class="nx">span</span><span class="o">&gt;</span>
      <span class="p">}</span>
       <span class="k">return</span> <span class="p">(</span><span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">token</span><span class="p">}</span> <span class="o">&lt;</span><span class="sr">/span&gt;</span><span class="err">)
</span>    <span class="p">})</span>
    <span class="k">return</span> <span class="nx">tokens</span>
<span class="p">}</span>
</code></pre></div></div>

<p>and then we can call <code class="language-plaintext highlighter-rouge">renderTextLine</code> on any message to map emote tokens to our table. Pretty straight forward.</p>

<p>Additionally, it’s not hard to support Youtube videos too</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">renderTextLine</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">tokens</span> <span class="o">=</span> <span class="nx">message</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">)</span>
    <span class="kd">let</span> <span class="nx">vids</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="nx">tokens</span> <span class="o">=</span> <span class="nx">tokens</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">token</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">token</span> <span class="k">in</span> <span class="nx">emoteTable</span><span class="p">)</span>  <span class="p">{</span>
        <span class="k">return</span> <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;&lt;</span><span class="nx">img</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">emote</span><span class="dl">"</span> <span class="nx">src</span><span class="o">=</span><span class="p">{</span> <span class="dl">"</span><span class="s2">https://cdn.betterttv.net/emote/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">emoteTable</span><span class="p">[</span><span class="nx">token</span><span class="p">]</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">/3x</span><span class="dl">"</span><span class="p">}</span><span class="sr">/&gt; &lt;/</span><span class="nx">span</span><span class="o">&gt;</span>
      <span class="p">}</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">token</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="dl">'</span><span class="s1">youtu</span><span class="dl">'</span><span class="p">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
          <span class="kd">let</span> <span class="nx">vid</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">parseYoutubeURL</span><span class="p">(</span><span class="nx">token</span><span class="p">)</span>
          <span class="c1">// Delete the video, add it to the list</span>
          <span class="k">if</span> <span class="p">(</span><span class="nx">vid</span><span class="p">)</span> <span class="p">{</span>
              <span class="nx">vids</span> <span class="o">=</span> <span class="nx">vids</span><span class="p">.</span><span class="nx">concat</span><span class="p">((</span><span class="o">&lt;</span><span class="nx">iframe</span> <span class="nx">width</span><span class="o">=</span><span class="dl">"</span><span class="s2">560</span><span class="dl">"</span> <span class="nx">height</span><span class="o">=</span><span class="dl">"</span><span class="s2">315</span><span class="dl">"</span> <span class="nx">src</span><span class="o">=</span><span class="p">{</span><span class="dl">"</span><span class="s2">https://www.youtube.com/embed/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">vid</span><span class="p">}</span> <span class="nx">frameborder</span><span class="o">=</span><span class="dl">"</span><span class="s2">0</span><span class="dl">"</span> <span class="nx">allow</span><span class="o">=</span><span class="dl">"</span><span class="s2">accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture</span><span class="dl">"</span> <span class="nx">allowfullscreen</span><span class="o">&gt;&lt;</span><span class="sr">/iframe&gt;</span><span class="se">)</span><span class="err">)
</span>              <span class="k">return</span>
          <span class="p">}</span>
      <span class="p">}</span>
       <span class="k">return</span> <span class="p">(</span><span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">token</span><span class="p">}</span> <span class="o">&lt;</span><span class="sr">/span&gt;</span><span class="err">)
</span>    <span class="p">})</span>
    
    <span class="c1">// Videos go at the end</span>
    <span class="nx">tokens</span> <span class="o">=</span> <span class="nx">tokens</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">vids</span><span class="p">)</span>
    <span class="k">return</span> <span class="nx">tokens</span>
<span class="p">}</span>

<span class="nx">parseYoutubeURL</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">regExp</span> <span class="o">=</span> <span class="sr">/^.*</span><span class="se">((</span><span class="sr">youtu.be</span><span class="se">\/)</span><span class="sr">|</span><span class="se">(</span><span class="sr">v</span><span class="se">\/)</span><span class="sr">|</span><span class="se">(\/</span><span class="sr">u</span><span class="se">\/\w\/)</span><span class="sr">|</span><span class="se">(</span><span class="sr">embed</span><span class="se">\/)</span><span class="sr">|</span><span class="se">(</span><span class="sr">watch</span><span class="se">\?))\??</span><span class="sr">v</span><span class="se">?</span><span class="sr">=</span><span class="se">?([^</span><span class="sr">#</span><span class="se">\&amp;\?]</span><span class="sr">*</span><span class="se">)</span><span class="sr">.*/</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">url</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">regExp</span><span class="p">);</span>
    <span class="k">return</span> <span class="p">(</span><span class="nx">match</span><span class="o">&amp;&amp;</span><span class="nx">match</span><span class="p">[</span><span class="mi">7</span><span class="p">].</span><span class="nx">length</span><span class="o">==</span><span class="mi">11</span><span class="p">)?</span> <span class="nx">match</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="p">:</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And that’s about it!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Recently I added Better TwitchTV emotes to claack. Below you can see them in action. Currently there are over 50k emotes supported. I’m making a post on this because it’s surprisingly easy to do but a lot of websites DON’T do this for some reason, despite it being actually very easy to rely on BTTV’s api much like a lot of the Twitch userbase does.]]></summary></entry><entry><title type="html">Why Batch Norm Works Explained With Minimal Math</title><link href="https://tarasyk.ca/2020/02/16/explaining-batch-norm.html" rel="alternate" type="text/html" title="Why Batch Norm Works Explained With Minimal Math" /><published>2020-02-16T15:33:36+00:00</published><updated>2020-02-16T15:33:36+00:00</updated><id>https://tarasyk.ca/2020/02/16/explaining-batch-norm</id><content type="html" xml:base="https://tarasyk.ca/2020/02/16/explaining-batch-norm.html"><![CDATA[<p>Whenever I look at innovations in machine learning I always think: “wow, how did someone come up with that?” and the surprising genius of various discoveries is in their simplicity. From GANs to the discovery of batch norm, there’s a dazzling amount of complexity behind the reasons simple equations or techniques result in high accuracy of machine learning models. The wonders of batch norm specifically stem from how we represent datasets mathematically in terms of an affine transform on a Gaussian distribution.</p>

<h1 id="batch-normalization">Batch normalization</h1>

<p>Batch normalization is a layer you can add to your neural network which normalizes the inputs you give it with respect to the batch it is in. It was introduced in 2015 and has been shown to reduce overfitting in networks as well as produce better generalization accuracy. The output of a BN layer with respect to input <code class="language-plaintext highlighter-rouge">x</code> is the normalized output of <code class="language-plaintext highlighter-rouge">x</code> with respect to the mean and variance of the batch sample you found it in.</p>

<h1 id="why-it-works">Why it works</h1>

<p>But why does it help to train a network better? Let’s think of this in terms of how a NN learns.</p>

<p>Whenever you feed data to your neural network from some sample <code class="language-plaintext highlighter-rouge">x</code> in a training set it has an underlying inherent distribution associated with it. That is, all of the samples can be thought of as coming from some distribution. For the sake of generalization, let’s say this distribution can be approximated using a Gaussian function parameterized by a mean and variance (something you will no doubt be familiar with if you have ever taken a STATS 101 class).</p>

<p>If you take an arbitrary sample from the distribution of your data and call this <code class="language-plaintext highlighter-rouge">x</code>, we can think of <code class="language-plaintext highlighter-rouge">x</code> as a transformation (specifically affine) on the dataset it came from with respect to the (approximate) mean/variance of this dataset to get <code class="language-plaintext highlighter-rouge">x'</code>.</p>

<p><img src="http://mathurl.com/render.cgi?x%27%20%3D%20%5Csigma_x%20x%20+%20%5Cmu_x%5Cnocache" alt="" class="center-image" /></p>

<p>But why is this useful? The reason is because the data we’re actually dealing with that is <code class="language-plaintext highlighter-rouge">x</code> in fact actually depends on the mean and variance of the sample it’s in, and this is very useful since it can be used to normalize <code class="language-plaintext highlighter-rouge">x</code> so that no matter which distribution it comes from the relative meaning of each sample stays the same. That is with respect to a Gaussian, when we normalize <code class="language-plaintext highlighter-rouge">x</code> directly at the mean it represents <code class="language-plaintext highlighter-rouge">0</code> and gets increasingly larger further away from the mean.</p>

<p>For instance, take two distributions of male and female heights. Both distributions will be Gaussian (this is a well known fact), but we can expect the distribution of male heights to have a higher mean average while the distribution of female heights has a lower average. Yet, if we fed this to our neural network to determine if someone is “tall” or not we should treat this data irrespective of distribution because at the mean we consider two people with the average, regardless of gender to be at the average.</p>

<p>We can explain why our neural network prefers learning this signal if we understand what happens when we pass data through our neural network WITHOUT applying batch norm. When you do this on some input, you can think of your data as undergoing the affine transformation (see above) before it is inserted which depends on <img src="http://mathurl.com/render.cgi?%5Csigma_x%5Cnocache" alt="" /> as well as <img src="http://mathurl.com/render.cgi?%5Cmu_x%5Cnocache" alt="" />. This is a HUGE problem because this changes for each input sample <code class="language-plaintext highlighter-rouge">x</code>! Mathematically this just means our gradients will depend on MORE parameters during training which include the mean and variance of the dataset each sample comes from which they come from (<em>cough</em> covariate shift <em>cough</em>). This reduces generalization accuracy significantly since out in the wild since you can imagine that your training data distribution may not match your testing set distribution. We would benefit from normalizing our input data with respect to the underlying distribution of the dataset we expect to feed it since this would reduce this phenomenon (though not get rid of it entirely).</p>

<p>In this case our network would have a hard time learning without batch norm if we fed it the untransformed data signals because when gradients propagate it would actually have to take into account that the data undergoes this affine transformation with respect to the distribution it came from, which is the assumption we make if we assume it comes from a Gaussian distribution. To conclude, put even more simply: our network without batch norm is learning based on the un-normalized input we gave it, which undergoes a transform <code class="language-plaintext highlighter-rouge">f(x)</code> that DEPENDS on the distribution <code class="language-plaintext highlighter-rouge">x</code> came from, but it is in fact way simpler for the network to learn on <code class="language-plaintext highlighter-rouge">x</code> itself. However, this explanation doesn’t capture the rich nature of what is actually going on when you train a neural network which I have described above.</p>

<p><img src="https://cdn.discordapp.com/attachments/583006164955037697/678650098288295946/1Hiq-rLFGDpESpr8QNsJ1jg.png" alt="" class="center-image" /></p>

<p>From the equation above we can see that we approximate the batch with respect to the entire datasaet. The reason this works is that with random shuffling we can assume that the batch should approximate the entire dataset in theory.</p>

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

<p>I feel like this topic is explained poorly in general. I didn’t learn it in my machine learning class, though I feel like it can be explained very simply with respect to an understanding of statistics. Hopefully this post has helped anyone still struggling with understanding why batch norm is used. I believe there are a ton of ways of looking at this topic, including the aspect of “covariate shift”, but I feel like I explained it better without using too much statistical terminology.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Whenever I look at innovations in machine learning I always think: “wow, how did someone come up with that?” and the surprising genius of various discoveries is in their simplicity. From GANs to the discovery of batch norm, there’s a dazzling amount of complexity behind the reasons simple equations or techniques result in high accuracy of machine learning models. The wonders of batch norm specifically stem from how we represent datasets mathematically in terms of an affine transform on a Gaussian distribution.]]></summary></entry></feed>