Jekyll2022-05-28T00:35:47+00:00/blog/feed.xmlMay Like The MonthMay's blog about math, music, programming, etc.The FQL Museum2022-05-27T00:00:00+00:002022-05-27T00:00:00+00:00/blog/2022/05/27/the-fql-museum<p>It’s been very slightly over two years since I’ve updated this blog. Let’s change that! I have a project to announce
that’s been in the works since 2020: The FQL Museum.</p>
<p>It was originally inspired by looking at retro game compilations by Atari, Sega, and Namco, and going “wouldn’t it be funny if I did that for my games?” The more I thought about it, though, the better the idea seemed. I had taken a lot of my old games off of
itch because they were kind of embarrassing and unprofessional, but they were games I was proud of then and am still kind of proud of
now. It’d also be a way to release some unreleased games, and it’d also be a good way to learn how to use <a href="https://www.electronjs.org/">Electron</a>. Two years of on-and-off work later, and it’s finally ready to release.</p>
<p>Note that I didn’t say it was <em>done.</em> That’s not really accurate. I’m sure that future hard drive spelunking will turn
up new things to put in the museum. I know for a fact that there are three or so games that could easily be slotted in, and a handful more that could be inserted with some porting. (The museum’s main menu could use some sprucing up, too, but that’s less important.) Still, I’m happy calling this 1.0 and moving on to other projects for now.</p>
<p>I haven’t used the name Finis Quartus Luna in a couple years, but it was my main identity online for a while. This museum is
the sendoff that pseudonym deserves, I think. Please enjoy.</p>
<iframe src="https://itch.io/embed/625066" width="552" height="167" frameborder="0"><a href="https://maymoonsley.itch.io/fql-museum">The FQL Museum by MayMoonsley</a></iframe>May LawverIt’s been very slightly over two years since I’ve updated this blog. Let’s change that! I have a project to announce that’s been in the works since 2020: The FQL Museum.Optimizing Cookie Clicker2020-05-25T00:00:00+00:002020-05-25T00:00:00+00:00/blog/2020/05/25/optimizing-cookie-clicker<p>Like many others, I’ve been staying at home to slow the spread of COVID-19. Also like many others, I’ve been trying to spend my time in quarantine productively. Nobody can work all the time, though, so in between work on my various projects I’ve been playing Cookie Clicker.</p>
<p><a href="https://orteil.dashnet.org/cookieclicker/">Cookie Clicker</a> took the internet by storm a while back. It’s a browser-based game where you click a cookie to bake cookies. You can then spend these cookies on cursors to click for you, grandmas to bake cookies for you, farms to grow cookies, and so on. Cookie Clicker’s a very charming game, and it’s easy to waste hours on it. The genre of <a href="https://en.wikipedia.org/wiki/Incremental_game">incremental games</a> didn’t start with Cookie Clicker, but it certainly popularized it.</p>
<p>These games are primarily about making a number go up; playing well means making that number go up quickly. How many cookies you’re making per second is the best measure of your performance in Cookie Clicker, since how many cookies you have in your bank at the moment varies as you buy buildings and upgrades and save up for the next ones. This raises an obvious question: what’s the best strategy? How can you get your Cookies per Second (or CpS) as high as possible as quickly as possible? If I was going to be scientific about this (and I was), I needed a way to test out different strategies. To do this, I spent some time…</p>
<h2 id="building-a-simulator">Building a Simulator</h2>
<p>This isn’t <em>strictly</em> necessary. It’s theoretically possible to test out strategies in the game itself by manually executing some algorithm whenever a decision needs to be made. However, I want to do something with my time other than play Cookie Clicker, so I decided to make the computer do the work. In other words, I needed to somehow simulate this game. My original plan was to take the game’s source code, strip out everything but the core game logic, and graft the simulator functionality onto that. The game’s source code is more than 13,000 lines long, though. Sifting through all of that would’ve been a nightmare, and a stripped-down simulation would probably perform better, so I reimplemented the core gameplay in Node.js. This is easier than it might sound; the game has a <a href="https://cookieclicker.fandom.com/wiki/Cookie_Clicker_Wiki">super helpful wiki</a> and the game itself isn’t super complicated. I cut out some mechanics that introduced too much randomness or distracted from buildings and their upgrades:</p>
<ul>
<li><a href="https://cookieclicker.fandom.com/wiki/Golden_Cookie">Golden Cookies</a></li>
<li><a href="https://cookieclicker.fandom.com/wiki/Sugar_Lump">Sugar Lumps</a></li>
<li><a href="https://cookieclicker.fandom.com/wiki/Grandmapocalypse">The Grandmapocalypse</a></li>
<li><a href="https://cookieclicker.fandom.com/wiki/Achievement">Achievements</a></li>
<li><a href="https://cookieclicker.fandom.com/wiki/Seasons">Seasons</a></li>
<li><a href="https://cookieclicker.fandom.com/wiki/Ascension">Ascension</a></li>
<li><a href="https://cookieclicker.fandom.com/wiki/Big_Cookie">The Big Cookie</a></li>
</ul>
<p>(Note that, since the simulator can’t click on the cookie, I always start it off with a single Cursor to make up for that.)</p>
<p>Once the set of mechanics was pruned and I had gotten all the buildings and relevant upgrades represented in code (many thanks to <a href="https://github.com/nprindle/">Nicole</a> for helping with that), creating the actual simulator was fairly simple. The basic building block is the <code class="language-plaintext highlighter-rouge">GameState</code> class that describes possible states the game could be in. <code class="language-plaintext highlighter-rouge">GameState</code>s keep track of what buildings and upgrades you have, how many cookies, how long you’ve been playing, and so on:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">GameState</span> <span class="p">{</span>
<span class="k">readonly</span> <span class="nx">cookies</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
<span class="k">readonly</span> <span class="nx">buildings</span><span class="p">:</span> <span class="nb">Record</span><span class="o"><</span><span class="nx">Building</span><span class="p">,</span> <span class="kr">number</span><span class="o">></span><span class="p">;</span>
<span class="k">readonly</span> <span class="nx">upgrades</span><span class="p">:</span> <span class="nx">Upgrade</span><span class="p">[];</span>
<span class="k">readonly</span> <span class="nx">timePlaying</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
<span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Using this information it can also calculate how many cookies you’re producing every second:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">GameState</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">cookiesPerSecond</span><span class="p">():</span> <span class="kr">number</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The final piece of the puzzle is that <code class="language-plaintext highlighter-rouge">GameState</code>s can handle doing things like waiting or buying buildings:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">GameState</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">buyBuilding</span><span class="p">(</span><span class="nx">building</span><span class="p">:</span> <span class="nx">Building</span><span class="p">):</span> <span class="nx">GameState</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="nx">buyUpgrade</span><span class="p">(</span><span class="nx">upgrade</span><span class="p">:</span> <span class="nx">Building</span><span class="p">):</span> <span class="nx">GameState</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="nx">wait</span><span class="p">(</span><span class="nx">time</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">GameState</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is everything our simplified Cookie Clicker needs. Note that all of these functions return a new <code class="language-plaintext highlighter-rouge">GameState</code> without altering the existing one. <code class="language-plaintext highlighter-rouge">GameState</code>s are <a href="https://en.wikipedia.org/wiki/Immutable_object">immutable</a>; you can’t alter their values, only create modified versions. In this case it’s really useful for evaluating potential futures without committing to any of them.</p>
<p>(Side note: While buildings and upgrades are technically different things, they share a lot of important attributes. The player has to spend cookies to get them, and they typically increase CpS by some amount. “Buildings and upgrades” is kind of a mouthful, so let’s use the term <em>item</em> as a catch-all for purchasable things.)</p>
<p>The other important component is the <code class="language-plaintext highlighter-rouge">Simulator</code> class. Each <code class="language-plaintext highlighter-rouge">Simulator</code> has a strategy it’s testing using the following process:</p>
<ol>
<li>Examine all available items.</li>
<li>Choose one according to your strategy.</li>
<li>If you can’t afford it, wait until you can.</li>
<li>Buy it.</li>
<li>Repeat.</li>
</ol>
<p>This process repeats until some time limit is reached.</p>
<h2 id="strategies">Strategies</h2>
<h3 id="minimize-cost">Minimize Cost</h3>
<p>This strategy always buys the cheapest item available. This makes some degree of sense. Buying items as soon as possible means that your CpS is increasing fairly regularly.</p>
<h3 id="maximize-cps-increase-per-cookie-spent">Maximize CpS Increase Per Cookie Spent</h3>
<p>This strategy buys the item with the highest value for \(\frac{\Delta \text{CpS}}{\text{Cost}}\). This is also pretty sensible; making sure your purchases are as efficient as possible can’t be a bad thing.</p>
<h3 id="minimize-payback-period">Minimize Payback Period</h3>
<p>Payback period is a measurement used by the add-on <a href="https://cookieclicker.fandom.com/wiki/Cookie_Monster_(JavaScript_Add-on)">Cookie Monster</a>. It’s equal to \(\text{Time to Afford} + \frac{\text{Cost}}{\Delta \text{CpS}}\). You can think of it as how long you have to wait to get the item and then how long it takes for said item to pay for itself.</p>
<h2 id="results">Results</h2>
<p>I simulated these strategies over a virtual week and used <a href="https://vega.github.io/vega-lite/">Vega-Lite</a> to graph the results.</p>
<p><img src="/blog/assets/img/purchase-strategies-cps.png" alt="A graph of CpS over time for the three strategies. Minimize Payback Period narrowly beats Maximize +CpS/Cost; Minimize Cost is a very distant third." /></p>
<p><code class="language-plaintext highlighter-rouge">Minimize Payback Period</code> did the best, closely followed by <code class="language-plaintext highlighter-rouge">Maximize +CpS/Cost</code>; <code class="language-plaintext highlighter-rouge">Minimize Cost</code> trailed far behind. This was surprising at first, but it makes sense in hindsight. Always buying the cheapest item is a hilariously greedy strategy; you’ll end up buying loads of low-tier buildings before you get any upgrades or higher-tier buildings. This is supported by the breakdown of what buildings each strategy ended up with:</p>
<p><img src="/blog/assets/img/purchase-strategies-buildings.png" alt="A breakdown of the buildings each strategy ends up with. Minimize Payback Period and Maximize +CpS/Cost end up with very similar sets of buildings. Minimize Cost favors the low tier buildings heavily." /></p>
<p><code class="language-plaintext highlighter-rouge">Minimize Cost</code> ends up with more buildings than the other two strategies, but those buildings are worse on average. The breakdown of where these strategies get their cookies is similarly enlightening:</p>
<p><img src="/blog/assets/img/purchase-strategies-cps-sources.png" alt="Minimize Payback Period and Maximize +CpS/Cost get most of their CpS from the higher tier buildings. Minimize Cost does the same, but a noticeable chunk of its CpS comes from Cursors." /></p>
<p>The effective strategies rely on the higher tier of buildings; <code class="language-plaintext highlighter-rouge">Minimize Cost</code> gets a surprising amount of mileage from their cursors.</p>
<p>As for <code class="language-plaintext highlighter-rouge">Minimize Payback Period</code> and <code class="language-plaintext highlighter-rouge">Maximize +CpS/Cost</code> performing similarly, that’s easily explained by them being very similar strategies. Maximizing \(\frac{\Delta \text{CpS}}{\text{Cost}}\) is the same thing as minimizing the time it takes for that item to pay for itself. (Apparently considering how long it takes to afford something is only a slight improvement.)</p>
<h2 id="other-experiments">Other Experiments</h2>
<p>After finding this optimal strategy, I decided to run some other experiments with my simulator to see what would happen. All the simulations below use <code class="language-plaintext highlighter-rouge">Minimize Payback Period</code> for their strategy.</p>
<h3 id="head-starts">Head Starts</h3>
<p>Exponential growth has a tendency to amplify head starts. This is a game all about exponential growth; how much of a head start does it take to make a noticeable difference?</p>
<p><img src="/blog/assets/img/headstarts-cps.png" alt="A graph showing that as the amount of starting cookies increases, we get correspondingly large jumps in starting CpS." /></p>
<p>This is basically exactly what we’d expect. Starting with cookies lets you buy more items at the start of the game, skipping the time you’d have to wait for them.</p>
<h3 id="clicking">Clicking</h3>
<p>The simulation previously hadn’t considered clicking. It was pretty simple to implement, though, and once I had added the <a href="https://cookieclicker.fandom.com/wiki/Upgrades#Clicking_Upgrades">relevant upgrades</a> I ran some tests with different clicking rates. I assume most players click very rarely, so the bottom of the scale is 0.1 clicks per second. The top of the scale is 30 clicks per second; the game’s framerate is at most 30 frames per second, and most sources I’ve found say the game can only register one click every frame.</p>
<p><img src="/blog/assets/img/click-cps.png" alt="A graph showing that CpS increases faster as rate of clicking increases." /></p>
<p>(Note that graphed CpS does not count cookies earned from clicking.)</p>
<p>This graph isn’t much of a surprise either. The faster you earn cookies, the faster you can buy new upgrades. Still, I think it’s interesting how wide a gulf there is between not clicking and clicking as quickly as possible. I’d always thought clicking became obsolete pretty quickly, but this graph shows that’s not the case.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This was a very silly endeavor, but at least it was an <em>educational</em> silly endeavor. Learning how to make graphs from the command line served this experiment well, and it’ll be a useful skill to have for future ones. In the end, it’s fun putting way too much effort into something trivial. Hopefully I’ll be able to use my experience here to optimize something useful someday.</p>May LawverLike many others, I’ve been staying at home to slow the spread of COVID-19. Also like many others, I’ve been trying to spend my time in quarantine productively. Nobody can work all the time, though, so in between work on my various projects I’ve been playing Cookie Clicker.Generating Aurora’s Music2020-04-24T00:00:00+00:002020-04-24T00:00:00+00:00/blog/2020/04/24/generating-auroras-music<p><a href="https://github.com/GRarer/Aurora">Aurora</a> is a game my friend Grace led for VGDev about managing a space colony. You play as the Aurora colony’s AI overseer, and your mission is to research the local alien ruins and unlock their secrets. Check it out! I think it’s tons of fun. My main contribution to Aurora was the code that generates its music. The game’s open source, so you can <a href="https://github.com/GRarer/Aurora/tree/master/src/music">look at the music code</a> if you’d like. If you’d prefer a written explanation of what it does, read on; this post will explain how Aurora’s music generation works.</p>
<h2 id="rhythm">Rhythm</h2>
<p>The first step is selecting a time signature. A time signature says how many beats there are in a measure and how they’re split up. For example, 4/4 means each measure is four quarter notes, while 7/8 means each measure is seven eighth notes. An important thing to keep in mind is that time signatures can’t quite be simplified the way fractions can; 3/4 and 6/8 sound similar but are still very different beasts.</p>
<p>At the start of each new section of music, a new time signature is chosen. It’s always either 5/8, 6/8, 7/8, 8/8, 9/8, 11/8, or 13/8. Most of these are pretty unusual—almost every song you’ll hear on the radio is in 4/4, for example. This is a game about an alien world, so it’s only natural the music would favor time signatures you don’t typically hear in pop music. (It helps that I’m a big fan of odd time signatures.)</p>
<h2 id="drums">Drums</h2>
<p>Once we’ve determined how many eighth notes there are in a measure, we can go about splitting them up into groups of two and three. I think I got this idea from <a href="https://www.youtube.com/watch?v=JQk-1IXRfew">an Andrew Huang video about odd time signatures</a>. The idea is that the smaller groupings make the whole more intelligible for both the musicians playing the music and the people listening to it. It’s easier to count 3 + 3 + 3 + 2 + 2 than it is to count 13.</p>
<p>Groupings are generated by shaving off chunks of two or three from the rest of the measure until there’s nothing left to shave:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// This isn't the actual code used in Aurora, but it gets the idea across.</span>
<span class="kd">function</span> <span class="nx">generateMeasureGroupings</span><span class="p">(</span><span class="nx">beats</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span><span class="p">[]</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">groups</span><span class="p">:</span> <span class="kr">number</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">const</span> <span class="nx">possibleGroups</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">];</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">beats</span> <span class="o">></span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(...</span><span class="nx">possibleGroups</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// exclude groups we don't have room for</span>
<span class="kd">const</span> <span class="nx">validGroups</span> <span class="o">=</span> <span class="nx">possibleGroups</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">num</span> <span class="o">=></span> <span class="nx">num</span> <span class="o">>=</span> <span class="nx">beats</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">currentGroup</span> <span class="o">=</span> <span class="nx">Random</span><span class="p">.</span><span class="nx">fromArray</span><span class="p">(</span><span class="nx">validGroups</span><span class="p">);</span>
<span class="nx">groups</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">currentGroup</span><span class="p">);</span>
<span class="nx">beats</span> <span class="o">-=</span> <span class="nx">currentGroup</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// make sure we include every beat in the measure</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">beats</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">groups</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">beats</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">subdivision</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This algorithm has a chance of producing a grouping with a trailing group of one, but it sounds fine, so I’m willing to consider that a feature.</p>
<p>These groupings make generating an acceptable drum groove pretty easy. The measure has been split up into smaller groups, and these groups can be considered more or less independently. A kick or snare plays on the first beat of each grouping; hi-hats play on the rest. Groups alternate between starting with kicks and starting with snares to create a sort of pseudo-<a href="https://en.wikipedia.org/wiki/Beat_(music)#Backbeat">backbeat</a> feel.</p>
<h2 id="scales-and-chords">Scales and Chords</h2>
<p>The harmony is only marginally more complicated than the rhythm section. Musical scales are stored as bit vectors using the method described in Ian Ring’s <a href="https://ianring.com/musictheory/scales/">A Study of Scales</a>. This representation has a lot of nice properties. For example, checking if a scale <code class="language-plaintext highlighter-rouge">S</code> has a note <code class="language-plaintext highlighter-rouge">n</code> semitones above the root is as simple as <code class="language-plaintext highlighter-rouge">S & (1 << n) != 0</code>. Aurora has a lot of similar utility methods for manipulating and analyzing scales, actually. Figuring out the right way to express some musical property in terms of these bit vectors is a fun mental exercise even if most of the resulting utility methods don’t get used.</p>
<p>Each section of music picks a random scale from the six non-diminished modes of the major scale. (That is, it picks any mode other than Locrian.) Once we have a scale, we can build chords off of it by stacking thirds. In layman’s terms, we can make nice-sounding chords by taking three or four alternating notes. For example, if our scale is C D E F G A B, CEG and DFA (C major and D minor) would both sound pretty nice. The reasons why this works are beyond the scope of this post, but <a href="https://en.wikipedia.org/wiki/Tertian">the Wikipedia article on tertian harmony</a> is a good starting point.</p>
<p>We have a set of seven different chords. The final step is determining which chords to play and in what order. The idea of <a href="https://en.wikipedia.org/wiki/Function_(music)">functional harmony</a>—chords having different “functions” or jobs within a key—is a good starting point. In general, tonic chords feel stable and at rest, while dominant chords are tense and “want” to resolve to stable chords. Subdominant chords set up dominant chords. The eternal cycle of tonic to subdominant to dominant to tonic again sounds nice enough to form the basis of our generative chord progressions. All we need is a table mapping functions to chords like this one:</p>
<table>
<thead>
<tr>
<th>Function</th>
<th>Chords</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tonic</td>
<td>I, VI</td>
</tr>
<tr>
<td>Subdominant</td>
<td>II, IV</td>
</tr>
<tr>
<td>Dominant</td>
<td>V, VII</td>
</tr>
<tr>
<td>Ambiguous</td>
<td>III</td>
</tr>
</tbody>
</table>
<p>Note that the III chord (E minor in the key of C major) is listed as “ambiguous”. The III chord is kind of difficult to assign a concrete function to. <a href="https://www.youtube.com/watch?v=xhft-tgPw-w">12tone made a video about the III chord</a> if you’d like to learn more. Basically, the III chord can have either dominant or tonic function depending on what kind of chord it comes after.</p>
<p>The last piece of the puzzle in place, we can generate chord progressions pretty easily. Each progression is four chords long and starts with the I chord to help establish the key. The next three chords can fit one of a couple different patterns, but the final chord progression always fits the pattern of tonic then subdominant then dominant.</p>
<p>The four chord loop plays twice accompanied by the drums, then the drums play on their own for a measure to make the transition between scales less jarring. After that, all the musical parameters get randomized again. Repeat ad infinitum. Here’s what it sounds like:</p>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/806262013&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
<div style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;"><a href="https://soundcloud.com/maymoonsley" title="May Lawver" target="_blank" style="color: #cccccc; text-decoration: none;">May Lawver</a> · <a href="https://soundcloud.com/maymoonsley/aurora-soundtrack" title="Aurora Soundtrack" target="_blank" style="color: #cccccc; text-decoration: none;">Aurora Soundtrack</a></div>
<h2 id="conclusion">Conclusion</h2>
<p>I’m proud of how this all turned out, but there are a lot of things like melody generation and more complicated synths that I hoped to implement but didn’t get around to. If I were to start over with this project, I’d probably use a library like <a href="https://tonejs.github.io/">Tone.js</a> to handle the sound synthesis. Letting a library handle that would allow me to focus on the musical side of things more. Still, I learned a lot working on this, and it’s left me with a lot of ideas for my next foray into generative audio. It was a learning experience and sounds pretty nice, and that’s all I can really ask for.</p>May LawverAurora is a game my friend Grace led for VGDev about managing a space colony. You play as the Aurora colony’s AI overseer, and your mission is to research the local alien ruins and unlock their secrets. Check it out! I think it’s tons of fun. My main contribution to Aurora was the code that generates its music. The game’s open source, so you can look at the music code if you’d like. If you’d prefer a written explanation of what it does, read on; this post will explain how Aurora’s music generation works.Random Poetry With RiTa2020-03-09T00:00:00+00:002020-03-09T00:00:00+00:00/blog/2020/03/09/random-poetry-with-rita<p>Last semester I wrote a Node program to randomly generate sonnets.</p>
<h2 id="whats-a-sonnet">What’s a Sonnet?</h2>
<p>A <a href="https://en.wikipedia.org/wiki/Sonnet">sonnet</a> is a kind of poem written in iambic pentameter with one of a couple different rhyme schemes. Iambic pentameter is a fancy way of saying “ten syllables with alternating stress.” The pattern is unstressed, stressed, repeat. An example of this is the line “But, soft! what light through yonder window breaks?” from <em>Romeo and Juliet</em>.</p>
<p>I’ll be using <code class="language-plaintext highlighter-rouge">0</code> to represent unstressed syllables and <code class="language-plaintext highlighter-rouge">1</code> to represent stressed syllables. For example, <em>puppetry</em> has stress pattern <code class="language-plaintext highlighter-rouge">1/0/0</code>.</p>
<h2 id="how-can-we-generate-one">How Can We Generate One?</h2>
<p>In order to randomly generate sonnets, I used two libraries:</p>
<ul>
<li>
<p><a href="https://rednoise.org/rita/index.php">RiTa</a> has a bunch of really useful language utilities. The most useful one for this is the ability to get a word’s stresses.</p>
</li>
<li>
<p><a href="https://www.npmjs.com/package/word-list">word-list</a> provides almost 275,000 words to work with—more than enough grist for the poetry mill.</p>
</li>
</ul>
<p>I was introduced to RiTa by <a href="https://tinysubversions.com/notes/sorting-bot/">Darius Kazemi’s blog post about procedural poetry</a>. As soon as I heard about it, I wanted to try something like this. He was using RiTa to take a predefined template and fill in the blanks with words that fit the rhyme scheme and meter; I’m using it to generate sonnets from scratch.</p>
<p>The algorithm I went with at first is pretty simple:</p>
<ol>
<li>
<p>Start with the stress pattern of a full line: <code class="language-plaintext highlighter-rouge">0/1/0/1/0/1/0/1/0/1</code></p>
</li>
<li>
<p>Generate a list of all the words that fit at the beginning of the line: all <code class="language-plaintext highlighter-rouge">0/1</code> words, all <code class="language-plaintext highlighter-rouge">0/1/0</code> words, all <code class="language-plaintext highlighter-rouge">0/1/0/1</code> words, etc. (Note that there are no <code class="language-plaintext highlighter-rouge">0</code> words, since all one syllable words are <code class="language-plaintext highlighter-rouge">1</code> according to RiTa. This isn’t really accurate—the stress wouldn’t fall on every word in an entirely monosyllabic sentence—but it’s accurate enough that it’s not a huge problem.)</p>
</li>
<li>
<p>Select a random word from this list and add it to the line.</p>
</li>
<li>
<p>Remove the word’s stresses from the start of the current stress pattern.</p>
</li>
<li>
<p>Repeat with the remaining stresses until the line is filled.</p>
</li>
<li>
<p>Repeat fourteen times. Now we have an unrhyming sonnet!</p>
</li>
</ol>
<p>Of course, since our sonnet doesn’t rhyme, it’s not really a sonnet. It has the same rhythm as one, though, so it’s a pretty good start. Once I got the algorithm working, this was its first output:</p>
<blockquote>
<p>Confuter luminaries by cumshaw</p>
<p>Maligners slither colloids delighters chabouks</p>
<p>Deducements soops ceiled galabiyas pheezed</p>
<p>Reflecting overdeviates satelles</p>
<p>Elastications wizes quersprung pejorate</p>
<p>Undried repriming windless toxin drole</p>
<p>Unbraste acquitted manitou contends</p>
<p>Preexisted flyblow metrication chawed</p>
<p>Desertizations gutta bodysurf</p>
<p>Commuters supper worn empassioned sines</p>
<p>Reformulation matte refortify</p>
<p>Attractant murrain depletion shrivers twist</p>
<p>Machining forenights delayer obtrude</p>
<p>Tequilas several fell unbrushed resail</p>
</blockquote>
<p>Most of this is complete nonsense, which is to be expected. Still, one line stood out to me: <em>Undried repriming windless toxin drole.</em> It evokes such strong imagery; when I read it I imagine a stagnant pool of water surrounded by sickly-sweet vapor. However, there are a lot of really obscure words throughout the poem. You basically have to have the dictionary open in another tab if you want to understand it, which is less than ideal.</p>
<h2 id="how-can-we-simplify-our-lexicon">How Can We Simplify Our Lexicon?</h2>
<p>I thought of a couple things to do to solve this problem, but what I decided on was pulling words from some given source text instead of from the entire dictionary. This keeps the poems relatively constrained in terms of vocabulary. <a href="https://www.gutenberg.org/">Project Gutenberg</a> makes it easy to find large source texts; with some minor cleaning, <em>Pride and Prejudice</em> was ready to process. Getting all the words out of the text was fairly simple, though I had to make sure not to include stuff like numbers.</p>
<p>This is the first sonnet generated from the <em>Pride and Prejudice</em> lexicon:</p>
<blockquote>
<p>Surmises lost describe admire resolve</p>
<p>Proportioned whist amazed confinement fare</p>
<p>Delicious laugh propose appear apace</p>
<p>Regained exact transition speed incur</p>
<p>Instead unfelt asserting high comprise</p>
<p>Obliging authorising talent noon</p>
<p>Resisting throwing here disliked correct</p>
<p>W sorry french dispute command therein</p>
<p>Attorney tanned respecting did redress</p>
<p>Repeated notions no convinced advice</p>
<p>Formation rail disarm required made</p>
<p>Indulgent parents deciding sized scenes bowl</p>
<p>Performs malicious bell adept subjects</p>
<p>Hotel aloud subsist imply about</p>
<p>Acknowledge carried glories did surmount</p>
</blockquote>
<p>I don’t know how the W snuck in there. (In case you’d like to read this poem aloud, note that RiTa thinks it should be pronounced “wuh”, not “double-you.”) This is still about as comprehensible as the first poem, though it feels like the second one has more interesting phrases in it. (I’m particularly fond of “asserting high compromise” and “carried glories did surmount.”)</p>
<p>Still, this isn’t really coherent. We can read meaning into some lines through a sort of linguistic <a href="https://en.wikipedia.org/wiki/Pareidolia">pareidolia</a>, but a lot of the time that’s pretty difficult. This algorithm could theoretically produce something stunning (remember that line about toxins?) but it also produces a lot of junk. <a href="http://www.possibilityspace.org/tutorial-generative-possibility-space/index.html">This excellent article</a> talks more about this idea; basically, forcing the poetry generator to be more coherent reduces the number of poems it could possibly produce. This is the point of it, of course. With that in mind…</p>
<h2 id="how-can-we-make-this-poetry-more-coherent">How Can We Make This Poetry More Coherent?</h2>
<p>This is a difficult question to answer since there are so many ways to answer it. Markov chains? Neural networks? Just leaving the generator as is and calling the incoherence a feature? These are all valid approaches, but the one I chose here was simple:</p>
<ol>
<li>
<p>Find all the fragments of text in some source that fit the meter.</p>
</li>
<li>
<p>Create a sonnet by pulling from these lines at random.</p>
</li>
</ol>
<p>Initially I was worried that there wouldn’t be enough suitable fragments to make a sonnet. However, this wasn’t the case. My program found 150 such fragments in <em>Pride and Prejudice</em> and 589 in <em>War and Peace</em>. (The algorithm initially found more, but I wasn’t sure it was handling letters with accents correctly, so I removed all the lines containing them.) Getting lines from multiple sources allows us to do something pretty nifty. Sonnets typically have what’s called a <a href="https://en.wikipedia.org/wiki/Volta_(literature)">volta</a> or turn. The idea is that the sonnet changes perspective or tone part of the way through. Again, Wikipedia explains it better than I can. My hypothesis is that we can emulate this by switching from one source to another in the middle of a sonnet. The sonnet as a whole still won’t be very coherent, but at least the lines should mostly make sense on their own.</p>
<p>Here’s the result of this experiment:</p>
<blockquote>
<p>The windows of the houses and the streets</p>
<p>Rouged faces dressed in glaring colors who</p>
<p>Dispatching of adjutants was confined</p>
<p>Expression of the princess pretty face</p>
<p>The thoughtful profile of her drooping face</p>
<p>Addressing princess mary for the first</p>
<p>Announce the peace negotiations and</p>
<p>Forget yourself again before her as</p>
<p>In his patroness lady catherine</p>
<p>The real the worst objections to the match</p>
<p>Were fastened on the parcels placed within</p>
<p>Unguarded and imprudent manner nay</p>
<p>The chaise arrived the trunks were fastened on</p>
<p>Ashamed especially when contrasted with</p>
</blockquote>
<p>The individual lines here make more sense than previous poems for obvious reasons. In my opinion, this makes the work as a whole worse, almost as if it’s fallen into a poetic <a href="https://en.wikipedia.org/wiki/Uncanny_valley">uncanny valley</a>. It’s harder to see meaning in the connections between random lines than it is to see meaning in a single nonsensical line, at least for me. There’s almost a link between the first two lines if you imagine them setting the scene, but the rest of the sonnet doesn’t really fit together. The turn also isn’t super obvious. Still, weird techniques like this are useful to play around with even if they don’t end up working well—you never know when they could come in handy.</p>
<h2 id="what-next">What Next?</h2>
<p>My first goal with these experiments was to have fun, and I’m proud to say that I accomplished that. They were also pretty good practice for <a href="https://nanogenmo.github.io/">NaNoGenMo</a>. (NaNoGenMo is still eight months out as I write this, but it’s good to be prepared.) With that in mind, an obvious next step is to make the sonnets rhyme. I have a lot of other ideas for where to go from here, too. Expect more generative poetry in the future. If nothing else, these techniques produce excellent idea fodder, and that’s a good enough reason to continue with them.</p>May LawverLast semester I wrote a Node program to randomly generate sonnets.Welcome to the New Page2020-02-02T00:00:00+00:002020-02-02T00:00:00+00:00/blog/2020/02/02/welcome-to-the-new-page<p>Remember how I <a href="http://www.maycod.es/blog/2018/09/15/viable/">switched to Wordpress</a> from raw HTML? I’ve come to the conclusion that may have been an overcorrection. It’s easier than the FTP method, but there’s a lot of overhead to it. When I decided to switch to GitHub Pages, it seemed good to switch the blog over, too. Now the blog’s powered by <a href="https://jekyllrb.com/">Jekyll</a>. <a href="https://github.com/MayMoonsley/blog/">The blog’s also open source</a>, which I’m sure is good for something.</p>
<p>This may be a honeymoon period, but I’m a big fan of Jekyll so far. LaTeX support was a lot easier to set up than I imagine it would’ve been on WordPress, which is very exciting. Support for inline code snippets is also really great. \(x^2 + x + 1\) and <code class="language-plaintext highlighter-rouge">foo()</code> look so much better than x^2 + x + 1 and foo().</p>
<p>It’s really nice having the new site up on the right URL. Expect more here soon - I’ve been working on some interesting things I’m excited to share.</p>May LawverRemember how I switched to Wordpress from raw HTML? I’ve come to the conclusion that may have been an overcorrection. It’s easier than the FTP method, but there’s a lot of overhead to it. When I decided to switch to GitHub Pages, it seemed good to switch the blog over, too. Now the blog’s powered by Jekyll. The blog’s also open source, which I’m sure is good for something.Finished Year One2019-05-10T16:00:56+00:002019-05-10T16:00:56+00:00/blog/2019/05/10/finished-year-one<p>It’s been a week since I finished my first year of college, so I thought it’d be good to summarize what I’ve been up to.</p>
<!-- more -->
<h2 id="academics">Academics</h2>
<p>College is a really good place to go to learn things! I know how Dijkstra’s algorithm works, I know how to project vectors onto other vectors, and I know what a proof by induction is. I learned a lot this year, and I’m really excited for next semester’s classes.</p>
<h2 id="vgdev">VGDev</h2>
<p>I got involved with <a href="https://twitter.com/VGDevGT/">VGDev</a>, Georgia Tech’s video game development club! Last semester I did a bunch of programming work on <a href="https://willbmartin.github.io/rune-search/">Rune Search</a>, a word search roguelike that I also wrote the soundtrack for. I also wrote some music for Hengliding, a delightful combination of a farming sim and racing game I wish I could link to.</p>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/727634172&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/752225433&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
<h2 id="other-projects">Other Projects</h2>
<p>I’m still writing music and working on my own personal projects – <em>Magical Monkey Adventure</em> is still under development! It’s gone through a complete overhaul, but I’m happy with where it’s at so far. The plan is to have it done before I go back to school in August, which seems doable.</p>
<h2 id="friends">Friends</h2>
<p>Belaboring this point feels either too schmaltzy or too much like bragging, but I’ve made friends! As someone who does most of her socializing online, it would’ve been easy to become a shut-in, but I’m glad I didn’t. Making time for my social life is not a decision I regret. To all my friends, online and off, new and old: I love y’all.</p>
<h2 id="summary">Summary</h2>
<p>I learned a lot, I wrote some music and code that I’m proud of, and generally I’m feeling pretty good. I’m excited for what the future will bring.</p>May LawverIt’s been a week since I finished my first year of college, so I thought it’d be good to summarize what I’ve been up to.Goofing on Numbers: Skyscraper BMI2019-03-13T22:52:00+00:002019-03-13T22:52:00+00:00/blog/2019/03/13/goofing-on-numbers-skyscraper-bmi<p>This is the start of <em>Goofing on Numbers</em>, a little semi-regular series I’d like to do here. The basic idea here is to take equations and gently misuse them to answer meaningless questions, sort of like <a href="https://what-if.xkcd.com/">What If?</a> if it cared less about describing any actual physical reality. It’s an illustration of the principle of <a href="https://en.wikipedia.org/wiki/Garbage_in,_garbage_out">Garbage In, Garbage Out</a>, but more importantly for my purposes, it’s tons of fun. Today I’m going to take a look at Body Mass Index, that often criticized metric of weight.</p>
<!-- more -->
<p><a href="https://en.wikipedia.org/wiki/Body_mass_index">Body Mass Index</a>, or BMI, is supposed to be a measure of how healthy someone’s weight is. Too low and they’re underweight, too high and they’re overweight. (I’m getting the <a href="https://www.cancer.org/cancer/cancer-causes/diet-physical-activity/body-weight-and-cancer-risk/adult-bmi.html">specific ranges</a> from the American Cancer Society, so they should be trustworthy.) There are a lot of criticisms of its usefulness. I won’t get into them here, since this is more for fun than entertainment, but they’re out there.</p>
<p>A person’s BMI is their weight in kilograms divided by their height in meters squared. The question I asked that sent me down this rabbit hole is, “What if we applied this equation to things that aren’t people?” I spent a fair amount of time idly wondering where a skyscraper would fall on the scale. My first instinct was that the mass of a skyscraper grows faster than its height, so a skyscraper would be super high on the scale. Then I thought that, since the height is squared, the denominator of this fraction grows faster than the numerator, so skyscrapers would become more “underweight” the taller they get. Skimming over the Wikipedia page for BMI, I’m beginning to suspect that my first intuition was right, but there’s no substitute for proper calculation. So, without further ado…</p>
<p><em>(Fair warning - I’m not doing super deep dives for this information, so some of it may be inaccurate. There are three different units called a ton, for instance, so it’s kind of hard to tell which is which sometimes. Sorry!)</em></p>
<p>The <a href="https://en.wikipedia.org/wiki/Empire_State_Building">Empire State Building</a> has a BMI of around 1,685. (That’s including the spire as part of the height, too, which is a little generous.) The <a href="https://en.wikipedia.org/wiki/Burj_Khalifa">Burj Khalifa</a>, meanwhile, has a BMI of around 565. (Side note - in my research, I found out that there’s a restaurant near the top of the Burj Khalifa called At.mosphere. I’m not a huge fan of the name. It sounds like the name of a PS2 game about a bunch of teenagers who travel into the Internet to save the world with the help of some friendly aliens.) The <a href="https://en.wikipedia.org/wiki/Washington_Monument">Washington Monument</a> clocks in at around 2,890, and the Eiffel Tower at around 70, which is the only one of these results that’s actually a semi-reasonable human BMI - <a href="http://www.guinnessworldrecords.com/world-records/heaviest-man">the world’s heaviest man</a> had a BMI of 186 at one point. A lot of these numbers are more than a little disappointing. It was too much to expect them to be within the realm of human possibility, but a girl can dream, can’t she?</p>
<p>This wasn’t a particularly productive exercise. That’s alright - nobody can be all business all the time. That’s why they invented the mullet, after all. Still, it was kind of fun to dig into what this statistic represents in a physical sense. (The units themselves imply something interesting - \(\frac{kg}{m^2}\) seems like a ratio between mass and surface area, or the density of <a href="https://en.wikipedia.org/wiki/Flatland">Flatlanders</a>.)</p>
<p>While we’ve had some fun here, the real moral of the story is this: if you shake equations hard enough, sometimes you can get the sentence “the Eiffel Tower is morbidly obese” to fall out, and that’s a good enough reason to mess around with math.</p>May LawverThis is the start of Goofing on Numbers, a little semi-regular series I’d like to do here. The basic idea here is to take equations and gently misuse them to answer meaningless questions, sort of like What If? if it cared less about describing any actual physical reality. It’s an illustration of the principle of Garbage In, Garbage Out, but more importantly for my purposes, it’s tons of fun. Today I’m going to take a look at Body Mass Index, that often criticized metric of weight.What Have I Been Up To?2019-03-12T16:00:55+00:002019-03-12T16:00:55+00:00/blog/2019/03/12/what-have-i-been-up-to<p>Regular readers will notice that I haven’t updated this blog in a third of a year. Magical Monkey Adventure has remained frozen in time with nary an update in sight. The obvious question is just what I’ve been up to over the past couple months. Well…</p>
<!-- more -->
<p>The obvious thing first: College! I started my second semester at Georgia Tech in January, and I’m having a great time. Discrete Math is probably the most fun I’ve ever had in a math class. Been getting a lot of walking around campus done, too. Healthy living and all that.</p>
<p>Second: I’ve been working on this project:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Announcing Rune Search, led by William Martin!<br /><br />Rune Search is a half word search, half roguelike web game where players must strategically maneuver their way around levels in order to save the princess or kill the monster or get the gold or something.<a href="https://twitter.com/hashtag/gamedev?src=hash&ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://t.co/Hkb2kb1GoI">pic.twitter.com/Hkb2kb1GoI</a></p>— VGDev (@VGDevGT) <a href="https://twitter.com/VGDevGT/status/1095080238403538944?ref_src=twsrc%5Etfw">February 11, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I’ve been writing code and music for <em>Rune Search</em>, a game that’s half word search, half roguelike. It’s been a ton of fun. I can’t really show you my programming work, but I can show you all the music I’ve written for it.</p>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/727634172&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
<p>College, VGDev, and social stuff have been taking up most of my time, hence the lack of posts here. But! Going forward, I’d like to post more regularly here, at least once a week or so. I’ve got some ideas for how to keep this place lively and interesting. If nothing else, it’ll be fun. (And yes, Magical Monkey Adventure will be completed. My goal’s by the end of the semester, but I’m not confident I’ll hit that goal.)</p>
<p>So! Thus ends my hiatus. See you tomorrow with a fun little diversion.</p>May LawverRegular readers will notice that I haven’t updated this blog in a third of a year. Magical Monkey Adventure has remained frozen in time with nary an update in sight. The obvious question is just what I’ve been up to over the past couple months. Well…Magical Monkey Adventure Devlog #4: Maps2018-11-10T16:00:26+00:002018-11-10T16:00:26+00:00/blog/2018/11/10/magical-monkey-adventure-devlog-4-roadmap<p>This post is a twofer. First: maps. Second: some code cleanup I did.</p>
<!-- more -->
<h2 id="maps">Maps</h2>
<p><img src="/blog/assets/img/map.png" alt="" /></p>
<p>Maps are working now! Scripts can be executed whenever you walk onto a space (which is where the lovely flavor text is coming from) and whenever you try to enter one. These scripts haven’t been set up yet, but that it can be done at all is what matters for now. Since everything’s connected to the scripting system, it’ll be relatively easy to put all the pieces together – a node on the map can send you to a battle or refill your health or give you more worms or whatever else the scripting system will be able to do.</p>
<h2 id="refactoring">Refactoring</h2>
<p>Last week, I mentioned that I’d do some code cleanup, and I did indeed clean up my code. Specifically, I messed with the scripting system, which was basically the ugliest thing in my code. Inside a <code class="language-plaintext highlighter-rouge">for</code> loop that iterated through the script, there was a giant <code class="language-plaintext highlighter-rouge">switch</code> statement, with a <code class="language-plaintext highlighter-rouge">case</code> for each command. In addition to looking ugly, it felt like the wrong way of doing things. Wasn’t there a better way?</p>
<p>Yes, there was! Instead of running through the <code class="language-plaintext highlighter-rouge">switch</code> statement, the functions are now stored in an object. The <code class="language-plaintext highlighter-rouge">for</code> loop is still there, but it just calls whatever function the command points to, passing in the arguments directly. While I haven’t run the numbers to see if it’s faster, it’s a heck of a lot cleaner and easier to parse, so I feel confident in calling it the better way.</p>
<p>There was one issue with this, though. When the code behind a command was called from inside the <code class="language-plaintext highlighter-rouge">for</code> loop, it had access to the current index. This was how I did branching code: through <code class="language-plaintext highlighter-rouge">goto</code>s and <code class="language-plaintext highlighter-rouge">label</code>s. <code class="language-plaintext highlighter-rouge">goto</code> just set the index to the first instance of the matching <code class="language-plaintext highlighter-rouge">label</code> in the script array. This wouldn’t work if the code wasn’t in the loop, though, barring making the array index global. (Yuck.)</p>
<p>So I did what I should’ve done a while ago: made a proper <code class="language-plaintext highlighter-rouge">if</code> command, based on how conditionals work in <a href="http://www.zachtronics.com/shenzhen-io/">Shenzhen I/O</a>. (It might be how things work in actual real-life assembly, too, but I wouldn’t know.) The <code class="language-plaintext highlighter-rouge">if</code> command just sets a variable in the scripting system to be either <code class="language-plaintext highlighter-rouge">true</code> or <code class="language-plaintext highlighter-rouge">false</code>. Lines preceded by plus signs only run if the condition is true; the opposite is true for lines preceded by minus signs.</p>
<p>Here’s some example code using the old method:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jumpif a a==b
log A is not B.
jump end
label a
log A is B.
label end
</code></pre></div></div>
<p>And here’s some code with the new method that does the same thing:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a==b
+log A is B.
-log A is not B.
</code></pre></div></div>
<p>I really should’ve done things this way from the start, but a late improvement is better than continuing to do things the wrong way.</p>
<p>So! That’s what I accomplished this week. Next time: a map of a different sort. (And maybe some worms.)</p>May LawverThis post is a twofer. First: maps. Second: some code cleanup I did.Magical Monkey Adventure Devlog #3: Hitting Things With Sticks2018-11-03T16:00:34+00:002018-11-03T16:00:34+00:00/blog/2018/11/03/magical-monkey-adventure-devlog-3-hitting-things-with-sticks<p>Right now, I only have half of the combat equation in place. Let’s fix that. Three weeks late and (ideally) worth the wait, it’s time to implement turn based combat! This was kind of tricky, but I’ve now got a functioning combat system. Let’s go into how.</p>
<!-- more -->
<p>The first thing I did was set up the UI to get a feel for the layout. It morphed over time, but this four-square design stayed pretty constant.</p>
<p><img src="/blog/assets/img/combatContinue.png" alt="" /></p>
<p>(The “In Your Corner” and “In Their Corner” are relics from when it was going to be possible to fight multiple enemies at once, and it’s probably going to be changed.)</p>
<p>Next came a scripting system to hook combat stuff into. Luckily, I had one handy for another project. All it took was some copying, pasting, and variable renaming to get it working. (This scripting system is also going to help once I move on to story sections and map movement, but let’s not get ahead of ourselves.) After adding commands to deal damage and other such RPG fixtures, next came the little choice menu, which also hooked into the scripting system. (It’s scripting all the way down.)</p>
<p>It’s all fairly standard, I’d imagine. Advancing through the turn list, assigning each skill to a choice, etc. Each skill has a special version of its script that’s only executed when a player character uses it; this version includes code to remove all the mana and worms that the skill costs.</p>
<p><img src="/blog/assets/img/combat.png" alt="" /></p>
<p>Something worth noting is how I decided to handle the enemy turn. This isn’t a particularly elegant solution, and I might revise it later, but right now the game just gives you the option to continue. That triggers the script associated with the enemy’s attack. There’s no real AI at this point. The enemy chooses a random skill and targets a random fighter - it doesn’t even check to see if the fighter is knocked out or not. This is going to need some work, but the whole point is to have a system that can be built upon.</p>
<p>Handling winning and losing were also a bit of a cheat. Internally, there are skills that have scripts to handle these things. If the enemy’s defeated, the game gives you a choice with the victory skill attached; the inverse happens if your party is defeated. Since it’s based on the scripting system, I can outsource this to future May.</p>
<p>This is all the combat system is right now. The code feels kind of messy and inelegant, but it works. Refactoring can come later. Next week: maps! (And a bunch of code cleanup I’m not going to write about.)</p>May LawverRight now, I only have half of the combat equation in place. Let’s fix that. Three weeks late and (ideally) worth the wait, it’s time to implement turn based combat! This was kind of tricky, but I’ve now got a functioning combat system. Let’s go into how.