
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Steve Industeves]]></title><description><![CDATA[Programming with Steve]]></description><link>https://steveindusteves.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Ir3D!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F01b8322c-5a41-477b-8fc5-84fcf9bd5e39_512x512.png</url><title>Steve Industeves</title><link>https://steveindusteves.substack.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 08 Jul 2026 11:51:19 GMT</lastBuildDate><atom:link href="https://steveindusteves.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Stephen Olsen]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[steveindusteves@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[steveindusteves@substack.com]]></itunes:email><itunes:name><![CDATA[Steve Olsen]]></itunes:name></itunes:owner><itunes:author><![CDATA[Steve Olsen]]></itunes:author><googleplay:owner><![CDATA[steveindusteves@substack.com]]></googleplay:owner><googleplay:email><![CDATA[steveindusteves@substack.com]]></googleplay:email><googleplay:author><![CDATA[Steve Olsen]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Winning Connect4]]></title><description><![CDATA[With Monte-Carlo Tree Search]]></description><link>https://steveindusteves.substack.com/p/connect4-mcts</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/connect4-mcts</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Tue, 08 Aug 2023 14:15:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9229a673-38f2-4aa0-aa3b-3a4f9c33e696_811x661.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Connect 4, each player takes turns dropping a chip in one of 7 columns. If they ever have 4 chips in a row, either vertically, horizontally, or diagonally, they win!</p><p>I wrote an agent that&#8217;s so good I can&#8217;t beat it. Here&#8217;s how it works.</p><p>First, think about the simplest possible agent. When it&#8217;s their turn, they see what actions they have available. These actions are each of the columns they could drop the chip into. There are 7 different choices (unless some of the columns are full, then there are fewer). The simple agent just randomly picks an available column. It doesn&#8217;t know anything about Connect4 or what move is best, it just picks one. This agent isn&#8217;t very good.</p><p>Another approach would be to try and write a smart agent that understands Connect4. Maybe there&#8217;s an optimal strategy, it could detect twos and threes in a row to build on or protect against, or maybe it&#8217;ll use heuristics for tricking the other player.</p><p>Or maybe we could train a machine learning model to analyze past games and figure out which game states are good and which actions lead to them. We could train it on professional Connect4 Twitch streams.</p><p>I didn&#8217;t do any of that.</p><p>The way my agent works is like this. Instead of using any sort of logic about the current state of the game, the agent runs simulations to figure out which move is best.</p><p>It needs to figure out which move has the highest probability of winning. It does this by trying every move. It copies the game state for each possible move and takes it. These are all the possible next states. To see which one is the best it simulates 10,000 &#8220;random&#8221; games that start from each of these states. In the simulated games it just picks random moves for each player. After these 10,000 games, it scores the state.</p><p><code>wins - losses / 10,000</code></p><p>Whichever action led to the state with the highest score is the action it takes in the real game.</p><p>So on each turn, the agent plays up to 70,000 games of Connect4 before picking a move.</p><p>This algorithm is called Monte-Carlo Tree Search. You can think of the game states as a tree where every action you could take is an edge leading to the next game state. The tree expands out to the leaf nodes which are the endgame states, where a player wins or it&#8217;s a tie.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IIzO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IIzO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 424w, https://substackcdn.com/image/fetch/$s_!IIzO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 848w, https://substackcdn.com/image/fetch/$s_!IIzO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 1272w, https://substackcdn.com/image/fetch/$s_!IIzO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IIzO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!IIzO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 424w, https://substackcdn.com/image/fetch/$s_!IIzO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 848w, https://substackcdn.com/image/fetch/$s_!IIzO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 1272w, https://substackcdn.com/image/fetch/$s_!IIzO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d4633e9-ee9c-42f3-a136-9c1a803ee4be_817x580.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p><em>Tree of Game States</em></p><p>This tree is huge since most nodes branch into 7 more. We can&#8217;t just traverse the whole tree so we do a bunch of depth-first random samples (the simulated games) to try and find a path more likely to lead to us winning.</p><p>This is cool because we don&#8217;t need to &#8220;understand&#8221; Connect4 to write the agent. There&#8217;s no logical evaluation of how &#8220;good&#8221; a move or game state is. We just need to simulate matches. The logic for turn selection is not much different than the random agent, we just pick a lot of random actions until we get a good vibe.</p><p>I&#8217;ve been doing a lot of research lately on game-playing agents. There are lots of different algorithms for different kinds of games. Connect4 has a lot of game states so it&#8217;s hard to just fully compute the probabilities of winning for each state and action. (You could do this in a simpler game like tic-tac-toe and can just calculate every possible move for every possible game.) Connect4 is easy to simulate though so this works well.</p><p>The agent is written in rust. It&#8217;s not really optimized, just runs all the simulations on a single thread, but it still goes fast enough (on my laptop) to play against.</p><p>The code for the agent (which you can play against) is here. <em><a href="https://github.com/saolsen/connect4?utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=winning-connect4">https://github.com/saolsen/connect4</a></em> Good luck trying to beat it!</p>]]></content:encoded></item><item><title><![CDATA[Tracking Books On Reddit]]></title><description><![CDATA[I rewrote my rust program in python for performance reasons and so it is more maintainable]]></description><link>https://steveindusteves.substack.com/p/tracking-books-on-reddit</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/tracking-books-on-reddit</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Tue, 03 Jan 2023 18:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dab60586-3509-43b6-85dd-88cc99b13af7_1200x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I was in college, I had a really good idea for an app. I would track everything people said on Twitter, figure out which products they are talking about, create a website that ranks those products over time, and then add affiliate links so I can make a million dollars and not have to work a real job.</p><p>There were a few hard problems I ran into back then that stopped me from shipping it.</p><ol><li><p>Consuming Twitter data is really annoying.</p></li><li><p>Telling which tweets are about products is hard.</p></li><li><p>I barely even knew how to program.</p></li></ol><p>I still think it's a pretty good idea so I did a little hacking on it over Christmas break</p><p>All of these are still problems today, but I think I can work around them. Twitter data is still annoying to consume so I decided to use Reddit data instead, which is slightly easier to scrape. To start, I decided to scrape book-related subreddits. I can use some off-the-shelf machine learning model to answer, "Is this post about a book and does the person like the book or not like the book?". And as far as being bad at programming goes, I just don't let that stop me anymore.</p><p>Reddit has some APIs to get data but I don't feel like doing the work to obey rate limits. I just found a tool that already does it, the <em><a href="https://github.com/aliparlakci/bulk-downloader-for-reddit?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=tracking-books-on-reddit">Bulk Downloaded for Reddit (bdfr)</a></em>. I can point it at a subreddit and it will pull a bunch of posts and comments. Perfect.</p><p>After running <code>bdfr</code> I get a directory full of JSON files, one for each post. I wrote a rust program to parse the file and store the posts and comments in a <em><a href="https://duckdb.org/?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=tracking-books-on-reddit">DuckDB</a></em> database.</p><p>This is where I hit a problem. Some of the posts with lots and lots of comments (each having replies that are also comments) created some extremely nested JSON objects. They were so nested that <em><a href="https://serde.rs/?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=tracking-books-on-reddit">serde</a></em> (the library to parse JSON in rust) was erroring out, hitting recursion limits. Also, I was using the DuckDB crate and it compiled all of DuckDB into my program, which takes forever when done from scratch.</p><p>Because of these errors, I did something I never thought I'd ever do. I rewrote my rust program in python for performance and maintainability.</p><p>The last step in collecting the data is just to run the program often. <code>bdfr</code> can only scrape the "new" feed, the "top" feed, or the "hot" feed so I need to check regularly to get all the posts and comments. I spun up a <em><a href="https://github.com/saolsen/fadbuys-data/blob/main/.github/workflows/reddit.yaml?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=tracking-books-on-reddit">GitHub actions workflow</a></em> to do it for me. I think this will work for at least a week or so until the data set is too large to even use <em><a href="https://git-lfs.com/?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=tracking-books-on-reddit">git lfs</a></em>. Then I guess I'll have to put them in s3 or something.</p>]]></content:encoded></item><item><title><![CDATA[Building A MacOS Debugger]]></title><description><![CDATA[Notes on what I'm learning and hacking on and sometimes bigger deep dive articles.]]></description><link>https://steveindusteves.substack.com/p/a-macos-debugger</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/a-macos-debugger</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Wed, 28 Dec 2022 18:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/bc37f1f6-1cee-4fed-83e5-2e847974d5f7_1220x650.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've had this idea for a while. I want to create a debugger that lets you visualize what your program is doing. Most debuggers will let you stop execution at points and take a look at variables and maybe a raw view of memory, see the call stack, and stuff like that. I want one that shows me where all my data is in memory and how the processor is accessing that memory. Memory access patterns are one of the main things you have to get right on modern processors to make code fast and I've found it really difficult to see what's going on. On my old blog, I wrote <em><a href="https://steveindusteves.substack.com/p/how-do-computers-work?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=building-a-macos-debugger">a post</a></em> about some memory access stuff but was bummed there was no good way to visualize it for a real program. I wonder if I could build the tool I needed.</p><p>I mostly work on macOS. As far as debuggers go <code>lldb</code> is really the only one you can use. It's what all the "codes" use (Xcode and VScode) and any other debuggers use. It's actually almost impossible to use anything else because the way <code>ptrace</code> works on macOS (the syscall that lets you do debugger stuff like pause execution) doesn't let you read or write registers. You would also have to use private APIs so you couldn't distribute the debugger without dealing with some weird codesigning stuff and some system preferences stuff people would have to enable and it's all just kinda annoying. This is probably why everybody just builds on top of <code>lldb</code>.</p><p>I did discover something in my research though. <code>lldb</code> has two parts. It has a server, the actual process that makes the <code>ptrace</code> calls and inspects registers and whatever else, and it has a client, the <code>lldb</code> cli that you type commands into. The server process is called <code>debugserver</code> and it ships with macOS as part of the Xcode command line tools. It's possible to write your own client that talks to <code>debugserver</code> directly, which means you can build a debugger that can do all the same stuff <code>lldb</code> can do, but without having to use any system APIs yourself. This seems promising<strong>&nbsp;</strong>and <strong>fun.</strong></p><p><code>debugserver</code> and <code>lldb</code> talk over the <em><a href="https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=building-a-macos-debugger">gdb remote protocol</a></em>. It's a low-level way for <code>lldb</code> to say stuff like "set this breakpoint" or "continue execution" and <code>debugserver</code> to reply. You can see all the packets sent between <code>lldb</code> and <code>debugserver</code> by starting an <code>lldb</code> session and turning on gdb-remote logging with <code>log enable gdb-remote all -f logfile</code>.</p><p>I compiled a simple program, <code>hello_world.c</code>.</p><pre><code>#include &lt;stdio.h&gt;

int main()
{
    printf("Hello, World!");
}</code></pre><p>Then I ran it with <code>lldb</code> and turned on logging. I just ran the program, no breakpoints or anything.</p><pre><code>&#10095; /usr/bin/lldb test/a.out
(lldb) target create "test/a.out"
Current executable set to '/Users/steve/dev/debugger/test/a.out' (arm64).
(lldb) log enable gdb-remote all -f lldb.log
(lldb) r
Process 48876 launched: '/Users/steve/dev/debugger/test/a.out' (arm64)
Hello, World!Process 48876 exited with status = 0 (0x00000000) 
(lldb) </code></pre><p>The log file for this is <em><a href="https://gist.github.com/saolsen/e56d14322e82b4c215e15bee6a47e281?ref=steveindusteves.ghost.io&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=building-a-macos-debugger">HUGE</a></em>. <code>lldb</code> does a lot of work before even starting the process. It sends commands to look up information about the processor like its registers. It sets a few different breakpoints in startup code and runs to them so it can look up things like what dynamic libraries are loaded and other things I can't even really tell yet. Finally, at the end, it sends a <code>continue</code> command and gets back a <code>process exited</code> reply.</p><pre><code>&lt;   5&gt; send packet: $c#63
&lt;   7&gt; read packet: $W00#00</code></pre><p>Cool, can we just do that without all the extra stuff <code>lldb</code> does? Time to write a debugger. First, launch the program under <code>debugserver</code>. It's put in a very specific place when you install the Xcode command line tools so it'll probably be here for everyone, if it isn't try <code>xcode-select --install</code>.</p><pre><code>&#10095; /Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/debugserver 0.0.0.0:1234 test/a.out
debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-1400.0.38.13
 for arm64.
Listening to port 1234 for a connection from 0.0.0.0...</code></pre><p>Then we can connect to that, I'm using telnet (you can get it with <code>brew install telnet</code>).</p><pre><code>&#10095; telnet 0.0.0.0 1234
Trying 0.0.0.0...
Connected to 0.0.0.0.
Escape character is '^]'.</code></pre><p>Then we can try sending just the <code>continue</code> command. Commands are a <code>$</code> followed by the command, followed by a checksum. The gdb server protocol works over TCP but it also works over serial and there's a lot of stuff in it about <code>ack</code>ing responses and requesting re-sends when things mess up.</p><pre><code>$c#63
+$O48656c6c6f2c20576f726c6421#c2</code></pre><p>That's not the <code>$W00#00</code> I expected but it's even better. <code>$O</code> is console output and the rest of the message is hex-encoded ASCII. Decoding it gives <code>Hello, World!</code>.</p><p>We can send <code>+</code> to say we received the message and then it gives us what we were looking for. The whole session looks like this.</p><pre><code>Trying 0.0.0.0...
Connected to 0.0.0.0.
Escape character is '^]'.
$c#63  
+$O48656c6c6f2c20576f726c6421#c2
+
$W00#b7
+
Connection closed by foreign host.</code></pre><p>And there you have it, you don't have to use <code>lldb</code> anymore, now you can debug your programs with <code>debugserver</code> and <code>telnet</code>!</p>]]></content:encoded></item><item><title><![CDATA[We're Moving]]></title><description><![CDATA[steve's computer has a new home]]></description><link>https://steveindusteves.substack.com/p/were-moving</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/were-moving</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Wed, 28 Dec 2022 16:57:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ir3D!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F01b8322c-5a41-477b-8fc5-84fcf9bd5e39_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Check us out at Check us out at https://www.steveindusteves.com/ We&#8217;ve got a great new really cool post for you to read when you get there https://www.steveindusteves.com/a-macos-debugger/</p>]]></content:encoded></item><item><title><![CDATA[Hello World (again)]]></title><description><![CDATA[Notes on what I'm learning and hacking on and sometimes bigger deep dive articles.]]></description><link>https://steveindusteves.substack.com/p/hello-world-again</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/hello-world-again</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Thu, 22 Dec 2022 18:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/01551846-40d6-449c-8e0d-2886d0fc211d_1200x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've had a blog a few times. My most recent was <em><a href="https://steveindusteves.substack.com/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">https://steveindusteves.substack.com/</a></em> but I haven't posted there in over a year. This next year I want to write more so obviously that means I needed a new blog. I'm planning to follow a style similar to "week notes"<sup>[1] </sup>where I post each week just about whatever new things I've discovered or started hacking on that week. I hope that this super low entry bar will help me just ship and get into a regular blogging habit.</p><p>Since next week is the first of the year we'll call that the first actual post. Here's a little dump of some things I've done or discovered this year to prepare for that.</p><h2>Last Year</h2><p>I really learned a lot about "modern" front-end stuff this year. There are some really exciting things going on. I knew a bit about nextjs and react because we use them at work. There are a lot of other cool things in that ecosystem like <em><a href="https://trpc.io/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">https://trpc.io/</a></em> that make the frontend/backend divide a lot easier. I played around with <em><a href="https://deno.land/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Deno</a></em> and <em><a href="https://astro.build/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Astro</a></em> and <em><a href="https://start.solidjs.com/getting-started/what-is-solidstart?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Solid</a></em> and even <em><a href="https://github.com/gbj/leptos?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Leptos</a></em>. These are all worth checking out and could be whole posts of their own in the future.</p><p>I also played around with a bunch of new products that I like a lot. <em><a href="https://supabase.com/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Supabase</a></em> and <em><a href="https://planetscale.com/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Planetscale</a></em> and <em><a href="https://clerk.dev/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Clerk</a></em> were all really nice finds and I like them all for different reasons.</p><p>One thing I started actually building is a programming language for random data generation. I was calling it "AmpleSamp" and I learned a lot building a small prototype. I built a little virtual machine which was really fun, I learned some new tricks from <em><a href="https://craftinginterpreters.com/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Crafting Interpreters</a></em> while I did it. I never got to the real features of the language which are "constrained random value generation" and I also sort of think a custom language for this isn't a great idea anymore. I'm going to try this again in the future just as a library or even just a configurable model thing. You can check out the <em><a href="https://amplesamp.com/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">prototype</a></em> and the <em><a href="https://github.com/saolsen/amplesamp-astro?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">code</a></em> if you want. I build the front-end in Astro and learned a lot of CSS in the process.</p><h2>This Year</h2><p>I'm not sure what I want to get into in the future. I have some ideas.</p><ul><li><p>A new kind of graphical debugger/VM hybrid that shows you exactly what the computer is doing.</p></li><li><p>A bot that plays some sort of game. Previously I've tried to write bots for the binding of Isaac but getting past the "recognize things on the screen" part was always too hard. Maybe a card game or a board game or something where I can play with some fun algorithms like Monte Carlo tree search rather than get into deep learning or something like that.</p></li><li><p>Make a game?</p></li><li><p>Start some simple products? IDK</p></li><li><p>Write a book?</p></li><li><p>Some sort of procedural generation art or game?</p></li><li><p>A new blog? (jk hopefully)</p></li></ul><p>Who knows what I'll do? If you wanna find out you can learn all about it here.</p><h2>Footnotes</h2><ol><li><p>Was introduced to the "weeknotes" format by <em><a href="https://weeknotes.buttondown.email/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Justin, the guy that runs Buttondown.</a></em> It's a really cool newsletter where he writes about building <em><a href="https://buttondown.email/?ref=steveindusteves.com&amp;utm_source=computer.beehiiv.com&amp;utm_medium=referral&amp;utm_campaign=hello-world-again">Buttondown</a></em>.</p></li></ol>]]></content:encoded></item><item><title><![CDATA[How We Built a Cross-Platform Library with Rust]]></title><description><![CDATA[Today&#8217;s post is published on the Oso blog (that&#8217;s where I work btw).]]></description><link>https://steveindusteves.substack.com/p/how-we-built-a-cross-platform-library</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/how-we-built-a-cross-platform-library</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Mon, 25 Oct 2021 19:57:41 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/88b285e0-8e73-489c-939c-2ecfaa7806ef_1474x900.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today&#8217;s post is published on the Oso blog (that&#8217;s where I work btw). I wrote a post on how we build our cross-platform library.</p><p>Check it out!</p><p><a href="https://www.osohq.com/post/cross-platform-rust-libraries">https://www.osohq.com/post/cross-platform-rust-libraries</a></p>]]></content:encoded></item><item><title><![CDATA[You should read "The AWK Programming Language"]]></title><description><![CDATA[&#8220;The AWK Programming Language&#8221; by Alfred V. Aho, Brian W. Kernighan, and Peter J. Weinberger is the best book on computer science ever written. That isn&#8217;t a joke, it&#8217;s kind of crazy how much this book fits into its ~200 pages. First, it teaches you AWK, the whole language is covered in about 60 pages. Then it shows you how to build some stuff. These projects include.]]></description><link>https://steveindusteves.substack.com/p/you-should-read-the-awk-programming</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/you-should-read-the-awk-programming</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Thu, 14 Oct 2021 20:34:27 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/a6ef055d-357e-4e23-a8ce-d2c037673fec_306x196.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#8220;The AWK Programming Language&#8221; by Alfred V. Aho, Brian W. Kernighan, and Peter J. Weinberger is the best book on computer science ever written.</p><p>That isn&#8217;t a joke, it&#8217;s kind of crazy how much this book fits into its ~200 pages. First, it teaches you AWK, the whole language is covered in about 60 pages. Then it shows you how to build some stuff. These projects include.</p><ul><li><p>Data Processing (sure, classic AWK)</p></li><li><p>Generating reports. (ok, makes sense)</p></li><li><p>A word processor. (uh)</p></li><li><p>A parser, compiler, assembler, and multiple programming languages. (what?)</p></li><li><p>A bunch of CS basics, sorting, searching, etc. (sure, why not)</p></li></ul><p>Yeah, all in AWK, all in 200 pages.</p><p>The simplicity of AWK is a big reason this book works. You can sit down and learn it in a few hours and the rest of the book can rely on that to explore a bunch of awesome things you can program with it. I wish more books were like this.</p><p>Go read it, you can just google the title and find the PDF. Make sure you do it before December so you can do all your Advent of Code<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> problems in AWK.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><a href="https://adventofcode.com">https://adventofcode.com</a> | The best time of the year.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[How do computers work?]]></title><description><![CDATA[A tale of two programs]]></description><link>https://steveindusteves.substack.com/p/how-do-computers-work</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/how-do-computers-work</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Mon, 22 Feb 2021 13:00:18 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/01b8322c-5a41-477b-8fc5-84fcf9bd5e39_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s time to tackle the most important computer topic of them all. What&#8217;s better, an array or a linked list? Can we solve this mystery?</p><h1>Pretend you&#8217;re a computer for a sec</h1><p>Sombody has given you a program to execute. It is a list of instruction that somebody smart (a programmer) using some smart tools (a programming language and a compiler) translated into something a dummy like you (a computer) can do. Lets start with a super simple <strong>model</strong> of how computers work and see if we can answer our question.</p><p>Computers have a few places they can keep numbers called <em>registers</em>. They can do some math on the numbers in these registers (like <code>ADD</code> the values in two registers together and put the result in another register). They also have a much larger place to store data called <em>memory</em>. Memory isn&#8217;t super close to the computer so they can&#8217;t do any operations on data stored there, all they can do is <code>LOAD</code> a pice of data from memory to a register and <code>STORE</code> a piece of data from a register back to Memory.</p><p>Lets assume our list is in memory and we&#8217;re going to execute a program that adds up all the elements. An array is a contiguous block of numbers in memory. All you need to know to handle an array is the address of the first element and how many elements there are.</p><p>An array in memory that starts at address 5 might look like this.</p><pre><code>...
5: 1
6: 2
7: 3
8: 4
9: 5
...</code></pre><p>A program to add the elements in an array could look like this.</p><pre><code>p_array = 5 // address the array starts at
length = 5  // length of the array
i = 0       // index of the element we're on
result = 0  // sum of all the elements
// Loop over the array
loop:
  p_element = ADD(p_array, i)      // address of element
  element   = LOAD(p_element)      // load the element
  result    = ADD(result, element) // add element to result
  i         = ADD(i, 1)            // increment index
  if i &lt; length GOTO loop          // loop</code></pre><p>In a linked list the elements don&#8217;t need to be in order in memory. Each element also contains a <em>pointer</em> to the next element which is the address the next element is at. So each <em>node</em> of the list takes two slots in memory, one for the number and one for the <em>pointer</em>. A liked list in memory that starts at address 5 might look like this.</p><pre><code>...
5:   1
6:   9
7:   5
8:   0
9:   2
10:  11
11:  3
12:  950
...
950: 4
951: 7
...</code></pre><p>A program to add the element in a linked list could look like this.</p><pre><code>p_element = 5
loop:
  element   = LOAD(p_element)      // load the element
  result    = ADD(element, result) // add element to result
  p_next    = ADD(p_element, 1)    // address of next pointer
  p_element = LOAD(p_next)         // load next element address
  if p_element != 0 GOTO loop      // loop</code></pre><p>Can we tell which one is better yet?</p><p>By instruction counts alone they are the same. Each loop has 5<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> instructions per iteration. Does that mean they are equivalent?</p><p>The array program has 3 <code>ADD</code> instructions and 1 <code>LOAD</code>, the linked list program has 2 <code>ADD</code> instructions and 2 <code>LOAD</code> instructions. So if <code>ADD</code>s are faster than <code>LOAD</code>s it seems like the array version would be faster.</p><p>This is true, adding two numbers together is faster than loading a number from memory. It&#8217;s actually WAY WAY WAY faster. Memory is really far away in computer terms.</p><p>Modern computers have a way to deal with this problem. Since loading from memory is so slow they don&#8217;t want to just wait for data all day. They do this in two ways. They have caches for data from memory that are closer to the cpu. There&#8217;s small fast ones close to the processor that tie into larger and slower ones chaining out to main memory. When you <code>LOAD</code> an address it first looks in the cache, if it&#8217;s not there it checks the next cache, etc...</p><p>If you <code>LOAD</code> address 7 it&#8217;s slow because you go all the way out to main memory but if you <code>LOAD</code> it again later it&#8217;s fast because it&#8217;s in the cache.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><p>That seems like it would help but in our program we&#8217;re only ever <code>LOAD</code>ing a single address once. This is where the other trick comes in. The caches don&#8217;t <code>LOAD</code> elements from memory one at a time but they <code>LOAD</code> them in big blocks called cache lines. If you ask to <code>LOAD</code> address 0 it&#8217;ll actually <code>LOAD</code> addresses 0-64 into the cache so any further <code>LOAD</code>s of those addresses will be fast. This means that for our array example we&#8217;ll <code>LOAD</code> the whole array into the cache at once and all the <code>LOAD</code>s will be fast. The linked list will work similarly but <strong>OH NO</strong> in our example we have one of the nodes out all the way at 950 which won&#8217;t be in the cache. That means we&#8217;ll have at least one more slow <code>LOAD</code> in that program than the array version so clearly arrays are faster than linked lists!</p><p>Hold on a sec though, what if all the nodes in the linked list were in the same cache line? Both programs would only be doing fast cache loads and adds so they would be equally fast. Is this the answer? That the data structure doesn&#8217;t matter, all that matters is the data locality? Sort of. This is one of the biggest factors in performance. Make sure all the data you&#8217;re using is packed together.</p><p>What if the lists were very long? In our <strong>model</strong> if a cache line is 64 addresses, that would store an array with 64 elements, or a linked list with 32 element. Since each slot in a linked list also has the address of the next element you can fit twice as much data in the array than the linked list. This can be a huge factor when the lists are large. Another point for the array!</p><p>There&#8217;s a big piece of this that our model doesn&#8217;t factor in yet. In our model each instruction is done one at a time. Modern computers don&#8217;t actually work this way.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> They try to execute many instructions at the same time. You don&#8217;t tell them which ones to do in parallel<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>, They figure it out based on the data dependencies of the individual instructions. If we have two instructions.</p><pre><code>a = ADD(b,c)
d = ADD(f,g)</code></pre><p>Those two adds could be done at the same time because they don&#8217;t depend on each other. If instead you have.</p><pre><code>a = ADD(b,c), d = ADD(a, e)</code></pre><p>They can be done at the same time.</p><p>Lets look at our examples. If they instructions had to execute one at a time a trace of the full loop would look like this in the array case.</p><pre><code>00: p=ADD(a,i),
01:            e=LOAD(p),
02:                      r=ADD(r,e),
03:                                 i=ADD(i,1)
04: p=ADD(a,i),
05:            e=LOAD(p),
06:                      r=ADD(r,e),
07:                                 i=ADD(i,1)
08: p=ADD(a,i),
09:            e=LOAD(p),
10:                      r=ADD(r,e),
11:                                 i=ADD(i,1)
12: p=ADD(a,i),
13:            e=LOAD(p),
14:                      r=ADD(r,e),
15:                                 i=ADD(i,1)
16: p=ADD(a,i),
17:            e=LOAD(p),
18:                      r=ADD(r,e),
19:                                 i=ADD(i,1)</code></pre><p>And this for the linked list.</p><pre><code>00: e=LOAD(p),
01:           r=ADD(e,r),
02:                      n=ADD(p,1),
03:                                 p=LOAD(n)
04: e=LOAD(p),
05:           r=ADD(e,r),
06:                      n=ADD(p,1),
07:                                 p=LOAD(n)
08: e=LOAD(p),
09:           r=ADD(e,r),
10:                      n=ADD(p,1),
11:                                 p=LOAD(n)
12: e=LOAD(p),
13:           r=ADD(e,r),
14:                      n=ADD(p,1),
15:                                 p=LOAD(n)
16: e=LOAD(p),
17:           r=ADD(e,r),
18:                      n=ADD(p,1),
19:                                 p=LOAD(n)</code></pre><p>Pretty much the same.</p><p>But in the array we can actually do all the steps at the same time if we look across loops. We can do the math and load the next element while we&#8217;re adding up the last one. A full trace would look like this.</p><pre><code>00: p1=ADD(a,i0),                        i1=ADD(i0,1)
01: p2=ADD(a,i1),e1=LOAD(p1),            i2=ADD(i1,1)
02: p3=ADD(a,i2),e2=LOAD(p2),r=ADD(r,e1),i3=ADD(i2,1)
03: p4=ADD(a,i3),e3=LOAD(p3),r=ADD(r,e2),i4=ADD(i3,1)
04: p5=ADD(a,i4),e4=LOAD(p4),r=ADD(r,e3),i5=ADD(i4,1)
05:              e5=LOAD(p5),r=ADD(r,e4),
06:                          r=ADD(r,e5),</code></pre><p>For the linked list we can&#8217;t do quite as well. We can&#8217;t load the next element until we&#8217;ve loaded the pointer so we can&#8217;t get multiple steps ahead like we can with arrays. A full trace would look like this.</p><pre><code>00: e1=LOAD(p0),
01:             r=ADD(e1,r),n1=ADD(p0,1),
02:                                      p1=LOAD(n1)
03: e2=LOAD(p1),
04:             r=ADD(e2,r),n2=ADD(p1,1),
05:                                      p2=LOAD(n2)
06: e3=LOAD(p2),
07:             r=ADD(e3,r),n3=ADD(p2,1),
08:                                      p3=LOAD(n3)
09: e4=LOAD(p3),
10:             r=ADD(e4,r),n4=ADD(p3,1),
11:                                      p4=LOAD(n4)
12: e5=LOAD(p4),
13:             r=ADD(e5,r),n5=ADD(p4,1),
15:                                      p5=LOAD(n5)</code></pre><p>Seems like arrays are the clear winner!<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a></p><div><hr></div><p>I&#8217;ve only recently started to understand a computer this way. Some of the places I&#8217;ve learned from include. </p><ul><li><p>This Mike Acton talk.</p><div id="youtube2-rX0ItVEVjHc" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;rX0ItVEVjHc&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/rX0ItVEVjHc?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></li><li><p>This <a href="https://fgiesen.wordpress.com">blog</a> by Fabian &#8220;ryg&#8221; Giesen. especially the post in the footnotes.</p></li><li><p><a href="https://handmadehero.org">Handmade Hero</a>, especially this episode.</p><div id="youtube2-qin-Eps3U_E" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;qin-Eps3U_E&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/qin-Eps3U_E?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></li></ul><p>I really want to see these things in action. Does anybody know if there are ways to see which instructions are executed each cycle or when loads miss the cache or anything like this on any mainstream computers / operating systems?</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>About 5, we&#8217;re sorta ignoring the magic loop part which probably isn&#8217;t just one instruction but we can assume that it&#8217;s the same amount of work in each of the examples.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Unless of course another thread has changed it! Across cores caches have to synchronize so unless each core is working on different cache lines of data things are gonna get slow.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>This whole section is a really bad version of this better blog post you should read. <a href="https://fgiesen.wordpress.com/2018/03/05/a-whirlwind-introduction-to-dataflow-graphs/">A whirlwind introduction to dataflow graphs</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>There is another thing called SIMD where you do tell the computer to do multiple things at the same time but it&#8217;s more like telling them to do the same thing on multiple pieces of data. ADD these 8 sets of numbers all at once and put the result in these 8 places.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>It&#8217;s not really clear, there are many more concerns than we&#8217;ve covered here.</p></div></div>]]></content:encoded></item><item><title><![CDATA[LSystems]]></title><description><![CDATA[skipping intermediate results]]></description><link>https://steveindusteves.substack.com/p/lsystems</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/lsystems</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Mon, 15 Feb 2021 23:28:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!55Qs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An LSystem<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> is a form of grammar based string rewriting. Take a string, like &#8220;<em>FX&#8221;</em> and some rules, like <em>&#8220;X&#8221; -&gt; &#8220;X+YF+&#8221;, &#8220;Y&#8221; -&gt; &#8220;-FX-Y&#8221;</em> and apply those rules to the string. Replace characters matching the left hand side of the rule with the production on the right.</p><p>After applying the rules once <em>&#8220;FX+YF+&#8221;</em>.</p><p>After applying them again<em>&#8220;FX+YF++-FX-YF+&#8221;</em>.</p><p>After applying them a third time <em>&#8220;FX+YF++-FX-YF++-FX+YF+--FX-YF+&#8221;</em>.</p><p>A little program could look like this.</p><pre><code>#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

typedef struct {
  char val;
  char *replacement;
  size_t replacement_len;
} Rule;

#define RULE(c, rep)               \
  (Rule) {                         \
    .val = c, .replacement = rep,  \
    .replacement_len = strlen(rep) \
  }

#define BUF_MAX 1024

int main() {
  char input[BUF_MAX] = "FX";
  size_t input_len = strlen(input);
  Rule rules[] = {
      RULE('X', "X+YF+"), RULE('Y', "-FX-Y"), {0, 0, 0}};

  char output[BUF_MAX] = {};
  size_t output_len = 0;

  for (int char_i = 0; char_i &lt; input_len; char_i++) {
    char c = input[char_i];
    Rule *rule = NULL;
    for (Rule *r = rules; r-&gt;val != 0; r++) {
      if (c == r-&gt;val) {
        rule = r;
        break;
      }
    }
    if (rule) {
      for (int i = 0; i &lt; rule-&gt;replacement_len; i++) {
        output[output_len++] = rule-&gt;replacement[i];
      }
    } else {
      output[output_len++] = c;
    }
  }
  output[output_len++] = 0;
  printf("%s\n", output);
  return 0;
}</code></pre><p>Iterate over the input string. For each character check if it matches a rule. If so output it&#8217;s production. Otherwise output the original character.</p><p>To evaluate it more than once, use the output of one pass as the input to the next.</p><pre><code>  int num_iterations = 2;
  
  int iteration = 0;
  do {
    for (int char_i = 0; char_i &lt; input_len; char_i++) {
      char c = input[char_i];
      Rule *rule = NULL;
      for (Rule *r = rules; r-&gt;val != 0; r++) {
        if (c == r-&gt;val) {
          rule = r;
          break;
        }
      }
      if (rule) {
        for (int i = 0; i &lt; rule-&gt;replacement_len; i++) {
          output[output_len++] = rule-&gt;replacement[i];
        }
      } else {
        output[output_len++] = c;
      }
    }
    output[output_len++] = 0;
    memcpy(input, output, output_len * sizeof(output[0]));
    input_len = output_len;
    output_len = 0;
  } while (++iteration &lt; num_iterations);
  printf("%s\n", output); </code></pre><p>Each evaluation depends on the evaluation before it. However each letter of each evaluation comes from a specific letter of the previous one. All the letters and where they came from form a tree.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!55Qs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!55Qs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!55Qs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!55Qs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!55Qs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!55Qs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg" width="1456" height="450" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:450,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:265223,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!55Qs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!55Qs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!55Qs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!55Qs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F54b8b716-cbad-40f6-9cc7-a5e2f1b6927d_3233x1000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Instead of evaluating this tree a whole layer at a time, we could instead <strong>walk</strong> the tree to output the lowest level directly.</p><p>Using a stack to keep track of the parent nodes we can do a depth first traversal of the tree.</p><pre><code>  typedef struct {
    int level;
    char *prod;
    size_t prod_len;
    size_t prod_pos;
  } Frame;

  Frame stack[26];  // At least as large as num_iterations
  int sp = 0;

  Frame frame;
  frame.level = 0;
  frame.prod = &amp;input[0];
  frame.prod_len = input_len;
  frame.prod_pos = 0;

  for (;;) {
    char c = frame.prod[frame.prod_pos++];

    // Check for a rule unless we're in the last level.
    Rule *rule = NULL;
    if (frame.level &lt; num_iterations) {
      for (Rule *r = rules; r-&gt;val != 0; r++) {
        if (c == r-&gt;val) {
          rule = r;
          break;
        }
      }
    }

    if (rule) {
      // drop down into rule.
      stack[sp++] = frame;
      frame.level += 1;
      frame.prod = rule-&gt;replacement;
      frame.prod_len = rule-&gt;replacement_len;
      frame.prod_pos = 0;
    } else {
      output[output_len++] = c;
      while (frame.prod_pos == frame.prod_len) {
        if (sp == 0) {
          goto done;
        } else {
          frame = stack[--sp];
        }
      }
    }
  }
done:
  printf("%s\n", output);</code></pre><p>It&#8217;s really easy to just thread the output of one algorithm as the input to the next. You can wrap the algorithm in a loop or a recursion or a fold or any other way to repeatedly evaluate the same thing. Bu thinking through the structure of the problem and the exact result you want can lead to better <a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> solutions.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><div><hr></div><h2>A little note on LSystems</h2><p>LSystems can be used to model biological systems. When you combine the output strings with some graphical interpretations they can create really cool fractal and recursive structures. <a href="https://github.com/saolsen/lsystems">I&#8217;ve played a bit with them myself.</a> To learn more there is an amazing book you can check out called <a href="http://algorithmicbotany.org/papers/abop/abop.pdf">The Algorithmic Beauty of Plants.</a></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Short for Lindenmayer System.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>&#8220;Better&#8221; in this case means faster or uses less resources.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>I do wonder if there&#8217;s a variant of this in a lazily evaluated language (like Haskell) where you can write the algorithm as if it&#8217;s the first version, piping the output of one run to the input of the next, but it evaluates it like it&#8217;s the second version, only fetching results as they&#8217;re needed. I don&#8217;t know enough Haskell to try.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Hello World]]></title><description><![CDATA[Welcome to Steve Industeves by me, Steve Olsen. I&#39;m a programmer, building a programming language at Oso and making games and other things for fun. Sign up now so you don&#8217;t miss the first issue. In the meantime, tell your friends!]]></description><link>https://steveindusteves.substack.com/p/coming-soon</link><guid isPermaLink="false">https://steveindusteves.substack.com/p/coming-soon</guid><dc:creator><![CDATA[Steve Olsen]]></dc:creator><pubDate>Fri, 12 Feb 2021 22:00:00 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/d0294e9d-a2c1-4318-8a65-f005a98c695c_512x512.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I like to start a new blog about once every 3 years, make a single post (maybe 2) and then never post to it again.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> This time I think I&#8217;ll try out SubStack.</p><p>Recently we&#8217;ve been shipping a ton of things at work<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> so my side project time has been much less about making things and more just about learning things. I think it would be nice to share some of those things I&#8217;m learning with others so going to try this out and see how it goes.</p><p>I hope you enjoy.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>I really hope this statement ages poorly.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>I&#8217;m an engineer at Oso | https://www.osohq.com</p></div></div>]]></content:encoded></item></channel></rss>