<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>djan.org</title>
    <subtitle>structure admirer djan</subtitle>
    <link rel="self" type="application/atom+xml" href="https://djan.org/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://djan.org/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-09-09T00:00:00+00:00</updated>
    <id>https://djan.org/atom.xml</id>
    <entry xml:lang="en">
        <title>showing online-go.com games on an e-ink inkplate display</title>
        <published>2026-09-09T00:00:00+00:00</published>
        <updated>2026-09-09T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://djan.org/blog/ogs-inkplate/"/>
        <id>https://djan.org/blog/ogs-inkplate/</id>
        
        <content type="html" xml:base="https://djan.org/blog/ogs-inkplate/">&lt;p&gt;i find deep beauty in &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?WhatIsGo&quot;&gt;the game of go&lt;/a&gt;. i&#39;ve always been fascinated with how much &lt;em&gt;meaning&lt;/em&gt; is imbued into decisions players make. this meaning makes moves memorable.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/divine-move.png&quot; alt=&quot;kifu diagram of move 78&quot;&gt;&lt;figcaption&gt;&lt;p&gt;lee sedol vs alphago &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://deepmind-media.storage.googleapis.com/alphago/pdf-files/english/ls-vs-ag4/LS%20vs%20AG%20-%20G4%20-%20English.pdf&quot;&gt;game 4 move 78&lt;/a&gt; (image generated with &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/julianandrews/sgf-render&quot;&gt;julianandrews/sgf-render&lt;/a&gt;)&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;when i started visiting my &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://www.pittsburghgo.com/index.php&quot;&gt;local go club&lt;/a&gt;, i was astounded by how dan-level players could replay our entire game (with hundreds of moves!) from memory, talking through each decision they considered and the relative strengths of each path. while i struggled to recall what (and why) i had played in a given position when prompted, they could narrate the game as if it were a story, with the deeper meaning and themes that i had failed to grasp graciously presented before me.&lt;/p&gt;
&lt;p&gt;there was a wide range of skills and attitudes at the club. some other players, closer to my own rank, would meticulously record every move that we played over the board on their phones using &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://smartgo.com/&quot;&gt;special software&lt;/a&gt;. they explained that this software could render our moves into &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?SmartGameFormat&quot;&gt;SGF&lt;/a&gt; files, which could then be analyzed with computer go software, or shared with other mentors.&lt;/p&gt;
&lt;p&gt;i was familiar with the SGF format from my time playing on the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://online-go.com&quot;&gt;online-go.com (OGS)&lt;/a&gt; servers. for any game played on the server, you can download an SGF record with &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://online-go.com/api-docs/#/games/games_sgf_._retrieve&quot;&gt;their public API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;in a very real sense, our game records are reflections of ourselves: who we are, who we&#39;ve been, and who we struggle to become. the game is like a mirror. if we play with honesty, it can help reveal our biases, our strengths, our delusions, our hunger, and our potential. i feel such a range of emotion when i revisit past games. i am grateful that post-game review has been an opportunity to develop humility, self-compassion, self-confidence, and a fighting spirit in every area of my life.&lt;/p&gt;
&lt;p&gt;relatedly, i recently acquired an &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://soldered.com/products/inkplate-4-tempera&quot;&gt;Inkplate TEMPERA4&lt;/a&gt; ESP32-WROVER 600x600 pixel e-ink display. inkplate devices come with a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/SolderedElectronics/Inkplate-Arduino-library&quot;&gt;well-documented arduino library&lt;/a&gt; with &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/SolderedElectronics/Inkplate-Arduino-library/tree/1751cbe578522e5ea9ef713f32980186bde38077/examples/Inkplate4TEMPERA&quot;&gt;extensive example code&lt;/a&gt;. the display refresh time is around 1 second, with a 200ms 1-bit mode fast partial refresh available. the arduino board definition gives access to &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/SolderedElectronics/Inkplate-Board-Definitions-for-Arduino-IDE/blob/6ddef7cecfe0690e0ea2ccd5251be214855deee7/Inkplate_core-3.0.0/boards.txt#L504&quot;&gt;4MB&lt;/a&gt; of &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://docs.arduino.cc/language-reference/en/variables/utilities/PROGMEM/&quot;&gt;flash storage&lt;/a&gt; (3MB on the application partition for firmware).&lt;/p&gt;
&lt;p&gt;with these creative constraints, i wanted to build something that was visually additive. go starts with an empty board, and stones are added over time. while the game is adversarial, players work together to fill the canvas with &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Shape&quot;&gt;shape&lt;/a&gt;, &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Moyo&quot;&gt;structure&lt;/a&gt;, &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Haengma&quot;&gt;direction&lt;/a&gt;, and &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Strategic&quot;&gt;intention&lt;/a&gt;. clarity emerges over time as possibilities collapse under &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?LifeAndDeath&quot;&gt;pressure&lt;/a&gt;, and &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Aji&quot;&gt;potential&lt;/a&gt; becomes &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Endgame&quot;&gt;concrete&lt;/a&gt;. i felt as though the inkplate tempera4 was an excellent device for displaying go.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/ogs-inkplate.webp&quot; alt=&quot;photo of inkplate displaying game&quot;&gt;&lt;figcaption&gt;&lt;p&gt;ogs-inkplate displaying a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://online-go.com/game/50646651&quot;&gt;game i played online&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;to accomplish this, i wrote &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/src/main.rs&quot;&gt;a small rust client for the OGS API&lt;/a&gt;, archived my game results in a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/src/lib.rs#L65-L92&quot;&gt;sqlite3 database&lt;/a&gt;, built an &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/engine/baduk_engine.cpp&quot;&gt;embedded go engine in C++&lt;/a&gt; handling captures, compressed my game records into C++ &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/arduino/generated_games_data.h&quot;&gt;header&lt;/a&gt; &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/arduino/generated_games_metadata.h&quot;&gt;files&lt;/a&gt;, wrote a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/tests/validator/validate_games.cpp&quot;&gt;C++ snapshot validator program&lt;/a&gt; that is &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/tests/generated_headers.rs&quot;&gt;executed from rust tests&lt;/a&gt; to ensure that my engine&#39;s replays of generated header files match the SGF files, and added a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/arduino/screenshot.cpp&quot;&gt;screenshot command&lt;/a&gt; over serial to extract pixel data from the internal framebuffer!&lt;/p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;in this article, i&#39;ll use the terms &quot;baduk&quot; and &quot;go&quot; interchangeably. baduk 바둑 is the Korean name for the game of go.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;acquiring-the-games&quot;&gt;acquiring the games&lt;a class=&quot;zola-anchor&quot; href=&quot;#acquiring-the-games&quot; aria-label=&quot;Anchor link for: acquiring-the-games&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;to get started, i needed to fetch my game records. i wrote a small rust crate, &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/src/main.rs#L102&quot;&gt;&lt;code&gt;ogs-fetch&lt;/code&gt;&lt;/a&gt;, which used the &lt;code&gt;/api/v1/players/{id}/games&lt;/code&gt; API to enumerate my game records.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;let&lt;/span&gt;&lt;span&gt; url&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; format!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;https://online-go.com/api/v1/players/&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;/games/?page_size=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;amp;page=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;amp;source=play&amp;amp;ended__isnull=false&amp;amp;height=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;amp;width=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;amp;ordering=-ended&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    user_id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; page_size&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; page&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i did not want to visualize every game record available. there are several reasons a game can come to an end, and some games might end before a move is even played. to keep the visualization consistent, i applied some filtering on game results, skipping:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;games started with &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Handicap&quot;&gt;handicap stones&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;games ended by resignation,&lt;/li&gt;
&lt;li&gt;games ended by disconnect,&lt;/li&gt;
&lt;li&gt;games ended by a timeout,&lt;/li&gt;
&lt;li&gt;games that were manually cancelled, and&lt;/li&gt;
&lt;li&gt;games that were annulled (usually a tournament match with a disqualified opponent)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot; data-name=&quot;ogs-fetch/src/lib.rs&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; fn&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; is_valid_game_outcome&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;str&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; bool&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    !&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Resignation&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        &amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; !&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        &amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; !&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Disconnect&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        &amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; !&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Cancelled&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        &amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; !&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Annulled&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        &amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;|&lt;/span&gt;&lt;span&gt; outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;contains&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;there&#39;s no reason to download SGF files for games we don&#39;t intend to render, so i stored game results from the API in a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/games.db&quot;&gt;sqlite3 database&lt;/a&gt; first, which makes &lt;code&gt;fetch-results&lt;/code&gt; and &lt;code&gt;fetch-games&lt;/code&gt; two separate steps.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;let&lt;/span&gt;&lt;span&gt; game&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; Game&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    game_id&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    black_player&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;players&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;clone&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    white_player&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;players&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;clone&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    result&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;outcome&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;clone&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    date&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span&gt;ended&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;clone&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    downloaded&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;db&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;insert_game&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;game&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;valid_count&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TABLE&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; IF&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; EXISTS&lt;/span&gt;&lt;span&gt; games (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    game_id &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; PRIMARY KEY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    black_player &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    white_player &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    result &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    date&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    downloaded &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; DEFAULT&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    created_at &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; DEFAULT&lt;/span&gt;&lt;span&gt; CURRENT_TIMESTAMP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TABLE&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; IF&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; EXISTS&lt;/span&gt;&lt;span&gt; fetch_state (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    key&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; PRIMARY KEY&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    value&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    updated_at &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; DEFAULT&lt;/span&gt;&lt;span&gt; CURRENT_TIMESTAMP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i did some rate-limiting to reduce strain on OGS infrastructure, sleeping 1 second between each request. there&#39;s an attempt at resumability here: when fetching pages, we &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/src/lib.rs#L108-L114&quot;&gt;mark the last page that we saw&lt;/a&gt; in the database (&lt;code&gt;UPDATE fetch_state SET value = ?, updated_at = CURRENT_TIMESTAMP WHERE key = &#39;last_page&#39;&lt;/code&gt;), and when fetching SGF files, we &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/src/lib.rs#L132-L140&quot;&gt;skip files that have already been downloaded&lt;/a&gt; (&lt;code&gt;SELECT game_id FROM games WHERE downloaded = 0&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;cargo&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; run&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-bin&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ogs-fetch&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; fetch-results&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-user-id&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 435842&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-board-size&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 19&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-db&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ./19x19.db&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Fetching game results for user 435842 (board size: 19x19, page size: 100)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Resuming from page 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Fetching page 1...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Found 100 games on this page&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Skipping game 34754081 (outcome: Resignation)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Skipping handicap game 32951640 (handicap: 5)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Skipping game 34082671 (outcome: Resignation)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Skipping game 32731877 (outcome: Timeout)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Skipping handicap game 32896002 (handicap: 2)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Added 19 valid games to database&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Fetching page 10...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Reached end of results (page 10 not found)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total games in database: 90&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; cargo&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; run&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-bin&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ogs-fetch&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; fetch-games&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-board-size&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 19&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-db&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ./19x19.db&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-output-dir&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ./19x19-sgfs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Fetching SGF files to directory: ./19x19-sgfs (board size: 19x19)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Found 90 games to download&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Downloaded 1/90 (1 total) - Game 45641686&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Downloaded 2/90 (2 total) - Game 49763279&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Downloaded 3/90 (3 total) - Game 49853014&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Downloaded 4/90 (4 total) - Game 49950251&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Downloaded 5/90 (5 total) - Game 50646651&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Download complete!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Downloaded 90 out of 90 total games&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;i ended up with 90 games (21,277 moves), which seemed more than enough.&lt;/p&gt;
&lt;p&gt;if i displayed each position for 8 seconds, that means my display has almost 48 hours of gameplay to observe!&lt;/p&gt;
&lt;h1 id=&quot;choosing-a-data-representation&quot;&gt;choosing a data representation&lt;a class=&quot;zola-anchor&quot; href=&quot;#choosing-a-data-representation&quot; aria-label=&quot;Anchor link for: choosing-a-data-representation&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;i wanted the display to function even without internet access, so i chose to store the game replay data on the device. implementing even a subset of parsing the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://www.red-bean.com/sgf/&quot;&gt;SGF specification&lt;/a&gt; in C++ felt intimidating, but i didn&#39;t need to: i just needed to translate the SGF file into a sequence of moves that the arduino code could understand and replay.&lt;/p&gt;
&lt;p&gt;the simplest encoding (which does not require implementing any game logic on the device) is to record the state of the board at every move. this representation is repetitive: instead of storing a single coordinate for each move, we would be storing the state of 19x19=361 coordinates &lt;strong&gt;for every move&lt;/strong&gt;, and each coordinate could be black, white or empty.&lt;/p&gt;
&lt;p&gt;if we naively used one byte per cell, 21,277 moves requires 7.68MB (21,277 moves * 361 cells * 1 byte), which already exceeds the flash storage we have available! we could use bit-packing to get that down to 2 bits per cell (since we have 3 possible states, we need at least 2), but we&#39;ll never reach the same efficiency as storing the moves by themselves - it&#39;ll still be at least 1.92MB (21,277 moves * 361 cells * 2 bits).&lt;/p&gt;
&lt;p&gt;instead of storing every intermediate state, i stored the sequence of moves themselves. this makes the board state derived data: we apply our compact representation against an empty board and build it up step-by-step to reconstruct the corresponding position. since we show games sequentially, this isn&#39;t expensive: we are only evaluating one board, and one move, at a time.&lt;/p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;this made generating headers simpler, but required the arduino code to resolve capture logic. thankfully, this wasn&#39;t a problem: the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?RuleOfCapture&quot;&gt;capture rules in go&lt;/a&gt; are very simple to implement.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;there are 361 points on a 19x19 board. we can assign every coordinate on the board a unique number. the numbering i chose is &lt;code&gt;(col * board_size) + row&lt;/code&gt;, with zero-based indexing.&lt;/p&gt;
&lt;p&gt;that means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the top left corner &lt;code&gt;(0,0)&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;the top right corner &lt;code&gt;(18,0)&lt;/code&gt; is &lt;code&gt;342&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;the bottom left corner &lt;code&gt;(0,18)&lt;/code&gt; is &lt;code&gt;18&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;the bottom right corner &lt;code&gt;(18,18)&lt;/code&gt; is &lt;code&gt;360&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;if we reserve &lt;code&gt;0xFFFF&lt;/code&gt; for a pass, &lt;code&gt;uint16_t&lt;/code&gt; is sufficient to encode a move.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;let&lt;/span&gt;&lt;span&gt; position&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;col_num&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; +&lt;/span&gt;&lt;span&gt; row_num&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; u16&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;in retrospect, this was a bit awkward. there are 3 separate incompatible coordinate systems at play:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SGF Coordinates&lt;/strong&gt; are &lt;em&gt;pairs of letters (col, row)&lt;/em&gt;, with an origin at the &lt;strong&gt;top-left&lt;/strong&gt; (&lt;code&gt;(a,a) -&amp;gt; (s,s)&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;My encoding&lt;/strong&gt; is built from &lt;em&gt;pairs of integers (col, row)&lt;/em&gt;, with an origin at the &lt;strong&gt;top-left&lt;/strong&gt; (&lt;code&gt;(0,0) -&amp;gt; (18,18)&lt;/code&gt;), and&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go coordinates&lt;/strong&gt; are a &lt;em&gt;lettered column and a numbered row&lt;/em&gt; , which &lt;strong&gt;skips the letter I because it looks like 1&lt;/strong&gt;  (?!), with an origin at the &lt;strong&gt;bottom-left&lt;/strong&gt; (&lt;code&gt;A1 -&amp;gt; T19&lt;/code&gt;: the lower left point at &lt;code&gt;A1&lt;/code&gt;, and the upper right point at &lt;code&gt;T19&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;go coordinates are weird!&lt;/p&gt;
&lt;p&gt;to demonstrate with an example, here&#39;s the encoding described above with a &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://online-go.com/game/9632069&quot;&gt;very short game&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;short game (header file)&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Game 86: 143 moves (from 9632069.sgf)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; uint16_t&lt;/span&gt;&lt;span&gt; GAME_86_MOVES&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    288&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 59&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 281&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 72&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 249&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 174&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 244&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 85&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    259&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 277&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 297&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 45&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 320&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; BADUK_PASS_MOVE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; BADUK_PASS_MOVE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the first move is &lt;code&gt;288&lt;/code&gt;. since our encoding means &lt;code&gt;288 = (col * 19) + row&lt;/code&gt;, integer division gives us the column, and the remainder gives us the row:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;engine/baduk_engine.cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  col &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; encoded_move &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;/&lt;/span&gt;&lt;span&gt; BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  row &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; encoded_move &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;%&lt;/span&gt;&lt;span&gt; BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;some quick maths:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; position&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 288&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 19&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; position&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; //&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; row_from_top&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; position&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; %&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; row_from_bottom&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; board_size&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; -&lt;/span&gt;&lt;span&gt; row_from_top&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; row_from_top&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; row_from_bottom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;15&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 16&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;15&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;so, we&#39;d expect the first stone to be placed at Q16. our transformation between representations is beginning to unfold:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;representation&lt;/th&gt;&lt;th&gt;first move&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;SGF&lt;/td&gt;&lt;td&gt;;B[pd]&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;integer coordinates&lt;/td&gt;&lt;td&gt;(15, 3)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;encoded value&lt;/td&gt;&lt;td&gt;288&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;go coordinates&lt;/td&gt;&lt;td&gt;Q16&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/short-game.png&quot; alt=&quot;kifu diagram of short game&quot;&gt;&lt;figcaption&gt;&lt;p&gt;short game (kifu)&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;ok! confusing, but it&#39;s consistent, and we don&#39;t need to think about it too hard.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; test_parse_sgf_moves&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;    //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Test with proper SGF game structure&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    let&lt;/span&gt;&lt;span&gt; sgf&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;(;FF[4]GM[1];B[pd];W[dp];B[pq])&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    let&lt;/span&gt;&lt;span&gt; moves&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; parse_sgf_moves&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sgf&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 19&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;    assert_eq!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;moves&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;len&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;    //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Verify first move is pd -&amp;gt; (15, 3) -&amp;gt; 15*19+3 = 288&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;    assert_eq!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;moves&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 288&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;generating-firmware-header-files&quot;&gt;generating firmware header files&lt;a class=&quot;zola-anchor&quot; href=&quot;#generating-firmware-header-files&quot; aria-label=&quot;Anchor link for: generating-firmware-header-files&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;the choices above established my data format, for better or for worse:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;moves are &lt;code&gt;uint16_t&lt;/code&gt; numbers,&lt;/li&gt;
&lt;li&gt;games are immutable structs in flash memory, with
&lt;ul&gt;
&lt;li&gt;a pointer to an immutable sequence of moves (also in flash memory), and&lt;/li&gt;
&lt;li&gt;an immutable total move count,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;a board is a &lt;em&gt;mutable&lt;/em&gt; 19x19 array of &lt;code&gt;uint8_t&lt;/code&gt; numbers in RAM, where
&lt;ul&gt;
&lt;li&gt;0 represents &lt;code&gt;BADUK_EMPTY&lt;/code&gt; (an unoccupied square),&lt;/li&gt;
&lt;li&gt;1 represents &lt;code&gt;BADUK_BLACK&lt;/code&gt; (a black stone), and&lt;/li&gt;
&lt;li&gt;2 represents &lt;code&gt;BADUK_WHITE&lt;/code&gt; (a white stone)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;the board keeps track of some additional mutable state:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the index of the current game being played,&lt;/li&gt;
&lt;li&gt;the index of the last move played,&lt;/li&gt;
&lt;li&gt;the total number of moves in the current game,&lt;/li&gt;
&lt;li&gt;the last row updated, and&lt;/li&gt;
&lt;li&gt;the last column updated&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;these help render the board onto the screen with appropriate metadata after the board has been updated with the previous move.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;engine/baduk_types.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_BOARD_SIZE&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 19&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_EMPTY&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_BLACK&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_WHITE&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_NO_LAST_MOVE&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 255&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_PASS_MOVE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;FFFF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;struct&lt;/span&gt;&lt;span&gt; BadukGameRecord &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; uint16_t&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;moves&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; move_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;struct&lt;/span&gt;&lt;span&gt; BadukState &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; board&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; move_index&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; move_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span&gt; last_row&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span&gt; last_col&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;after we&#39;ve generated every game, we create a &lt;code&gt;BadukGameRecord GAMES[]&lt;/code&gt; table and store the total &lt;code&gt;GAME_COUNT&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/generated_games_data.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span&gt; BadukGameRecord GAMES&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt; GAME_0_MOVES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 216&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt; GAME_89_MOVES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 232&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; uint16_t&lt;/span&gt;&lt;span&gt; GAME_COUNT &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 90&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;each &lt;code&gt;BadukGameRecord&lt;/code&gt; in the &lt;code&gt;GAMES&lt;/code&gt; table is small: just a count of total moves in the game, and a pointer to those moves. we can quickly copy any &lt;code&gt;BadukGameRecord&lt;/code&gt; into RAM without copying the moves array itself, and we can use the &lt;code&gt;moves&lt;/code&gt; pointer to retrieve the next move from flash whenever it&#39;s needed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GameMetadata&lt;/code&gt; is similar: it&#39;s a small struct with pointers to strings in flash memory, so we can copy it into SRAM and use the pointers to look up these immutable strings at runtime:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/game_metadata.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;struct&lt;/span&gt;&lt;span&gt; GameMetadata &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;black_player&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;white_player&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;black_rank&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;white_rank&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/generated_games_metadata.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_0_DATE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;2017-09-23&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_0_BLACK&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;djanatyn&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_0_WHITE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;wang.bigstone1&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_0_BLACK_RANK&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;12k&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_0_WHITE_RANK&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;1k&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_1_DATE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;2017-09-28&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_1_BLACK&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;djanatyn&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_1_WHITE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;aliebling&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_1_BLACK_RANK&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;11k&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; char&lt;/span&gt;&lt;span&gt; GAME_1_WHITE_RANK&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;10k&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span&gt; GameMetadata GAMES_METADATA&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;[]&lt;/span&gt;&lt;span&gt; PROGMEM &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt; GAME_0_DATE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_0_BLACK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_0_WHITE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_0_BLACK_RANK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_0_WHITE_RANK &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt; GAME_1_DATE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_1_BLACK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_1_WHITE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_1_BLACK_RANK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_1_WHITE_RANK &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt; GAME_2_DATE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_2_BLACK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_2_WHITE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_2_BLACK_RANK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_2_WHITE_RANK &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span&gt; GAME_89_DATE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_89_BLACK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_89_WHITE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_89_BLACK_RANK&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_89_WHITE_RANK &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it would make sense to generate these headers with a structured template library like &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://docs.rs/tera/latest/tera/&quot;&gt;tera&lt;/a&gt;, but i just &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/ogs-inkplate/blob/b82a100bbc9eff71fc71f7e088d275879e9bcdf6/ogs-fetch/src/compression.rs#L268-L332&quot;&gt;concatenated strings very carefully&lt;/a&gt; this time: the codegen is less interesting than the generated code.&lt;/p&gt;
&lt;p&gt;i wrapped this header generation in a &lt;code&gt;compress-games&lt;/code&gt; subcommand:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; cargo&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; run&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-bin&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ogs-fetch&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; compress-games&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-input-dir&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; 19x19-sgfs/&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-board-size&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 19&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-output-dir&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; ../arduino&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-max-games&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Compressing games from directory: 19x19-sgfs/ (board size: 19x19)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Max games to compress: 1000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Compressed game 1 (216 moves) from 10069202.sgf (djanatyn vs wang.bigstone1 on 2017-09-23)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Compressed game 2 (240 moves) from 10118120.sgf (djanatyn vs aliebling on 2017-09-28)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Compressed game 90 (232 moves) from 9973821.sgf (djanatyn vs Tatapaulette on 2017-09-13)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Compressed 90 games total&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Compression Statistics:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Total games: 90&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Total moves: 21277&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Average moves per game: 236&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Estimated data size: 42826 bytes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Generating C header files in: ../arduino&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Generated: ../arduino/generated_games_data.h&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Generated: ../arduino/generated_games_metadata.h&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Compression complete!&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when we compile, our &lt;code&gt;PROGMEM&lt;/code&gt; objects become part of the firmware image, stored in flash memory on the ESP32-WROVER :)&lt;/p&gt;
&lt;h1 id=&quot;reading-generated-data-from-flash-memory&quot;&gt;reading generated data from flash memory&lt;a class=&quot;zola-anchor&quot; href=&quot;#reading-generated-data-from-flash-memory&quot; aria-label=&quot;Anchor link for: reading-generated-data-from-flash-memory&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;we want to avoid loading every game&#39;s set of moves into SRAM. instead, we load pointers to game moves!&lt;/p&gt;
&lt;p&gt;you&#39;ll recall that our &lt;code&gt;BadukGameRecord&lt;/code&gt; struct is small when initialized in flash memory, and only includes a pointer to moves (not the moves directly):&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;struct&lt;/span&gt;&lt;span&gt; BadukGameRecord &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; uint16_t&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;moves&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; move_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;when a game begins, we can copy an initialized struct from flash into SRAM:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BadukGameRecord game&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;memcpy_P&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;game&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;GAMES&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;game_index&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; sizeof&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BadukGameRecord&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;memcpy_P&lt;/code&gt; copies the struct into SRAM, but doesn&#39;t follow the pointer. instead, when we need to read a move, we use the &lt;code&gt;game.moves&lt;/code&gt; pointer to retrieve the appropriate &lt;code&gt;move_index&lt;/code&gt; from flash using &lt;code&gt;pgm_read_word&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;encoded_move &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; pgm_read_word&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;game.&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;moves&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;move_index&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;metadata works similarly, but instead of using &lt;code&gt;pgm_read_word&lt;/code&gt;, we use &lt;code&gt;strcpy_P&lt;/code&gt; to retrieve the string from flash memory into temporary buffers in SRAM:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/display.cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Read metadata from PROGMEM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;GameMetadata metadata&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;memcpy_P&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;metadata&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;GAMES_METADATA&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;baduk_state&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;game_index&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        sizeof&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;GameMetadata&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; dateBuffer&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;11&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;strcpy_P&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dateBuffer&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; metadata.date&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;display&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dateBuffer&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;processing-moves&quot;&gt;processing moves&lt;a class=&quot;zola-anchor&quot; href=&quot;#processing-moves&quot; aria-label=&quot;Anchor link for: processing-moves&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;ok: we have our game data and we can access it at runtime. now that we have access to our moves, we need code to evaluate a move against a board!&lt;/p&gt;
&lt;p&gt;if you recall that this is our state:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;struct&lt;/span&gt;&lt;span&gt; BadukState &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; board&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; move_index&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; move_count&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span&gt; last_row&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span&gt; last_col&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we&#39;ll need some functions to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reset the state (clear the board for the next game),&lt;/li&gt;
&lt;li&gt;load a game into memory, (setting up the board&#39;s &lt;code&gt;game_index&lt;/code&gt;, &lt;code&gt;move_index&lt;/code&gt;, and &lt;code&gt;move_count&lt;/code&gt;), and&lt;/li&gt;
&lt;li&gt;to play an encoded move (given a &lt;code&gt;color&lt;/code&gt;, because players can pass)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;engine/baduk_engine.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; baduk_reset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BadukState &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; baduk_load_game&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BadukState &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    const&lt;/span&gt;&lt;span&gt; BadukGameRecord &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;games&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    uint16_t&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; game_count&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    uint16_t&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; baduk_play_next_move&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BadukState &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    const&lt;/span&gt;&lt;span&gt; BadukGameRecord &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;games&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; baduk_play_move&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BadukState &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    uint16_t&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; encoded_move&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    uint8_t&lt;/span&gt;&lt;span&gt; color&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;building-a-baduk-engine&quot;&gt;building a baduk engine&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-a-baduk-engine&quot; aria-label=&quot;Anchor link for: building-a-baduk-engine&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;placing a stone involves 5 steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;decode the &lt;code&gt;uint16_t&lt;/code&gt; move into a (row, col) coordinate,&lt;/li&gt;
&lt;li&gt;place the stone temporarily, pending validation,&lt;/li&gt;
&lt;li&gt;remove neighboring enemy groups with no liberties,&lt;/li&gt;
&lt;li&gt;make sure the placed stone&#39;s group has a liberty, and finally&lt;/li&gt;
&lt;li&gt;freak out if the placed stone is a suicide (that&#39;s not legal!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;calculating neighbors is just applying cardinal offsets to the target coordinate, and then looping through &lt;code&gt;next_row&lt;/code&gt; and &lt;code&gt;next_col&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; dirs&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;4&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;2&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;1&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;board&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_row&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_col&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; ==&lt;/span&gt;&lt;span&gt; enemy&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; visited&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;baduk_group_has_liberty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_row&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_col&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; visited&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;    baduk_remove_group&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_row&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; next_col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tracking liberties is a prerequisite, and more complex. we use depth-first search, pushing the coordinate of neighboring friendly stones onto a stack, and visiting each neighbor exactly once. if we ever find an empty neighboring point for &lt;em&gt;any&lt;/em&gt; adjacent friendly stones, then the group is (currently) alive, and we terminate early. if we exhaust our stack and fail to find &lt;em&gt;any&lt;/em&gt; empty neighboring points, we&#39;ve checked every stone in the group, and none have liberties: the group is captured.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;board&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_row&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_col&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; ==&lt;/span&gt;&lt;span&gt; color &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; visited&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_row&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_col&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;    visited&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_row&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;next_col&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;    stack_row&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;stack_size&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; next_row&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;    stack_col&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;stack_size&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; next_col&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    stack_size&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;++&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we have to resolve captures before checking &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://senseis.xmp.net/?Suicide&quot;&gt;suicide rules&lt;/a&gt; because a move that initially has no liberties might &lt;em&gt;gain&lt;/em&gt; liberties after it captures adjacent enemy groups. this is a common situation when fighting for life and death inside of an eye space. if we checked for suicide first, we would reject all of those killing moves as invalid. instead, we resolve captures first, and &lt;em&gt;then&lt;/em&gt; check for validity.&lt;/p&gt;
&lt;p&gt;we don&#39;t expect to encounter any invalid moves (after all, these games have already finished!), but if we do, it&#39;s informative: we&#39;ve likely introduced some logic error in another part of our code.&lt;/p&gt;
&lt;p&gt;our &lt;code&gt;baduk_remove_group&lt;/code&gt; function works very similarly to the &lt;code&gt;baduk_group_has_liberty&lt;/code&gt; and uses depth-first search. instead of checking for liberties, it just removes adjacent connected stones, replacing them with &lt;code&gt;BADUK_EMPTY&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;how-do-we-know-our-engine-works&quot;&gt;how do we know our engine works?&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-do-we-know-our-engine-works&quot; aria-label=&quot;Anchor link for: how-do-we-know-our-engine-works&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;don&#39;t let yourself think i got this right the first time: i forgot that passes exist :&#39;) the engine needs some external validation that it&#39;s functioning properly.&lt;/p&gt;
&lt;p&gt;this was much easier than i expected using snapshot testing!&lt;/p&gt;
&lt;p&gt;if we wanted to be sure our engine evaluated moves correctly, it would help to have &lt;em&gt;another&lt;/em&gt; engine that we were confident is correct. if we believed in this Hypothetical Probably Correct Engine, we could compare the results of the two engines with the same sets of moves.&lt;/p&gt;
&lt;p&gt;if the snapshots agree at each step, then both implementations agree on positions and rules, which is a strong indicator that our hand-written engine matches the logic of the Hypothetical Probably Correct Engine (or at least its evaluation of moves and positions)!&lt;/p&gt;
&lt;p&gt;if any two steps diverge, we know 1). where our logic fails, 2). what the expected result is, and 3). what our computed result is, which is helpful information for debugging.&lt;/p&gt;
&lt;p&gt;fortunately, that Hypothetical Probably Correct Engine exists, and it is called the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://lib.rs/crates/goban&quot;&gt;&lt;code&gt;goban&lt;/code&gt; crate&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Library to play with a rusty &quot;Goban&quot; (name of the board where we play Go !), It&#39;s built with performance in mind. The library can perform a full playout of a random game in 1.5 ms checking all legal moves.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;conceptually, if our &lt;code&gt;engine/baduk_engine.cpp&lt;/code&gt; code is correct, and we generate snapshots of each board position from our header file, it should match the snapshots generated from the &lt;code&gt;goban&lt;/code&gt; crate &lt;em&gt;exactly&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; generated_headers_match_sgf_replay&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    let&lt;/span&gt;&lt;span&gt; root&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; Path&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;env!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;CARGO_MANIFEST_DIR&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;    assert_eq!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;goban_snapshots&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; validator_snapshots&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this rust test will both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;run &lt;code&gt;goban&lt;/code&gt; to generate snapshots from a parsed SGF game, and also&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;validate_games.cpp&lt;/code&gt; (with the shared engine code) to generate snapshots from a compiled header file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;you&#39;ll recall that our games are normally stored in flash memory with the &lt;code&gt;PROGMEM&lt;/code&gt; macro. to make this work outside of the device, we have a small &lt;code&gt;baduk_platform.h&lt;/code&gt; that uses conditional preprocessor macros to stub the arduino functions (&lt;code&gt;memcpy_P&lt;/code&gt;, &lt;code&gt;pgm_read_word&lt;/code&gt;, and &lt;code&gt;pgm_read_byte&lt;/code&gt;), and to make &lt;code&gt;PROGMEM&lt;/code&gt; a no-op:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;ifndef&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_PLATFORM_H&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BADUK_PLATFORM_H&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;ifdef&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; ARDUINO&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Arduino.h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;stdint.h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;string.h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;ifndef&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; PROGMEM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; PROGMEM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;ifndef&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; memcpy_P&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; memcpy_P&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;dest&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; src&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; memcpy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dest&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;ifndef&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; pgm_read_word&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; pgm_read_word&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;addr&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; uint16_t&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;addr&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;ifndef&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; pgm_read_byte&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; pgm_read_byte&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;addr&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; uint8_t&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;addr&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this means we can use the same functions in our tests that we use on our embedded device, with &lt;code&gt;baduk_platform.h&lt;/code&gt; handling the differences.&lt;/p&gt;
&lt;p&gt;we have enough context to look at the entire implementation now. the &lt;code&gt;validate_games&lt;/code&gt; C++ program just loops through games printing out computed positions:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;ogs-fetch/tests/validator/validate_games.cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;baduk_engine.h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;generated_games_data.h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;stdio.h&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; print_board&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BadukState &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;state&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span&gt; row&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint8_t&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;row &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; row &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; row&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;++&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;col &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; col &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; BADUK_BOARD_SIZE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;++&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;      printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;board&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;void&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  BadukState state&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;game_index &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; game_index &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; GAME_COUNT&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;++&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;baduk_load_game&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAMES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAME_COUNT&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;      printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;load_error &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;      return&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;    printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;game &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; moves &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;move_count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;move_index&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;move_count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;      if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;baduk_play_next_move&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; GAMES&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;        printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;move_error &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; %u&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; game_index&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;move_index&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; +&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;      printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;move &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;%u&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;move_index&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;      print_board&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;      printf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  return&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;our &lt;code&gt;goban&lt;/code&gt; code does the same thing, with a few tweaks to respect the crate&#39;s conventions:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; goban_snapshots&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;Path&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; Vec&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;Vec&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;String&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;    fixture_paths&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        .&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;iter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        .&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;|&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;            let&lt;/span&gt;&lt;span&gt; sgf&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; fs&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;read_to_string&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;expect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;failed to read SGF fixture&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;            let&lt;/span&gt;&lt;span&gt; moves&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; parse_sgf_moves&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;sgf&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; BOARD_SIZE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; u32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;            let&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; mut&lt;/span&gt;&lt;span&gt; game&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; Game&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;GobanSizes&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;Nineteen&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; CHINESE&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;            let&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; mut&lt;/span&gt;&lt;span&gt; snapshots&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; Vec&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;            for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;move_index&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; encoded_move&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; in&lt;/span&gt;&lt;span&gt; moves&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;iter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;enumerate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;                let&lt;/span&gt;&lt;span&gt; move_to_play&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; if&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;encoded_move&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; PASS_MOVE&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;                    Move&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;Pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;                    let&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;encoded_move&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; BOARD_SIZE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; u16&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;                    let&lt;/span&gt;&lt;span&gt; row&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; *&lt;/span&gt;&lt;span&gt;encoded_move&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; %&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; BOARD_SIZE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; u16&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;                    Move&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;Play&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; u8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; col&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; as&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; u8&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                game&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;try_play&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;move_to_play&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;unwrap_or_else&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;|&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;|&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;                    panic!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;                        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;Goban rejected move &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; in &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;:?&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;error:?&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        move_index&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; +&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                snapshots&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;board_snapshot&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;game&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            snapshots&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;        .&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;collect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you&#39;ll note that both paths use the same sgf parsing and move encoding logic, which we assume is correct. the goal is to validate that the C++ engine code returns the same positions as our &lt;code&gt;goban&lt;/code&gt; crate.&lt;/p&gt;
&lt;p&gt;as you saw previously, &lt;code&gt;assert_eq!&lt;/code&gt; is all we need at this point:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; cargo&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; test&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-test&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; generated_headers&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;    generated_headers_match_sgf_replay&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-nocapture&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;running 1 test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Compressed game 1 (250 moves) from 31064633.sgf (shelly613 vs djanatyn on 2021-02-13)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Compressed game 2 (231 moves) from 50646651.sgf (djanatyn vs Arbitraria on 2023-01-30)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Compressed game 3 (279 moves) from 65134095.sgf (Rembane vs djanatyn on 2024-06-13)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Compressed 3 games total&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;test generated_headers_match_sgf_replay ... ok&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.21s&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we&#39;re only using 3 test fixture SGF games to validate in this test, as opposed to our previous 90 games downloaded.&lt;/p&gt;
&lt;p&gt;we could do more, but i&#39;d say that&#39;s good enough for me to ship!&lt;/p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;there are more rules to go that i did not validate, including some rules that goban implements, but my engine does not. as an example, my implementation does not attempt to respect ko rules. it&#39;s ok, somebody else will check.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;shuffling-game-order&quot;&gt;shuffling game order&lt;a class=&quot;zola-anchor&quot; href=&quot;#shuffling-game-order&quot; aria-label=&quot;Anchor link for: shuffling-game-order&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;i was admittedly surprised that i did not have access to an array shuffling function. i used the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle&quot;&gt;Fisher-Yates&lt;/a&gt; algorithm, which is functionally equivalent to repeatedly selecting a random card from remaining cards in a deck, until the deck is empty.&lt;/p&gt;
&lt;p&gt;the &lt;code&gt;GAMES[]&lt;/code&gt; array in flash is immutable, so i introduced a small &lt;code&gt;GameOrder&lt;/code&gt; struct to translate sequential indexes into their shuffled equivalent:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/game_order.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;struct&lt;/span&gt;&lt;span&gt; GameOrder &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; games&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;GAME_COUNT&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; index&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the &lt;code&gt;games[GAME_COUNT]&lt;/code&gt; field is just an array of indexes into &lt;code&gt;GAMES[]&lt;/code&gt;. the field starts off sequential, but gets shuffled to mix up the ordering each time the device boots:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/game_order.cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;i &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; GAME_COUNT &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;--&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; j &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; random&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;i &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  uint16_t&lt;/span&gt;&lt;span&gt; tmp &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;games&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  order&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;games&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; order&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;games&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;j&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  order&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;games&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;j&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; tmp&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;there&#39;s a &lt;code&gt;config.h&lt;/code&gt; setting to disable this if you&#39;d like, but i prefer to leave it on for variety.&lt;/p&gt;
&lt;h1 id=&quot;rendering-the-board&quot;&gt;rendering the board&lt;a class=&quot;zola-anchor&quot; href=&quot;#rendering-the-board&quot; aria-label=&quot;Anchor link for: rendering-the-board&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;rendering is driven by preprocessor &lt;code&gt;#define&lt;/code&gt; constants as much as possible:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/config.h&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; GRID_SPACING&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 26&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Space between grid lines (px)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; ACTUAL_BOARD_SIZE&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;GRID_SPACING &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; 468px&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; HEADER_HEIGHT&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 40&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Space for game/move text at top&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BOARD_OFFSET_X&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;                                                         \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; -&lt;/span&gt;&lt;span&gt; ACTUAL_BOARD_SIZE&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; center horizontally = 66px&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BOARD_OFFSET_Y&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;                                                         \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; -&lt;/span&gt;&lt;span&gt; HEADER_HEIGHT &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span&gt; ACTUAL_BOARD_SIZE&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; +&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;                             \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   HEADER_HEIGHT&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;                      //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; center vertically with header margin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BOARD_WIDTH&lt;/span&gt;&lt;span&gt; ACTUAL_BOARD_SIZE&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; 468px&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; BOARD_HEIGHT&lt;/span&gt;&lt;span&gt; ACTUAL_BOARD_SIZE&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; 468px&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; STONE_RADIUS&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 11&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Stone radius (slightly smaller than spacing)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;converting go coordinates to on-screen pixels is essential, computed with &lt;code&gt;BOARD_OFFSET_X&lt;/code&gt;, &lt;code&gt;BOARD_OFFSET_Y&lt;/code&gt;, and &lt;code&gt;GRID_SPACING&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot; data-name=&quot;arduino/display.cpp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_x&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; board_col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  return&lt;/span&gt;&lt;span&gt; BOARD_OFFSET_X &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;+&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;board_col &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt; GRID_SPACING&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_y&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; board_row&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  return&lt;/span&gt;&lt;span&gt; BOARD_OFFSET_Y &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;+&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;board_row &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt; GRID_SPACING&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we can render the entire board to the framebuffer before we add any stones. the internal top-level function for this is &lt;code&gt;display_draw_board&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_draw_board&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Inkplate &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;display&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;setTextSize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;setTextColor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BLACK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Draw grid lines&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;  display_draw_grid_lines&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Draw hoshi (star) points&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;  display_draw_hoshi_points&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Draw board edge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;drawRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;display_pixel_x&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_y&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    GRID_SPACING &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    GRID_SPACING &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BADUK_BOARD_SIZE &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; BLACK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the arduino inkplate library makes it very easy to draw lines, shapes, and text, especially when we have authoritative &lt;code&gt;display_pixel_x&lt;/code&gt; and &lt;code&gt;display_pixel_y&lt;/code&gt; functions.&lt;/p&gt;
&lt;p&gt;it was quite fun to enumerate the hoshi points:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; void&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_draw_hoshi_points&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Inkplate &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;display&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  //&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; Hoshi (star) points on a 19x19 board&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  int&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt; hoshis&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;9&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;2&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 3&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 9&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 15&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;9&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 3&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;9&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 9&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;9&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 15&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;15&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 3&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;15&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 9&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;15&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 15&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;  for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;int&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 9&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;++&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    int&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_x&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;hoshis&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    int&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_y&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-12&quot;&gt;hoshis&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;1&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;fillCircle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; BLACK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;these &lt;code&gt;display_pixel_x&lt;/code&gt; and &lt;code&gt;display_pixel_y&lt;/code&gt; functions really do drive everything, which is handy when tweaking the layout. once we&#39;ve finished rendering our board, it tells us exactly where to draw the stones:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;cell &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;==&lt;/span&gt;&lt;span&gt; BADUK_BLACK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;fillCircle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;display_pixel_x&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_y&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      STONE_RADIUS&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; BLACK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; else&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;cell &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;==&lt;/span&gt;&lt;span&gt; BADUK_WHITE&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;drawCircle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;display_pixel_x&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_y&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      STONE_RADIUS&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; BLACK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;fillCircle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;display_pixel_x&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;col&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; display_pixel_y&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      STONE_RADIUS &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; 1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; WHITE&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;wow-so-cool-can-i-try-it&quot;&gt;wow! so cool! can i try it?&lt;a class=&quot;zola-anchor&quot; href=&quot;#wow-so-cool-can-i-try-it&quot; aria-label=&quot;Anchor link for: wow-so-cool-can-i-try-it&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;thank you, friendly reader! if you have an inkplate TEMPERA4, of course you can!&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; run setup.sh first to install dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;arduino-cli&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; compile&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-upload&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;-fqbn&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; soldered-inkplate-boards:esp32:Inkplate4TEMPERA:UploadSpeed=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;115200&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt;    arduino&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;    -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; /dev/cu.usbserial-1410&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; #&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; replace this with your usb serial device!&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;what-s-next-for-you&quot;&gt;what&#39;s next for you?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next-for-you&quot; aria-label=&quot;Anchor link for: what-s-next-for-you&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;a break. but after that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;support for both 19x19 and 9x9 games,&lt;/li&gt;
&lt;li&gt;reading games from a microSD card,&lt;/li&gt;
&lt;li&gt;explaining the screenshot functionality in a subsequent blog post,&lt;/li&gt;
&lt;li&gt;touch-screen controls,&lt;/li&gt;
&lt;li&gt;frontlight controls,&lt;/li&gt;
&lt;li&gt;a battery display&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>understanding melee peach item pull rng logic</title>
        <published>2023-12-20T00:00:00+00:00</published>
        <updated>2023-12-20T00:00:00+00:00</updated>
        
        <author>
          <name>Unknown</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://djan.org/blog/peach-rng/"/>
        <id>https://djan.org/blog/peach-rng/</id>
        
        <content type="html" xml:base="https://djan.org/blog/peach-rng/">&lt;h1 id=&quot;peach-pulls-mystery-items&quot;&gt;peach pulls mystery items&lt;a class=&quot;zola-anchor&quot; href=&quot;#peach-pulls-mystery-items&quot; aria-label=&quot;Anchor link for: peach-pulls-mystery-items&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;as a melee peach player, i spend a lot of my time pulling random items out of the ground.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/peach-turnip-pull.gif&quot; alt=&quot;gif of peach pulling a turnip&quot;&gt;&lt;figcaption&gt;&lt;p&gt;image from ssbwiki&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;princess peach has a special move called &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://www.ssbwiki.com/Vegetable&quot;&gt;&quot;Vegetable&quot;&lt;/a&gt;, activated by pressing down-special while grounded. if peach is able to complete her pull animation successfully, she retrieves an item from the ground - either a turnip, a bobomb, a beamsword, or a mr saturn (with turnips being the most common). it&#39;s a defining move for her character, and for many players is the default option to consider when in a safe position. although she loses access to a few options while holding a turnip (most notably grab), the possibility of throwing it at an opponent demands respect. it can open up new approaches, enable otherwise impossible combo routes, and empowers her to interact with her opponent from a distance.&lt;/p&gt;
&lt;p&gt;over the 20+ year history of the game, the community has created resources to help players understand how often to expect particular outcomes. this image is what i see referenced most often:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/expected-frequency-chart.png&quot; alt=&quot;table of peach item pull percentages&quot;&gt;&lt;figcaption&gt;&lt;p&gt;credit: Magus420&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;as shown above, not all pulls are equally likely. there are different kinds of turnips, and some turnips do more damage than others. some sequences of pulls are highly valuable, but extremely unlikely to occur - pulling two stitchfaces in a row (especially &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://www.youtube.com/watch?v=rrzFeWC5kmc&quot;&gt;when you know how to use them&lt;/a&gt;) is considered a special occassion. there are even advanced techniques like &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://smashboards.com/threads/postmodern-rng-tactics-knitting-panning-theory-discussion.414626/&quot;&gt;knitting&lt;/a&gt; that allow peach to continuously pull items faster than she could normally.&lt;/p&gt;
&lt;p&gt;understanding how turnip rng works won&#39;t give you any competitive advantage in the game. but it is fun! so, let&#39;s continue.&lt;/p&gt;
&lt;h1 id=&quot;counting-turnips&quot;&gt;counting turnips&lt;a class=&quot;zola-anchor&quot; href=&quot;#counting-turnips&quot; aria-label=&quot;Anchor link for: counting-turnips&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;when playing melee online these days, it&#39;s common to record game records using the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/project-slippi/slippi-wiki/blob/master/SPEC.md&quot;&gt;SLP replay format&lt;/a&gt;. these replays can be processed programmatically to collect statistics on the occurrence of specific events ingame.&lt;/p&gt;
&lt;p&gt;previously, i had been very curious about whether &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/djanatyn/turnip-counter&quot;&gt;my actual replay files matched up to the expected values &lt;/a&gt; predicted by the chart above. at the time, &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/hohav&quot;&gt;hohav&lt;/a&gt; had already put together a rust crate called &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://github.com/hohav/peppi&quot;&gt;peppi&lt;/a&gt; which could process these replays. it didn&#39;t take very long to put together a program which would iterate over a directory of replay files, identify any peach item pulls that matched my character, and record them to a simple sqlite database:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TABLE&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; IF&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; EXISTS&lt;/span&gt;&lt;span&gt; games (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; PRIMARY KEY&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    filename&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    start_time &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    p1_name &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    p1_code &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    p2_name &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    p2_code &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; TABLE&lt;/span&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt; IF&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; EXISTS&lt;/span&gt;&lt;span&gt; items (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; PRIMARY KEY&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    game_id &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    item_id &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    frame &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    kind &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;TEXT&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; NOT NULL&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    FOREIGN KEY&lt;/span&gt;&lt;span&gt; (game_id) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;REFERENCES&lt;/span&gt;&lt;span&gt; games (id)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;even with a small sample of 3,505 pulls and 418 games, my results were very close to expectations:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sqlite&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; SELECT&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; COUNT&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;FROM&lt;/span&gt;&lt;span&gt; items;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;3505&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sqlite&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; SELECT&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; COUNT&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;FROM&lt;/span&gt;&lt;span&gt; games;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;418&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sqlite&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; SELECT&lt;/span&gt;&lt;span&gt; kind, &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;CAST&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;COUNT&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; REAL&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;/&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;SELECT&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; COUNT&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;*&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;FROM&lt;/span&gt;&lt;span&gt; items) &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;FROM&lt;/span&gt;&lt;span&gt; items &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;GROUP BY&lt;/span&gt;&lt;span&gt; kind;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Beamsword      &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;00114122681883024&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~0.11% actual vs 0.13% predicted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Bobomb         &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;00228245363766049&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~0.23% actual vs 0.26% predicted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;DotEyesTurnip  &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0145506419400856&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~1.46% actual vs 1.71% predicted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;MrSaturn       &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;00485021398002853&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~0.46% actual vs 0.39% predicted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;NormalTurnip   &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;888445078459344&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;   --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~88.84% actual vs 88.95% predicted (59.873 + 10.264 + 8.553 + 5.132 + 5.132)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;StitchTurnip   &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0182596291012839&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~1.82% actual vs 1.71% predicted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;WinkyTurnip    &lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;0704707560627675&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt;  --&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-3&quot;&gt; ~7.04% actual vs 6.84% predicted&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but i was still curious. i had a chart of frequencies to expect, and i saw that my actual games were close to these expectations, but i still didn&#39;t understand how the game logic determined which item comes out of the ground.&lt;/p&gt;
&lt;h1 id=&quot;a-reddit-post-on-melee-rng&quot;&gt;a reddit post on melee rng&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-reddit-post-on-melee-rng&quot; aria-label=&quot;Anchor link for: a-reddit-post-on-melee-rng&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;at some point, i encountered &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://www.reddit.com/r/SSBM/comments/71gn1d/the_basics_of_rng_in_melee/&quot;&gt;a reddit post from september 2017 by twotwelvedegrees&lt;/a&gt;, titled &quot;The Basics of RNG in Melee&quot;. not only did this post explain the basics of melee&#39;s &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://en.wikipedia.org/wiki/Linear_congruential_generator&quot;&gt;linear congruential generator&lt;/a&gt; implementation, it also broke down the particular RNG calls which were executed for peach&#39;s item pulls:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All random events in the game are controlled by the output of two functions: get_random_int(max_val) and get_random_float()...On any turnip pull Peach starts with a call to get_random_int(128) which proceeds as follows:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Value&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;Peach pulls an item&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1-127&lt;/td&gt;&lt;td&gt;Peach pulls a turnip&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;On pulling an item, there is then a call to get_random_int(6) which proceeds as follows:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Value&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0-1&lt;/td&gt;&lt;td&gt;Peach pulls a bomb&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2-4&lt;/td&gt;&lt;td&gt;Peach pulls a Mr. Saturn&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Peach pulls a beam sword&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;On pulling a turnip, there is then a call to get_random_int(58) which proceeds as follows:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Value&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0-34&lt;/td&gt;&lt;td&gt;Peach pulls a regular turnip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;35-40&lt;/td&gt;&lt;td&gt;Peach pulls an unamused turnip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;41-45&lt;/td&gt;&lt;td&gt;Peach pulls a line eyes turnip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;46-48&lt;/td&gt;&lt;td&gt;Peach pulls a circle eyes turnip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;49-51&lt;/td&gt;&lt;td&gt;Peach pulls a super happy turnip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;52-55&lt;/td&gt;&lt;td&gt;Peach pulls a winky turnip&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt;Peach pulls a dot eyes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;57&lt;/td&gt;&lt;td&gt;Peach pulls a stitch&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;this was eye-opening for me. if i could analyze the code that was running while peach pulled an item, i could see (and provide a citation for) the conditional branches that were executed. i would no longer need to trust the chart - i could derive this information on my own. i was excited! and thankfully, much of the hard work had already been done for me.&lt;/p&gt;
&lt;h1 id=&quot;the-ssbm-datasheet&quot;&gt;the ssbm datasheet&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-ssbm-datasheet&quot; aria-label=&quot;Anchor link for: the-ssbm-datasheet&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;in the very first paragraph, twotwelvedegrees mentions a spreadsheet:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Major props to u/dansalvato and this spreadsheet since it basically did the research for me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;this is the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://docs.google.com/spreadsheets/d/1JX2w-r2fuvWuNgGb6D3Cs4wHQKLFegZe2jhbBuIhCG8/preview#gid=19&quot;&gt;SSBM Data Sheet (1.02)&lt;/a&gt;. it contains function addresses for many of the subroutines the game engine executes during normal gameplay. this document represents an extraordinary amount of effort reverse-engineering the game, made available for any curious hackers like myself.&lt;/p&gt;
&lt;p&gt;one of the sections under the &quot;Function Addresses&quot; tab is labeled &quot;Random Number Generator&quot;. the &lt;code&gt;get_random_int&lt;/code&gt; function mentioned in the reddit post is described in some detail here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there is a main RNG function at &lt;code&gt;0x80380580&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;it&#39;s input is &lt;code&gt;r3&lt;/code&gt;, which is the number of possible numbers to generate for the call,&lt;/li&gt;
&lt;li&gt;it&#39;s output is &lt;code&gt;r3&lt;/code&gt;, which is the random number returned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;if this is accurate, it means that every time peach attempts to pulls an item, we expect there&#39;s a call to &lt;code&gt;get_random_int(128)&lt;/code&gt; at &lt;code&gt;0x80380580&lt;/code&gt;. we should be able to load up our copy of melee, start the game, identify the function, and set some breakpoints to see it in action!&lt;/p&gt;
&lt;h1 id=&quot;enabling-dolphin-s-debugger&quot;&gt;enabling dolphin&#39;s debugger&lt;a class=&quot;zola-anchor&quot; href=&quot;#enabling-dolphin-s-debugger&quot; aria-label=&quot;Anchor link for: enabling-dolphin-s-debugger&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;i&#39;m on nixos, so to load dolphin, i pull down a version from nixpkgs:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;nix&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; build&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; nixpkgs#dolphin-emu&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; dolphin-hacking&lt;/span&gt;&lt;span&gt; ;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; get&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; dolphin&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; package&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-d-7&quot;&gt;./dolphin-hacking/bin/dolphin-emu&lt;/span&gt;&lt;span&gt;                ;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-4&quot;&gt; :&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; execute&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-6&quot;&gt; dolphin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you&#39;re on your own for acquiring a copy of melee&#39;s disk image.&lt;/p&gt;
&lt;p&gt;on my copy of dolphin, the debugging interface wasn&#39;t visible by default, so i had to enable it:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/enable-debugging-ui.png&quot; alt=&quot;screenshot of &#39;Enable Debugging UI&#39; checkbox&quot;&gt;&lt;figcaption&gt;&lt;p&gt;Options &amp;gt; Configuration &amp;gt; Interface&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;i also toggled the &#39;Code&#39;, &#39;Registers&#39;, and &#39;Memory&#39; pane into view:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/dolphin-view-settings.png&quot; alt=&quot;enabling &#39;Code&#39;, &#39;Registers&#39;, and &#39;Memory&#39; view&quot;&gt;&lt;figcaption&gt;&lt;p&gt;View Toggles&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;i also moved some panes around:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/dolphin-ui.png&quot; alt=&quot;screenshot of dolphin debugger ui&quot;&gt;&lt;figcaption&gt;&lt;p&gt;ready for action&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;i&#39;ve got an adapter for my gamecube controller setup, and i have a few gecko codes enabled which change the default behavior of the game to make investigation a little easier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unlock All Characters and Stages&lt;/li&gt;
&lt;li&gt;VS 1 player (set time to none)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;the game loads as expected, and we&#39;re at the character select screen:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/dolphin-debugger-game-running.png&quot; alt=&quot;screenshot of melee running with debug pane to the side&quot;&gt;&lt;figcaption&gt;&lt;p&gt;debugging activated&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;you might notice that all instructions are marked as &lt;code&gt;&amp;lt;unknown&amp;gt;&lt;/code&gt; while the game is running. if we hit the pause button, we can see the instructions:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/dolphin-unpaused.png&quot; alt=&quot;screenshot of instructions in debug pane&quot;&gt;&lt;figcaption&gt;&lt;p&gt;instructions are now visible&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;cool! we can see the address of each instruction, it&#39;s parameters, and there&#39;s even a callstack showing how we got here.&lt;/p&gt;
&lt;p&gt;we already know where &lt;code&gt;get_random_int&lt;/code&gt; is, so let&#39;s try searching for it.&lt;/p&gt;
&lt;h1 id=&quot;finding-get-random-int&quot;&gt;finding get_random_int&lt;a class=&quot;zola-anchor&quot; href=&quot;#finding-get-random-int&quot; aria-label=&quot;Anchor link for: finding-get-random-int&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;there&#39;s a &quot;Search Address&quot; text input available. typing in &lt;code&gt;0x80380580&lt;/code&gt; and hitting enter will highlight the line for that address. we can right-click this line, select &quot;Add Function&quot; from the context menu, and we&#39;ll see a set of instructions highlighted:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/add_function.png&quot; alt=&quot;instructions for get_random_int&quot;&gt;&lt;figcaption&gt;&lt;p&gt;get_random_int assembly instructions&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;the name &lt;code&gt;zz_80380580_&lt;/code&gt; is not very helpful. we can change that by right-clicking the same line, selecting &quot;Rename symbol&quot; from the context menu, and giving this symbol a better name, &lt;code&gt;get_random_int&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;we can even select &quot;Copy function&quot; in the context menu to get a listing for all of the instructions:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;get_random_int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;80380580: lwz	r5, -0x570C (r13)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;80380584: lis	r4, 0x0003&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;80380588: addi	r0, r4, 17405&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8038058c: lwz	r4, 0 (r5)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;80380590: mullw	r4, r4, r0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;80380594: addis	r4, r4, 39&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;80380598: subi	r0, r4, 24893&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8038059c: stw	r0, 0 (r5)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805a0: lwz	r4, -0x570C (r13)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805a4: lwz	r0, 0 (r4)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805a8: rlwinm	r0, r0, 16, 16, 31 (ffff0000)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805ac: mullw	r0, r3, r0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805b0: srawi	r3, r0,16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805b4: addze	r3, r3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;803805b8: blr	&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;understanding what all of these instructions do is outside the scope of this post. instead, let&#39;s ask dolphin to log every time this function is executed by adding a breakpoint!&lt;/p&gt;
&lt;h1 id=&quot;logging-get-random-int-calls&quot;&gt;logging get_random_int calls&lt;a class=&quot;zola-anchor&quot; href=&quot;#logging-get-random-int-calls&quot; aria-label=&quot;Anchor link for: logging-get-random-int-calls&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;first, let&#39;s enable the breakpoint view (i forgot to turn it on earlier):&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/dolphin-view-breakpoints.png&quot; alt=&quot;enabling breakpoints&quot;&gt;&lt;figcaption&gt;&lt;p&gt;oops we need those&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;navigate to the breakpoint tab, click &quot;New&quot;, and let&#39;s observe these calls as they execute:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/log-breakpoint.png&quot; alt=&quot;breakpoint configuration&quot;&gt;&lt;figcaption&gt;&lt;p&gt;breakpoint configuration&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;activate this breakpoint at &lt;code&gt;0x80380580&lt;/code&gt; (our suspected &lt;code&gt;get_random_int&lt;/code&gt; function),&lt;/li&gt;
&lt;li&gt;our condition &lt;code&gt;r3, 1&lt;/code&gt; means to display the register &lt;code&gt;r3&lt;/code&gt; in the log (which should be the range on numbers generated),&lt;/li&gt;
&lt;li&gt;don&#39;t stop execution of the game, just log it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;it should look like this (it even includes our renamed symbol from earlier):&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/breakpoint-success.png&quot; alt=&quot;configured breakpoint&quot;&gt;&lt;figcaption&gt;&lt;p&gt;configured breakpoint enabled&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;in melee, attempting to place the character select token when it isn&#39;t hovering over a particular character will select a random character - the random button is a modern innovation.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/random-characters.gif&quot; alt=&quot;randomly selecting characters&quot;&gt;&lt;figcaption&gt;&lt;p&gt;og random button&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;that random choice has to come from somewhere. in the log, we can actually see some &lt;code&gt;get_random_int&lt;/code&gt; calls happening already:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Breakpoint condition returned: 1. Vars:  r3=25&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Vars: r3=25&lt;/code&gt; sounds about right - there are 25 characters in the game!&lt;/p&gt;
&lt;p&gt;if you don&#39;t see any logs for breakpoints, check your logging configuration (&quot;View&quot; &amp;gt; &quot;Show Logging Configuration&quot;) and try enabling all log types.&lt;/p&gt;
&lt;h1 id=&quot;identifying-where-peach-rng-calls-happen&quot;&gt;identifying where peach rng calls happen&lt;a class=&quot;zola-anchor&quot; href=&quot;#identifying-where-peach-rng-calls-happen&quot; aria-label=&quot;Anchor link for: identifying-where-peach-rng-calls-happen&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;let&#39;s get ingame and try pulling a turnip! hit down-b, hit start to pause the game, and let&#39;s see what happened.&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/pull-turnip.png&quot; alt=&quot;screenshot of daisy pulling turnip&quot;&gt;&lt;figcaption&gt;&lt;p&gt;daisy is the best peach costume&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Breakpoint condition returned: 1. Vars:  r3=128&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Breakpoint condition returned: 1. Vars:  r3=58&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_random_int(128)&lt;/code&gt; is determining whether we pull an item or a turnip, and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_random_int(58)&lt;/code&gt; is determining the face of the turnip&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;we&#39;re not logging the return value of the RNG call right now, but we&#39;ve identified the &lt;code&gt;get_random_int&lt;/code&gt; calls that are being executed when peach pulls an item. that&#39;s great progress!&lt;/p&gt;
&lt;p&gt;what code is calling &lt;code&gt;get_random_int(128)&lt;/code&gt;? let&#39;s modify our breakpoint with a different condition, and set it to break (not just log):&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/modify-condition.png&quot; alt=&quot;new breakpoint condition&quot;&gt;&lt;figcaption&gt;&lt;p&gt;check for a specific argument&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;after pulling another turnip, the game should pause, stopping at &lt;code&gt;get_random_int&lt;/code&gt;. interestingly, even though peach may have entered the item pull animation, the animation does not show which item peach has pulled, since it hasn&#39;t decided yet. let&#39;s check out our callstack:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/link-register.png&quot; alt=&quot;callstack screenshot&quot;&gt;&lt;figcaption&gt;&lt;p&gt;the path that led us here&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;LR&lt;/code&gt; represents the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://en.wikipedia.org/wiki/Link_register&quot;&gt;&quot;link register&quot;&lt;/a&gt;. whenever we call a subroutine with the &lt;code&gt;bl&lt;/code&gt; instruction, we populate a special register with the address to return the program counter to once our subroutine finishes executing (usually with a &lt;code&gt;blr&lt;/code&gt; instruction). if we click on the &lt;code&gt;LR = 8011d088&lt;/code&gt; address, we can get some context on what&#39;s happening before this RNG call is executed, and what we do with the result.&lt;/p&gt;
&lt;p&gt;this instruction is happening in the middle of the subroutine, so i scrolled up the previous &lt;code&gt;blr&lt;/code&gt; instruction (denoting the end of the last subroutine), moved onto the next instruction (&lt;code&gt;0x8011d018&lt;/code&gt;), and defined a new function as we did previously. i named it &lt;code&gt;turnip_rng_caller&lt;/code&gt; - it may be used for other interactions, but we&#39;re fairly confident it&#39;s part of peach&#39;s item pull logic.&lt;/p&gt;
&lt;blockquote class=&quot;markdown-alert-tip&quot;&gt;
&lt;/blockquote&gt;
&lt;p&gt;the address &lt;code&gt;0x8011d018&lt;/code&gt; is included in the &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https://smashboards.com/threads/smashboards-community-symbol-map.426763/&quot;&gt;smashboards community symbol map&lt;/a&gt;, given the name &lt;code&gt;_$_wP_Peach_DownB_GenTurnip&lt;/code&gt; - looks like we&#39;re on the right track.&lt;/p&gt;
&lt;h1 id=&quot;peach-downb-genturnip-logic&quot;&gt;Peach_DownB_GenTurnip logic&lt;a class=&quot;zola-anchor&quot; href=&quot;#peach-downb-genturnip-logic&quot; aria-label=&quot;Anchor link for: peach-downb-genturnip-logic&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;let&#39;s take a closer look at the logic around &lt;code&gt;0x8011d088&lt;/code&gt; in the body of the function:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d088: bl	-&amp;gt;0x80380580&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d08c: cmpwi	r3, 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d090: bne-	 -&amp;gt;0x8011D0A0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d094: mr	r3, r29&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d098: bl	-&amp;gt;0x8011CE48&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d09c: mr	r31, r3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0a0: lwz	r4, 0x010C (r30)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0a4: mr	r6, r31&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0a8: lfs	f1, 0x002C (r30)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0ac: mr	r3, r29&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0b0: lwz	r4, 0x0008 (r4)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0b4: lbz	r5, 0x0010 (r4)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0b8: addi	r4, sp, 52&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8011d0bc: bl	-&amp;gt;0x802BD4AC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;there&#39;s a few things happening here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;we call &lt;code&gt;get_random_int(128)&lt;/code&gt; (denoted by address &lt;code&gt;0x80380580&lt;/code&gt;, with &lt;code&gt;r3&lt;/code&gt; set to &lt;code&gt;128&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;when we return to the subroutine, &lt;code&gt;r3&lt;/code&gt; is populated with the random return value&lt;/li&gt;
&lt;li&gt;we check &lt;code&gt;r3&lt;/code&gt; with the &lt;code&gt;cmpwi&lt;/code&gt; instruction to see if the return value is 0,&lt;/li&gt;
&lt;li&gt;if &lt;em&gt;the return value is not 0&lt;/em&gt;, we branch to &lt;code&gt;0x8011d0a0&lt;/code&gt; using the &lt;code&gt;bne&lt;/code&gt; instruction&lt;/li&gt;
&lt;li&gt;if &lt;em&gt;the return value is 0&lt;/em&gt;, we continue execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;this maps directly to the explanation we saw in &quot;The Basics of RNG in Melee&quot; previously, but this time we&#39;re doing it live.&lt;/p&gt;
&lt;p&gt;we can try stepping through the &lt;code&gt;get_random_int&lt;/code&gt; call until we get to the return &lt;code&gt;blr&lt;/code&gt; instruction at &lt;code&gt;0x803805b8&lt;/code&gt;. if we check the value of the &lt;code&gt;r3&lt;/code&gt; register, we can see the random result returned:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/r3-return.png&quot; alt=&quot;r3 is returning 0x20&quot;&gt;&lt;figcaption&gt;&lt;p&gt;0x20 == 32&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;32 != 0&lt;/code&gt;, so this is going to be a turnip. but now that we know how this logic works...what if we got a little mischevious?&lt;/p&gt;
&lt;h1 id=&quot;giving-peach-a-buff&quot;&gt;giving peach a buff&lt;a class=&quot;zola-anchor&quot; href=&quot;#giving-peach-a-buff&quot; aria-label=&quot;Anchor link for: giving-peach-a-buff&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;navigate to the &lt;code&gt;cmpwi&lt;/code&gt; instruction at &lt;code&gt;0x8011d08c&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/cmpwi.png&quot; alt=&quot;cmpwi instruction&quot;&gt;&lt;figcaption&gt;&lt;p&gt;our fate is in our own hands&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;let&#39;s create a new breakpoint:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/cmpwi-conditions.png&quot; alt=&quot;cmpwi breakpoint conditions&quot;&gt;&lt;figcaption&gt;&lt;p&gt;this is not tournament legal&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;instead of using the returned value from the &lt;code&gt;get_random_int(128)&lt;/code&gt; call, we&#39;re just going to set register &lt;code&gt;r3&lt;/code&gt; to 0 when evaluating this instruction.&lt;/p&gt;
&lt;p&gt;let&#39;s disable our previous breakpoint for now so we can test this out:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/disable-old-breakpoint.png&quot; alt=&quot;disabling get_random_int breakpoint&quot;&gt;&lt;figcaption&gt;&lt;p&gt;otherwise, our game stops on every item pull&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;hit start, and let&#39;s get pulling!&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/buffed-peach.gif&quot; alt=&quot;peach is only throwing items&quot;&gt;&lt;figcaption&gt;&lt;p&gt;the dream&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h1 id=&quot;final-destination-no-items-beamswords-only&quot;&gt;final destination, no items, beamswords only&lt;a class=&quot;zola-anchor&quot; href=&quot;#final-destination-no-items-beamswords-only&quot; aria-label=&quot;Anchor link for: final-destination-no-items-beamswords-only&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;i&#39;m quite fond of beamswords, so let&#39;s see if we can make them a little more popular. if we refer to our charts from earlier, we expect that there will be a &lt;code&gt;get_random_int(6)&lt;/code&gt; call. we can modify our &lt;code&gt;get_random_int&lt;/code&gt; breakpoint condition accordingly:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/breakpoint-modify.png&quot; alt=&quot;r3 == 6&quot;&gt;&lt;figcaption&gt;&lt;p&gt;r3 == 6&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;if you&#39;ve followed along this far, hopefully you&#39;re getting the hang of the dolphin debugger. hit the &lt;code&gt;LR == 8011cf0c&lt;/code&gt; address in the callstack, a familiar &lt;code&gt;bl -&amp;gt;0x80380580&lt;/code&gt; instruction. let&#39;s navigate to the &lt;em&gt;next instruction&lt;/em&gt; (&lt;code&gt;0x8011cf10&lt;/code&gt;) and create a new breakpoint, setting &lt;code&gt;r3 = 5&lt;/code&gt; and continuing execution:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/mr-saturn-breakpoint.png&quot; alt=&quot;r3 = 5&quot;&gt;&lt;figcaption&gt;&lt;p&gt;r3 = 5&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;once again, let&#39;s disable our &lt;code&gt;get_rand_int&lt;/code&gt; item RNG call:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/disable-item-rng.png&quot; alt=&quot;disabling item rng breakpoint&quot;&gt;&lt;figcaption&gt;&lt;p&gt;we&#39;re almost there&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;and finally:&lt;/p&gt;
&lt;figure&gt;
&lt;img src=&quot;/img/sword-peach.gif&quot; alt=&quot;peach only pulls beamswords now&quot;&gt;&lt;figcaption&gt;&lt;p&gt;watch out marth&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h1 id=&quot;closing-thoughts&quot;&gt;closing thoughts&lt;a class=&quot;zola-anchor&quot; href=&quot;#closing-thoughts&quot; aria-label=&quot;Anchor link for: closing-thoughts&quot; style=&quot;visibility: hidden;&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;nothing here is novel - my goal is only to share the fun things i&#39;ve been learning about recently.
i spend much of my time working towards understanding through experimentation, and i&#39;m hoping this blog can serve as a lab notebook.&lt;/p&gt;
&lt;p&gt;if you have any questions, comments, or corrections, feel free to send me an email at &lt;a href=&quot;mailto:djanatyn@gmail.com&quot;&gt;djanatyn@gmail.com&lt;/a&gt;. thanks for taking the time to read, take care!&lt;/p&gt;
</content>
        
    </entry>
</feed>
