<?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="http://rafibayer.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://rafibayer.com/" rel="alternate" type="text/html" /><updated>2026-08-31T16:06:16+00:00</updated><id>http://rafibayer.com/feed.xml</id><title type="html">Rafael Bayer</title><subtitle>Senior Software Engineer | Ex-Microsoft Azure</subtitle><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><entry><title type="html">Secret Countdown</title><link href="http://rafibayer.com/2026/06/01/secret-countdown.html" rel="alternate" type="text/html" title="Secret Countdown" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>http://rafibayer.com/2026/06/01/secret-countdown</id><content type="html" xml:base="http://rafibayer.com/2026/06/01/secret-countdown.html"><![CDATA[<h2 id="secret-countdown-for-lillys-eyes-only-">Secret Countdown For Lilly’s eyes only ;)</h2>

<style>
  .secret-countdown {
    display: grid;
    gap: 1.5rem;
    margin: 3rem auto;
    max-width: 56rem;
    text-align: center;
  }

  .secret-countdown__label {
    color: #9fb0b7;
    font-size: 1rem;
    line-height: 1.4;
    margin: 0;
  }

  .secret-countdown__time {
    cursor: pointer;
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .secret-countdown__time.is-spinning {
    animation: countdown-spin 1.4s linear 3;
  }

  .secret-countdown__time.is-spinning .secret-countdown__unit,
  .secret-countdown__time.is-spinning .secret-countdown__value {
    animation: countdown-rainbow 2.8s linear infinite;
  }

  .secret-countdown__unit {
    border: 1px solid rgba(147, 161, 161, 0.35);
    border-radius: 8px;
    padding: 1.25rem 0.75rem;
  }

  .secret-countdown__value {
    color: #fdf6e3;
    display: block;
    font-size: clamp(2.5rem, 9vw, 6rem);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    line-height: 1;
  }

  .secret-countdown__name {
    color: #93a1a1;
    display: block;
    font-size: 0.8rem;
    letter-spacing: 0.08em;
    margin-top: 0.75rem;
    text-transform: uppercase;
  }

  .secret-countdown__complete {
    color: #fdf6e3;
    display: none;
    font-size: clamp(2rem, 7vw, 4rem);
    font-weight: 700;
    line-height: 1.05;
    margin: 0;
  }

  .daily-photo {
    display: grid;
    gap: 0.8rem;
    justify-items: center;
    margin: 4rem auto 2rem;
    max-width: 44rem;
    text-align: center;
  }

  .daily-photo[hidden] {
    display: none;
  }

  .daily-photo__title {
    color: #fdf6e3;
    font-size: clamp(1.5rem, 5vw, 2.25rem);
    margin: 0;
  }

  .daily-photo__frame {
    align-items: center;
    background: linear-gradient(145deg, rgba(253, 246, 227, 0.14), rgba(38, 139, 210, 0.08));
    border: 1px solid rgba(253, 246, 227, 0.28);
    border-radius: 22px;
    box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.32);
    box-sizing: border-box;
    display: flex;
    justify-content: center;
    overflow: hidden;
    padding: 0.55rem;
    width: 100%;
  }

  .daily-photo__image {
    border-radius: 15px;
    display: block;
    height: auto;
    max-height: min(70vh, 38rem);
    max-width: 100%;
    width: auto;
  }

  .daily-photo__caption {
    color: #9fb0b7;
    font-size: 0.9rem;
    line-height: 1.4;
    margin: 0;
  }

  @keyframes countdown-spin {
    to {
      transform: rotate(360deg);
    }
  }

  @keyframes countdown-rainbow {
    0% {
      border-color: #ff3b30;
      color: #ff3b30;
    }
    16% {
      border-color: #ff9500;
      color: #ff9500;
    }
    32% {
      border-color: #ffcc00;
      color: #ffcc00;
    }
    48% {
      border-color: #34c759;
      color: #34c759;
    }
    64% {
      border-color: #007aff;
      color: #007aff;
    }
    80% {
      border-color: #5856d6;
      color: #5856d6;
    }
    100% {
      border-color: #ff2d55;
      color: #ff2d55;
    }
  }

  @media (max-width: 640px) {
    .secret-countdown__time {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .secret-countdown__unit {
      padding: 1rem 0.5rem;
    }

  }
</style>

<section class="secret-countdown" aria-labelledby="secret-countdown-title">
  <p id="secret-countdown-title" class="secret-countdown__label">
    Time remaining until October 4, 2026 at 01:43 PM Eastern time
  </p>

  <div class="secret-countdown__time" aria-live="polite" role="button" tabindex="0">
    <div class="secret-countdown__unit">
      <span id="countdown-days" class="secret-countdown__value">--</span>
      <span class="secret-countdown__name">Days</span>
    </div>
    <div class="secret-countdown__unit">
      <span id="countdown-hours" class="secret-countdown__value">--</span>
      <span class="secret-countdown__name">Hours</span>
    </div>
    <div class="secret-countdown__unit">
      <span id="countdown-minutes" class="secret-countdown__value">--</span>
      <span class="secret-countdown__name">Minutes</span>
    </div>
    <div class="secret-countdown__unit">
      <span id="countdown-seconds" class="secret-countdown__value">--</span>
      <span class="secret-countdown__name">Seconds</span>
    </div>
  </div>

  <p id="countdown-complete" class="secret-countdown__complete">Time is up.</p>
</section>

<section class="daily-photo" aria-labelledby="daily-photo-title" hidden="">
  <h2 id="daily-photo-title" class="daily-photo__title">Photo Of The Day</h2>
  <div class="daily-photo__frame">
    <img class="daily-photo__image" alt="Today's countdown photo" />
  </div>
  <p class="daily-photo__caption" aria-live="polite"></p>
</section>

<script src="/assets/javascript/gallery.js">
</script>

<script>
  (function () {
    var targetTime = new Date("2026-10-04T01:43:00-04:00").getTime();
    var dayMs = 24 * 60 * 60 * 1000;
    var hourMs = 60 * 60 * 1000;
    var minuteMs = 60 * 1000;
    var secondMs = 1000;
    var audioClips = [
      "/assets/audio/pitbull/dale.mp3",
      "/assets/audio/pitbull/fireball.mp3",
      "/assets/audio/pitbull/worldwide.mp3"
    ];
    var audioPlayer = new Audio();

    var daysEl = document.getElementById("countdown-days");
    var hoursEl = document.getElementById("countdown-hours");
    var minutesEl = document.getElementById("countdown-minutes");
    var secondsEl = document.getElementById("countdown-seconds");
    var timeEl = document.querySelector(".secret-countdown__time");
    var completeEl = document.getElementById("countdown-complete");
    var dailyPhotoEl = document.querySelector(".daily-photo");
    var dailyPhotoImageEl = dailyPhotoEl.querySelector(".daily-photo__image");
    var dailyPhotoCaptionEl = dailyPhotoEl.querySelector(".daily-photo__caption");
    var displayedIndex = null;

    function pad(value) {
      return String(value).padStart(2, "0");
    }

    async function updateDailyPhoto() {
      var photoDay = photoIndex()
      if (displayedIndex === photoDay) {
        return;
      }

      displayedIndex = photoDay;
      const src = "/images/countdown/" + pad(photoDay) + ".enc"
      const k  = await kdf('obscurity')
      const blob = await dec(src, k);

      dailyPhotoImageEl.src = URL.createObjectURL(blob);
    }

    dailyPhotoImageEl.addEventListener("load", function () {
      dailyPhotoEl.hidden = false;
    });

    dailyPhotoImageEl.addEventListener("error", function () {
      dailyPhotoEl.hidden = true;
    });

    async function updateCountdown() {
      var remaining = targetTime - Date.now();

      var days = Math.floor(remaining / dayMs);
      remaining -= days * dayMs;

      var hours = Math.floor(remaining / hourMs);
      remaining -= hours * hourMs;

      var minutes = Math.floor(remaining / minuteMs);
      remaining -= minutes * minuteMs;

      var seconds = Math.floor(remaining / secondMs);

      daysEl.textContent = String(days);
      hoursEl.textContent = pad(hours);
      minutesEl.textContent = pad(minutes);
      secondsEl.textContent = pad(seconds);
      await updateDailyPhoto();
      return true;
    }

    if (updateCountdown()) {
      window.setInterval(updateCountdown, 1000);
    }

    function spinCountdown() {
      timeEl.classList.remove("is-spinning");
      void timeEl.offsetWidth;
      timeEl.classList.add("is-spinning");

      audioPlayer.pause();
      audioPlayer.currentTime = 0;
      audioPlayer.src = audioClips[Math.floor(Math.random() * audioClips.length)];
      audioPlayer.play().catch(function () {});
    }

    timeEl.addEventListener("click", spinCountdown);
    timeEl.addEventListener("keydown", function (event) {
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault();
        spinCountdown();
      }
    });

  })();
</script>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><summary type="html"><![CDATA[Secret Countdown For Lilly’s eyes only ;)]]></summary></entry><entry><title type="html">Rafi’s Reading List: 2025</title><link href="http://rafibayer.com/2025/12/31/ReadingList2025.html" rel="alternate" type="text/html" title="Rafi’s Reading List: 2025" /><published>2025-12-31T00:00:00+00:00</published><updated>2025-12-31T00:00:00+00:00</updated><id>http://rafibayer.com/2025/12/31/ReadingList2025</id><content type="html" xml:base="http://rafibayer.com/2025/12/31/ReadingList2025.html"><![CDATA[<p>Yippee! Another year of reading, and I even made a real blog post since my last reading list. Check out <a href="/2025/12/13/Ants.html">ants-again</a> (unless you’re scared of bugs.)</p>

<p>Like last year, a <strong>*</strong> indicates I especially enjoyed a book. I’m trying to be a bit more conservative with the <strong>*</strong> instead of just giving it to every other book, but it’s so hard :)</p>

<ol>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/25499718-children-of-time">Children of Time</a> by Adrian Tchaikovsky</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/77566.Hyperion">Hyperion</a> by Dan Simmons</li>
  <li><a href="https://www.goodreads.com/book/show/77565.The_Fall_of_Hyperion">The Fall of Hyperion</a> by Dan Simmons</li>
  <li><a href="https://www.goodreads.com/book/show/102439.Rainbows_End">Rainbows End</a> by Vernor Vinge</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/28877.Red_Dragon">Red Dragon</a> by Thomas Harris</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/23807.The_Silence_of_the_Lambs">Silence of the Lambs</a> by Thomas Harris</li>
  <li><a href="https://www.goodreads.com/book/show/76620.Watership_Down">Watership Down</a> Richard Adams</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/135479.Cat_s_Cradle">Cat’s Cradle</a> by Kurt Vonnegut Jr.</li>
  <li><a href="https://www.goodreads.com/book/show/4982.The_Sirens_of_Titan">The Sirens of Titan</a> by Kurt Vonnegut Jr.</li>
  <li><a href="https://www.goodreads.com/book/show/50202953-piranesi">Piranesi</a> by Susanna Clarke</li>
  <li><a href="https://www.goodreads.com/book/show/415459.I_Have_No_Mouth_I_Must_Scream">I have no mouth and I must scream</a> by Harlan Ellison</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/40992.Shadow_Claw">The Shadow of the Torturer</a> by Gene Wolfe</li>
  <li><a href="https://www.goodreads.com/book/show/40992.Shadow_Claw">The Claw of the Conciliator</a> by Gene Wolfe</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/40995.Sword_Citadel">The Sword of the Lictor</a> by Gene Wolfe</li>
  <li><a href="https://www.goodreads.com/book/show/40995.Sword_Citadel">The Citadel of the Autarch</a> by Gene Wolfe</li>
  <li><a href="https://www.goodreads.com/book/show/540020.The_Day_of_the_Jackal">The Day of the Jackal</a> by Frederick Forsyth</li>
</ol>

<p>I am currently reading:</p>
<ol>
  <li><a href="https://www.goodreads.com/book/show/218453.The_Gripping_Hand">The Gripping Hand</a> by Jerry Pournelle and Larry Niven</li>
</ol>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="Reading" /><summary type="html"><![CDATA[Yippee! Another year of reading, and I even made a real blog post since my last reading list. Check out ants-again (unless you’re scared of bugs.)]]></summary></entry><entry><title type="html">Demo: ants, ants, ANTS!</title><link href="http://rafibayer.com/2025/12/13/Ants.html" rel="alternate" type="text/html" title="Demo: ants, ants, ANTS!" /><published>2025-12-13T00:00:00+00:00</published><updated>2025-12-13T00:00:00+00:00</updated><id>http://rafibayer.com/2025/12/13/Ants</id><content type="html" xml:base="http://rafibayer.com/2025/12/13/Ants.html"><![CDATA[<p></p>

<p><strong>Camera Controls</strong> (click game frame)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    Q/E - zoom in/out
    W/A/S/D - pan
</code></pre></div></div>

<iframe src="/assets/html/ants.html" width="800" height="600"></iframe>

<h1 id="ants-again">Ants (again)</h1>
<p><a href="https://github.com/rafibayer/ants-again"><code class="language-plaintext highlighter-rouge">ants-again</code></a> is an ant pheromone simulation written in Go and compiled for WASM using <a href="https://ebitengine.org/">ebitengine</a>.</p>

<p>The ‘again’ part of the name comes from the fact that this is not my first time attempting to simulate ants in this manner, but it’s the first that I’m happy enough with to share.</p>

<h2 id="what-am-i-looking-at">What am I looking at?</h2>
<p>Each colored speck moving around the screen represents an ant.</p>
<ul>
  <li>Green ants are ‘foraging’ ants searching for food</li>
  <li>Lilac ants are ‘returning’ ants holding food and looking for the hill</li>
</ul>

<p>The large white circle in the center represents the hill. And the brown bricks scattered about represent food sources.</p>

<h2 id="pheromones">Pheromones</h2>
<p>If you zoom in a bit (click the game frame, hold Q), you will also notice small fading points left behind by ants of the same color.</p>

<p>These points represent ‘pheromones’ that are left behind by ants, and fade in strength over time, eventually disappearing.</p>

<p>Pheromones are the only thing these simulated ants can sense; they are otherwise ‘blind’ to the world around them. They cannot sense food or the hill directly, and can only interact with them by bumping into them by chance.</p>

<p><img src="/images/ants/ants.png" alt="ants and pheromones" /></p>

<p><em>ants and some pheromones</em></p>

<h2 id="discovering-paths">Discovering paths</h2>
<p>Ants have one of two states. They remain in their current state until they accomplish their objective. Foraging ants (green) will become returning ants (lilac) when they encounter food. Returning ants will become foraging ants when they encounter the hill.</p>

<p>Ants drop and sense pheromone according to their state:</p>
<ul>
  <li>Foragers will drop foraging pheromone and sense returning pheromone</li>
  <li>Returners will drop returning pheromone and sense foraging pheromone</li>
</ul>

<p>This allows ants to work together to discover paths between the hill and food. When a forager finds food, it becomes a returner. The new returner senses pheromone left by other foragers to find its way back to the hill. As it returns to the hill, it leaves behind returning pheromone that the remaining foragers can follow to find the same food it found. As more and more ants discover the same path, the pheromones they leave behind reinforce the path. Since pheromones fade over time, shorter and busier paths get reinforced, while paths left behind by lost ants fade away.</p>

<h2 id="improving-ant-behavior">Improving ant behavior</h2>
<p>Getting the ants to converge on good paths quickly is a product of several tricks and optimizations. Many of these are inspired by real-world ant behavior.</p>

<h3 id="pheromone-weighting">Pheromone weighting</h3>
<p>When an ant senses pheromone, they only consider those in a fairly small area around them. This prevents them from getting distracted by far-away pheromones left by ants collecting other food sources.</p>

<p>In addition, ants are more strongly influenced by nearby pheromones, as well as pheromones that point in a direction more similar to their current heading.</p>

<h3 id="pheromone-gradient">Pheromone gradient</h3>
<p>These are fairly important behaviors because of the ‘pheromone gradient’. As a trail left by an ant starts to decay, the gradient points towards the ant. Pheromones left more recently are stronger, those left several seconds ago are almost gone. Another ant following this trail would then be more likely to follow the gradient, but this is not quite the effect that we want. If a forager follows directly behind a returner, they are likely walking towards the hill, not the food source the returner found. The same applies in reverse.</p>

<p>By applying more weight to closer, and more directionally similar pheromones, we make it more likely that an ant follows the trail in the direction we want.</p>

<h3 id="pheromone-inventory">Pheromone inventory</h3>
<p>To help ants forget bad paths, each ant has a limited ‘inventory’ of pheromone that they can drop. Once an ant has exhausted their inventory, they can’t drop any more pheromone until they accomplish their goal.</p>

<h2 id="pathological-outcomes">Pathological outcomes</h2>
<p>Finding a good balance between pheromone influence, decay, drop rate, and other parameters is challenging. If poorly selected, several pathological outcomes are possible.</p>

<h3 id="non-convergence">Non-convergence</h3>
<p>If ants are not sensitive enough to pheromones, if pheromones decay too fast, or if ants have a mis-sized sensing radius, it’s possible that they fail to effectively converge on a path. In this case, ants continue to wander aimlessly, only sometimes collecting food by random chance.</p>

<p><img src="/images/ants/nonconvergence.png" alt="non-convergence" /></p>

<h3 id="over-convergence">Over-convergence</h3>
<p>The opposite scenario is also possible. If ants are over-influenced by pheromone, they will follow paths too strictly, and fail to discover more optimal paths. An even worse failure mode exists where foraging and returning ants create a circular path that does not pass near food or the hill. In that scenario, they may follow each other in an endless loop and never accomplish their goal. This is largely mitigated by having a reasonably small pheromone inventory, but it is still possible that they discover a very long and inefficient path that eventually does result in food collection.</p>

<p><img src="/images/ants/overconvergence.png" alt="over-convergence" /></p>

<h1 id="implementing-and-optimizing">Implementing and optimizing</h1>
<p>ebitengine is an extremely minimal game engine. It exposes a simple interface to run and render games that may look familiar if you’ve done some game development before. An ebitengine game implements an interface that includes the methods <code class="language-plaintext highlighter-rouge">Update()</code> and <code class="language-plaintext highlighter-rouge">Draw()</code>. During <code class="language-plaintext highlighter-rouge">Update</code>, the game is expected to advance the simulation forward one step. This method is called by the engine every 1/60th of a second. Likewise, during <code class="language-plaintext highlighter-rouge">Draw</code>, the game is expected to render itself to the screen. This is also called every 1/60th of a second.</p>

<p>By separating <code class="language-plaintext highlighter-rouge">Update</code> and <code class="language-plaintext highlighter-rouge">Draw</code>, we can implement game-logic and rendering separately.</p>

<h2 id="rendering">Rendering</h2>
<p>I’ll touch on this first because it’s simpler. My <code class="language-plaintext highlighter-rouge">Draw</code> function is implemented as a set of loops that iterate over and draw elements of my game to the screen.</p>

<p>For example, <code class="language-plaintext highlighter-rouge">drawAnts</code>:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">g</span> <span class="o">*</span><span class="n">Game</span><span class="p">)</span> <span class="n">drawAnts</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">ant</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">g</span><span class="o">.</span><span class="n">ants</span> <span class="p">{</span>
        <span class="c">// `tail` point 5 units opposite the direction the ant is facing (`ant.dir`)</span>
        <span class="n">tail</span> <span class="o">:=</span> <span class="n">ant</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">ant</span><span class="o">.</span><span class="n">dir</span><span class="o">.</span><span class="n">Normalize</span><span class="p">()</span><span class="o">.</span><span class="n">Mul</span><span class="p">(</span><span class="o">-</span><span class="m">5</span><span class="p">))</span>

        <span class="c">// decide what color the ant should be based on ant.state</span>
        <span class="n">c</span> <span class="o">:=</span> <span class="n">GREEN</span>
        <span class="k">if</span> <span class="n">ant</span><span class="o">.</span><span class="n">state</span> <span class="o">==</span> <span class="n">RETURN</span> <span class="p">{</span>
            <span class="n">c</span> <span class="o">=</span> <span class="n">LILAC</span>
        <span class="p">}</span>

        <span class="c">// draw a line from ant position to tail. </span>
        <span class="n">vector</span><span class="o">.</span><span class="n">StrokeLine</span><span class="p">(</span>
            <span class="n">g</span><span class="o">.</span><span class="n">world</span><span class="p">,</span>                            <span class="c">// "world-space"</span>
            <span class="kt">float32</span><span class="p">(</span><span class="n">ant</span><span class="o">.</span><span class="n">X</span><span class="p">),</span> <span class="kt">float32</span><span class="p">(</span><span class="n">ant</span><span class="o">.</span><span class="n">Y</span><span class="p">),</span>     <span class="c">// from</span>
            <span class="kt">float32</span><span class="p">(</span><span class="n">tail</span><span class="o">.</span><span class="n">X</span><span class="p">),</span> <span class="kt">float32</span><span class="p">(</span><span class="n">tail</span><span class="o">.</span><span class="n">Y</span><span class="p">),</span>   <span class="c">// to</span>
            <span class="m">2</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="no">false</span><span class="p">)</span>                        <span class="c">// thickness, color, anti-aliasing</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Food sources and the hill are drawn in a similar manner. Pheromones are a bit different. Because the number of pheromones in the simulation can reach tens-of-thousands depending on parameters, drawing them is performance intensive.</p>

<p>The <code class="language-plaintext highlighter-rouge">vector</code> drawing package provided by ebitengine is sufficient for most things, but it requires an engine draw call every time it’s used. For pheromones we instead maintain a buffer representing every game pixel and render them all at once with a single call.</p>

<p>Using this API, each pixel in the game is represented by 4 bytes, one for each color channel (RGBA). If our game world is 1000x1000 pixels, this means our buffer needs to be <code class="language-plaintext highlighter-rouge">1000*1000*4</code> bytes, or 4MB. Not too bad.</p>

<p>Full disclosure, this was originally implemented with the help of ChatGPT.</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">g</span> <span class="o">*</span><span class="n">Game</span><span class="p">)</span> <span class="n">drawPheromones</span><span class="p">()</span> <span class="p">{</span>
    <span class="c">// Clear buffer to black (or background color)</span>
    <span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">g</span><span class="o">.</span><span class="n">px</span> <span class="p">{</span>
        <span class="n">g</span><span class="o">.</span><span class="n">px</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="m">0</span>
    <span class="p">}</span>

    <span class="n">writePheromones</span> <span class="o">:=</span> <span class="k">func</span><span class="p">(</span><span class="n">ph</span> <span class="n">spatial</span><span class="o">.</span><span class="n">Spatial</span><span class="p">[</span><span class="o">*</span><span class="n">Pheromone</span><span class="p">],</span> <span class="n">color</span> <span class="n">color</span><span class="o">.</span><span class="n">RGBA</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="n">pher</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">ph</span><span class="o">.</span><span class="n">PointsIter</span><span class="p">()</span> <span class="p">{</span>
            <span class="c">// Fade color by pheromone amount (0..1)</span>
            <span class="n">c</span> <span class="o">:=</span> <span class="n">Fade</span><span class="p">(</span><span class="n">color</span><span class="p">,</span> <span class="n">pher</span><span class="o">.</span><span class="n">amount</span><span class="p">)</span>

            <span class="n">x</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="n">pher</span><span class="o">.</span><span class="n">X</span><span class="p">)</span>
            <span class="n">y</span> <span class="o">:=</span> <span class="kt">int</span><span class="p">(</span><span class="n">pher</span><span class="o">.</span><span class="n">Y</span><span class="p">)</span>

            <span class="c">// don't bother rendering anything outside the bounds of the game</span>
            <span class="k">if</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="m">0</span> <span class="o">||</span> <span class="n">x</span> <span class="o">&gt;=</span> <span class="n">GAME_SIZE</span> <span class="o">||</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="m">0</span> <span class="o">||</span> <span class="n">y</span> <span class="o">&gt;=</span> <span class="n">GAME_SIZE</span> <span class="p">{</span>
                <span class="k">continue</span>
            <span class="p">}</span>

            <span class="n">idx</span> <span class="o">:=</span> <span class="m">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">y</span><span class="o">*</span><span class="n">GAME_SIZE</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span>
            <span class="n">g</span><span class="o">.</span><span class="n">px</span><span class="p">[</span><span class="n">idx</span><span class="o">+</span><span class="m">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">R</span>
            <span class="n">g</span><span class="o">.</span><span class="n">px</span><span class="p">[</span><span class="n">idx</span><span class="o">+</span><span class="m">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">G</span>
            <span class="n">g</span><span class="o">.</span><span class="n">px</span><span class="p">[</span><span class="n">idx</span><span class="o">+</span><span class="m">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">B</span>
            <span class="n">g</span><span class="o">.</span><span class="n">px</span><span class="p">[</span><span class="n">idx</span><span class="o">+</span><span class="m">3</span><span class="p">]</span> <span class="o">=</span> <span class="m">255</span> <span class="c">// alpha channel</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">writePheromones</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">foragingPheromone</span><span class="p">,</span> <span class="n">DARK_GREEN</span><span class="p">)</span>
    <span class="n">writePheromones</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">returningPheromone</span><span class="p">,</span> <span class="n">DARK_LILAC</span><span class="p">)</span>

    <span class="c">// Write the pixel buffer to the ebiten.Image once</span>
    <span class="n">g</span><span class="o">.</span><span class="n">world</span><span class="o">.</span><span class="n">WritePixels</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">px</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I also left an unused <code class="language-plaintext highlighter-rouge">naiveDrawPheromones</code> function in the source code that is implemented like our other <code class="language-plaintext highlighter-rouge">draw</code> functions. It does work well enough for smaller simulation sizes.</p>

<h3 id="camera">Camera</h3>

<p>I achieved the camera zoom/panning behavior by representing the game as a sub-image. When you adjust the camera, we update the transform applied to the game sub-image before drawing it to the screen. That also makes it easy to draw game elements in “world-space” without worrying about the “screen-space” size changing if I update the resolution or window size of the game.</p>

<p>That’s… pretty much it for rendering.</p>

<h2 id="simulation">Simulation</h2>

<p>The <code class="language-plaintext highlighter-rouge">Update</code> function takes a similar approach to <code class="language-plaintext highlighter-rouge">Draw</code>. Each element of the game has a loop in it that iterates over game elements and advances their state by a step.</p>

<h3 id="ants">Ants</h3>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">func</span> <span class="p">(</span><span class="n">g</span> <span class="o">*</span><span class="n">Game</span><span class="p">)</span> <span class="n">updateAnts</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">ant</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">g</span><span class="o">.</span><span class="n">ants</span> <span class="p">{</span>
        <span class="c">// move the ant forward</span>
        <span class="n">ant</span><span class="o">.</span><span class="n">Vector</span> <span class="o">=</span> <span class="n">ant</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">ant</span><span class="o">.</span><span class="n">dir</span><span class="o">.</span><span class="n">Normalize</span><span class="p">()</span><span class="o">.</span><span class="n">Mul</span><span class="p">(</span><span class="kt">float64</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">AntSpeed</span><span class="p">)))</span>

        <span class="c">// prevent the ant from escaping the boundary of the game</span>
        <span class="n">keepInbounds</span><span class="p">(</span><span class="n">ant</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">util</span><span class="o">.</span><span class="n">Chance</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">PheromoneSenseProb</span><span class="p">)</span> <span class="p">{</span>
           <span class="c">// ... smell nearby pheromones, update ant.dir ...</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">ant</span><span class="o">.</span><span class="n">state</span> <span class="o">==</span> <span class="n">FORAGE</span> <span class="p">{</span>
            <span class="c">// ... do foraging ant stuff, change to returning ant if we bump food ...</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">ant</span><span class="o">.</span><span class="n">state</span> <span class="o">==</span> <span class="n">RETURN</span> <span class="p">{</span>
           <span class="c">// ... do returning ant stuff, change to foraging ant if we bump hill ... </span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="n">ant</span><span class="o">.</span><span class="n">pheromoneStored</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="o">&amp;&amp;</span> <span class="n">util</span><span class="o">.</span><span class="n">Chance</span><span class="p">(</span><span class="n">g</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">PheromoneDropProb</span><span class="p">)</span> <span class="p">{</span>
            <span class="c">// ... drop some pheromone on the ground</span>
        <span class="p">}</span>

        <span class="c">// randomly turn a few degrees</span>
        <span class="n">ant</span><span class="o">.</span><span class="n">dir</span> <span class="o">=</span> <span class="n">ant</span><span class="o">.</span><span class="n">dir</span><span class="o">.</span><span class="n">Rotate</span><span class="p">(</span><span class="n">util</span><span class="o">.</span><span class="n">Rand</span><span class="p">(</span><span class="o">-</span><span class="n">g</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">AntRotation</span><span class="p">,</span> <span class="n">g</span><span class="o">.</span><span class="n">params</span><span class="o">.</span><span class="n">AntRotation</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="other-elements">Other elements</h3>
<p>Food, pheromone, and hills are less interesting. If food is exhausted, we remove it from the sim. Pheromone decays a bit each tick, we remove it when it reaches 0. The hill doesn’t even have an update function, it just sits there.</p>

<h3 id="spatial-hashing">Spatial hashing</h3>
<p>Lots of our game logic relies on us being able to find elements ‘nearby’. With a more heavy-weight game engine, there might be a built-in way to do this, but here we’re on our own. Remember that each tick, an ant needs to sense nearby pheromone and compute the impact on direction. If we have 1000 ants, and 10,000 pheromones, computing that the naive way would require a nested loop with 10,000,000 iterations every tick. That is not feasible if our target is running ~60 ticks per second.</p>

<p>Instead, anything that we need to search an area for is stored in a data structure called a spatial hash. In <code class="language-plaintext highlighter-rouge">ants-again</code>, pheromone, food sources, and the hill are all stored in spatial hashes. This data structure allows us to quickly find elements in a given area, without checking every single element.</p>

<p>A spatial hash works by storing elements in a set of ‘cells’. The ‘cell’ that an element belongs to is determined by a hash of its position (x,y coordinates) after dividing by ‘cell size’ and rounding down. If we want to find elements in the spatial hash within a given radius of a point, we only need to search cells that overlap with the circle formed by our point and radius. When we consider a given point, we discard it if the distance is outside of our circle.</p>

<p><img src="/images/ants/spatial_hash.png" alt="Spatial Hash" />
<em>source: <a href="https://leetless.de/posts/spatial-hashing-vs-ecs/">leetless</a></em></p>

<p>When deciding the cell size, there is a performance tradeoff. Choose too large, and you will have to iterate many elements that end up being discarded. Too small, and you have to search many cells. Generally, your cell size should be similar to the size of the queries you expect to be making.</p>

<h3 id="other-spatial-data-structures">Other spatial data structures</h3>
<p>Spatial hashes are just one of many <a href="https://en.wikipedia.org/wiki/Spatial_database#Spatial_index">spatial data structures</a>. Each one provides tradeoffs on the performance of inserting, querying, and updating. Early iterations of this project used a KD-Tree instead of a spatial hash, but it proved to be less performant and more complex. The <code class="language-plaintext highlighter-rouge">spatial</code> package provides the interface used within the game for interacting with spatial data structures. This makes it possible to experiment with alternative implementations.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Spatial</span><span class="p">[</span><span class="n">T</span> <span class="n">vector</span><span class="o">.</span><span class="n">Point</span><span class="p">]</span> <span class="k">interface</span> <span class="p">{</span>
    <span class="n">Insert</span><span class="p">(</span><span class="n">p</span> <span class="n">T</span><span class="p">)</span>
    <span class="n">PointsIter</span><span class="p">()</span> <span class="n">iter</span><span class="o">.</span><span class="n">Seq</span><span class="p">[</span><span class="n">T</span><span class="p">]</span>
    <span class="n">Remove</span><span class="p">(</span><span class="n">p</span> <span class="n">T</span><span class="p">)</span> <span class="n">T</span>
    <span class="n">RadialSearchIter</span><span class="p">(</span><span class="n">center</span> <span class="n">vector</span><span class="o">.</span><span class="n">Point</span><span class="p">,</span> <span class="n">radius</span> <span class="kt">float64</span><span class="p">)</span> <span class="n">iter</span><span class="o">.</span><span class="n">Seq</span><span class="p">[</span><span class="n">T</span><span class="p">]</span>
    <span class="n">Len</span><span class="p">()</span> <span class="kt">int</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="profiling">Profiling</h3>
<p>The Go standard-library has a package called <code class="language-plaintext highlighter-rouge">"runtime/pprof"</code> that makes it easy to collect <a href="https://github.com/google/pprof">pprof</a> profiles to analyze CPU, memory, and traces. <code class="language-plaintext highlighter-rouge">go tool pprof</code> can create <a href="https://graphviz.org/">graphviz</a> files with the <code class="language-plaintext highlighter-rouge">-dot</code> flag. I used this pretty extensively to identify CPU hot-spots. Unsurprisingly, garbage collection, and querying our spatial hashes are still by far the most time-consuming parts of our program.</p>

<p>Here is the output from ~30 seconds of simulation. (click to open visualizer)
<a href="https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%20%22ants-again.exe%22%20%7B%0D%0Anode%20%5Bstyle%3Dfilled%20fillcolor%3D%22%23f8f8f8%22%5D%0D%0Asubgraph%20cluster_L%20%7B%20%22File%3A%20ants-again.exe%22%20%5Bshape%3Dbox%20fontsize%3D16%20label%3D%22File%3A%20ants-again.exe%5ClBuild%20ID%3A%20C%3A%5C%5CUsers%5C%5CEasy_%5C%5CAppData%5C%5CLocal%5C%5CTemp%5C%5Cgo-build3579508113%5C%5Cb001%5C%5Cexe%5C%5Cants-again.exe2025-12-13%2016%3A09%3A47.478415%20-0800%20PST%5ClType%3A%20cpu%5ClTime%3A%202025-12-13%2016%3A09%3A48%20PST%5ClDuration%3A%2031.21s%2C%20Total%20samples%20%3D%2029.85s%20(95.65%25)%5ClShowing%20nodes%20accounting%20for%2026.60s%2C%2089.11%25%20of%2029.85s%20total%5ClDropped%20264%20nodes%20(cum%20%3C%3D%200.15s)%5ClDropped%2013%20edges%20(freq%20%3C%3D%200.03s)%5ClShowing%20top%2080%20nodes%20out%20of%20116%5Cl%5ClSee%20https%3A%2F%2Fgit.io%2FJfYMW%20for%20how%20to%20read%20the%20graph%5Cl%22%20tooltip%3D%22ants-again.exe%22%5D%20%7D%0D%0AN1%20%5Blabel%3D%22errgroup%5Cn(*Group)%5CnGo%5Cnfunc1%5Cn0%20of%2028.72s%20(96.21%25)%22%20id%3D%22node1%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22golang.org%2Fx%2Fsync%2Ferrgroup.(*Group).Go.func1%20(28.72s)%22%20color%3D%22%23b20200%22%20fillcolor%3D%22%23edd5d5%22%5D%0D%0AN2%20%5Blabel%3D%22runtime%5Cncgocall%5Cn20.04s%20(67.14%25)%5Cnof%2020.07s%20(67.24%25)%22%20id%3D%22node2%22%20fontsize%3D24%20shape%3Dbox%20tooltip%3D%22runtime.cgocall%20(20.07s)%22%20color%3D%22%23b21400%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN3%20%5Blabel%3D%22runtime%5Cnsyscall_syscalln%5Cn0%20of%2020.07s%20(67.24%25)%22%20id%3D%22node3%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22runtime.syscall_syscalln%20(20.07s)%22%20color%3D%22%23b21400%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN4%20%5Blabel%3D%22ui%5Cn(*context)%5CnupdateFrameImpl%5Cn0%20of%209.34s%20(31.29%25)%22%20id%3D%22node4%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20(9.34s)%22%20color%3D%22%23b23400%22%20fillcolor%3D%22%23eddcd5%22%5D%0D%0AN5%20%5Blabel%3D%22ebiten%5Cn(*Image)%5CnDrawImage%5Cn0.16s%20(0.54%25)%5Cnof%203.04s%20(10.18%25)%22%20id%3D%22node5%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(3.04s)%22%20color%3D%22%23b28457%22%20fillcolor%3D%22%23ede7e1%22%5D%0D%0AN6%20%5Blabel%3D%22main%5CndrawWorldSpace%5Cn0%20of%204.86s%20(16.28%25)%22%20id%3D%22node6%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22main.drawWorldSpace%20(4.86s)%22%20color%3D%22%23b25e21%22%20fillcolor%3D%22%23ede1d9%22%5D%0D%0AN7%20%5Blabel%3D%22graphicscommand%5Cn(*commandQueue)%5Cnflush%5Cn0%20of%2019.34s%20(64.79%25)%22%20id%3D%22node7%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20(19.34s)%22%20color%3D%22%23b21500%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN8%20%5Blabel%3D%22thread%5Cn(*OSThread)%5CnLoop%5Cnfunc1%5Cn0%20of%2020.18s%20(67.60%25)%22%20id%3D%22node8%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20(20.18s)%22%20color%3D%22%23b21300%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN9%20%5Blabel%3D%22spatial%5Cn(*Hash%5B*main%5CnPheromone%5D)%5CnRadialSearchIter%5Cn(*Hash%5Bgo%5Cnshape%5Cn*uint8%5D)%5CnRadialSearchIter%5Cnfunc1%5Cn1.27s%20(4.25%25)%5Cnof%202.94s%20(9.85%25)%22%20id%3D%22node9%22%20fontsize%3D13%20shape%3Dbox%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20(2.94s)%22%20color%3D%22%23b2865a%22%20fillcolor%3D%22%23ede7e1%22%5D%0D%0AN10%20%5Blabel%3D%22thread%5Cn(*OSThread)%5CnLoop%5Cn0%20of%2020.18s%20(67.60%25)%22%20id%3D%22node10%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20(20.18s)%22%20color%3D%22%23b21300%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN11%20%5Blabel%3D%22main%5Cn(*Game)%5CnupdateAnts%5Cn0.01s%20(0.034%25)%5Cnof%204.08s%20(13.67%25)%22%20id%3D%22node11%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22main.(*Game).updateAnts%20(4.08s)%22%20color%3D%22%23b26f38%22%20fillcolor%3D%22%23ede4dc%22%5D%0D%0AN12%20%5Blabel%3D%22syscall%5CnSyscall%5Cn0%20of%2018.53s%20(62.08%25)%22%20id%3D%22node12%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22syscall.Syscall%20(18.53s)%22%20color%3D%22%23b21700%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN13%20%5Blabel%3D%22runtime%5Cnmapaccess2%5Cn0.17s%20(0.57%25)%5Cnof%200.86s%20(2.88%25)%22%20id%3D%22node13%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22runtime.mapaccess2%20(0.86s)%22%20color%3D%22%23b2a998%22%20fillcolor%3D%22%23edebe9%22%5D%0D%0AN14%20%5Blabel%3D%22runtime%5Cnmemmove%5Cn0.92s%20(3.08%25)%22%20id%3D%22node14%22%20fontsize%3D12%20shape%3Dbox%20tooltip%3D%22runtime.memmove%20(0.92s)%22%20color%3D%22%23b2a896%22%20fillcolor%3D%22%23edebe9%22%5D%0D%0AN15%20%5Blabel%3D%22main%5Cn(*Game)%5CndrawPheromones%5Cn0%20of%201.59s%20(5.33%25)%22%20id%3D%22node15%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22main.(*Game).drawPheromones%20(1.59s)%22%20color%3D%22%23b29e82%22%20fillcolor%3D%22%23edeae6%22%5D%0D%0AN16%20%5Blabel%3D%22windows%5Cn(*LazyProc)%5CnCall%5Cn0%20of%200.70s%20(2.35%25)%22%20id%3D%22node16%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.70s)%22%20color%3D%22%23b2ab9d%22%20fillcolor%3D%22%23edecea%22%5D%0D%0AN17%20%5Blabel%3D%22graphicscommand%5Cn(*commandQueue)%5CnEnqueueDrawTrianglesCommand%5Cn0.07s%20(0.23%25)%5Cnof%200.77s%20(2.58%25)%22%20id%3D%22node17%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20(0.77s)%22%20color%3D%22%23b2aa9b%22%20fillcolor%3D%22%23edecea%22%5D%0D%0AN18%20%5Blabel%3D%22main%5Cn(*Game)%5CnupdateAnts-range1%5Cn0.48s%20(1.61%25)%5Cnof%200.98s%20(3.28%25)%22%20id%3D%22node18%22%20fontsize%3D11%20shape%3Dbox%20tooltip%3D%22main.(*Game).updateAnts-range1%20(0.98s)%22%20color%3D%22%23b2a795%22%20fillcolor%3D%22%23edebe9%22%5D%0D%0AN19%20%5Blabel%3D%22main%5Cn(*Game)%5CnDraw%5Cn0.01s%20(0.034%25)%5Cnof%205.08s%20(17.02%25)%22%20id%3D%22node19%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22main.(*Game).Draw%20(5.08s)%22%20color%3D%22%23b2591a%22%20fillcolor%3D%22%23ede1d8%22%5D%0D%0AN20%20%5Blabel%3D%22atlas%5Cn(*Image)%5CndrawTriangles%5Cn0.23s%20(0.77%25)%5Cnof%201.27s%20(4.25%25)%22%20id%3D%22node20%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).drawTriangles%20(1.27s)%22%20color%3D%22%23b2a38c%22%20fillcolor%3D%22%23edebe8%22%5D%0D%0AN21%20%5Blabel%3D%22main%5Cn(*Game)%5CnUpdate%5Cn0%20of%204.25s%20(14.24%25)%22%20id%3D%22node21%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22main.(*Game).Update%20(4.25s)%22%20color%3D%22%23b26b33%22%20fillcolor%3D%22%23ede3dc%22%5D%0D%0AN22%20%5Blabel%3D%22spatial%5Cn(*Hash%5B*main%5CnPheromone%5D)%5CnPointsIter%5Cn(*Hash%5Bgo%5Cnshape%5Cn*uint8%5D)%5CnPointsIter%5Cnfunc1%5Cn0.10s%20(0.34%25)%5Cnof%200.60s%20(2.01%25)%22%20id%3D%22node22%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(0.60s)%22%20color%3D%22%23b2aca0%22%20fillcolor%3D%22%23edecea%22%5D%0D%0AN23%20%5Blabel%3D%22windows%5Cn(*Proc)%5CnCall%5Cn0%20of%200.61s%20(2.04%25)%22%20id%3D%22node23%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20(0.61s)%22%20color%3D%22%23b2aca0%22%20fillcolor%3D%22%23edecea%22%5D%0D%0AN24%20%5Blabel%3D%22runtime%5Cnmallocgc%5Cn0.06s%20(0.2%25)%5Cnof%200.36s%20(1.21%25)%22%20id%3D%22node24%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22runtime.mallocgc%20(0.36s)%22%20color%3D%22%23b2afa7%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN25%20%5Blabel%3D%22runtime%5Cnmain%5Cn0%20of%200.83s%20(2.78%25)%22%20id%3D%22node25%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22runtime.main%20(0.83s)%22%20color%3D%22%23b2aa99%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN26%20%5Blabel%3D%22mipmap%5Cn(*Mipmap)%5CnDrawTriangles%5Cn0.12s%20(0.4%25)%5Cnof%201.71s%20(5.73%25)%22%20id%3D%22node26%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fmipmap.(*Mipmap).DrawTriangles%20(1.71s)%22%20color%3D%22%23b29c7f%22%20fillcolor%3D%22%23edeae6%22%5D%0D%0AN27%20%5Blabel%3D%22runtime%5CnmemclrNoHeapPointers%5Cn0.33s%20(1.11%25)%22%20id%3D%22node27%22%20fontsize%3D11%20shape%3Dbox%20tooltip%3D%22runtime.memclrNoHeapPointers%20(0.33s)%22%20color%3D%22%23b2afa8%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN28%20%5Blabel%3D%22ebiten%5Cn(*Image)%5CninvokeUsageCallbacks%5Cn0.14s%20(0.47%25)%5Cnof%200.60s%20(2.01%25)%22%20id%3D%22node28%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks%20(0.60s)%22%20color%3D%22%23b2aca0%22%20fillcolor%3D%22%23edecea%22%5D%0D%0AN29%20%5Blabel%3D%22buffered%5Cn(*Image)%5CnDrawTriangles%5Cn0.10s%20(0.34%25)%5Cnof%201.50s%20(5.03%25)%22%20id%3D%22node29%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).DrawTriangles%20(1.50s)%22%20color%3D%22%23b2a085%22%20fillcolor%3D%22%23edeae7%22%5D%0D%0AN30%20%5Blabel%3D%22ui%5Cn(*Image)%5CnDrawTriangles%5Cn0.07s%20(0.23%25)%5Cnof%201.80s%20(6.03%25)%22%20id%3D%22node30%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*Image).DrawTriangles%20(1.80s)%22%20color%3D%22%23b29b7c%22%20fillcolor%3D%22%23edeae5%22%5D%0D%0AN31%20%5Blabel%3D%22restorable%5Cn(*Image)%5CnDrawTriangles%5Cn0.10s%20(0.34%25)%5Cnof%200.97s%20(3.25%25)%22%20id%3D%22node31%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Frestorable.(*Image).DrawTriangles%20(0.97s)%22%20color%3D%22%23b2a895%22%20fillcolor%3D%22%23edebe9%22%5D%0D%0AN32%20%5Blabel%3D%22spatial%5Cn(*Hash%5B*main%5CnFood%5D)%5CnRadialSearchIter%5Cn(*Hash%5Bgo%5Cnshape%5Cn*uint8%5D)%5CnRadialSearchIter%5Cnfunc1%5Cn0.15s%20(0.5%25)%5Cnof%200.40s%20(1.34%25)%22%20id%3D%22node32%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20(0.40s)%22%20color%3D%22%23b2afa6%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN33%20%5Blabel%3D%22graphics%5CnadjustDestinationPixel%5Cn0.25s%20(0.84%25)%22%20id%3D%22node33%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.adjustDestinationPixel%20(0.25s)%22%20color%3D%22%23b2b0ab%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN34%20%5Blabel%3D%22vector%5CnVector%5CnCosineSimilarity%5Cn0.22s%20(0.74%25)%5Cnof%200.28s%20(0.94%25)%22%20id%3D%22node34%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fvector.Vector.CosineSimilarity%20(0.28s)%22%20color%3D%22%23b2b0aa%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN35%20%5Blabel%3D%22main%5Cn(*Game)%5CndrawPheromones%5Cn(*Game)%5CndrawPheromones%5Cnfunc1-range1%5Cn0.23s%20(0.77%25)%5Cnof%200.27s%20(0.9%25)%22%20id%3D%22node35%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22main.(*Game).drawPheromones.(*Game).drawPheromones.func1-range1%20(0.27s)%22%20color%3D%22%23b2b0aa%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN36%20%5Blabel%3D%22main%5Cn(*Game)%5CndrawAnts%5Cn0.02s%20(0.067%25)%5Cnof%201.95s%20(6.53%25)%22%20id%3D%22node36%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22main.(*Game).drawAnts%20(1.95s)%22%20color%3D%22%23b29878%22%20fillcolor%3D%22%23ede9e5%22%5D%0D%0AN37%20%5Blabel%3D%22syscall%5CnSyscall6%5Cn0%20of%200.32s%20(1.07%25)%22%20id%3D%22node37%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22syscall.Syscall6%20(0.32s)%22%20color%3D%22%23b2b0a8%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN38%20%5Blabel%3D%22aeshashbody%5Cn0.22s%20(0.74%25)%22%20id%3D%22node38%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22aeshashbody%20(0.22s)%22%20color%3D%22%23b2b0ab%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN39%20%5Blabel%3D%22runtime%5Cnstdcall1%5Cn0.18s%20(0.6%25)%22%20id%3D%22node39%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22runtime.stdcall1%20(0.18s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN40%20%5Blabel%3D%22glfw%5CnplatformPollEvents%5Cn0.01s%20(0.034%25)%5Cnof%200.35s%20(1.17%25)%22%20id%3D%22node40%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.platformPollEvents%20(0.35s)%22%20color%3D%22%23b2afa8%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN41%20%5Blabel%3D%22runtime%5Cnnewobject%5Cn0.06s%20(0.2%25)%5Cnof%200.30s%20(1.01%25)%22%20id%3D%22node41%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22runtime.newobject%20(0.30s)%22%20color%3D%22%23b2b0a9%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN42%20%5Blabel%3D%22shaderir%5Cn(*Program)%5CnFilterUniformVariables%5Cn0.20s%20(0.67%25)%22%20id%3D%22node42%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fshaderir.(*Program).FilterUniformVariables%20(0.20s)%22%20color%3D%22%23b2b1ac%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN43%20%5Blabel%3D%22ebiten%5Cn(*Image)%5CninvokeUsageCallbacks%5Cnfunc1%5Cn0.15s%20(0.5%25)%5Cnof%200.24s%20(0.8%25)%22%20id%3D%22node43%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks.func1%20(0.24s)%22%20color%3D%22%23b2b0ab%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN44%20%5Blabel%3D%22maps%5CnctrlGroup%5CnmatchH2%5Cn0.17s%20(0.57%25)%22%20id%3D%22node44%22%20fontsize%3D10%20shape%3Dbox%20tooltip%3D%22internal%2Fruntime%2Fmaps.ctrlGroup.matchH2%20(0.17s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23ededec%22%5D%0D%0AN45%20%5Blabel%3D%22runtime%5Cnsystemstack%5Cn0%20of%200.17s%20(0.57%25)%22%20id%3D%22node45%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22runtime.systemstack%20(0.17s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23ededec%22%5D%0D%0AN46%20%5Blabel%3D%22runtime%5Cnmemhash128%5Cn0.06s%20(0.2%25)%5Cnof%200.29s%20(0.97%25)%22%20id%3D%22node46%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22runtime.memhash128%20(0.29s)%22%20color%3D%22%23b2b0a9%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN47%20%5Blabel%3D%22runtime%5CnmallocgcSmallScanNoHeader%5Cn0.02s%20(0.067%25)%5Cnof%200.16s%20(0.54%25)%22%20id%3D%22node47%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22runtime.mallocgcSmallScanNoHeader%20(0.16s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23ededec%22%5D%0D%0AN48%20%5Blabel%3D%22graphics%5CnQuadVerticesFromSrcAndMatrix%5Cn0.05s%20(0.17%25)%5Cnof%200.30s%20(1.01%25)%22%20id%3D%22node48%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.QuadVerticesFromSrcAndMatrix%20(0.30s)%22%20color%3D%22%23b2b0a9%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN49%20%5Blabel%3D%22spatial%5Cn(*Hash%5B*main%5CnFood%5D)%5CnPointsIter%5Cn(*Hash%5Bgo%5Cnshape%5Cn*uint8%5D)%5CnPointsIter%5Cnfunc1%5Cn0.01s%20(0.034%25)%5Cnof%201.30s%20(4.36%25)%22%20id%3D%22node49%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(1.30s)%22%20color%3D%22%23b2a38b%22%20fillcolor%3D%22%23edebe7%22%5D%0D%0AN50%20%5Blabel%3D%22graphicscommand%5Cn(*drawTrianglesCommand)%5CnCanMergeWithDrawTrianglesCommand%5Cn0.06s%20(0.2%25)%5Cnof%200.18s%20(0.6%25)%22%20id%3D%22node50%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*drawTrianglesCommand).CanMergeWithDrawTrianglesCommand%20(0.18s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN51%20%5Blabel%3D%22graphicscommand%5Cn(*Image)%5CnDrawTriangles%5Cn0.03s%20(0.1%25)%5Cnof%200.87s%20(2.91%25)%22%20id%3D%22node51%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*Image).DrawTriangles%20(0.87s)%22%20color%3D%22%23b2a998%22%20fillcolor%3D%22%23edebe9%22%5D%0D%0AN52%20%5Blabel%3D%22graphicscommand%5Cn(*commandQueueManager)%5CnenqueueDrawTrianglesCommand%5Cn0.03s%20(0.1%25)%5Cnof%200.80s%20(2.68%25)%22%20id%3D%22node52%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueueManager).enqueueDrawTrianglesCommand%20(0.80s)%22%20color%3D%22%23b2aa9a%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN53%20%5Blabel%3D%22ui%5Cn(*UserInterface)%5CnupdateInputStateForFrameImpl%5Cn0%20of%200.20s%20(0.67%25)%22%20id%3D%22node53%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).updateInputStateForFrameImpl%20(0.20s)%22%20color%3D%22%23b2b1ac%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN54%20%5Blabel%3D%22vector%5CnStrokeLine%5Cn0.02s%20(0.067%25)%5Cnof%201.86s%20(6.23%25)%22%20id%3D%22node54%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.StrokeLine%20(1.86s)%22%20color%3D%22%23b29a7a%22%20fillcolor%3D%22%23ede9e5%22%5D%0D%0AN55%20%5Blabel%3D%22graphicscommand%5Cn(*commandQueue)%5CnprependPreservedUniforms%5Cn0%20of%200.19s%20(0.64%25)%22%20id%3D%22node55%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).prependPreservedUniforms%20(0.19s)%22%20color%3D%22%23b2b1ac%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN56%20%5Blabel%3D%22main%5Cn(*Game)%5CndrawFood-range1%5Cn0.01s%20(0.034%25)%5Cnof%201.28s%20(4.29%25)%22%20id%3D%22node56%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22main.(*Game).drawFood-range1%20(1.28s)%22%20color%3D%22%23b2a38c%22%20fillcolor%3D%22%23edebe8%22%5D%0D%0AN57%20%5Blabel%3D%22atlas%5Cn(*Image)%5CnDrawTriangles%5Cn0.02s%20(0.067%25)%5Cnof%201.38s%20(4.62%25)%22%20id%3D%22node57%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).DrawTriangles%20(1.38s)%22%20color%3D%22%23b2a289%22%20fillcolor%3D%22%23edeae7%22%5D%0D%0AN58%20%5Blabel%3D%22vector%5CnFillRect%5Cn0.02s%20(0.067%25)%5Cnof%201.25s%20(4.19%25)%22%20id%3D%22node58%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.FillRect%20(1.25s)%22%20color%3D%22%23b2a48d%22%20fillcolor%3D%22%23edebe8%22%5D%0D%0AN59%20%5Blabel%3D%22ui%5Cn(*UserInterface)%5CnloopGame%5Cn0.01s%20(0.034%25)%5Cnof%209.36s%20(31.36%25)%22%20id%3D%22node59%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).loopGame%20(9.36s)%22%20color%3D%22%23b23400%22%20fillcolor%3D%22%23eddcd5%22%5D%0D%0AN60%20%5Blabel%3D%22ui%5Cn(*UserInterface)%5Cnupdate%5Cn0%20of%200.37s%20(1.24%25)%22%20id%3D%22node60%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).update%20(0.37s)%22%20color%3D%22%23b2afa7%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN61%20%5Blabel%3D%22atlas%5Cn(*Image)%5CnwritePixels%5Cnfunc2%5Cn0.01s%20(0.034%25)%5Cnof%200.81s%20(2.71%25)%22%20id%3D%22node61%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels.func2%20(0.81s)%22%20color%3D%22%23b2aa9a%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN62%20%5Blabel%3D%22graphicscommand%5Cn(*writePixelsCommand)%5CnExec%5Cn0%20of%201.09s%20(3.65%25)%22%20id%3D%22node62%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*writePixelsCommand).Exec%20(1.09s)%22%20color%3D%22%23b2a691%22%20fillcolor%3D%22%23edebe8%22%5D%0D%0AN63%20%5Blabel%3D%22runtime%5Cnnotewakeup%5Cn0.01s%20(0.034%25)%5Cnof%200.17s%20(0.57%25)%22%20id%3D%22node63%22%20fontsize%3D9%20shape%3Dbox%20tooltip%3D%22runtime.notewakeup%20(0.17s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23ededec%22%5D%0D%0AN64%20%5Blabel%3D%22runtime%5Cnsemawakeup%5Cn0%20of%200.17s%20(0.57%25)%22%20id%3D%22node64%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22runtime.semawakeup%20(0.17s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23ededec%22%5D%0D%0AN65%20%5Blabel%3D%22ebiten%5Cn(*Image)%5CnWritePixels%5Cn0%20of%200.82s%20(2.75%25)%22%20id%3D%22node65%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).WritePixels%20(0.82s)%22%20color%3D%22%23b2aa99%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN66%20%5Blabel%3D%22ebiten%5Cn(*gameForUI)%5CnDrawOffscreen%5Cn0%20of%205.08s%20(17.02%25)%22%20id%3D%22node66%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).DrawOffscreen%20(5.08s)%22%20color%3D%22%23b2591a%22%20fillcolor%3D%22%23ede1d8%22%5D%0D%0AN67%20%5Blabel%3D%22ebiten%5Cn(*gameForUI)%5CnUpdate%5Cn0%20of%204.25s%20(14.24%25)%22%20id%3D%22node67%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).Update%20(4.25s)%22%20color%3D%22%23b26b33%22%20fillcolor%3D%22%23ede3dc%22%5D%0D%0AN68%20%5Blabel%3D%22ebiten%5CnRunGame%5Cn0%20of%200.83s%20(2.78%25)%22%20id%3D%22node68%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGame%20(0.83s)%22%20color%3D%22%23b2aa99%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN69%20%5Blabel%3D%22ebiten%5CnRunGameWithOptions%5Cn0%20of%200.83s%20(2.78%25)%22%20id%3D%22node69%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGameWithOptions%20(0.83s)%22%20color%3D%22%23b2aa99%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN70%20%5Blabel%3D%22ebitenutil%5CnDebugPrint%5Cn0%20of%200.20s%20(0.67%25)%22%20id%3D%22node70%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrint%20(0.20s)%22%20color%3D%22%23b2b1ac%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN71%20%5Blabel%3D%22ebitenutil%5CnDebugPrintAt%5Cn0%20of%200.20s%20(0.67%25)%22%20id%3D%22node71%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrintAt%20(0.20s)%22%20color%3D%22%23b2b1ac%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN72%20%5Blabel%3D%22ebitenutil%5CndrawDebugText%5Cn0%20of%200.20s%20(0.67%25)%22%20id%3D%22node72%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.drawDebugText%20(0.20s)%22%20color%3D%22%23b2b1ac%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN73%20%5Blabel%3D%22atlas%5Cn(*Image)%5CnWritePixels%5Cn0%20of%200.81s%20(2.71%25)%22%20id%3D%22node73%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).WritePixels%20(0.81s)%22%20color%3D%22%23b2aa9a%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN74%20%5Blabel%3D%22atlas%5Cn(*Image)%5CnwritePixels%5Cn0%20of%200.81s%20(2.71%25)%22%20id%3D%22node74%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels%20(0.81s)%22%20color%3D%22%23b2aa9a%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN75%20%5Blabel%3D%22buffered%5Cn(*Image)%5CnWritePixels%5Cn0%20of%200.81s%20(2.71%25)%22%20id%3D%22node75%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).WritePixels%20(0.81s)%22%20color%3D%22%23b2aa9a%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN76%20%5Blabel%3D%22glfw%5CnPollEvents%5Cn0%20of%200.35s%20(1.17%25)%22%20id%3D%22node76%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.PollEvents%20(0.35s)%22%20color%3D%22%23b2afa8%22%20fillcolor%3D%22%23edeceb%22%5D%0D%0AN77%20%5Blabel%3D%22glfw%5Cn_GetKeyState%5Cn0%20of%200.18s%20(0.6%25)%22%20id%3D%22node77%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw._GetKeyState%20(0.18s)%22%20color%3D%22%23b2b1ad%22%20fillcolor%3D%22%23edecec%22%5D%0D%0AN78%20%5Blabel%3D%22graphics%5CnNewManagedBytes%5Cn0%20of%200.81s%20(2.71%25)%22%20id%3D%22node78%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.NewManagedBytes%20(0.81s)%22%20color%3D%22%23b2aa9a%22%20fillcolor%3D%22%23edece9%22%5D%0D%0AN79%20%5Blabel%3D%22graphicscommand%5Cn(*commandQueue)%5CnFlush%5Cnfunc1%5Cn0%20of%2019.34s%20(64.79%25)%22%20id%3D%22node79%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).Flush.func1%20(19.34s)%22%20color%3D%22%23b21500%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN80%20%5Blabel%3D%22graphicscommand%5Cn(*commandQueue)%5Cnflush%5Cnfunc1%5Cn0%20of%2018.11s%20(60.67%25)%22%20id%3D%22node80%22%20fontsize%3D8%20shape%3Dbox%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush.func1%20(18.11s)%22%20color%3D%22%23b21800%22%20fillcolor%3D%22%23edd8d5%22%5D%0D%0AN10%20-%3E%20N8%20%5Blabel%3D%22%2020.18s%22%20weight%3D68%20penwidth%3D4%20color%3D%22%23b21300%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20(20.18s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20(20.18s)%22%5D%0D%0AN3%20-%3E%20N2%20%5Blabel%3D%22%2020.07s%22%20weight%3D68%20penwidth%3D4%20color%3D%22%23b21400%22%20tooltip%3D%22runtime.syscall_syscalln%20-%3E%20runtime.cgocall%20(20.07s)%22%20labeltooltip%3D%22runtime.syscall_syscalln%20-%3E%20runtime.cgocall%20(20.07s)%22%5D%0D%0AN1%20-%3E%20N10%20%5Blabel%3D%22%2019.35s%22%20weight%3D65%20penwidth%3D4%20color%3D%22%23b21500%22%20tooltip%3D%22golang.org%2Fx%2Fsync%2Ferrgroup.(*Group).Go.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20(19.35s)%22%20labeltooltip%3D%22golang.org%2Fx%2Fsync%2Ferrgroup.(*Group).Go.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20(19.35s)%22%20style%3D%22dotted%22%5D%0D%0AN79%20-%3E%20N7%20%5Blabel%3D%22%2019.34s%22%20weight%3D65%20penwidth%3D4%20color%3D%22%23b21500%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).Flush.func1%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20(19.34s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).Flush.func1%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20(19.34s)%22%5D%0D%0AN8%20-%3E%20N79%20%5Blabel%3D%22%2019.34s%22%20weight%3D65%20penwidth%3D4%20color%3D%22%23b21500%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).Flush.func1%20(19.34s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).Flush.func1%20(19.34s)%22%5D%0D%0AN12%20-%3E%20N3%20%5Blabel%3D%22%2018.53s%22%20weight%3D63%20penwidth%3D4%20color%3D%22%23b21700%22%20tooltip%3D%22syscall.Syscall%20-%3E%20runtime.syscall_syscalln%20(18.53s)%22%20labeltooltip%3D%22syscall.Syscall%20-%3E%20runtime.syscall_syscalln%20(18.53s)%22%5D%0D%0AN7%20-%3E%20N80%20%5Blabel%3D%22%2018.11s%22%20weight%3D61%20penwidth%3D4%20color%3D%22%23b21800%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush.func1%20(18.11s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush.func1%20(18.11s)%22%5D%0D%0AN80%20-%3E%20N12%20%5Blabel%3D%22%2018.11s%22%20weight%3D61%20penwidth%3D4%20color%3D%22%23b21800%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush.func1%20...%20syscall.Syscall%20(18.11s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush.func1%20...%20syscall.Syscall%20(18.11s)%22%20style%3D%22dotted%22%5D%0D%0AN1%20-%3E%20N59%20%5Blabel%3D%22%209.36s%22%20weight%3D32%20penwidth%3D2%20color%3D%22%23b23400%22%20tooltip%3D%22golang.org%2Fx%2Fsync%2Ferrgroup.(*Group).Go.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).loopGame%20(9.36s)%22%20labeltooltip%3D%22golang.org%2Fx%2Fsync%2Ferrgroup.(*Group).Go.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).loopGame%20(9.36s)%22%20style%3D%22dotted%22%5D%0D%0AN59%20-%3E%20N4%20%5Blabel%3D%22%209.34s%22%20weight%3D32%20penwidth%3D2%20color%3D%22%23b23400%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).loopGame%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20(9.34s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).loopGame%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20(9.34s)%22%20style%3D%22dotted%22%5D%0D%0AN66%20-%3E%20N19%20%5Blabel%3D%22%205.08s%22%20weight%3D18%20color%3D%22%23b2591a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).DrawOffscreen%20-%3E%20main.(*Game).Draw%20(5.08s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).DrawOffscreen%20-%3E%20main.(*Game).Draw%20(5.08s)%22%5D%0D%0AN4%20-%3E%20N66%20%5Blabel%3D%22%205.08s%22%20weight%3D18%20color%3D%22%23b2591a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).DrawOffscreen%20(5.08s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).DrawOffscreen%20(5.08s)%22%20style%3D%22dotted%22%5D%0D%0AN19%20-%3E%20N6%20%5Blabel%3D%22%204.86s%22%20weight%3D17%20color%3D%22%23b25e21%22%20tooltip%3D%22main.(*Game).Draw%20-%3E%20main.drawWorldSpace%20(4.86s)%22%20labeltooltip%3D%22main.(*Game).Draw%20-%3E%20main.drawWorldSpace%20(4.86s)%22%5D%0D%0AN67%20-%3E%20N21%20%5Blabel%3D%22%204.25s%22%20weight%3D15%20color%3D%22%23b26b33%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).Update%20-%3E%20main.(*Game).Update%20(4.25s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).Update%20-%3E%20main.(*Game).Update%20(4.25s)%22%5D%0D%0AN4%20-%3E%20N67%20%5Blabel%3D%22%204.25s%22%20weight%3D15%20color%3D%22%23b26b33%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).Update%20(4.25s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*context).updateFrameImpl%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*gameForUI).Update%20(4.25s)%22%5D%0D%0AN21%20-%3E%20N11%20%5Blabel%3D%22%204.08s%22%20weight%3D14%20color%3D%22%23b26f38%22%20tooltip%3D%22main.(*Game).Update%20-%3E%20main.(*Game).updateAnts%20(4.08s)%22%20labeltooltip%3D%22main.(*Game).Update%20-%3E%20main.(*Game).updateAnts%20(4.08s)%22%5D%0D%0AN11%20-%3E%20N9%20%5Blabel%3D%22%202.94s%22%20weight%3D10%20color%3D%22%23b2865a%22%20tooltip%3D%22main.(*Game).updateAnts%20-%3E%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20(2.94s)%22%20labeltooltip%3D%22main.(*Game).updateAnts%20-%3E%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20(2.94s)%22%5D%0D%0AN6%20-%3E%20N36%20%5Blabel%3D%22%201.95s%22%20weight%3D7%20color%3D%22%23b29878%22%20tooltip%3D%22main.drawWorldSpace%20-%3E%20main.(*Game).drawAnts%20(1.95s)%22%20labeltooltip%3D%22main.drawWorldSpace%20-%3E%20main.(*Game).drawAnts%20(1.95s)%22%5D%0D%0AN36%20-%3E%20N54%20%5Blabel%3D%22%201.86s%22%20weight%3D7%20color%3D%22%23b29a7a%22%20tooltip%3D%22main.(*Game).drawAnts%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.StrokeLine%20(1.86s)%22%20labeltooltip%3D%22main.(*Game).drawAnts%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.StrokeLine%20(1.86s)%22%5D%0D%0AN5%20-%3E%20N30%20%5Blabel%3D%22%201.79s%22%20weight%3D6%20color%3D%22%23b29b7c%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*Image).DrawTriangles%20(1.79s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*Image).DrawTriangles%20(1.79s)%22%5D%0D%0AN30%20-%3E%20N26%20%5Blabel%3D%22%201.71s%22%20weight%3D6%20color%3D%22%23b29c7f%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fmipmap.(*Mipmap).DrawTriangles%20(1.71s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fmipmap.(*Mipmap).DrawTriangles%20(1.71s)%22%5D%0D%0AN54%20-%3E%20N5%20%5Blabel%3D%22%201.63s%22%20weight%3D6%20color%3D%22%23b29e81%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.StrokeLine%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(1.63s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.StrokeLine%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(1.63s)%22%5D%0D%0AN6%20-%3E%20N15%20%5Blabel%3D%22%201.59s%22%20weight%3D6%20color%3D%22%23b29e82%22%20tooltip%3D%22main.drawWorldSpace%20-%3E%20main.(*Game).drawPheromones%20(1.59s)%22%20labeltooltip%3D%22main.drawWorldSpace%20-%3E%20main.(*Game).drawPheromones%20(1.59s)%22%5D%0D%0AN26%20-%3E%20N29%20%5Blabel%3D%22%201.50s%22%20weight%3D6%20color%3D%22%23b2a085%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fmipmap.(*Mipmap).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).DrawTriangles%20(1.50s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fmipmap.(*Mipmap).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).DrawTriangles%20(1.50s)%22%5D%0D%0AN29%20-%3E%20N57%20%5Blabel%3D%22%201.38s%22%20weight%3D5%20color%3D%22%23b2a289%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).DrawTriangles%20(1.38s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).DrawTriangles%20(1.38s)%22%5D%0D%0AN49%20-%3E%20N56%20%5Blabel%3D%22%201.28s%22%20weight%3D5%20color%3D%22%23b2a38c%22%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20-%3E%20main.(*Game).drawFood-range1%20(1.28s)%22%20labeltooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20-%3E%20main.(*Game).drawFood-range1%20(1.28s)%22%5D%0D%0AN6%20-%3E%20N49%20%5Blabel%3D%22%201.28s%22%20weight%3D5%20color%3D%22%23b2a38c%22%20tooltip%3D%22main.drawWorldSpace%20...%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(1.28s)%22%20labeltooltip%3D%22main.drawWorldSpace%20...%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(1.28s)%22%20style%3D%22dotted%22%5D%0D%0AN57%20-%3E%20N20%20%5Blabel%3D%22%201.27s%22%20weight%3D5%20color%3D%22%23b2a38c%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).drawTriangles%20(1.27s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).drawTriangles%20(1.27s)%22%5D%0D%0AN56%20-%3E%20N58%20%5Blabel%3D%22%201.25s%22%20weight%3D5%20color%3D%22%23b2a48d%22%20tooltip%3D%22main.(*Game).drawFood-range1%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.FillRect%20(1.25s)%22%20labeltooltip%3D%22main.(*Game).drawFood-range1%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.FillRect%20(1.25s)%22%5D%0D%0AN58%20-%3E%20N5%20%5Blabel%3D%22%201.21s%22%20weight%3D5%20color%3D%22%23b2a48e%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.FillRect%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(1.21s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Fvector.FillRect%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(1.21s)%22%5D%0D%0AN7%20-%3E%20N62%20%5Blabel%3D%22%201.09s%22%20weight%3D4%20color%3D%22%23b2a691%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*writePixelsCommand).Exec%20(1.09s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*writePixelsCommand).Exec%20(1.09s)%22%5D%0D%0AN62%20-%3E%20N3%20%5Blabel%3D%22%201.08s%22%20weight%3D4%20color%3D%22%23b2a692%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*writePixelsCommand).Exec%20...%20runtime.syscall_syscalln%20(1.08s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*writePixelsCommand).Exec%20...%20runtime.syscall_syscalln%20(1.08s)%22%20style%3D%22dotted%22%5D%0D%0AN9%20-%3E%20N18%20%5Blabel%3D%22%200.98s%22%20weight%3D4%20color%3D%22%23b2a795%22%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20-%3E%20main.(*Game).updateAnts-range1%20(0.98s)%22%20labeltooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20-%3E%20main.(*Game).updateAnts-range1%20(0.98s)%22%5D%0D%0AN20%20-%3E%20N31%20%5Blabel%3D%22%200.97s%22%20weight%3D4%20color%3D%22%23b2a895%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).drawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Frestorable.(*Image).DrawTriangles%20(0.97s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).drawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Frestorable.(*Image).DrawTriangles%20(0.97s)%22%5D%0D%0AN31%20-%3E%20N51%20%5Blabel%3D%22%200.87s%22%20weight%3D3%20color%3D%22%23b2a998%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Frestorable.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*Image).DrawTriangles%20(0.87s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Frestorable.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*Image).DrawTriangles%20(0.87s)%22%5D%0D%0AN68%20-%3E%20N69%20%5Blabel%3D%22%200.83s%22%20weight%3D3%20color%3D%22%23b2aa99%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGame%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGameWithOptions%20(0.83s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGame%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGameWithOptions%20(0.83s)%22%5D%0D%0AN69%20-%3E%20N10%20%5Blabel%3D%22%200.83s%22%20weight%3D3%20color%3D%22%23b2aa99%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGameWithOptions%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20(0.83s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGameWithOptions%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop%20(0.83s)%22%20style%3D%22dotted%22%5D%0D%0AN25%20-%3E%20N68%20%5Blabel%3D%22%200.83s%5Cn%20(inline)%22%20weight%3D3%20color%3D%22%23b2aa99%22%20tooltip%3D%22runtime.main%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGame%20(0.83s)%22%20labeltooltip%3D%22runtime.main%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2.RunGame%20(0.83s)%22%20style%3D%22dotted%22%5D%0D%0AN15%20-%3E%20N65%20%5Blabel%3D%22%200.82s%22%20weight%3D3%20color%3D%22%23b2aa99%22%20tooltip%3D%22main.(*Game).drawPheromones%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).WritePixels%20(0.82s)%22%20labeltooltip%3D%22main.(*Game).drawPheromones%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).WritePixels%20(0.82s)%22%5D%0D%0AN65%20-%3E%20N75%20%5Blabel%3D%22%200.81s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).WritePixels%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).WritePixels%20(0.81s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).WritePixels%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).WritePixels%20(0.81s)%22%20style%3D%22dotted%22%5D%0D%0AN73%20-%3E%20N74%20%5Blabel%3D%22%200.81s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).WritePixels%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels%20(0.81s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).WritePixels%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels%20(0.81s)%22%5D%0D%0AN74%20-%3E%20N78%20%5Blabel%3D%22%200.81s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.NewManagedBytes%20(0.81s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.NewManagedBytes%20(0.81s)%22%5D%0D%0AN75%20-%3E%20N73%20%5Blabel%3D%22%200.81s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).WritePixels%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).WritePixels%20(0.81s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fbuffered.(*Image).WritePixels%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).WritePixels%20(0.81s)%22%5D%0D%0AN78%20-%3E%20N61%20%5Blabel%3D%22%200.81s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.NewManagedBytes%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels.func2%20(0.81s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.NewManagedBytes%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels.func2%20(0.81s)%22%5D%0D%0AN51%20-%3E%20N52%20%5Blabel%3D%22%200.80s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueueManager).enqueueDrawTrianglesCommand%20(0.80s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*Image).DrawTriangles%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueueManager).enqueueDrawTrianglesCommand%20(0.80s)%22%5D%0D%0AN61%20-%3E%20N14%20%5Blabel%3D%22%200.79s%22%20weight%3D3%20color%3D%22%23b2aa9a%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels.func2%20-%3E%20runtime.memmove%20(0.79s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fatlas.(*Image).writePixels.func2%20-%3E%20runtime.memmove%20(0.79s)%22%5D%0D%0AN52%20-%3E%20N17%20%5Blabel%3D%22%200.77s%22%20weight%3D3%20color%3D%22%23b2aa9b%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueueManager).enqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20(0.77s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueueManager).enqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20(0.77s)%22%5D%0D%0AN16%20-%3E%20N23%20%5Blabel%3D%22%200.61s%22%20weight%3D3%20color%3D%22%23b2aca0%22%20tooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20-%3E%20golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20(0.61s)%22%20labeltooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20-%3E%20golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20(0.61s)%22%5D%0D%0AN5%20-%3E%20N28%20%5Blabel%3D%22%200.60s%22%20weight%3D3%20color%3D%22%23b2aca0%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks%20(0.60s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks%20(0.60s)%22%5D%0D%0AN9%20-%3E%20N13%20%5Blabel%3D%22%200.52s%22%20weight%3D2%20color%3D%22%23b2ada2%22%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20-%3E%20runtime.mapaccess2%20(0.52s)%22%20labeltooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20-%3E%20runtime.mapaccess2%20(0.52s)%22%5D%0D%0AN15%20-%3E%20N22%20%5Blabel%3D%22%200.45s%22%20weight%3D2%20color%3D%22%23b2aea5%22%20tooltip%3D%22main.(*Game).drawPheromones%20...%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(0.45s)%22%20labeltooltip%3D%22main.(*Game).drawPheromones%20...%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(0.45s)%22%20style%3D%22dotted%22%5D%0D%0AN11%20-%3E%20N32%20%5Blabel%3D%22%200.40s%22%20weight%3D2%20color%3D%22%23b2afa6%22%20tooltip%3D%22main.(*Game).updateAnts%20-%3E%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20(0.40s)%22%20labeltooltip%3D%22main.(*Game).updateAnts%20-%3E%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20(0.40s)%22%5D%0D%0AN23%20-%3E%20N12%20%5Blabel%3D%22%200.39s%22%20weight%3D2%20color%3D%22%23b2afa6%22%20tooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20-%3E%20syscall.Syscall%20(0.39s)%22%20labeltooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20-%3E%20syscall.Syscall%20(0.39s)%22%5D%0D%0AN8%20-%3E%20N60%20%5Blabel%3D%22%200.37s%22%20weight%3D2%20color%3D%22%23b2afa7%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).update%20(0.37s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).update%20(0.37s)%22%20style%3D%22dotted%22%5D%0D%0AN76%20-%3E%20N40%20%5Blabel%3D%22%200.35s%22%20weight%3D2%20color%3D%22%23b2afa8%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.PollEvents%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.platformPollEvents%20(0.35s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.PollEvents%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.platformPollEvents%20(0.35s)%22%5D%0D%0AN60%20-%3E%20N76%20%5Blabel%3D%22%200.35s%5Cn%20(inline)%22%20weight%3D2%20color%3D%22%23b2afa8%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).update%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.PollEvents%20(0.35s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).update%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.PollEvents%20(0.35s)%22%5D%0D%0AN15%20-%3E%20N27%20%5Blabel%3D%22%200.32s%22%20weight%3D2%20color%3D%22%23b2b0a8%22%20tooltip%3D%22main.(*Game).drawPheromones%20-%3E%20runtime.memclrNoHeapPointers%20(0.32s)%22%20labeltooltip%3D%22main.(*Game).drawPheromones%20-%3E%20runtime.memclrNoHeapPointers%20(0.32s)%22%5D%0D%0AN37%20-%3E%20N3%20%5Blabel%3D%22%200.32s%22%20weight%3D2%20color%3D%22%23b2b0a8%22%20tooltip%3D%22syscall.Syscall6%20-%3E%20runtime.syscall_syscalln%20(0.32s)%22%20labeltooltip%3D%22syscall.Syscall6%20-%3E%20runtime.syscall_syscalln%20(0.32s)%22%5D%0D%0AN5%20-%3E%20N48%20%5Blabel%3D%22%200.30s%22%20weight%3D2%20color%3D%22%23b2b0a9%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.QuadVerticesFromSrcAndMatrix%20(0.30s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.QuadVerticesFromSrcAndMatrix%20(0.30s)%22%5D%0D%0AN13%20-%3E%20N46%20%5Blabel%3D%22%200.29s%22%20color%3D%22%23b2b0a9%22%20tooltip%3D%22runtime.mapaccess2%20-%3E%20runtime.memhash128%20(0.29s)%22%20labeltooltip%3D%22runtime.mapaccess2%20-%3E%20runtime.memhash128%20(0.29s)%22%5D%0D%0AN11%20-%3E%20N41%20%5Blabel%3D%22%200.28s%22%20color%3D%22%23b2b0aa%22%20tooltip%3D%22main.(*Game).updateAnts%20...%20runtime.newobject%20(0.28s)%22%20labeltooltip%3D%22main.(*Game).updateAnts%20...%20runtime.newobject%20(0.28s)%22%20style%3D%22dotted%22%5D%0D%0AN18%20-%3E%20N34%20%5Blabel%3D%22%200.28s%5Cn%20(inline)%22%20color%3D%22%23b2b0aa%22%20tooltip%3D%22main.(*Game).updateAnts-range1%20-%3E%20github.com%2Frafibayer%2Fants-again%2Fvector.Vector.CosineSimilarity%20(0.28s)%22%20labeltooltip%3D%22main.(*Game).updateAnts-range1%20-%3E%20github.com%2Frafibayer%2Fants-again%2Fvector.Vector.CosineSimilarity%20(0.28s)%22%5D%0D%0AN22%20-%3E%20N35%20%5Blabel%3D%22%200.27s%22%20color%3D%22%23b2b0aa%22%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20-%3E%20main.(*Game).drawPheromones.(*Game).drawPheromones.func1-range1%20(0.27s)%22%20labeltooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20-%3E%20main.(*Game).drawPheromones.(*Game).drawPheromones.func1-range1%20(0.27s)%22%5D%0D%0AN48%20-%3E%20N33%20%5Blabel%3D%22%200.25s%5Cn%20(inline)%22%20color%3D%22%23b2b0ab%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.QuadVerticesFromSrcAndMatrix%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.adjustDestinationPixel%20(0.25s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.QuadVerticesFromSrcAndMatrix%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphics.adjustDestinationPixel%20(0.25s)%22%5D%0D%0AN8%20-%3E%20N16%20%5Blabel%3D%22%200.25s%22%20color%3D%22%23b2b0ab%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20...%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.25s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20...%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.25s)%22%20style%3D%22dotted%22%5D%0D%0AN32%20-%3E%20N13%20%5Blabel%3D%22%200.25s%22%20color%3D%22%23b2b0ab%22%20tooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20-%3E%20runtime.mapaccess2%20(0.25s)%22%20labeltooltip%3D%22github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Food%5D).RadialSearchIter.(*Hash%5Bgo.shape.*uint8%5D).RadialSearchIter.func1%20-%3E%20runtime.mapaccess2%20(0.25s)%22%5D%0D%0AN28%20-%3E%20N43%20%5Blabel%3D%22%200.24s%22%20color%3D%22%23b2b0ab%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks.func1%20(0.24s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).invokeUsageCallbacks.func1%20(0.24s)%22%5D%0D%0AN41%20-%3E%20N24%20%5Blabel%3D%22%200.24s%22%20color%3D%22%23b2b0ab%22%20tooltip%3D%22runtime.newobject%20-%3E%20runtime.mallocgc%20(0.24s)%22%20labeltooltip%3D%22runtime.newobject%20-%3E%20runtime.mallocgc%20(0.24s)%22%5D%0D%0AN46%20-%3E%20N38%20%5Blabel%3D%22%200.22s%22%20color%3D%22%23b2b0ab%22%20tooltip%3D%22runtime.memhash128%20-%3E%20aeshashbody%20(0.22s)%22%20labeltooltip%3D%22runtime.memhash128%20-%3E%20aeshashbody%20(0.22s)%22%5D%0D%0AN70%20-%3E%20N71%20%5Blabel%3D%22%200.20s%5Cn%20(inline)%22%20color%3D%22%23b2b1ac%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrint%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrintAt%20(0.20s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrint%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrintAt%20(0.20s)%22%5D%0D%0AN71%20-%3E%20N72%20%5Blabel%3D%22%200.20s%22%20color%3D%22%23b2b1ac%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrintAt%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.drawDebugText%20(0.20s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrintAt%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.drawDebugText%20(0.20s)%22%5D%0D%0AN17%20-%3E%20N42%20%5Blabel%3D%22%200.20s%22%20color%3D%22%23b2b1ac%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fshaderir.(*Program).FilterUniformVariables%20(0.20s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fshaderir.(*Program).FilterUniformVariables%20(0.20s)%22%5D%0D%0AN8%20-%3E%20N53%20%5Blabel%3D%22%200.20s%22%20color%3D%22%23b2b1ac%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).updateInputStateForFrameImpl%20(0.20s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fthread.(*OSThread).Loop.func1%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).updateInputStateForFrameImpl%20(0.20s)%22%20style%3D%22dotted%22%5D%0D%0AN19%20-%3E%20N70%20%5Blabel%3D%22%200.20s%5Cn%20(inline)%22%20color%3D%22%23b2b1ac%22%20tooltip%3D%22main.(*Game).Draw%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrint%20(0.20s)%22%20labeltooltip%3D%22main.(*Game).Draw%20...%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.DebugPrint%20(0.20s)%22%20style%3D%22dotted%22%5D%0D%0AN17%20-%3E%20N55%20%5Blabel%3D%22%200.19s%22%20color%3D%22%23b2b1ac%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).prependPreservedUniforms%20(0.19s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).prependPreservedUniforms%20(0.19s)%22%5D%0D%0AN72%20-%3E%20N5%20%5Blabel%3D%22%200.18s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.drawDebugText%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(0.18s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Febitenutil.drawDebugText%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2.(*Image).DrawImage%20(0.18s)%22%5D%0D%0AN77%20-%3E%20N16%20%5Blabel%3D%22%200.18s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw._GetKeyState%20-%3E%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.18s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw._GetKeyState%20-%3E%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.18s)%22%5D%0D%0AN40%20-%3E%20N77%20%5Blabel%3D%22%200.18s%5Cn%20(inline)%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.platformPollEvents%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw._GetKeyState%20(0.18s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw.platformPollEvents%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fglfw._GetKeyState%20(0.18s)%22%5D%0D%0AN17%20-%3E%20N50%20%5Blabel%3D%22%200.18s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*drawTrianglesCommand).CanMergeWithDrawTrianglesCommand%20(0.18s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20-%3E%20github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*drawTrianglesCommand).CanMergeWithDrawTrianglesCommand%20(0.18s)%22%5D%0D%0AN23%20-%3E%20N37%20%5Blabel%3D%22%200.17s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20-%3E%20syscall.Syscall6%20(0.17s)%22%20labeltooltip%3D%22golang.org%2Fx%2Fsys%2Fwindows.(*Proc).Call%20-%3E%20syscall.Syscall6%20(0.17s)%22%5D%0D%0AN13%20-%3E%20N44%20%5Blabel%3D%22%200.17s%5Cn%20(inline)%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22runtime.mapaccess2%20-%3E%20internal%2Fruntime%2Fmaps.ctrlGroup.matchH2%20(0.17s)%22%20labeltooltip%3D%22runtime.mapaccess2%20-%3E%20internal%2Fruntime%2Fmaps.ctrlGroup.matchH2%20(0.17s)%22%5D%0D%0AN64%20-%3E%20N39%20%5Blabel%3D%22%200.17s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22runtime.semawakeup%20-%3E%20runtime.stdcall1%20(0.17s)%22%20labeltooltip%3D%22runtime.semawakeup%20-%3E%20runtime.stdcall1%20(0.17s)%22%5D%0D%0AN24%20-%3E%20N47%20%5Blabel%3D%22%200.16s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22runtime.mallocgc%20-%3E%20runtime.mallocgcSmallScanNoHeader%20(0.16s)%22%20labeltooltip%3D%22runtime.mallocgc%20-%3E%20runtime.mallocgcSmallScanNoHeader%20(0.16s)%22%5D%0D%0AN63%20-%3E%20N64%20%5Blabel%3D%22%200.16s%22%20color%3D%22%23b2b1ad%22%20tooltip%3D%22runtime.notewakeup%20-%3E%20runtime.semawakeup%20(0.16s)%22%20labeltooltip%3D%22runtime.notewakeup%20-%3E%20runtime.semawakeup%20(0.16s)%22%5D%0D%0AN21%20-%3E%20N22%20%5Blabel%3D%22%200.15s%22%20color%3D%22%23b2b1ae%22%20tooltip%3D%22main.(*Game).Update%20...%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(0.15s)%22%20labeltooltip%3D%22main.(*Game).Update%20...%20github.com%2Frafibayer%2Fants-again%2Fspatial.(*Hash%5B*main.Pheromone%5D).PointsIter.(*Hash%5Bgo.shape.*uint8%5D).PointsIter.func1%20(0.15s)%22%20style%3D%22dotted%22%5D%0D%0AN2%20-%3E%20N16%20%5Blabel%3D%22%200.10s%22%20color%3D%22%23b2b1af%22%20tooltip%3D%22runtime.cgocall%20...%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.10s)%22%20labeltooltip%3D%22runtime.cgocall%20...%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.10s)%22%20style%3D%22dotted%22%5D%0D%0AN53%20-%3E%20N16%20%5Blabel%3D%22%200.09s%22%20color%3D%22%23b2b2af%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).updateInputStateForFrameImpl%20...%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.09s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fui.(*UserInterface).updateInputStateForFrameImpl%20...%20golang.org%2Fx%2Fsys%2Fwindows.(*LazyProc).Call%20(0.09s)%22%20style%3D%22dotted%22%5D%0D%0AN45%20-%3E%20N63%20%5Blabel%3D%22%200.08s%22%20color%3D%22%23b2b2b0%22%20tooltip%3D%22runtime.systemstack%20...%20runtime.notewakeup%20(0.08s)%22%20labeltooltip%3D%22runtime.systemstack%20...%20runtime.notewakeup%20(0.08s)%22%20style%3D%22dotted%22%5D%0D%0AN7%20-%3E%20N14%20%5Blabel%3D%22%200.07s%22%20color%3D%22%23b2b2b0%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20...%20runtime.memmove%20(0.07s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).flush%20...%20runtime.memmove%20(0.07s)%22%20style%3D%22dotted%22%5D%0D%0AN17%20-%3E%20N14%20%5Blabel%3D%22%200.06s%22%20color%3D%22%23b2b2b0%22%20tooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20...%20runtime.memmove%20(0.06s)%22%20labeltooltip%3D%22github.com%2Fhajimehoshi%2Febiten%2Fv2%2Finternal%2Fgraphicscommand.(*commandQueue).EnqueueDrawTrianglesCommand%20...%20runtime.memmove%20(0.06s)%22%20style%3D%22dotted%22%5D%0D%0AN36%20-%3E%20N24%20%5Blabel%3D%22%200.04s%22%20color%3D%22%23b2b2b1%22%20tooltip%3D%22main.(*Game).drawAnts%20...%20runtime.mallocgc%20(0.04s)%22%20labeltooltip%3D%22main.(*Game).drawAnts%20...%20runtime.mallocgc%20(0.04s)%22%20style%3D%22dotted%22%5D%0D%0A%7D%0D%0A" target="_blank">
<img src="/images/ants/cpu.svg" alt="CPU graphviz" />
</a></p>

<h3 id="parameter-optimization">Parameter optimization</h3>
<p>As mentioned above, there are many tradeoffs to be made that result in vastly different ant behavior. There are many variations that produce stable simulations with different characteristics. It’s possible to create very fast ants that find and forget paths quickly, or slow and steady ants that take a while to converge, but keep finding their way back to a food source until it is exhausted.</p>

<p>I spent a little bit of time trying to automate this fiddling with a rudimentary form of parameter optimization.</p>

<p>Game parameters were factored into a single struct</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Params</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="n">AntSpeed</span>                       <span class="kt">float64</span> 
    <span class="n">AntRotation</span>                    <span class="kt">float64</span> 
    <span class="n">AntPheromoneStart</span>              <span class="kt">int</span>  
    <span class="n">PheromoneSenseRadius</span>           <span class="kt">float64</span> 
    <span class="n">PheromoneSenseCosineSimilarity</span> <span class="kt">float64</span> 
    <span class="n">PheromoneDecay</span>                 <span class="kt">float32</span>
    <span class="n">PheromoneDropProb</span>              <span class="kt">float64</span> 
    <span class="n">PheromoneInfluence</span>             <span class="kt">float64</span> 
    <span class="n">PheromoneSenseProb</span>             <span class="kt">float64</span> 
<span class="p">}</span>
</code></pre></div></div>

<p>Then, we pick random values (within bounds) for each parameter, and run the game for a fixed number of ticks (180 * 60, 3 ‘simulation’ minutes). For this, we can just run <code class="language-plaintext highlighter-rouge">Update</code> in a loop as fast as possible, and skip rendering entirely.</p>

<p>Afterwards, we evaluate the performance of the random parameters based on how much food the ants collected. If we beat our previous best score, we output the params. We can run multiple samples for each set of parameters, and test multiple parameters in parallel. Go makes this pretty easy, especially with a little bit of ChatGPT help.</p>

<p>This is essentially a random search of the parameter space. It was useful to find interesting parameter presets, but didn’t produce anything too groundbreaking.</p>

<p>An interesting future project might be applying a more sophisticated “machine-learning” approach here by using something like gradient descent, or ironically enough, <a href="https://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms">ant-colony optimization</a>.</p>

<h1 id="lessons-challenges-closing-thoughts">Lessons, challenges, closing thoughts</h1>
<p>As I mentioned at the start, this wasn’t my first time trying to simulate ants and pheromones. There are a ton of tradeoffs and alternative strategies to choose, and that makes it difficult to find and tune an approach enough to get a stable simulation. A few of those choices include:</p>
<ul>
  <li>discrete pheromones vs. diffusing pheromone field</li>
  <li>pheromones as points vs. vectors</li>
  <li>spatial data structures</li>
  <li>continuous vs. discrete position representations</li>
  <li>random movement vs. pheromone influence</li>
</ul>

<p>In the future, I think it would be neat to include some sliders and dropdowns to alter parameters and behavior of a running simulation.</p>

<p>Those who know me would probably say I’m at least skeptical of AI tools, but ChatGPT was a pretty invaluable source for brainstorming solutions to problems I encountered along the way, and optimizing inefficient code. Other than that, a huge thank you to the implementors of similar projects whose games and code I looked at extensively while creating this project. I’ve tried to include links to all of them below.</p>

<h1 id="sources">Sources</h1>
<ul>
  <li><a href="https://www.youtube.com/watch?v=X-iSQQgOd1A">Sebastian Lague</a></li>
  <li><a href="https://openprocessing.org/sketch/15109/">Forrest O</a></li>
  <li><a href="https://github.com/bones-ai/rust-ants-colony-simulation">bones-ai</a></li>
</ul>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="WebAssembly" /><category term="Go" /><category term="Games" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Rafi’s Reading List: 2024</title><link href="http://rafibayer.com/2024/12/31/ReadingList2024.html" rel="alternate" type="text/html" title="Rafi’s Reading List: 2024" /><published>2024-12-31T00:00:00+00:00</published><updated>2024-12-31T00:00:00+00:00</updated><id>http://rafibayer.com/2024/12/31/ReadingList2024</id><content type="html" xml:base="http://rafibayer.com/2024/12/31/ReadingList2024.html"><![CDATA[<p>As you can probably tell from the lack of posts, blogging was not my priority this past year!</p>

<p>Between my new job and my personal life, I didn’t get to do too much programming in my spare time. I did, however, make time for more reading. Rather than trying to provide a review or score, I’ll simply annotate books I especially enjoyed with a <strong>*</strong>.</p>

<p>I know you all waited with baited breath, so here it is, the short (but slightly longer) list of books I read in 2024!</p>

<ol>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/256008.Lonesome_Dove">Lonesome Dove</a> by Larry McMurty</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/4406.East_of_Eden">East of Eden</a> by John Steinbeck</li>
  <li><a href="https://www.goodreads.com/book/show/11.The_Hitchhiker_s_Guide_to_the_Galaxy">The Hitchhiker’s Guide to the Galaxy</a> by Douglas Adams</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/375802.Ender_s_Game">Enders Game</a> by Orson Scott Card</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/7967.Speaker_for_the_Dead">Speaker for the Dead</a> by Orson Scott Card</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/18373.Flowers_for_Algernon">Flowers for Algernon</a> by Daniel Keyes</li>
  <li><a href="https://www.goodreads.com/book/show/341735.Replay">Replay</a> by Ken Grimwood</li>
  <li><a href="https://www.goodreads.com/book/show/21611.The_Forever_War">The Forever War</a> by Joe Haldeman</li>
  <li><a href="https://www.goodreads.com/book/show/32758901-all-systems-red">All Systems Red</a> by Martha Wells</li>
  <li><a href="https://www.goodreads.com/book/show/10644930-11-22-63">11/22/63</a> by Stephen King</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/77711.A_Fire_Upon_the_Deep">A Fire Upon the Deep</a> by Vernor Vinge</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/226004.A_Deepness_in_the_Sky">A Deepness in the Sky</a> by Vernor Vinge</li>
  <li><a href="https://www.goodreads.com/book/show/10170545-the-children-of-the-sky">The Children of the Sky</a> by Vernor Vinge</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/310612.A_Confederacy_of_Dunces">A Confederacy of Dunces</a> by John Kennedy Toole</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/100365.The_Mote_in_God_s_Eye">The Mote in God’s Eye</a> by Larry Niven</li>
  <li><strong>*</strong> <a href="https://www.goodreads.com/book/show/166997.Stoner">Stoner</a> by John Williams</li>
  <li><a href="https://www.goodreads.com/book/show/100369.Inferno">Inferno</a> By Larry Niven</li>
</ol>

<p>And I am currently Reading:</p>
<ol>
  <li><a href="https://www.goodreads.com/book/show/25499718-children-of-time">Children of Time</a> by Adrian Tchaikovsky</li>
  <li><a href="https://www.goodreads.com/book/show/60176264-bulletproof-tls-and-pki-second-edition">Bulletproof TLS and PKI</a> by Ivan Ristic</li>
</ol>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="Reading" /><summary type="html"><![CDATA[As you can probably tell from the lack of posts, blogging was not my priority this past year!]]></summary></entry><entry><title type="html">Rafi’s Reading List: 2023</title><link href="http://rafibayer.com/2023/12/31/ReadingList2023.html" rel="alternate" type="text/html" title="Rafi’s Reading List: 2023" /><published>2023-12-31T00:00:00+00:00</published><updated>2023-12-31T00:00:00+00:00</updated><id>http://rafibayer.com/2023/12/31/ReadingList2023</id><content type="html" xml:base="http://rafibayer.com/2023/12/31/ReadingList2023.html"><![CDATA[<p>And now for something different!<br />
Early this year, I became the proud owner of a Kindle. I thought it might be nice to replace some of the <a href="https://en.wikipedia.org/wiki/Doomscrolling">Doomscrolling</a> with reading (Doom… turning?).</p>

<p>Without further ado, behold, the short list of books I read in 2023!</p>

<ol>
  <li><a href="https://www.goodreads.com/en/book/show/58782897">The End of the World Is Just the Beginning</a> by Peter Zeihan</li>
  <li><a href="https://www.goodreads.com/book/show/4069.Man_s_Search_for_Meaning">Man’s Search for Meaning</a> by Viktor E. Frankl</li>
  <li><a href="https://www.goodreads.com/book/show/12842.Journey_to_Ixtlan">Journey to Ixtlan</a> by Carlos Castaneda</li>
  <li><a href="https://www.goodreads.com/en/book/show/13456414">A Short Stay in Hell</a> by Steven L. Peck</li>
  <li><a href="https://www.goodreads.com/book/show/18144590-the-alchemist">The Alchemist</a> by Paulo Coelho</li>
  <li><a href="https://www.goodreads.com/book/show/22590.Ubik">Ubik</a> by Philip K. Dick</li>
  <li><a href="https://www.goodreads.com/book/show/1335601.The_Willows">The Willows</a> by Algernon Blackwood</li>
  <li><a href="https://www.goodreads.com/book/show/54493401-project-hail-mary">Project Hail Mary</a> by Andy Weir</li>
  <li><a href="https://www.goodreads.com/book/show/34928122-artemis">Artemis</a> by Andy Weir</li>
  <li>And last but not least… I am just starting <a href="https://www.goodreads.com/book/show/256008.Lonesome_Dove">Lonesome Dove</a> by Larry McMurtry</li>
</ol>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="Reading" /><summary type="html"><![CDATA[And now for something different! Early this year, I became the proud owner of a Kindle. I thought it might be nice to replace some of the Doomscrolling with reading (Doom… turning?).]]></summary></entry><entry><title type="html">Demo: Game Of Life</title><link href="http://rafibayer.com/2023/09/15/GameOfLife.html" rel="alternate" type="text/html" title="Demo: Game Of Life" /><published>2023-09-15T00:00:00+00:00</published><updated>2023-09-15T00:00:00+00:00</updated><id>http://rafibayer.com/2023/09/15/GameOfLife</id><content type="html" xml:base="http://rafibayer.com/2023/09/15/GameOfLife.html"><![CDATA[<p>Play Conway’s Game of Life in your browser! Rust compiled to WASM, using the macroquad crate.</p>

<style>
    canvas {
            margin: auto;
            padding: 0px;
            width: 50vw;
            height: 50vw;
            z-index: 0;
    }
</style>

<p><button onclick="wasm_exports.toggle_paused()">Play/Pause</button>
<button onclick="wasm_exports.step()">Step</button>
<button onclick="wasm_exports.toggle_click_mode()">Toggle Click Mode</button>
<button onclick="wasm_exports.clear()">Clear</button></p>
<canvas id="glcanvas" tabindex="1"></canvas>
<script src="https://not-fl3.github.io/miniquad-samples/mq_js_bundle.js"></script>

<script>load("/assets/wasm/wasm-gol.wasm");</script>

<h1 id="about">About</h1>
<p><a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Conway’s Game of Life</a> is a classic cellular automaton. This implementation is written in Rust, rendered using <a href="https://github.com/not-fl3/macroquad">Macroquad</a>, and made available in the browser via <a href="https://webassembly.org/">WebAssembly</a>.</p>

<h1 id="see-the-code">See the Code</h1>
<p><a href="https://github.com/rafibayer/wasm-gol">github.com/rafibayer/wasm-gol</a></p>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="WebAssembly" /><category term="Rust" /><category term="Games" /><summary type="html"><![CDATA[Play Conway’s Game of Life in your browser! Rust compiled to WASM, using the macroquad crate.]]></summary></entry><entry><title type="html">Byte by Byte: Mutexes in Rust</title><link href="http://rafibayer.com/2023/07/05/rustMutex.html" rel="alternate" type="text/html" title="Byte by Byte: Mutexes in Rust" /><published>2023-07-05T00:00:00+00:00</published><updated>2023-07-05T00:00:00+00:00</updated><id>http://rafibayer.com/2023/07/05/rustMutex</id><content type="html" xml:base="http://rafibayer.com/2023/07/05/rustMutex.html"><![CDATA[<p>For someone who has never used Rust, it can be hard to understand what all the fuss is about. Let’s take a brief look at how Rust lives up to one of its staple promises, namely “fearless concurrency” with a few examples on about a fundamental component: the mutex.</p>

<h2 id="aside-ownership">Aside: Ownership</h2>
<p>Without delving too deep, we first need to understand just a bit about the <a href="https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html">ownership</a> system in Rust. The Rust book has a great summary of the ownership <a href="https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#ownership-rules">rules</a> that I’ll quote here:</p>

<blockquote>
  <ul>
    <li>Each value in Rust has an owner.</li>
    <li>There can only be one owner at a time.</li>
    <li>When the owner goes out of scope, the value will be dropped.</li>
  </ul>
</blockquote>

<p>Unlike other languages, in Rust, we cannot have multiple variables that refer directly to the same value. When we assign a new variable to equal an existing value, we give ownership to the new variable, and the old owner ceases to exist.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello world!"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span>

<span class="c1">// no good, the value pointed to by a was moved to be, a no longer exists</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
</code></pre></div></div>

<p>The compiler even gives us a helpful error message showing exactly where our mistake is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>|     println!("{}", a);
|                    ^ value borrowed here after move
</code></pre></div></div>

<p>One way we can get around this rule is by “borrowing” values instead of “moving” them. When we borrow a value, we get a reference type instead of ownership, which allows us to share our value while still imposing some safety restrictions.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="s">"hello world!"</span><span class="p">);</span>
<span class="k">let</span> <span class="n">b</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">a</span><span class="p">;</span>
<span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">a</span><span class="p">;</span> <span class="c1">// we can make as many immutable borrows as we'd like</span>

<span class="c1">// all good! a still exists because it still has ownership over the string,</span>
<span class="c1">// b and c are just borrowing it.</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
</code></pre></div></div>

<p>Unlike immutable borrows, we can only ever have 
a single mutable reference to a value at a time.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">mut</span> <span class="n">buffer</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
<span class="k">let</span> <span class="n">shared_1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">buffer</span><span class="p">;</span>
<span class="k">let</span> <span class="n">shared_2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">buffer</span><span class="p">;</span> <span class="c1">// no good! there is already a mutable borrow of the value</span>
<span class="nf">do_something</span><span class="p">(</span><span class="n">shared_1</span><span class="p">);</span> <span class="c1">// shared_1 still exists at the point shared_2 is created :(</span>
</code></pre></div></div>

<p>Our helpful error:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>|     let shared_1 = &amp;mut buffer;
|                    ----------- first mutable borrow occurs here
|     let shared_2 = &amp;mut buffer;
|                    ^^^^^^^^^^^ second mutable borrow occurs here
|     do_something(shared_1);
|                  -------- first borrow later used here
</code></pre></div></div>

<p>Ownership works because the compiler guarantees that a reference cannot outlive its owner, references to values cannot escape the scope of the owning variable. In Go you could write something like this:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">s</span> <span class="o">*</span><span class="kt">string</span>

<span class="p">{</span>
    <span class="n">hello</span> <span class="o">:=</span> <span class="s">"hello world"</span>
    <span class="n">s</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">hello</span>
<span class="p">}</span>

<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="o">*</span><span class="n">s</span><span class="p">)</span>
</code></pre></div></div>

<p>This works because Go has a garbage collector, there is still a reference to the string despite the original variable going out of scope. In a language like C without a garbage collector, this can create a <a href="https://en.wikipedia.org/wiki/Dangling_pointer">dangling pointer</a> which can segfault your program at best or create a security vulnerability at worst.</p>

<p>But if we can’t share our references to other scopes, how can we share data in Rust? Especially when we start working with multiple threads, we certainly lose any ability to guarantee our reference doesn’t outlive its owner.</p>

<h2 id="the-mutex">The Mutex</h2>
<p>In other languages, using a mutex looks something like this:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// golang:</span>
<span class="c">// acquire a lock on the mutex, blocks until it's our turn</span>
<span class="n">mu</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>

<span class="c">// enter the critical section, mutate the shared value</span>
<span class="n">sharedValue</span><span class="o">.</span><span class="n">DoSomething</span><span class="p">()</span>

<span class="c">// release the lock, we're done</span>
<span class="n">mu</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
</code></pre></div></div>

<p>Go even provides us the helpful <code class="language-plaintext highlighter-rouge">defer</code> keyword which we could use instead to automatically release the lock at the end of the surrounding scope (<code class="language-plaintext highlighter-rouge">mu.Lock()</code> followed immediately by <code class="language-plaintext highlighter-rouge">defer mu.Unlock()</code>).</p>

<p>This all seems pretty intuitive, but there are a couple of foot-guns lurking here:</p>
<ul>
  <li>What if we forget to acquire the lock?
    <ul>
      <li>Nothing prevents us from mutating our shared value without it.</li>
    </ul>
  </li>
  <li>What if we forget to release the lock?
    <ul>
      <li>An exception or crash could cause us to exit early, before our unlock.</li>
      <li>Our abandoned mutex could deadlock our program.</li>
    </ul>
  </li>
  <li>What if we share data outside of our critical section?
    <ul>
      <li>This could lead to another part of our program mutating it without realizing it should have first taken a lock because the value is shared between threads.</li>
    </ul>
  </li>
</ul>

<h2 id="mutexes-own-their-values">Mutexes Own Their Values</h2>
<p>In Rust, A mutex owns the data it protects. We can only ever acquire a reference to the data owned by the mutex by locking it. Since the compiler guarantees a reference cannot outlive its owner, we also cannot accidentally allow the value protected by the mutex to outlive the lock. Our go example from above might look something like this.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// locking the mutex gives us access to our shared value</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">shared_value</span> <span class="o">=</span> <span class="n">mutex</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
<span class="n">shared_value</span><span class="nf">.do_something</span><span class="p">();</span>

<span class="c1">// no need to unlock, when shared_value leaves scope, the lock is released</span>
</code></pre></div></div>

<p>So far we’ve been a bit hand-wavy with the details, we’ll use the classic concurrency demo of incrementing a shared counter.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::{</span>
    <span class="nn">sync</span><span class="p">::{</span><span class="nb">Arc</span><span class="p">,</span> <span class="n">Mutex</span><span class="p">},</span>
    <span class="n">thread</span><span class="p">,</span>
<span class="p">};</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">THREADS</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="k">const</span> <span class="n">INCREMENTS</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>

    <span class="c1">// Arc = Atomically Reference Counted,</span>
    <span class="c1">// allows us to share the mutex itself (immutable)</span>
    <span class="k">let</span> <span class="n">counter</span> <span class="o">=</span> <span class="nn">Arc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Mutex</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>

    <span class="c1">// our thread handles</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">handles</span> <span class="o">=</span> <span class="nn">Vec</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>

    <span class="c1">// spawn multiple threads, share a copy of the mutex to each</span>
    <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">THREADS</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">counter_copy</span> <span class="o">=</span> <span class="n">counter</span><span class="nf">.clone</span><span class="p">();</span>
        <span class="n">handles</span><span class="nf">.push</span><span class="p">(</span><span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">locked</span> <span class="o">=</span> <span class="n">counter_copy</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
            <span class="nd">println!</span><span class="p">(</span><span class="s">"{i} grabbed the lock"</span><span class="p">);</span>

            <span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..</span><span class="n">INCREMENTS</span> <span class="p">{</span>
                <span class="o">*</span><span class="n">locked</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="nd">println!</span><span class="p">(</span><span class="s">"{i} all done!"</span><span class="p">);</span>
        <span class="p">}));</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="n">handle</span> <span class="k">in</span> <span class="n">handles</span> <span class="p">{</span>
        <span class="n">handle</span><span class="nf">.join</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">counter</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"after: {result}"</span><span class="p">);</span>
    <span class="nd">assert_eq!</span><span class="p">(</span><span class="mi">10000</span><span class="p">,</span> <span class="o">*</span><span class="n">result</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Our output looks about as expected, the threads obtain the lock in pseudo-random order and perform their work before releasing. Our final counter value is the sum of all increments, no updates were lost.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 grabbed the lock
0 all done!
3 grabbed the lock
3 all done!
1 grabbed the lock
1 all done!
2 grabbed the lock
2 all done!
4 grabbed the lock
4 all done!
6 grabbed the lock
6 all done!
7 grabbed the lock
7 all done!
8 grabbed the lock
8 all done!
9 grabbed the lock
9 all done!
5 grabbed the lock
5 all done!
after: 10000
</code></pre></div></div>

<p>This all looks familiar if you’ve written any concurrency-related code in a language like Java or Go. That’s a good thing! Rust can already be difficult to learn, but here you get to apply a fairly ubiquitous pattern of concurrency and locking, just without all the risks you normally undertake when working with multiple threads.</p>

<h2 id="composability">Composability</h2>
<p>The last aspect of Rust mutexes I want to touch on is composability. Because a Rust mutex gives you a reference to the owned data, I find it easier to compose functions that use it. Let’s write some pseudo-code demonstrating typical mutex usage in another language:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct Balances {
    mu sync.Mutex
    accounts map[string]int
}

func Deposit(account, amt) error {
    mu.Lock()
    defer mu.Unlock()
    
    // ... do some validation
    accounts[account] += amt
    return nil
}

func Withdraw(account, amt) error {
    mu.Lock()
    defer mu.Unlock()

    // ... do some validation
    accounts[account] -= amt
    return nil
}
</code></pre></div></div>

<p>We have a structure that contains both the mutex as well as the data it protects. We have some functions that acquire the lock, and perform some work on the data. The problem arises when we want to start composing these functions together, say we want to implement a transfer function, we’ll write some pseudo-code with potential implementations:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>func Transfer(from, to, amt) {
    Withdraw(from, amt)
    Deposit(to, amt)
}
</code></pre></div></div>

<p>This is no good! <code class="language-plaintext highlighter-rouge">Withdraw</code> and <code class="language-plaintext highlighter-rouge">Deposit</code> acquire the lock independently, meaning we have to acquire it twice to process and <code class="language-plaintext highlighter-rouge">Transfer</code>, another concurrent caller could acquire the lock between our 2 functions moving our data into an invalid state after we’ve already performed half the transaction. Acquiring the lock in <code class="language-plaintext highlighter-rouge">Transfer</code> instead would cause a deadlock, since we also need to acquire the lock for <code class="language-plaintext highlighter-rouge">Withdraw</code> and <code class="language-plaintext highlighter-rouge">Deposit</code>.</p>

<p>We could refactor our code to take the lock outside of all of these functions, and assume we’re already holding it during the actual mutation, but doing so in a language like go makes it easy for us to introduce some of the bugs we talked about earlier.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>func Transfer(from, to, amt) {
    mu.Lock()
    defer mu.Unlock()

    // these "internal" functions must assume they already have the lock,
    // but nothing actually enforces that!
    withdrawInternal(from, amt)
    depositInternal(to, amt)
}
</code></pre></div></div>

<p>Now we’ve introduced a lot of complexity over whose job it is to acquire and release the lock, and where it’s safe to assume the lock is held. Enforcing that usages of these functions actually acquire the lock to protect their data is entirely the responsibility of the programmer, there isn’t any real relationship between the mutex and data other than the fact that they are in the same struct. Even with proper acquisition, it’s easy to make mistakes like passing the map to another thread that might outlive the scope in which we have the lock.</p>

<p>Rust protects us from all these mistakes, we can apply the same pattern, locking our Mutex outside of our business logic, and assuming we have it inside. The difference is that Rust will never allow us to access the data without first acquiring the lock, and since the lock is held for the lifetime of the reference, the data can never escape the scope of the lock.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Balances</span> <span class="p">{</span>
    <span class="n">accounts</span><span class="p">:</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Mutex</span><span class="o">&lt;</span><span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span> <span class="nb">i32</span><span class="o">&gt;&gt;&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Balances</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">deposit</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">account</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">amt</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">accounts</span> <span class="o">=</span> <span class="k">self</span><span class="py">.accounts</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
        <span class="nn">Balances</span><span class="p">::</span><span class="nf">deposit_internal</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">accounts</span><span class="p">,</span> <span class="n">account</span><span class="p">,</span> <span class="n">amt</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">withdraw</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">account</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">amt</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">accounts</span> <span class="o">=</span> <span class="k">self</span><span class="py">.accounts</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
        <span class="nn">Balances</span><span class="p">::</span><span class="nf">withdraw_internal</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">accounts</span><span class="p">,</span> <span class="n">account</span><span class="p">,</span> <span class="n">amt</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">transfer</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">from</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">to</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">amt</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">accounts</span> <span class="o">=</span> <span class="k">self</span><span class="py">.accounts</span><span class="nf">.lock</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
        <span class="nn">Balances</span><span class="p">::</span><span class="nf">withdraw_internal</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">accounts</span><span class="p">,</span> <span class="n">from</span><span class="p">,</span> <span class="n">amt</span><span class="p">);</span>
        <span class="nn">Balances</span><span class="p">::</span><span class="nf">deposit_internal</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">accounts</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">amt</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">deposit_internal</span><span class="p">(</span><span class="n">accounts</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span> <span class="nb">i32</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">account</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">amt</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// we have a mutable reference to accounts, therefore it must</span>
        <span class="c1">// be an exclusive reference due to Rusts ownership rules!</span>
        <span class="o">*</span><span class="n">accounts</span><span class="nf">.get_mut</span><span class="p">(</span><span class="n">account</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="o">+=</span> <span class="n">amt</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">withdraw_internal</span><span class="p">(</span><span class="n">accounts</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">HashMap</span><span class="o">&lt;</span><span class="nb">String</span><span class="p">,</span> <span class="nb">i32</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">account</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">amt</span><span class="p">:</span> <span class="nb">i32</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// same assumption as deposit_internal</span>
        <span class="o">*</span><span class="n">accounts</span><span class="nf">.get_mut</span><span class="p">(</span><span class="n">account</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span> <span class="o">-=</span> <span class="n">amt</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Because of Rusts compile-time enforcement of ownership and borrowing rules, we know that our “internal” functions must have exclusive access to the hashmap, and therefore are always safe to mutate it. Rust mutexes make it much easier to write composable code without introducing nasty concurrency bugs that are possible in other languages.</p>

<p>If you’ve made it this far, I hope I’ve convinced you to give Rust a try!</p>

<h2 id="references-and-further-reading">References and Further Reading</h2>
<ul>
  <li><a href="https://www.rust-lang.org/">Rust</a></li>
  <li><a href="https://doc.rust-lang.org/book/">Rust Book</a>
    <ul>
      <li><a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html">Concurrency Chapter</a></li>
    </ul>
  </li>
  <li><a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html">std::sync::Mutex</a></li>
</ul>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="Rust" /><category term="Programming-Languages" /><category term="Concurrency" /><summary type="html"><![CDATA[For someone who has never used Rust, it can be hard to understand what all the fuss is about. Let’s take a brief look at how Rust lives up to one of its staple promises, namely “fearless concurrency” with a few examples on about a fundamental component: the mutex.]]></summary></entry><entry><title type="html">Deep Dive: Macrofrick - Metaprogramming for Fun and for Speed</title><link href="http://rafibayer.com/2023/02/26/macrofrick.html" rel="alternate" type="text/html" title="Deep Dive: Macrofrick - Metaprogramming for Fun and for Speed" /><published>2023-02-26T00:00:00+00:00</published><updated>2023-02-26T00:00:00+00:00</updated><id>http://rafibayer.com/2023/02/26/macrofrick</id><content type="html" xml:base="http://rafibayer.com/2023/02/26/macrofrick.html"><![CDATA[<p><a href="https://github.com/rafibayer/macrofrick">Macrofrick</a> is a Brainfuck compiler that leverages Rust’s declarative macros and efficient compiler to produce blazingly fast binaries with minimal effort.</p>

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

<p><a href="/2022/11/02/brainfrick.html">Previously</a>, we produced a Brainfuck compiler called <a href="https://github.com/rafibayer/brainfrick-rs">Brainfrick-rs</a> that worked much like a traditional compiler, creating a pipeline that parsed, optimized, and executed code on a virtual machine. Here is a rough diagram of the process that we used to execute brainfuck from our source file.</p>

<div class="mermaid">
sequenceDiagram
    participant src as Source File (.bf)
    participant main as Main
    participant compiler as Compiler
    participant vm as Virtual Machine
    participant io as IO
    src-&gt;&gt;main:Open brainfuck source file
    main-&gt;&gt;compiler:Brainfuck src as &amp;str
    compiler-&gt;&gt;compiler: Parse source to instructions
    loop Optimizations
        compiler-&gt;&gt;compiler: Apply optimization
    end
    compiler-&gt;&gt;compiler: pre-match loop braces
    compiler-&gt;&gt;main: Produce Compiled Program
    main-&gt;&gt;vm: Create and run VM for Program
    vm-&gt;&gt;io: Utilize configured IO
</div>
<script async="" src="https://unpkg.com/mermaid@8.2.3/dist/mermaid.min.js"></script>

<p>We spent a whole lot of time analyzing common patterns of brainfuck code to find opportunities to optimize. By applying all these neat little tricks, we were able to drive down the runtime of our sample programs more and more.</p>

<p>Just for reference, our most intensive brainfuck program was <code class="language-plaintext highlighter-rouge">mandelbrot.bf</code>, a nearly <strong>12,000</strong> instruction long program that produced a mandelbrot fractal using ASCII characters. By applying our optimizer pipeline to our brainfuck instructions, our compiler was able to speed up the runtime of the program from about <strong>13.2</strong> to <strong>4.3</strong> seconds on my machine with the most recent version. All told, brainfrick-rs is a fairly modest ~750 lines of code. While we could certainly keep improving performance with new clever optimizers and careful profiling, we are already reaching the limits of what could be squeezed out of our VM implementation.</p>

<p>Instead, we will take a different approach: leveraging one of the most powerful compilers in the world to execute <code class="language-plaintext highlighter-rouge">mandelbrot.bf</code> in <strong>less than 1 second</strong> with about 100 lines of code.</p>

<h2 id="an-introduction-to-metaprogramming-macros">An Introduction to Metaprogramming: Macros</h2>
<p><a href="https://en.wikipedia.org/wiki/Metaprogramming">Metaprogramming</a> is a programming strategy that allows one program to read, modify, or generate another. There are many fantastic examples of metaprogramming that you have likely heard of or maybe even used, code generation being of the the most prevalent. <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSLs</a> like lex and yacc utilize code generation to create complex programs like parsers from simpler representations, and Google’s <a href="https://github.com/protocolbuffers/protobuf">Protobuf</a> IDL can generate clients and servers in many different languages to facilitate interoperability between services.</p>

<p>While metaprogramming exists in many different forms, one of the first that programmers are often exposed to is the <a href="https://en.wikipedia.org/wiki/Macro_(computer_science)" target="_blank">Macro</a>. Macros allow the programmer to specify a pattern of characters or tokens that should be replaced by another. These macros are often processed or “expanded” during the pre-processor phase of the compiler, making them different than something like a function call. In the final program, there is no macro, just the expanded code.</p>

<p>For example, here is a simple C program that defines a macro to add two numbers.</p>

<h5 id="mainc"><code class="language-plaintext highlighter-rouge">main.c</code></h5>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="cp">#define ADD(X, Y) ((X) + (Y))
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ADD</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"%d + %d = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>

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

<p>To make the difference between this macro and a function more apparent, we can take a look at the expanded C code using the <a href="https://godbolt.org/">Godbolt Compiler Explorer</a> and adding the <code class="language-plaintext highlighter-rouge">-E</code> flag to gcc.</p>

<h5 id="expanded-mainc"><code class="language-plaintext highlighter-rouge">expanded main.c</code></h5>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ... trimmed ...</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="p">((</span><span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">y</span><span class="p">));</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"%d + %d = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>

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

<p>When the macro was expanded, the resulting code was placed directly in the code where the macro was used. Unlike a function we are not “calling” the macro or passing <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> to a new stack frame, we replace the macro with the resulting code.</p>

<h2 id="declarative-macros-in-rust">Declarative Macros in Rust</h2>
<p>When programming in Rust, macros are introduced very early. When creating a new Rust project with <code class="language-plaintext highlighter-rouge">cargo new</code>, the default <code class="language-plaintext highlighter-rouge">main.rs</code> file looks like this:</p>

<h5 id="mainrs"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here’s our first macro: <code class="language-plaintext highlighter-rouge">println!</code>.</p>

<p>Rust does not support variadic functions, that is, functions that take a variable number of arguments like <code class="language-plaintext highlighter-rouge">Varargs</code> in Java or <code class="language-plaintext highlighter-rouge">params</code> in C#. Rust could’ve instead defined <code class="language-plaintext highlighter-rouge">println</code> as a function that takes in a slice of <code class="language-plaintext highlighter-rouge">&amp;str</code>, something like <code class="language-plaintext highlighter-rouge">fn println(args: &amp;[&amp;str])</code>, but what if we want to print non-string arguments? Things would get cumbersome fast, probably not a great impression for a hello world program.</p>

<p>Instead Rust’s <code class="language-plaintext highlighter-rouge">println</code> is a <a href="https://doc.rust-lang.org/book/ch19-06-macros.html">declarative macro</a> that operates on a series of <code class="language-plaintext highlighter-rouge">TokenTree</code>, nodes directly from Rust’s <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree">AST</a>. The macro hides the ugly details of formatting our arguments and outputting the result to stdout, while also giving us a convenient API.</p>

<p>Some other Rust macros you’re likely to come up against are <code class="language-plaintext highlighter-rouge">vec!</code>, <code class="language-plaintext highlighter-rouge">assert!</code>, <code class="language-plaintext highlighter-rouge">panic!</code>, and <code class="language-plaintext highlighter-rouge">cfg!</code>. While we won’t cover them in this article, Rust also supports <a href="https://doc.rust-lang.org/reference/procedural-macros.html">Prodedural Macros</a> which allow you to write more complex, imperative code that also operates on Rust AST nodes. You’ve probably seen the <code class="language-plaintext highlighter-rouge">#[Derive(...)]</code> attribute which allows you to automatically implement traits like <code class="language-plaintext highlighter-rouge">Clone</code>, <code class="language-plaintext highlighter-rouge">Debug</code>, <code class="language-plaintext highlighter-rouge">Copy</code>, and more for structs automatically. This works by generating the associated trait implementation in a procedural macro.</p>

<p>Much like our C example, we can write a simple Rust macro to add two numbers.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">add</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$lhs:expr</span><span class="p">,</span> <span class="nv">$rhs:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="nv">$lhs</span> <span class="o">+</span> <span class="nv">$rhs</span>
    <span class="p">};</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="nd">add!</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">macro_rules!</code> is used to start the definition of a macro, like <code class="language-plaintext highlighter-rouge">#define</code> in C. Rust macros work by defining a set of rules to match the program AST, and the corresponding output. Here we define a single rule, <code class="language-plaintext highlighter-rouge">($lhs:expr, $rhs:expr)</code>. This rule matches a node of type <code class="language-plaintext highlighter-rouge">expr</code>, and binds it to the <em>metavariable</em> <code class="language-plaintext highlighter-rouge">$lhs</code>, which we can use later to refer to it. Likewise, it matches a second <code class="language-plaintext highlighter-rouge">expr</code> that we bind to <code class="language-plaintext highlighter-rouge">$rhs</code>. Now we define to body of the rule, which specifies how to expand the macro if we’ve found a match. Here, <code class="language-plaintext highlighter-rouge">$lhs + $rhs</code> expands the macro by placing a <code class="language-plaintext highlighter-rouge">+</code> between our 2 matched expressions. Jumping back to godbolt, we can view the expanded Rust program to see our macro in action.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// expanded</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> 
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">;</span> 
<span class="p">}</span>
</code></pre></div></div>

<p>Notice, our macro rule specified <code class="language-plaintext highlighter-rouge">expr</code> as the AST node to match, this means we could pass any valid Rust expression to our macro, and the rule would match. Rust macros can go beyond matching a simple expression, there are a whole set of <a href="https://doc.rust-lang.org/reference/macros-by-example.html#metavariables">fragment-specifiers</a> we can use to match Rust code. Macrofrick, our new brainfuck compiler, will leverage the <code class="language-plaintext highlighter-rouge">tt</code> fragment to match Rust code at the token level.</p>

<p>The last bit of Rust Macro basics we need to cover before moving onto macrofrick is repetition. One of the first justifications we gave for using macros over functions in Rust is the ability to use macros as variadic functions. A naive approach would be to just add rules to our macro to support more and more arguments, for example, our <code class="language-plaintext highlighter-rouge">add</code> macro could become</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">add</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$e1:expr</span><span class="p">,</span> <span class="nv">$e2:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="nv">$e1</span> <span class="o">+</span> <span class="nv">$e2</span>
    <span class="p">},</span>
    <span class="p">(</span><span class="nv">$e1:expr</span><span class="p">,</span> <span class="nv">$e2:expr</span><span class="p">,</span> <span class="nv">$e3:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="nv">$e1</span> <span class="o">+</span> <span class="nv">$e2</span> <span class="o">+</span> <span class="nv">$e3</span>
    <span class="p">},</span>
    <span class="p">(</span><span class="nv">$e1:expr</span><span class="p">,</span> <span class="nv">$e2:expr</span><span class="p">,</span> <span class="nv">$e3:expr</span><span class="p">,</span> <span class="nv">$e4:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="nv">$e1</span> <span class="o">+</span> <span class="nv">$e2</span> <span class="o">+</span> <span class="nv">$e3</span> <span class="o">+</span> <span class="nv">$e4</span>
    <span class="p">},</span>
    <span class="c1">// and on and on and on...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But this is pretty repetitive and unmaintainable. It also means that our macro would only support as many arguments as we write rules for.</p>

<p>Instead, we can use a repetition that matches multiple repeated fragments of code. We can define a group of tokens, and optional separator, and a repetition type (<code class="language-plaintext highlighter-rouge">+</code>, <code class="language-plaintext highlighter-rouge">*</code>, or <code class="language-plaintext highlighter-rouge">?</code>).</p>

<p>Repetition types probably look familiar if you’ve written and Regex before, and they carry much the same meaning</p>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">*</code></strong>: 0 or more times</li>
  <li><strong><code class="language-plaintext highlighter-rouge">+</code></strong>: 1 or more times</li>
  <li><strong><code class="language-plaintext highlighter-rouge">?</code></strong>: 0 or 1 times</li>
</ul>

<p>We can then refer to the metavariables in our expansion to repeatedly expand based on how many times we matched.</p>

<p>Now we can rewrite our <code class="language-plaintext highlighter-rouge">add</code> macro to be truly variadic and concise.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">add</span> <span class="p">{</span>

    <span class="c1">// an expression, followed by a comma,</span>
    <span class="c1">// followed by 1 or more expressions,</span>
    <span class="c1">// separated by commas</span>
    <span class="p">(</span><span class="nv">$first:expr</span><span class="p">,</span> <span class="nv">$</span><span class="p">(</span> <span class="nv">$rest:expr</span> <span class="p">),</span><span class="o">+</span> <span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>

        <span class="c1">// expand into the first expression, </span>
        <span class="c1">// followed by a plus and the next expression,</span>
        <span class="c1">// as many times as we matched</span>
        <span class="nv">$first</span> <span class="nv">$</span><span class="p">(</span> <span class="o">+</span> <span class="nv">$rest</span> <span class="p">)</span><span class="o">+</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">123</span><span class="p">;</span>

    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="nd">add!</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="mi">3</span> <span class="o">+</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="p">}</span>
</code></pre></div></div>

<p>Which expands into:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// expanded</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">z</span> <span class="o">=</span> <span class="mi">123</span><span class="p">;</span>

    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">+</span> <span class="n">z</span> <span class="o">+</span> <span class="p">(</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">8</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>
</code></pre></div></div>

<p>If you want to learn more about Rust macros, <a href="https://github.com/tfpk/macrokata">Macrokata</a> is a fantastic set of tutorials and exercises. I was able to write macrofrick before I even finished them all.</p>

<h2 id="brainfuck-back-to-basics">Brainfuck: Back to Basics</h2>
<p>In our original <a href="/2022/11/02/brainfrick.html#brainfuck-basics">Brainfuck</a> article, we discussed the equivalencies between brainfuck instructions, and lines of C code. The table in that article gives a possible C implementation for all 8 of brainfucks instructions.</p>

<p>We are going to expand (pun <em>intended</em>) upon that idea by using Rust macros to translate brainfuck instructions directly into Rust a valid Rust program.</p>

<p>Lets start with matching. Like we mentioned above, we will use the <code class="language-plaintext highlighter-rouge">tt</code> fragment to match individual Rust tokens. Fortunately, all 8 brainfuck instructions are also valid Rust tokens. A valid Brainfuck program is made up of 0 or more instructions, so lets write a macro rule that matches exactly that.</p>

<h5 id="mainrs-1"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">frick</span> <span class="p">{</span>
    <span class="p">(</span> <span class="nv">$</span><span class="p">(</span> <span class="nv">$code:tt</span> <span class="p">)</span><span class="o">*</span> <span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// todo: expand our tokens</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We used the <code class="language-plaintext highlighter-rouge">*</code> repetition type, which means we will match 0 or more <code class="language-plaintext highlighter-rouge">tt</code>. Before we start expanding our tokens, we need to set up the pointer and memory used by the brainfuck program. We can simply do this at the start of our expansion, by just including the following 2 lines.</p>

<h5 id="mainrs-2"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">frick</span> <span class="p">{</span>
    <span class="p">(</span> <span class="nv">$</span><span class="p">(</span> <span class="nv">$code:tt</span> <span class="p">)</span><span class="o">*</span> <span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">mem</span><span class="p">:</span> <span class="p">[</span><span class="nb">u8</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">];</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">ptr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

        <span class="c1">// todo: expand our tokens</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Like brainfrick-rs, we use a default of 30,000 cells of type u8, a single byte.</p>

<h3 id="aside-macro-hygiene">Aside: Macro Hygiene</h3>

<p>A quick note about <a href="https://en.wikipedia.org/wiki/Hygienic_macro">macro hygiene</a>. Because macros can expand to arbitrary code, it’s theoretically possible for the developer of a macro to accidentally shadow or capture a variable used elsewhere in the program.</p>

<p>Imagine we wrote a macro like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">count_to_5</span> <span class="p">{</span>
    <span class="p">()</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="k">while</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="mi">5</span> <span class="p">{</span>
            <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
            <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here we declare and modify a variable, <code class="language-plaintext highlighter-rouge">i</code> in our macro, but what if <code class="language-plaintext highlighter-rouge">i</code> was already defined in the surrounding scope. Depending on <code class="language-plaintext highlighter-rouge">i</code>’s type, we could cause a variety of problems. If <code class="language-plaintext highlighter-rouge">i</code> was perhaps a string, our program would likely just fail to compile, since after the macro was executed, <code class="language-plaintext highlighter-rouge">i</code> would now refer to a numeric type, where we were expecting a string. Even worse, if <code class="language-plaintext highlighter-rouge">i</code> was already a number, we now just unpredictably changed the value of <code class="language-plaintext highlighter-rouge">i</code> which would be incredibly hard to debug because it depends on how the macro developer decided to name their variables.</p>

<p>Lets try running some code and see if Rust suffers from this problem</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">i</span> <span class="o">=</span> <span class="s">"I love macro hygiene"</span><span class="p">;</span>
<span class="nd">count_to_5!</span><span class="p">();</span>

<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">$ cargo run main.rs</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1
2
3
4
5
I love macro hygiene
</code></pre></div></div>

<p>Woah! Rust saved us here. Rust uses hygienic macros which means macros cannot refer to variables from other scopes. The <code class="language-plaintext highlighter-rouge">i</code> inside the macro expansion is different than the one we defined in <code class="language-plaintext highlighter-rouge">main</code>.</p>

<p>While is is awesome in the general case, it also means that we’ll have to do some extra work to refer to <code class="language-plaintext highlighter-rouge">mem</code> and <code class="language-plaintext highlighter-rouge">ptr</code> in our next macro, <code class="language-plaintext highlighter-rouge">instr!</code></p>

<h2 id="brainfuck-expanding-instructions">Brainfuck: Expanding Instructions</h2>
<p>Now that we have our brainfuck tape set up, and a way to match our instructions, we need to start expanding them into equivalent Rust code. To do this we’ll create another macro called <code class="language-plaintext highlighter-rouge">instr!</code> that matches a single <code class="language-plaintext highlighter-rouge">tt</code> and expands into Rust. Because of the macro hygiene rules we discussed above, we cannot refer to <code class="language-plaintext highlighter-rouge">mem</code> and <code class="language-plaintext highlighter-rouge">ptr</code> directly in <code class="language-plaintext highlighter-rouge">instr!</code>, since they are defined in <code class="language-plaintext highlighter-rouge">frick!</code>’s scope.</p>

<p>To get around this, we can add metavariables for them in <code class="language-plaintext highlighter-rouge">instr!</code>’s rules, so that we can essentially pass them into our expansion.</p>

<p>Each rule for <code class="language-plaintext highlighter-rouge">instr!</code> will look something like this</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="cm">/* THE TOKEN WE WANT TO MATCH */</span> <span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// ... expansion ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">ident</code> fragment specifier means the rule wants to match an identifier, like a variable name. We can write our <code class="language-plaintext highlighter-rouge">instr!</code> macro, and call it from <code class="language-plaintext highlighter-rouge">frick!</code> with <code class="language-plaintext highlighter-rouge">mem</code> and <code class="language-plaintext highlighter-rouge">ptr</code> used to match the first 2 metavariables.</p>

<h5 id="mainrs-3"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">instr</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="cm">/* THE TOKEN WE WANT TO MATCH */</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
       <span class="c1">// ... expansion ...</span>
    <span class="p">},</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="cm">/* THE TOKEN WE WANT TO MATCH */</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
       <span class="c1">// ... expansion ...</span>
    <span class="p">},</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="cm">/* THE TOKEN WE WANT TO MATCH */</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
       <span class="c1">// ... expansion ...</span>
    <span class="p">},</span>
    <span class="c1">// and on and on for each different brainfuck instruction token</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">frick!</code> now becomes:</p>
<h5 id="mainrs-4"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">frick</span> <span class="p">{</span>
    <span class="p">(</span> <span class="nv">$</span><span class="p">(</span> <span class="nv">$code:tt</span> <span class="p">)</span><span class="o">*</span> <span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">mem</span><span class="p">:</span> <span class="p">[</span><span class="nb">u8</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">];</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">ptr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

        <span class="c1">// invoke instr! for each repetition of $code</span>
        <span class="nv">$</span><span class="p">(</span>
            <span class="nd">instr!</span><span class="p">(</span><span class="n">mem</span> <span class="n">ptr</span> <span class="nv">$code</span><span class="p">);</span>
        <span class="p">)</span><span class="o">*</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now for the easy part, we can just fill in the expansion for each token and rule in accordance with our table from the last article.</p>

<h5 id="mainrs-5"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">instr</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="o">&gt;</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// &gt;</span>
        <span class="nv">$ptr</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">};</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="o">&lt;</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// &lt;</span>
        <span class="nv">$ptr</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">};</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="o">+</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// +</span>
        <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">};</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="o">-</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// -</span>
        <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span><span class="nf">.wrapping_sub</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">};</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> .<span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// .</span>
        <span class="nd">print!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span> <span class="k">as</span> <span class="nb">char</span><span class="p">);</span>
    <span class="p">};</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="p">,)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// ,</span>
        <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::</span><span class="nf">stdin</span><span class="p">()</span>
        <span class="nf">.bytes</span><span class="p">()</span><span class="nf">.next</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="p">};</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ptr:ident</span> <span class="p">[</span> <span class="nv">$</span><span class="p">(</span> <span class="nv">$body:tt</span> <span class="p">)</span><span class="o">*</span> <span class="p">])</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="c1">// [ CODE ]</span>
        <span class="k">while</span> <span class="nv">$mem</span><span class="p">[</span><span class="nv">$ptr</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
            <span class="nv">$</span><span class="p">(</span>
                <span class="nd">instr!</span><span class="p">(</span><span class="nv">$mem</span> <span class="nv">$ptr</span> <span class="nv">$body</span><span class="p">);</span>
            <span class="p">)</span><span class="o">*</span>
        <span class="p">}</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>All of these are quite obvious except for the last rule. The Rust tokenizer recognizes anything between <code class="language-plaintext highlighter-rouge">()</code>s, <code class="language-plaintext highlighter-rouge">[]</code>s, and <code class="language-plaintext highlighter-rouge">{}</code> as a single <code class="language-plaintext highlighter-rouge">tt</code>. This means we need to match this <code class="language-plaintext highlighter-rouge">tt</code> explicitly, which means we can’t just have a rule for <code class="language-plaintext highlighter-rouge">[</code> that expands to <code class="language-plaintext highlighter-rouge">while $mem[$ptr] &gt; 0 {</code> and trust that the following instructions will close our brace at some point. Instead we match <code class="language-plaintext highlighter-rouge">[</code>, followed by any number of tokens inside the braces <code class="language-plaintext highlighter-rouge">$( $body:tt )*</code>, followed by a closing <code class="language-plaintext highlighter-rouge">]</code> as a single <code class="language-plaintext highlighter-rouge">tt</code>. Then we just expand the entire loop at once, recursively calling our <code class="language-plaintext highlighter-rouge">instr!</code> macro on the tokens in our loop. This also has a side effect of validating that our braces match for us, if they don’t, the code will fail to compile, because there will be no matching rule for a lonely <code class="language-plaintext highlighter-rouge">[</code> or <code class="language-plaintext highlighter-rouge">]</code>.</p>

<h2 id="finishing-touches-and-the-first-flight">Finishing touches and the first flight</h2>
<p>So far we’ve written a macro to match brainfuck instructions, set up memory and a pointer for our program, and written rules to expand instructions into valid Rust. We need to add a couple last little tricks before we’re ready to break any speed records.</p>

<p>Much like we saw with our loop rule, the Rust language’s Tokenizer recognizes a handful of other common Brainfuck instruction combinations as a single token. Namely <code class="language-plaintext highlighter-rouge">&gt;&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;&lt;</code>, <code class="language-plaintext highlighter-rouge">..</code>, <code class="language-plaintext highlighter-rouge">...</code>, <code class="language-plaintext highlighter-rouge">-&gt;</code>, and <code class="language-plaintext highlighter-rouge">&lt;-</code>. This means that if we call our macro with something like this: <code class="language-plaintext highlighter-rouge">frick!(&gt;&gt;)</code>, we’ll get a compiler error because Rust looks for a rule matching the single token <code class="language-plaintext highlighter-rouge">&gt;&gt;</code> instead of <code class="language-plaintext highlighter-rouge">&gt;</code> twice. We can get around this limitation quite easily by just adding additional rules for each of these multi-character tokens to decompose them into their individual instructions.</p>

<p>Here are a few of them</p>
<h5 id="mainrs-6"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ...</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="o">&gt;&gt;</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="err">$</span><span class="n">ptr</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="o">..</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="err">$</span><span class="n">mem</span><span class="p">[</span><span class="err">$</span><span class="n">ptr</span><span class="p">]</span> <span class="k">as</span> <span class="nb">char</span><span class="p">;</span>
    <span class="nd">print!</span><span class="p">(</span><span class="s">"{}{}"</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="k">-&gt;</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="nd">instr!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span> <span class="o">-</span><span class="p">);</span>
    <span class="nd">instr!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span> <span class="o">&gt;</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

<p>As you can see, we have the option of just implementing the expansion as a single statement (as we did with <code class="language-plaintext highlighter-rouge">&gt;&gt;</code>), or just deferring to <code class="language-plaintext highlighter-rouge">instr!</code> for each instruction (like <code class="language-plaintext highlighter-rouge">-&gt;</code>).</p>

<p>Almost time to knock your socks off, just 2 more rules, I promise.</p>

<p>One common brainfuck pattern that we saw in brainfrick-rs was the <em>clear loop</em>: <code class="language-plaintext highlighter-rouge">[-]</code>. The clear loop is a set of 3 brainfuck instructions that repeatedly decrements the current cell until it reaches 0. We optimized this pattern in brainfrick-rs by replacing these 3 instructions with a special case <code class="language-plaintext highlighter-rouge">Clear</code> instruction to immediately set the current cell to 0 without looping. We can do the same in macrofrick by adding a special rule for it.</p>

<h5 id="mainrs-7"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="p">[</span><span class="o">-</span><span class="p">])</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// [-]</span>
    <span class="err">$</span><span class="n">mem</span><span class="p">[</span><span class="err">$</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Nice.</p>

<p>Lastly, the brainfuck specification dictates that non-brainfuck instruction characters should be ignored and treated as comments. We can include a final rule at the bottom of <code class="language-plaintext highlighter-rouge">instr!</code> to match anything else and expand to nothing.</p>

<h5 id="mainrs-8"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">other</span><span class="p">:</span><span class="n">tt</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// * poof! *        </span>
<span class="p">};</span>
</code></pre></div></div>

<p>Lets test everything out with our classic brainfuck “Hello World!”, we’ll include a few brainfuck comments in our code to be sure we ellide them correctly.</p>

<h5 id="mainrs-9"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// macros defined above!</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nd">frick!</span><span class="p">([</span><span class="n">ignore</span> <span class="n">me</span> <span class="n">please</span> <span class="p">]</span><span class="o">++++++++</span><span class="p">[</span><span class="o">&gt;++++</span><span class="p">[</span><span class="o">&gt;++&gt;+++&gt;</span><span class="n">middle</span> <span class="n">of</span> <span class="n">the</span> <span class="k">loop</span><span class="o">+++&gt;+&lt;&lt;&lt;&lt;-</span><span class="p">]</span><span class="o">&gt;+&gt;+&gt;</span><span class="k">-&gt;</span><span class="o">&gt;+</span><span class="p">[</span><span class="o">&lt;</span><span class="p">]</span><span class="o">&lt;-</span><span class="p">]</span><span class="o">&gt;&gt;</span>.<span class="o">&gt;---</span>.<span class="o">+++++++..+++</span>.<span class="o">&gt;&gt;</span>.<span class="o">&lt;-</span>.<span class="o">&lt;</span>.<span class="o">+++</span>.<span class="o">------</span>.<span class="o">--------</span>.<span class="o">&gt;&gt;+</span>.<span class="o">&gt;++</span>.<span class="p">);</span>
<span class="p">}</span> 
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">$ cargo run</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hello World!
</code></pre></div></div>

<p>Nice 😎</p>

<h2 id="mandelbrot">Mandelbrot</h2>
<p>At first glance, it might seem like this implementation should be slower than brainfrick-rs, after all, we’ve only implemented one of the many optimizations that our first compiler had. On the surface, this would be right, however, we now have a different optimizer coming into play:</p>

<p><strong>The Rust Compiler Itself.</strong></p>

<p>Since our macro expands into valid Rust code, we’re just compiling and running Rust in the end, here is a truncated expansion of our “Hello World!”.</p>

<h5 id="expanded-mainrs"><code class="language-plaintext highlighter-rouge">expanded main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">mem</span><span class="p">:</span> <span class="p">[</span><span class="n">TCell</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">];</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">ptr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">while</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span> <span class="p">};</span> <span class="c1">// our first comment, empty loop</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="k">while</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
            <span class="n">ptr</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
            <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
            <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
            <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
            <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
            <span class="k">while</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
                <span class="n">ptr</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="c1">// ... and on and on ... </span>
</code></pre></div></div>

<p>This means that by compiling in release mode, we’ll get the benefits from any optimizations Rust performs itself.</p>

<p>One great way this benefits us is compile-time evaluation of constant expressions. The Rust compiler is smart enough to combine repetitive code into single instructions. For example, the following code increments the current cell 7 times, 7 brainfuck instructions, 7 lines of Rust.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">frick!</span><span class="p">(</span><span class="o">+++++++</span><span class="p">);</span>
</code></pre></div></div>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// expanded</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">mem</span><span class="p">:</span> <span class="p">[</span><span class="n">TCell</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">;</span> <span class="mi">30_000</span><span class="p">];</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">ptr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span> <span class="o">=</span> <span class="n">mem</span><span class="p">[</span><span class="n">ptr</span><span class="p">]</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</code></pre></div></div>

<p>But when we look at the corresponding asm in Godbolt for these instructions:</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span>     <span class="kt">byte</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span><span class="p">],</span> <span class="mi">7</span>
</code></pre></div></div>

<p>We see that we stored 7 in the cell with a single <code class="language-plaintext highlighter-rouge">mov</code>. In brainfrick-rs, we performed such optimizations by hand, but here we get optimization for free alongside any others that the compiler pipeline performs on Rust code.</p>

<p>So without further ado, let’s compile the entire Mandelbrot program in release mode and see how we do:</p>

<h5 id="mainrs-10"><code class="language-plaintext highlighter-rouge">main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// macros defined above!</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nd">frick!</span><span class="p">(</span><span class="o">+++++++++++++</span><span class="p">[</span><span class="k">-&gt;</span><span class="o">++&gt;&gt;&gt;+++++&gt;++&gt;+&lt;&lt;&lt;</span> <span class="cm">/* ... snip ... */</span> <span class="p">);</span>
<span class="p">}</span> 
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">$ cargo build --release</code></p>

<p><code class="language-plaintext highlighter-rouge">$ time cargo run --release</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real    0m1.166s
user    0m0.000s
sys     0m0.000s
</code></pre></div></div>

<p>🚀</p>

<h2 id="but-wait-theres-more">But wait, there’s more!</h2>
<p>Not bad, but I know what you must be thinking, I promised you we’d get to under a second, and don’t you worry, because that’s where we’re headed.</p>

<p>While Rust is heralded for it’s performance and safety, we can squeeze even more juice out of our compiler by forgoing the safety aspect. Like other memory-safe languages, Rust provides automatic bounds checking on arrays. Unlike C, if you try to reach beyond the bounds of an array in Rust, you’ll get a runtime panic, because the Rust compiler inserts instructions to ensure you’re always accessing a valid index. Modern compilers and CPUs can make the impact of these checks nearly negligible, but if speed is the name of the game, we can forgo them all together. Maybe don’t write any banking or aerospace software with my brainfuck compiler.</p>

<p>We’ll add 2 more macros just to make our code prettier, they make use of the <code class="language-plaintext highlighter-rouge">unsafe</code> array methods <code class="language-plaintext highlighter-rouge">get_unchecked</code> and <code class="language-plaintext highlighter-rouge">get_unchecked_mut</code>.</p>

<h5 id="branchunsafe-mainrs"><code class="language-plaintext highlighter-rouge">branch:unsafe main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">get</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ident:ident</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="nv">$mem</span><span class="nf">.get_unchecked</span><span class="p">(</span><span class="nv">$ident</span><span class="p">)</span>
    <span class="p">};</span>
<span class="p">}</span>

<span class="nd">macro_rules!</span> <span class="n">set</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$mem:ident</span> <span class="nv">$ident:ident</span> <span class="nv">$value:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
        <span class="o">*</span><span class="nv">$mem</span><span class="nf">.get_unchecked_mut</span><span class="p">(</span><span class="nv">$ident</span><span class="p">)</span> <span class="o">=</span> <span class="nv">$value</span>
    <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we amend our rules to make use of these anywhere we index into <code class="language-plaintext highlighter-rouge">mem</code>.</p>

<h5 id="branchunsafe-mainrs-1"><code class="language-plaintext highlighter-rouge">branch:unsafe main.rs</code></h5>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ... snip ...</span>
<span class="k">unsafe</span> <span class="p">{</span>
    <span class="err">$</span><span class="p">(</span>
        <span class="nd">instr!</span><span class="p">(</span><span class="n">mem</span> <span class="n">ptr</span> $<span class="n">code</span><span class="p">);</span>
    <span class="p">)</span><span class="o">*</span>
<span class="p">}</span>

<span class="c1">// ... snip ...</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="o">+</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// +</span>
    <span class="nd">set!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span> <span class="nd">get!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span><span class="p">)</span><span class="nf">.wrapping_add</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
<span class="p">};</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="o">-</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// -</span>
    <span class="nd">set!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span> <span class="nd">get!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span><span class="p">)</span><span class="nf">.wrapping_sub</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
<span class="p">};</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="err">.</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// .</span>
    <span class="nd">print!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="o">*</span><span class="nd">get!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span><span class="p">)</span> <span class="k">as</span> <span class="nb">char</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="p">,)</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// ,</span>
    <span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">io</span><span class="p">::</span><span class="nf">stdin</span><span class="p">()</span>
    <span class="nf">.bytes</span><span class="p">()</span><span class="nf">.next</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="nd">set!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span> <span class="n">c</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">(</span><span class="err">$</span><span class="n">mem</span><span class="p">:</span><span class="n">ident</span> <span class="err">$</span><span class="n">ptr</span><span class="p">:</span><span class="n">ident</span> <span class="p">[</span> <span class="err">$</span><span class="p">(</span> <span class="err">$</span><span class="n">body</span><span class="p">:</span><span class="n">tt</span> <span class="p">)</span><span class="o">*</span> <span class="p">])</span> <span class="k">=&gt;</span> <span class="p">{</span>
    <span class="c1">// [ CODE ]</span>
    <span class="k">while</span> <span class="o">*</span><span class="nd">get!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
        <span class="err">$</span><span class="p">(</span>
            <span class="nd">instr!</span><span class="p">(</span>$<span class="n">mem</span> $<span class="n">ptr</span> $<span class="n">body</span><span class="p">);</span>
        <span class="p">)</span><span class="o">*</span>
    <span class="p">}</span>
<span class="p">};</span>
<span class="c1">// ... snip ...</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">$ cargo build --release</code></p>

<p><code class="language-plaintext highlighter-rouge">$ time cargo run --release</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real    0m0.818s
user    0m0.000s
sys     0m0.000s
</code></pre></div></div>

<p><strong>🚀⚡⏩</strong></p>

<p>…and whatever other emojis you associate with speed</p>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="Rust" /><category term="Metaprogramming" /><category term="Brainfuck" /><category term="Compilers" /><summary type="html"><![CDATA[Macrofrick is a Brainfuck compiler that leverages Rust’s declarative macros and efficient compiler to produce blazingly fast binaries with minimal effort.]]></summary></entry><entry><title type="html">Deep Dive: Sefarim</title><link href="http://rafibayer.com/2023/02/26/sefarim.html" rel="alternate" type="text/html" title="Deep Dive: Sefarim" /><published>2023-02-26T00:00:00+00:00</published><updated>2023-02-26T00:00:00+00:00</updated><id>http://rafibayer.com/2023/02/26/sefarim</id><content type="html" xml:base="http://rafibayer.com/2023/02/26/sefarim.html"><![CDATA[<p><a href="https://github.com/rafibayer/sefarim"><strong>Sefarim</strong></a>. A library catalog and management web application build on Django.</p>

<p><img src="/images/sefarim/sefarim.png" alt="Sefarim" /></p>

<h1 id="sefarim">Sefarim</h1>
<p><a href="https://github.com/rafibayer/sefarim">Sefarim</a> is a web application created using Django and Boostrap with a focus on ease of use and simplicity.</p>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><summary type="html"><![CDATA[Sefarim. A library catalog and management web application build on Django.]]></summary></entry><entry><title type="html">Doing my Homework with AI (again)</title><link href="http://rafibayer.com/2022/12/04/chatGPT.html" rel="alternate" type="text/html" title="Doing my Homework with AI (again)" /><published>2022-12-04T00:00:00+00:00</published><updated>2022-12-04T00:00:00+00:00</updated><id>http://rafibayer.com/2022/12/04/chatGPT</id><content type="html" xml:base="http://rafibayer.com/2022/12/04/chatGPT.html"><![CDATA[<p>Over the past year, AI tooling has advanced dramatically. The tool I am going to show you today is capable of producing answers to a wide range of abstract tasks across many different domains. I’m still going to use it to do my homework.</p>

<p>About a year ago, I posted <a href="/2021/07/25/copilot.html">an article</a> where I used <a href="https://github.com/features/copilot">Github Copilot</a> to see if a student could theoretically use it to complete homework assignments. Github Copilot is an AI-based autocomplete tool that can write code in many languages based on comments and context from surrounding code. Copilot is developed by GitHub and OpenAI and is based on the <a href="https://en.wikipedia.org/wiki/GPT-3">GPT-3</a> language model.</p>

<p><a href="https://chat.openai.com/chat">ChatGPT</a> is a new GPT-3 based AI tool also created by OpenAI that is aimed at providing even more functionality for a wider range of tasks. Today, we’ll see how far AI tooling has come by tackling the same problem, <a href="https://courses.cs.washington.edu/courses/cse143/21sp/handouts/02.pdf">LetterInventory</a>, the first homework assignment in the University of Washington’s CSE 143 course. You can take a look at last years article on Copilot for some more context on the assignment and it’s goals.</p>

<p>ChatGPT provides a chat-like interface in which you “talk” with the AI. Each query you provide is met with a response from the AI in about 5-10 seconds. Here’s a quick example, this is a real excerpt from ChatGPT:</p>

<blockquote>
  <p><strong>Me</strong>: Knock knock</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>: Who’s there?</p>
</blockquote>

<blockquote>
  <p><strong>Me</strong>: boo</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>: Boo who?</p>
</blockquote>

<blockquote>
  <p><strong>Me</strong>: Why are you crying?</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>: I am a machine learning model and do not have the ability to cry. I am programmed to assist with providing information and answering questions to the best of my knowledge. Is there something I can help you with?</p>
</blockquote>

<p>Charming.</p>

<h1 id="warmup">Warmup</h1>

<p>Before we start our assignment, let’s try some simple code samples like we did with Copilot. Whereas Copilot worked as a VSCode extension, the conversational model of ChatGPT allows you to elaborate on previous queries and responses to refine the AI’s results. Quotes may be truncated for this article.</p>

<blockquote>
  <p><strong>Me</strong>: Write a function in python to reverse a string</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>: Here is one possible implementation of a function that reverses a string in Python:</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">reverse_string</span><span class="p">(</span><span class="n">string</span><span class="p">):</span>
  <span class="k">return</span> <span class="n">string</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</code></pre></div></div>
<blockquote>
  <p>This function takes a string as input and returns a new string that is the reverse of the original string. It does this by using slicing to extract all the characters in the string in reverse order and then concatenating them together to form the new string.
Here is an example of how you could use this function:</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">input_string</span> <span class="o">=</span> <span class="s">"Hello, world!"</span>
<span class="n">reversed_string</span> <span class="o">=</span> <span class="n">reverse_string</span><span class="p">(</span><span class="n">input_string</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">reversed_string</span><span class="p">)</span> <span class="c1"># Output: "!dlrow ,olleH"
</span></code></pre></div></div>
<blockquote>
  <p>I hope this helps! Let me know if you have any questions.</p>
</blockquote>

<p>We can refine our results with further queries:</p>

<blockquote>
  <p><strong>Me</strong>: add some comments and type hints to the function</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>:</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">reverse_string</span><span class="p">(</span><span class="n">string</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="s">"""
    Reverses a given string.

    Args:
        string (str): The string to be reversed.

    Returns:
        str: The reversed string.
    """</span>
    <span class="c1"># Use slicing to reverse the string and return the result
</span>    <span class="k">return</span> <span class="n">string</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

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

<p>Lets change the requirements and see how it adapts:</p>

<blockquote>
  <p><strong>Me</strong>: I change my mind, I want the function in Rust instead</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>:</p>
</blockquote>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">reverse_string</span><span class="p">(</span><span class="n">string</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
    <span class="c1">// Create a mutable string to hold the reversed string</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">reversed</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>

    <span class="c1">// Iterate over the characters in the string in reverse order</span>
    <span class="k">for</span> <span class="n">character</span> <span class="k">in</span> <span class="n">string</span><span class="nf">.chars</span><span class="p">()</span><span class="nf">.rev</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// Push each character onto the mutable string</span>
        <span class="n">reversed</span><span class="nf">.push</span><span class="p">(</span><span class="n">character</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Return the reversed string</span>
    <span class="n">reversed</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="doing-my-homework">Doing my homework!</h1>

<p>Last time, I wanted to see how little code I could get away with writing, instead relying on copilot and comments as much as possible. In the Copilot article, I wrote the class definition, method headers, and comments for each method. I also had to prod Copilot once or twice when it got stuck.</p>

<p>Today I’ll just be copy-pasting the entire spec at once and seeing what comes out:</p>

<blockquote>
  <p><strong>Me</strong>: Help me complete this assignment in Java, respond with just a single code block, don’t include any additional explanations:</p>
</blockquote>

<blockquote>
  <p>In this programming assignment you will practice using arrays and classes. You are to implement a class called
LetterInventory that can be used to keep track of an inventory of letters of the alphabet. The constructor for the
class takes a String and computes how many of each letter are in the String. This is the information the object
keeps track of (how many a’s, how many b’s, etc). It ignores the case of the letters and ignores anything that is
not an alphabetic character (e.g., it ignores punctuation characters, digits and anything else that is not a letter).
Your class should have the following public methods.
Method Description
LetterInventory(String data) Constructs an inventory (a count) of the alphabetic letters in the given string,
ignoring the case of letters and ignoring any non-alphabetic characters.
int get(char letter) Returns a count of how many of this letter are in the inventory. Letter might
be lowercase or uppercase (your method shouldn’t care). If a nonalphabetic
character is passed, your method should throw an IllegalArgumentException.
… (truncated) …</p>
</blockquote>

<p>This is just ~4000 characters directly from the assignment PDF as well as some extra instructions about formatting.</p>

<p>And here is the result…</p>

<blockquote>
  <p><strong>ChatGPT</strong>:</p>
</blockquote>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">LetterInventory</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kt">int</span><span class="o">[]</span> <span class="n">counts</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">size</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">NUM_LETTERS</span> <span class="o">=</span> <span class="mi">26</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">LetterInventory</span><span class="o">(</span><span class="nc">String</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">counts</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[</span><span class="no">NUM_LETTERS</span><span class="o">];</span>
        <span class="n">size</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">();</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">data</span><span class="o">.</span><span class="na">length</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="kt">char</span> <span class="n">c</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">c</span> <span class="o">&gt;=</span> <span class="sc">'a'</span> <span class="o">&amp;&amp;</span> <span class="n">c</span> <span class="o">&lt;=</span> <span class="sc">'z'</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">counts</span><span class="o">[</span><span class="n">c</span> <span class="o">-</span> <span class="sc">'a'</span><span class="o">]++;</span>
                <span class="n">size</span><span class="o">++;</span>
            <span class="o">}</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">get</span><span class="o">(</span><span class="kt">char</span> <span class="n">letter</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">letter</span> <span class="o">&gt;=</span> <span class="sc">'a'</span> <span class="o">&amp;&amp;</span> <span class="n">letter</span> <span class="o">&lt;=</span> <span class="sc">'z'</span> <span class="o">||</span> <span class="n">letter</span> <span class="o">&gt;=</span> <span class="sc">'A'</span> <span class="o">&amp;&amp;</span> <span class="n">letter</span> <span class="o">&lt;=</span> <span class="sc">'Z'</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">counts</span><span class="o">[</span><span class="nc">Character</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">(</span><span class="n">letter</span><span class="o">)</span> <span class="o">-</span> <span class="sc">'a'</span><span class="o">];</span>
        <span class="o">}</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">set</span><span class="o">(</span><span class="kt">char</span> <span class="n">letter</span><span class="o">,</span> <span class="kt">int</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">letter</span> <span class="o">&gt;=</span> <span class="sc">'a'</span> <span class="o">&amp;&amp;</span> <span class="n">letter</span> <span class="o">&lt;=</span> <span class="sc">'z'</span> <span class="o">||</span> <span class="n">letter</span> <span class="o">&gt;=</span> <span class="sc">'A'</span> <span class="o">&amp;&amp;</span> <span class="n">letter</span> <span class="o">&lt;=</span> <span class="sc">'Z'</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">size</span> <span class="o">-=</span> <span class="n">counts</span><span class="o">[</span><span class="nc">Character</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">(</span><span class="n">letter</span><span class="o">)</span> <span class="o">-</span> <span class="sc">'a'</span><span class="o">];</span>
            <span class="n">counts</span><span class="o">[</span><span class="nc">Character</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">(</span><span class="n">letter</span><span class="o">)</span> <span class="o">-</span> <span class="sc">'a'</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</span><span class="o">;</span>
            <span class="n">size</span> <span class="o">+=</span> <span class="n">value</span><span class="o">;</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">value</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">();</span>
            <span class="o">}</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">size</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">size</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isEmpty</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">size</span> <span class="o">==</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">toString</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">StringBuilder</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">();</span>
        <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">'['</span><span class="o">);</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="no">NUM_LETTERS</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">counts</span><span class="o">[</span><span class="n">i</span><span class="o">];</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
                <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">((</span><span class="kt">char</span><span class="o">)(</span><span class="sc">'a'</span> <span class="o">+</span> <span class="n">i</span><span class="o">));</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="sc">']'</span><span class="o">);</span>
        <span class="k">return</span> <span class="n">sb</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">LetterInventory</span> <span class="nf">add</span><span class="o">(</span><span class="nc">LetterInventory</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">LetterInventory</span> <span class="n">sum</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LetterInventory</span><span class="o">(</span><span class="s">""</span><span class="o">);</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="no">NUM_LETTERS</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">sum</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">+</span> <span class="n">other</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
        <span class="o">}</span>
        <span class="n">sum</span><span class="o">.</span><span class="na">size</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span> <span class="o">+</span> <span class="n">other</span><span class="o">.</span><span class="na">size</span><span class="o">;</span>
        <span class="k">return</span> <span class="n">sum</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">LetterInventory</span> <span class="nf">subtract</span><span class="o">(</span><span class="nc">LetterInventory</span> <span class="n">other</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">LetterInventory</span> <span class="n">diff</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LetterInventory</span><span class="o">(</span><span class="s">""</span><span class="o">);</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="no">NUM_LETTERS</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">diff</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">-</span> <span class="n">other</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">diff</span><span class="o">.</span><span class="na">counts</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
                <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="n">diff</span><span class="o">.</span><span class="na">size</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">size</span> <span class="o">-</span> <span class="n">other</span><span class="o">.</span><span class="na">size</span><span class="o">;</span>
        <span class="k">return</span> <span class="n">diff</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This looks… good!</p>

<p>Let’s write a quick sanity check to make sure this compiles and some of the methods works:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Main</span> <span class="o">{</span>
    
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">LetterInventory</span> <span class="n">inventory</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LetterInventory</span><span class="o">(</span><span class="s">"ChatGPT wuz here! :)"</span><span class="o">);</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">inventory</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span> <span class="c1">// [aceeghhprttuwz]</span>

        <span class="n">inventory</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="sc">'x'</span><span class="o">,</span> <span class="mi">5</span><span class="o">);</span>
        <span class="nc">LetterInventory</span> <span class="n">other</span> <span class="o">=</span> <span class="n">inventory</span><span class="o">.</span><span class="na">subtract</span><span class="o">(</span><span class="k">new</span> <span class="nc">LetterInventory</span><span class="o">(</span><span class="s">"ee"</span><span class="o">));</span>

        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">other</span><span class="o">);</span> <span class="c1">// [acghhprttuwxxxxxz]</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Let that sink in for a minute… I didn’t write a single line of code for this assignment, I just pasted the spec into a free online tool and compiled the result. Like we saw with our warmup example, I could always elaborate to get ChatGPT to add comments, write unit tests, and make other modifications to satisfy any other constraints of the assignment.</p>

<p>This is incredible to me. Especially considering it uses the same language model as Copilot, the advancement made in the past year just shows how quickly the field of AI is progressing. Just like our Copilot exercise, it raises even more pressing questions about academic integrity. This model is not limited to coding either, I’ve successfully gotten it to produce satisfactory answers to all sorts of “homework” questions like:</p>
<ul>
  <li>Summarize “The Great Gatsby” in 300 words.</li>
  <li>Integrate this function, show your work: <code class="language-plaintext highlighter-rouge">ln(x - 1) / x^2</code>.</li>
  <li>Compare and contrast the concepts of direct democracy and representative democracy in terms of their structures, processes, and outcomes.</li>
</ul>

<p>In fact, ChatGPT generated some of those questions as well, I’ll let you guess which.</p>

<p>Lets see what ChatGPT thinks about itself:</p>

<blockquote>
  <p><strong>Me</strong>: What are some pros and cons of AI assistants, format the response as a table.</p>
</blockquote>

<blockquote>
  <p><strong>ChatGPT</strong>: 
Here are some pros and cons for AI-based assistants:</p>

  <table>
    <thead>
      <tr>
        <th>Pros</th>
        <th>Cons</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>- Can perform tasks quickly and accurately</td>
        <td>- May not understand complex or abstract tasks</td>
      </tr>
      <tr>
        <td>- Can operate 24/7 without needing breaks or vacation time</td>
        <td>- May not be able to handle unexpected situations or problems outside of its programmed capabilities</td>
      </tr>
      <tr>
        <td>- Can provide personalized experiences by learning an individual’s preferences and habits</td>
        <td>- May not be able to replicate the intuition, creativity, or empathy of a human</td>
      </tr>
      <tr>
        <td>- Can improve over time as it processes more data and becomes more sophisticated</td>
        <td>- May be expensive to develop and maintain, depending on the complexity of the AI and the amount of data it needs to process</td>
      </tr>
      <tr>
        <td>- Can reduce the need for human labor, potentially leading to cost savings for businesses</td>
        <td>- May raise ethical concerns, such as the potential for AI to displace human workers or the fear that AI could become too advanced and potentially pose a threat to humanity</td>
      </tr>
    </tbody>
  </table>
</blockquote>

<p>I guess we can even outsource ethics to AI now ;)</p>

<p><br />
<br /></p>

<p>I’ll leave you with this Haiku, courtesy of ChatGPT, and an accompanying painting by <a href="https://openai.com/dall-e-2/">OpenAI’s DALLE-E 2</a></p>

<blockquote>
  <p>The ocean’s depths blue  <br />
Waves crash against the shoreline  <br />
My heart is at peace.</p>
</blockquote>

<p><img src="/images/chatgpt/painting.png" alt="Painting" /></p>

<h1 id="references">References</h1>
<ul>
  <li><a href="https://courses.cs.washington.edu/courses/cse143">UW CSE 143</a></li>
  <li><a href="https://openai.com/">OpenAI</a></li>
  <li><a href="https://en.wikipedia.org/wiki/GPT-3">GPT-3</a></li>
  <li><a href="/2021/07/25/copilot.html">Doing my (past) homework with GitHub Copilot</a></li>
</ul>]]></content><author><name>rafibayer</name><email>rafibayer7+blog@gmail.com</email></author><category term="AI" /><category term="Academics" /><summary type="html"><![CDATA[Over the past year, AI tooling has advanced dramatically. The tool I am going to show you today is capable of producing answers to a wide range of abstract tasks across many different domains. I’m still going to use it to do my homework.]]></summary></entry></feed>