<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Tim Knauf&#39;s Blog</title>
  <subtitle>Video games, programming, music and creativity.</subtitle>
  <link href="https://tim.knauf.kiwi/feed.xml" rel="self" />
  <link href="https://tim.knauf.kiwi/" />
  <updated>2025-09-10T00:00:00Z</updated>
  <id>https://tim.knauf.kiwi/</id>
  <author>
    <name>Tim Knauf</name>
  </author>
  <entry>
    <title>Family fan art</title>
    <link href="https://tim.knauf.kiwi/blog/posts/2025/family-fan-art/" />
    <updated>2025-09-10T00:00:00Z</updated>
    <id>https://tim.knauf.kiwi/blog/posts/2025/family-fan-art/</id>
    <content type="html">&lt;p&gt;I just had to share this – with the creator&#39;s permission, of course!&lt;/p&gt;
&lt;p&gt;I&#39;m blessed to have four enthusiastic fans of &lt;a href=&quot;https://tim.knauf.kiwi/games&quot;&gt;my games&lt;/a&gt; in my children, and Andy (9) has taken it up a notch with this fan art.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://tim.knauf.kiwi/img/Oa7MJlB-on-1280.jpeg&quot; alt=&quot;Fan art of Tim Knauf&#39;s games, featuring GATECRASHER, Laser League and Signed by &#39;89.&quot; width=&quot;1280&quot; height=&quot;774&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the left, you&#39;ll see Kaito from &lt;a href=&quot;https://timknauf.itch.io/gatecrasher&quot;&gt;GATECRASHER&lt;/a&gt;, blasting off in his jetboots and (spoiler!) even making a friend. Then the incredible three from &lt;a href=&quot;https://timknauf.itch.io/laser-league&quot;&gt;Laser League&lt;/a&gt;: Bridget, Sophie and Zelda all showing off their unique skills.&lt;/p&gt;
&lt;p&gt;And – a personal favourite – the band from &lt;a href=&quot;https://timknauf.itch.io/signed-by-89&quot;&gt;Signed by &#39;89&lt;/a&gt;, back together and rocking harder than ever.&lt;/p&gt;
&lt;p&gt;Thank you Andy! You rock pretty hard yourself.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A Game in F#? Why, yes indeed.</title>
    <link href="https://tim.knauf.kiwi/blog/posts/2024/making-of-truffle-wizard-fsharp/" />
    <updated>2024-12-07T00:00:00Z</updated>
    <id>https://tim.knauf.kiwi/blog/posts/2024/making-of-truffle-wizard-fsharp/</id>
    <content type="html">&lt;h3&gt;What the heck is a Truffle Wizard?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://timknauf.itch.io/truffle-wizard&quot;&gt;Truffle Wizard&lt;/a&gt; is a short incremental game with truffles, clicking and &lt;em&gt;almost definitely&lt;/em&gt; no forbidden magic.&lt;/p&gt;
&lt;p&gt;An unusual thing about Truffle Wizard is that it isn&#39;t written in either a classic &#39;gaming&#39; language, like C++, C# or even Lua, nor a &#39;web&#39; language like Javascript or TypeScript. Truffle Wizard is written in the functional language F#. This is the story about how that came to be, and my experience with developing a game entirely in a functional language. Spoiler #1: it was a very positive experience!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My honest heart compels me to tell you that I&#39;ve simplified some of my timeline here for the sake of clarity. :-)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Truffle Wizard is open source! So if anything in this post arouses your curiosity, &lt;a href=&quot;https://hg.sr.ht/~timknauf/truffle&quot;&gt;check out the repository&lt;/a&gt; to see how the (truffle) sausage is made.&lt;/p&gt;
&lt;h3&gt;Who is this for?&lt;/h3&gt;
&lt;p&gt;This post is aimed at two audiences:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Game developers who have learned a little about functional programming, see the appeal, but are wondering whether it could ever be applied to games &lt;em&gt;(this was me a few months back)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;F# afficionados, who love the language but aren&#39;t sure whether they can confidently recommend it as a language for game development &lt;em&gt;(spoiler #2: I think the answer is a qualified yes!)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;My background&lt;/h3&gt;
&lt;p&gt;I&#39;ve been making video games of various sorts since the early-mid naughties (goodness!). In my day job, I&#39;ve been in Unity shops for over a decade now, immersed in C#. Before then, I slung Objective-C in the early days of the iPhone, and before &lt;em&gt;that&lt;/em&gt;, I was part of the last big wave of Flash developers -- and one of the seemingly few who enjoyed writing ActionScript 3! For hobby projects, I love the creative constraints of PICO-8 and its cousin Picotron; Lua is the scripting language of choice for both of those. Way back in the mists of time, I was sole developer for what we&#39;d now call a web app, writing Rails-flavoured Ruby as best I could.&lt;/p&gt;
&lt;h3&gt;Glimpses of functional beauty&lt;/h3&gt;
&lt;p&gt;Looking at those programming languages, you&#39;ll note that not one of them is what we&#39;d call a &#39;functional&#39; language! Games have grown up wedded to the object-oriented tradition, and that&#39;s been my world for my entire career.&lt;/p&gt;
&lt;p&gt;But I&#39;ve long had an awareness that objects, with behaviour all bound up with state, aren&#39;t the only way to express yourself to a computer! I remember really enjoying learning some Haskell at university. Some of my classmates hated it -- why not just tell the computer what you want instead of having to express it as a maths equation? (Their words, not mine!) For myself, I couldn&#39;t see how I could apply this paradigm to my first love, game development, but I &lt;em&gt;did&lt;/em&gt; see the beauty of purely transforming data, even if the &#39;zen of Haskell&#39; eluded me.&lt;/p&gt;
&lt;p&gt;I mean, how can you not love the inscrutible beauty of this?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-haskell&quot;&gt;qs [] = []
qs (x:z) = qs [y|y &amp;lt;- z, y&amp;lt;x] ++ [x] ++ qs [y|y &amp;lt;- z, y &amp;gt;=x]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The impetus: reasoning about state&lt;/h3&gt;
&lt;p&gt;I have this long-running side project idea that I keep returning to -- a strategy game. (Truffle Wizard is not that game, but I&#39;ll get to that, I promise!) The design is simulation-heavy, with a proper economy and a bunch of feedback loops.&lt;/p&gt;
&lt;p&gt;Some months back I decided that I decided that it was high time to actually make a prototype of that game. I got stuck in with Picotron, and made some good progress with a little Lua-based strategy engine.&lt;/p&gt;
&lt;p&gt;Lua is an &lt;em&gt;extremely&lt;/em&gt; flexible language, and I wouldn&#39;t want to just lump it in with classic object-oriented languages. Yet despite its flexibilty -- or maybe because of it?! -- development on the project started to feel more and more painful.&lt;/p&gt;
&lt;p&gt;I had a good think about what was going on.&lt;/p&gt;
&lt;p&gt;My colleagues will tell you that I&#39;m, ah, &lt;em&gt;very keen&lt;/em&gt; on being able to &amp;quot;reason about state&amp;quot;. What does this mean? Basically, I want to be able to &amp;quot;run the code in my head&amp;quot; with at least reasonable confidence. I want to be able to look at a running game that I (or we) have written, and have some chance of understanding:
a. the internal state it&#39;s in,
b. how it arrived at that state, and
c. the state it will be in next, for any given action the player might take.&lt;/p&gt;
&lt;p&gt;Writing game code that allows us to do that is surprisingly hard! Games are state machines at heart, but usually (in my experience) they&#39;re state machines with a &lt;em&gt;truckload&lt;/em&gt; of possible states, and an almost infinite variety of ways you can move from one state to another.&lt;/p&gt;
&lt;p&gt;So I&#39;ve developed this mini obsession with well-defined state that moves through well-defined transitions. Ideally, each state is cleanly derived from a) a player action and b) a previous state. Why, it almost sounds like we could plug those two things into some sort of well-defined formula that could derive the new state. Some sort of state-deriving... &lt;strong&gt;function&lt;/strong&gt;?!&lt;/p&gt;
&lt;h3&gt;Slow down to speed up&lt;/h3&gt;
&lt;p&gt;So, my strategy game side project? That sort of systems-heavy game design isn&#39;t my native tongue (though I am a keen learner!). The code was getting away from me, even in the early prototype. I was finding it hard to reason about the next &#39;tick&#39; of the simulation; how exactly would the state change, and why? In a surely related problem, my data structures were getting hard to hold in my head. I bolted on types ad-hoc with &lt;a href=&quot;https://github.com/LuaLS/lua-language-server/wiki/Annotations&quot;&gt;Lua language server annotations&lt;/a&gt;, but now I had what felt like a ton of boilerplate type annotations to manage.&lt;/p&gt;
&lt;p&gt;Possibly worst of all, I needed to be extremely careful lest I set up a state mutation, in a way I wouldn&#39;t be able to track or remember later. Lua can support immutable data structures -- like I said, it&#39;s flexible! -- but its idioms push you towards big ol&#39; key-value stores. Keys may be present or may be nil, values are expected to change -- and Lua APIs, libraries and code samples are all set up with those expectations. For me, it requires a lot of discipline to write Lua in a different mode to that!&lt;/p&gt;
&lt;p&gt;If you know anything about functional programming, you&#39;ll be smiling as you read my summarised list of complaints:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;quot;It&#39;s hard to reason about my state!&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Detailed types are helpful but now I&#39;m writing loads of boilerplate definitions!&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Yikes, it&#39;s easy to accidentally mutate state!&amp;quot;&lt;/li&gt;
&lt;li&gt;Bonus complaint! &amp;quot;Despite my best intentions, I&#39;m doing many null checks.&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I really believe that Lua is a great language for games: it&#39;s an all-purpose multi-tool that is, has been, and should continue to be used across most game genres. But if I wanted to get serious about learning to design and program systemic games (and I did!), it felt like my opportunity to research and learn whether there might, for me, be a different way. I don&#39;t like abandoning work-in-progress, but as a wise colleague once said, &amp;quot;Sometimes you have to slow down to speed up.&amp;quot;&lt;/p&gt;
&lt;h3&gt;F# for Fun and Profit&lt;/h3&gt;
&lt;p&gt;At this point I could have reached for C# and Unity. I know them very well from my day job! But around the time I was having these thoughts, I was drawn back to a site that I had browsed with interest in the past, but couldn&#39;t quite see how it could apply to me: Scott
Wlaschin&#39;s wonderful &lt;a href=&quot;https://fsharpforfunandprofit.com/&quot;&gt;F# for Fun and Profit&lt;/a&gt;. In particular, the thought of &lt;a href=&quot;https://fsharpforfunandprofit.com/rop/&quot;&gt;Railway-Oriented Programming&lt;/a&gt;, also known as &amp;quot;happy path programming&amp;quot;, just seemed so delightful:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;let executeUseCase =
  receiveRequest
  &amp;gt;&amp;gt; validateRequest
  &amp;gt;&amp;gt; canonicalizeEmail
  &amp;gt;&amp;gt; updateDbFromRequest
  &amp;gt;&amp;gt; sendEmail
  &amp;gt;&amp;gt; returnMessage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Upon seeing this, and others like it, my brain lit up. It&#39;s so, well, easy to reason about! I guess it&#39;s not beautiful zen code like the Haskell example; this speaks more of elegant pragmatism. And that&#39;s an important thing I realised about the F# community and the F# style, actually: as best I can tell, yes, the community does love clean, elegant design, but at heart, it&#39;s a pragmatic language for people who like to ship things. Both of those things reasonate really strongly with me!&lt;/p&gt;
&lt;p&gt;So, I got to thinking: what if all my state transitions could be defined right out in the open? What if rich types could be omnipresent, but inferred 95% of the time? Could I have immutable by default, and also never have to do another null check? Sign me up, surely!&lt;/p&gt;
&lt;h3&gt;For games, though?&lt;/h3&gt;
&lt;p&gt;But could this be applied to game development? Well, maybe it could! C# rules the .NET ecosystem, but I could see that F# still gets plenty of support both from Microsoft and the open-source community. My understanding is that F# can do &lt;em&gt;nearly&lt;/em&gt; everything C# can do, with many tricks of its own.&lt;/p&gt;
&lt;p&gt;It felt too intimidating to dive back into my full strategy game project while trying to learn a new language. I&#39;m big on creative constraints, and so I &#39;dreamed smaller&#39;. I knew that I should still do a systemic game, with an economy -- that was the impetus for this whole learning journey, after all! But what could be a small enough project, yet still meaningfully a game?&lt;/p&gt;
&lt;p&gt;Ah, of course: a clicker. I could even do it without graphics. And that simplified clicker concept is what eventually became Truffle Wizard!&lt;/p&gt;
&lt;p&gt;Could I build a clicker with all the game logic in a functional language -- specifically, F#? And could I do it while sticking to what I understood as idiomatic F#: pure functions, algebraic types, immutable state?&lt;/p&gt;
&lt;h3&gt;Choosing the engine&lt;/h3&gt;
&lt;h4&gt;Unity: a mild disappointment&lt;/h4&gt;
&lt;p&gt;Early experiments with Unity were a little disappointing. You can build a .NET assembly from F#, and Unity will accept it just fine. But Unity is so, so mutable and object-oriented at its heart. It felt like there was a deep mismatch, which I&#39;d be constantly having to shim -- or perhaps treat Unity purely as a renderer for state created in F#? Either way, it didn&#39;t feel like I&#39;d be working to what I&#39;ve come to see as Unity&#39;s strengths: component-based design and an editor-heavy workflow.&lt;/p&gt;
&lt;h4&gt;Raylib: very promising&lt;/h4&gt;
&lt;p&gt;I moved from heavyweight to bantamweight: Raylib, specifically the supremely awkwardly-named &lt;a href=&quot;https://github.com/NotNotTech/Raylib-CsLo&quot;&gt;Raylib-CsLo&lt;/a&gt; .NET bindings. I&#39;m still learning about Raylib, but I&#39;m already a big fan. For a lot of side-project type games, it could be a total sweet spot, and I wondered if it might be right for Truffle Wizard.&lt;/p&gt;
&lt;p&gt;My initial proof-of-concept with Raylib saw me using imperitive-style C# to instruct Raylib: run the Raylib render loop, poll the inputs etc. Then F# ran the game&#39;s systemic &#39;core&#39;: a separate .NET assembly, running the game logic. I did it this way because I assumed I would have to! Surely, I thought, with F# being so functional, it&#39;s going to do a poor job of running an imperative loop?&lt;/p&gt;
&lt;p&gt;I&#39;m delighted to say that I was wrong -- very wrong! Unity has that deeply object-heavy approach, but Raylib, as far as I can tell so far, isn&#39;t like that at all. In fact, I found that I could drive Raylib&#39;s core loop in &lt;em&gt;fewer&lt;/em&gt; lines of F# than I had used for C#.&lt;/p&gt;
&lt;p&gt;Raylib does have some state of its own to set up, but once that&#39;s done, you can generate your game state cleanly and immutably, then just &#39;feed&#39; that to Raylib imperatively each frame. As far as I can tell so far, it works beautifully!&lt;/p&gt;
&lt;p&gt;For my next side project, I intend to jump right back in with Raylib. I really think F# + Raylib could make a great marriage, and I&#39;m excited to try it for future side projects.&lt;/p&gt;
&lt;h4&gt;Fable and Feliz: the right fit for &lt;em&gt;this&lt;/em&gt; project&lt;/h4&gt;
&lt;p&gt;But you don&#39;t have to be very observant to notice that Truffle Wizard is not, in fact, running in Raylib. It&#39;s not even using a traditional game engine at all. It&#39;s running on a web stack.&lt;/p&gt;
&lt;p&gt;Why did I turn away from Raylib, which was looking so promising? Truffle Wizard&#39;s spreadsheet-looking presentation amused me &lt;em&gt;(okay, I&#39;m easily amused)&lt;/em&gt;, and it made a great gameplay constraint to stop my design ideas from spiralling out of control. On the other hand, it &lt;em&gt;almost immediately&lt;/em&gt; became an awkward fit for a traditional, single-screen, non-scrolling game view. I had to hand-manage the UI, and had no idea what I would do when the table got big enough to spill off the bottom of the screen -- implement a bunch of my own scrolling routines, I guess?&lt;/p&gt;
&lt;p&gt;So I had some needs: presenting tables of data with many small bits of writing, flexible scrolling, and a helpful UI framework wouldn&#39;t hurt. Hmm, I think I know a platform where those needs might be met!&lt;/p&gt;
&lt;p&gt;So yes, I started looking at what might be available to me for &amp;quot;F# on the web&amp;quot;.&lt;/p&gt;
&lt;p&gt;Enter the incredible &lt;a href=&quot;https://fable.io/&quot;&gt;Fable&lt;/a&gt;. Fable is, among other things, a very sophisticated F#-to-Javascript compiler (transpiler?). You&#39;re able to write F#, some magic happens, and a full Javascript-powered web app pops out the other end. Add to that &lt;a href=&quot;https://github.com/Zaid-Ajaj/Feliz&quot;&gt;Feliz&lt;/a&gt;, which gives you the power of React, while letting you continue to write idiomatic F#, largely pretending that the giant web stack underneath you doesn&#39;t exist.&lt;/p&gt;
&lt;p&gt;This type of &amp;quot;tower of technologies&amp;quot; isn&#39;t normally my happy place for a side project! I feel like I get enough &amp;quot;towers of technologies&amp;quot; in my day job, so having a bunch of layers I didn&#39;t understand made me nervous. But in practice, I didn&#39;t run into &lt;em&gt;any&lt;/em&gt; problems -- the web stuff &#39;just worked&#39;, and continued to just work. Huge kudos to the creators; obviously incredible work has gone on behind the scenes there! (And thank goodness for me, because I generally try to learn just one hard new thing at a time!)&lt;/p&gt;
&lt;h3&gt;Game logic in F#&lt;/h3&gt;
&lt;p&gt;Finally, we reach the nuts and bolts of how my game loop actually runs in F#. I use the &lt;a href=&quot;https://elmish.github.io/elmish/&quot;&gt;Elmish&lt;/a&gt; style borrowed from the &lt;a href=&quot;https://elm-lang.org/&quot;&gt;Elm&lt;/a&gt; language*, which is conceptually beautifully simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An &lt;code&gt;init&lt;/code&gt; function produces a starting state &lt;strong&gt;state&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;dispatch&lt;/code&gt; function routes a &lt;strong&gt;message&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;update&lt;/code&gt; function takes the existing state and a message, and produces a &lt;strong&gt;new state&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;render&lt;/code&gt; function turns the state into what the player sees: a &lt;strong&gt;view&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I highly, highly recommend this model, and I&#39;ll try to explain why as I go.&lt;/p&gt;
&lt;p&gt;I don&#39;t think you need to literally use the Elmish package, by the way. While I need to do some more experimentation, I think a simple hand-rolled &#39;Elmish style&#39; architecture would probably work just fine, at least to the level that games need.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;* Why didn&#39;t I implement Truffle Wizard in Elm? I think that would have been a fine choice, actually! I persisted with F# because that was the language I had set out to learn, and because I think there&#39;s so much potential for F# to marry up with other technologies, post-Truffle Wizard.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;The state&lt;/h4&gt;
&lt;p&gt;The important thing to understand about the state data structure is that it contains &lt;em&gt;every single tiny little piece of state the game ever needs&lt;/em&gt;, all in the one place.&lt;/p&gt;
&lt;p&gt;If that sounds awful, it might help to think of it this way: your game, written in any language, is going to need all that state anyway. You&#39;re just collecting it all into the one place. Also (and I think this is key!), you&#39;re not tracking a hundred &#39;usually nil&#39; fields. Here&#39;s the entire State type for Truffle Wizard:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;type State = {
    Tick: TickCount
    Inventory: Inventory
    Unlocks: Unlocks;
    SaveLog: string;
    View: ViewState;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, some of those are data structures in their own right, but thanks to F#&#39;s discriminated unions, state only ever contains the data that it actually needs at any given time.&lt;/p&gt;
&lt;p&gt;A side benefit of keeping State in one place is that saving and loading games is suspiciously easy! I mean, this tiny function is basically the entire save system:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;let save state =
    let saveData = state |&amp;gt; toSaveDto |&amp;gt; serialize
    localStorage.setItem (keyName, saveData) |&amp;gt; ignore
    { state with SaveLog = &amp;quot;Saved to local storage&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Dispatching messages&lt;/h4&gt;
&lt;p&gt;I won&#39;t dwell on this one too long, partly because I&#39;m not sure that I understand the internal details of how dispatches are routed by Feliz and Elmish! But in use, dispatch functions are nice and simple to deploy from your view code. Here&#39;s the relevant line for one of the first actions you take in the game, harvesting a truffle:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;prop.onClick (fun _ -&amp;gt; dispatch (Produce resource))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;prop&lt;/code&gt; is a React button -- &amp;quot;Rootle for Truffles&amp;quot;. &lt;code&gt;Produce&lt;/code&gt; is a message (or you can think of it as a command) and &lt;code&gt;resource&lt;/code&gt; is the resource in question -- in this case, a &lt;code&gt;Truffle&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Truffle Wizard also runs autonomously, of course -- that&#39;s part of its whole deal. To do this, the game sets up a timer, to send itself a &lt;code&gt;Tick&lt;/code&gt; message every 250 milliseconds:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;JS.setInterval
    (fun _ -&amp;gt; dispatch (onTick DateTime.Now))
    250
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;JS&lt;/code&gt; there is the Javascript interop provided by Fable.&lt;/p&gt;
&lt;p&gt;And then later, I set up a &lt;code&gt;subscribe&lt;/code&gt; function, which dispatches the &lt;code&gt;Tick&lt;/code&gt; message to the update function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;let subscribe model =
    [ [&amp;quot;timer&amp;quot;], timer Tick ]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;The update function&lt;/h4&gt;
&lt;p&gt;The update function has to take existing state -- the entire state of the game -- plus a message, then return a new state. You might think that would make for a crazy-complicated function, right? It took a bit of thinking it through, but eventually I was able to land on what I think is a lovely, easy-to-follow core update logic:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;function
| Produce resource -&amp;gt; modifyInventory (incrementResource resource)
| BuildGenerator resource -&amp;gt; modifyInventory (incrementGenerators resource)
| BuildStorageFacility resource -&amp;gt; modifyInventory (incrementStorageFacilities resource)
| Venture adventure -&amp;gt; modifyInventory (haveAdventure adventure)
| Tick _ -&amp;gt; tryTick
| Save -&amp;gt; Persistence.save
| Load -&amp;gt; Persistence.load
| Reset -&amp;gt; reset
| Menu RequestReset -&amp;gt; requestReset
| Menu CancelReset -&amp;gt; cancelReset
| Menu Show -&amp;gt; showMenu
| Menu Hide -&amp;gt; hideMenu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if you have no clue how F# works, I think you could speak with some confidence about what&#39;s going on there.&lt;/p&gt;
&lt;p&gt;(If you&#39;re wondering, &amp;quot;but will this scale?!&amp;quot;, I think the answer is probably, &amp;quot;yes!&amp;quot; I would start by delegating to sub-functions. For instance, the four &lt;code&gt;Menu&lt;/code&gt;-related messages there could be collapsed into one, delegated to a menu-processing function.)&lt;/p&gt;
&lt;p&gt;F#&#39;s &lt;code&gt;with&lt;/code&gt; keyword gives the power to generate a new state while still preserving immutability. Here&#39;s a bit of code I consider a bit ugly -- yet it still gets its state-update job done in a safe way that I find easy to reason about:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;let tryTick state =
    // Autosave every 2 seconds
    let shouldAutoSave = (not Cheat.manualSaves) &amp;amp;&amp;amp; state.Tick % 8L = 0L
    let state = if shouldAutoSave then Persistence.save state else state
    
    let cadence = 4L / (Effects.generatorMultiplier state.Inventory)
    let inventory = if state.Tick % cadence = 0L then tickGenerators state.Inventory else state.Inventory
    { state with Inventory = inventory; Tick = state.Tick + 1L }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;state with&lt;/code&gt; at the end is saying, &amp;quot;make a copy of the existing state, please, but with the modifications I&#39;m about to give you.&amp;quot;&lt;/p&gt;
&lt;h4&gt;The render function&lt;/h4&gt;
&lt;p&gt;Again there&#39;s a single function for the view: &lt;code&gt;render&lt;/code&gt;. It takes a state and a dispatcher function, and returns a view.&lt;/p&gt;
&lt;p&gt;What does &amp;quot;return a view&amp;quot; mean? Elmish isn&#39;t opinionated about that; the idea is that you build up a data structure describing how to display your game, and hand it off to whatever &#39;engine&#39; your game uses to display stuff. For Truffle Wizard, I build up a hierarchy of DOM elements using Feliz, as shown in this function which renders each of the table rows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-fsharp&quot;&gt;let resourceRow state dispatch resource =
    [
        Html.tr [
            prop.children (
                [renderProduceCell; renderStorageCell; renderGeneratorCell]
                |&amp;gt; List.map (fun f -&amp;gt; f resource state dispatch)
            )
        ]
    ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Feliz takes care of the details for me, thank goodness. Presumably React then does some differential DOM magic, and the end result is that a &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; table row element shows up in your browser.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(If I do go back to Raylib next, I&#39;ll have to figure out what a &#39;good&#39; render data structure looks like there. Currently, I assume that I could build up a list of rendering function calls -- somehow -- and then get Raylib to just run through the list from top to bottom, calling them all. But we shall see!)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Short aside: ChatGPT&lt;/h3&gt;
&lt;p&gt;I have to call out ChatGPT as a very capable* &amp;quot;F# advisor&amp;quot;. Learning the language with this on hand was vastly different to the last time I learned a new technology. Asking, &lt;em&gt;&amp;quot;I&#39;ve written this F# code and it works, but I suspect it&#39;s not idiomatic; can you offer suggestions?&amp;quot;&lt;/em&gt; produced helpful answers, and I believe it&#39;s lead to my code being more concise and yes, more idiomatic.&lt;/p&gt;
&lt;p&gt;This is way better than the results I tend to get for Unity questions. My assumption is that the traing data for the LLM just includes a lot of high-quality answers from the wise F# community! (I&#39;ll leave the implications of the Unity training data as an exercise for the reader...)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;* With the &#39;4o&#39; model. I&#39;m sure &#39;4o-mini&#39; is good for some things, but it seems to be rubbish at F#.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Did I achieve Railway-Oriented Programming?&lt;/h3&gt;
&lt;p&gt;There are &lt;em&gt;occasional&lt;/em&gt; nice moments in Truffle Wizard&#39;s code where the data &#39;flows&#39;, piped from line to line, processed from one form to another. But no, I haven&#39;t yet internalised the lessons of Railway-Oriented Programming -- there are a lot of more ugly-looking places in my code.&lt;/p&gt;
&lt;p&gt;But I think that&#39;s okay! Even with my amateur use of F#&#39;s rich set of features, I found I could introduce, prototype and sometimes remove systems with ease. I could refactor with so little fuss that I sometimes couldn&#39;t believe a refactor had happened at all. Bugs were rare, were entirely content bugs, and generally I already knew exactly what line to change the moment I saw them in-game.&lt;/p&gt;
&lt;p&gt;And most importantly for me, I found the gameplay state &lt;em&gt;so easy to reason about&lt;/em&gt;. Ah, bliss.&lt;/p&gt;
&lt;h3&gt;What&#39;s next?&lt;/h3&gt;
&lt;p&gt;Will my next side project game use F#? Almost certainly! Working with it on this project was honestly a joy. I&#39;m trying to think of something to complain about, but nothing is coming to mind!&lt;/p&gt;
&lt;p&gt;I&#39;m particularly keen to dive back into Raylib and put some of my theories to the test.&lt;/p&gt;
&lt;p&gt;Further in the future, I note that Fable can compile not just into Javascript, but other languages as well. Now, if there were to be a Lua compilation target, I would be able to write in F# for my beloved PICO-8 and Picotron. Sadly, adding this to Fable myself is currently a bit beyond me, but a dev can dream!&lt;/p&gt;
&lt;h3&gt;Thanks!&lt;/h3&gt;
&lt;p&gt;If you&#39;ve read this far, thank you! This was my first longer-form technical blog post. I really appreciate your attention.&lt;/p&gt;
&lt;p&gt;Thanks to {proofreaders} for your feedback on the draft.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>A site of one&#39;s own</title>
    <link href="https://tim.knauf.kiwi/blog/posts/2024/site-of-ones-own/" />
    <updated>2024-12-01T00:00:00Z</updated>
    <id>https://tim.knauf.kiwi/blog/posts/2024/site-of-ones-own/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;&amp;quot;Tim,&amp;quot;&lt;/em&gt; I said to myself, gently yet firmly, &lt;em&gt;&amp;quot;It&#39;s high time you took control of the way you present yourself online! Your creative output is scattered to the four winds across different sites, and your so-called blog... lives on Tumblr.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So here we are! A real website of my own, the likes of which I haven&#39;t had since the 90s.&lt;/p&gt;
&lt;p&gt;Teenaged Tim tried to add every 1998 technology in sight to his homepage! In the intervening years I&#39;ve learned a &lt;em&gt;lot&lt;/em&gt; about controlling scope, so I&#39;ve kept things simple around these parts for now.&lt;/p&gt;
&lt;p&gt;I&#39;ve decided not to post across the posts from &lt;a href=&quot;https://www.tumblr.com/timknauf&quot;&gt;my old Tumblr blog&lt;/a&gt;. For the most part, I&#39;ve just incorporated the content inline here, e.g. in the information about the games and music, which I think works well!&lt;/p&gt;
&lt;p&gt;More to come soon!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you&#39;re tech-curious: the site is made with the rather wonderful &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; with &lt;a href=&quot;https://bulma.io/&quot;&gt;Bulma CSS&lt;/a&gt;. Nice.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
</feed>