<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dynamic Programming on Dear Fortuna</title><link>http://dearfortuna.blog/tags/dynamic-programming/</link><description>Recent content in Dynamic Programming on Dear Fortuna</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>© 2026 Vasyl Bodnar | Text: CC BY 4.0 | Code: BSD-3</copyright><lastBuildDate>Wed, 08 Jul 2026 18:00:00 -0400</lastBuildDate><atom:link href="http://dearfortuna.blog/tags/dynamic-programming/index.xml" rel="self" type="application/rss+xml"/><item><title>Playing with Uiua for Optimal Mindustry Mining Pt. 1</title><link>http://dearfortuna.blog/posts/playing-with-uiua/</link><pubDate>Wed, 08 Jul 2026 18:00:00 -0400</pubDate><guid>http://dearfortuna.blog/posts/playing-with-uiua/</guid><description>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>I happened to be playing &lt;a href="https://mindustrygame.github.io">Mindustry&lt;/a> one evening.
But I kept getting annoyed by an issue: my mining was suboptimal.&lt;/p>
&lt;p>&lt;img src="http://dearfortuna.blog/images/mindustry-drills.jpg" alt="Picture of resource tiles in Mindustry with a small drill over a 2 by 2 tiles of them">&lt;/p>
&lt;p>If you can see the picture, the drills cover 2×2 tiles,
whereas the resources are a large uneven cluster.
It is, unfortunately, an innate and natural desire to make those drills cover all possible resources you can get.
Thankfully, I have an outlet for such unrefined desires in my life.
I could just write an optimal algorithm for it. And, there was a language I was trying to learn for a while&amp;hellip;&lt;/p></description><content:encoded><![CDATA[<h2 id="intro">Intro</h2>
<p>I happened to be playing <a href="https://mindustrygame.github.io">Mindustry</a> one evening.
But I kept getting annoyed by an issue: my mining was suboptimal.</p>
<p><img src="/images/mindustry-drills.jpg" alt="Picture of resource tiles in Mindustry with a small drill over a 2 by 2 tiles of them"></p>
<p>If you can see the picture, the drills cover 2×2 tiles,
whereas the resources are a large uneven cluster.
It is, unfortunately, an innate and natural desire to make those drills cover all possible resources you can get.
Thankfully, I have an outlet for such unrefined desires in my life.
I could just write an optimal algorithm for it. And, there was a language I was trying to learn for a while&hellip;</p>
<h2 id="the-question-to-ask">The Question to Ask</h2>
<p>First, we need to state the problem properly so that the algorithm can be well-defined.
This is generally an easy problem to state, especially so, as long as we ignore some extra minor details
(clusters can overlap, the big problem).</p>
<p>We have a matrix of ones and zeros as an input, A is e.g.</p>





<pre tabindex="0"><code>0 1 1 0 
1 1 1 1 
0 1 1 0</code></pre><p>Then, we must return the optimal packing (i.e. maximum number of 1s covered for our purposes) of non-overlapping 2×2 squares, result would be e.g.</p>





<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span>a a b b
</span></span><span style="display:flex;"><span>a a b b
</span></span><span style="display:flex;"><span><span style="color:#f92672">-</span> <span style="color:#f92672">-</span> <span style="color:#f92672">-</span> <span style="color:#f92672">-</span></span></span></code></pre></div><p>Where a is one square, b is another square, and dashes are a waste and my tears.</p>
<h2 id="easy-enough">Easy Enough</h2>
<p>At first, I thought about solving this problem in Scheme,
but given all the matrices, I felt that it was time to learn Uiua.
By the way, while I do use Uiua386 font for this page,
if you forbid page fonts or do not have it for whatever other reasons,
the glyphs might look off if they exist at all. No pretty colors though, see the online interpreter for that.</p>
<p>This is a walkthrough of how my code works, with some explanations of how Uiua works.
It is no tutorial, unless you like to be kept in the dark on most things and forged through fire.
There is a good tutorial on the official website.</p>
<p>Anyway, I felt that a good start in Uiua would be <code>⧈∘2_2</code>.
This produces 2×2 windows/squares over the matrix (it is <code>stencil identity [2 2]</code> if you want names and a more common array notation).
Exactly what we need.
Using the example matrix we would get a new matrix:</p>





<pre tabindex="0"><code>0 1  1 1  1 0
1 1  1 1  1 1
               
1 1  1 1  1 1
0 1  1 1  1 0</code></pre><p>Unfortunately, we hit an ouch here, NP-Hard.
This has become the Maximum Weight Independent Set problem.
Thankfully, this gives me an opportunity to test Uiua for something more interesting than just nice array operations.
It does mean that my solution is probably not as idiomatic or optimized as it could be by a Uiua expert.
At least it is safe from LLMs, those things seem to be useless when working with Uiua.</p>
<p>We can start with preparing our data a little further, and introduce the language a little faster:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">Prep ← ◴⊸⊚ ≡₂(/+/+) ⧈∘2_2</code></pre><p><code>Prep ←</code> is just binding to a variable.
Uiua is a stack language like Forth so right-to-left is a more proper reading order.
Our windows part is to the right. Note that it is a 2×3×2×2 matrix.
As such, we reduce with plus <code>/+</code> over second rank (using <code>≡</code> with <code>₂</code>), i.e. the 2×2 matrices inside the 2×3 matrix.
If we did not use <code>≡</code> (<code>rows</code>) it would be 2 rows of squares, the default rank for reduce and most Uiua functions.
Because we use 2×2 matrices, we need to reduce twice to first combine rows and then the columns.
Finally, we run the last part where we get coordinates to non-zero squares, and deduplicate them.</p>
<p>One of the more interesting parts is the <code>⊸</code>, which preserves the argument and puts it to the right of the output.
In this case, with <code>⊚</code>, it preserves our sums on the right side.
Thus, from our example we get:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">╭─
╷ 0 0
  0 1    ╭─
  0 2    ╷ 3 4 3
  1 0      3 4 3
  1 1            ╯
  1 2
      ╯</code></pre><p>Two matrices on the stack with fancy formatting. Again it is a stack language.</p>
<h2 id="recursion-is-ill-advised">Recursion is Ill-Advised</h2>
<p>I thought of a top-down dynamic programming solution, not the fastest but it is optimal.
Yet, the Uiua docs seem to say Uiua is not good with recursion.
Conflictingly, Uiua also has <code>memo</code>, which memoizes the calls to a function (or combinations thereof).
We shall proceed.</p>
<p>The core idea would be to use the coordinates as a visit list.
On each step, by either visiting the coordinate or skipping it,
and then keeping the max value.
When visiting we naturally delete neighbors that would overlap otherwise and recurse further while adding the score at the coordinate.
Remember that our coordinates point to 2×2 square sums behind them on the stack, so we can pick the score we need anytime.</p>
<p>We will start with our base case complete and begin our recursive case:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">Dyn ← |2 memo⍣(
  ⊢
| 0)</code></pre><p><code>|2</code> is just a signature for how many arguments we take, <code>memo</code> is memoize, and so we are left with <code>⍣</code>.
This is <code>try</code>, pattern matching by failing.
There are ways to avoid it and prettier tricks, but to pattern match we genuinely throw an error.
In this case, <code>⊢</code> is <code>first</code>, that is, it takes the first element of the array.
This function can fail on the empty list, hence our pattern matching is possible.</p>
<p>Next, we extend our recursive case</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">⌵ ˜◡ ≡₁- ⊸⊢</code></pre><p>For the first <code>⊸⊢</code>, as before,
that <code>⊸</code> preserves the argument on the right,
so we can do <code>⊢</code> while still having our coordinates after.
Next, subtract on each row, the <code>₁</code> tells us it is for a vector, we skip a dimension.
It is not easy to follow the stack if you never used Forth, but think about what is on the stack before subtract.
We already have our first coordinate and we have a list of coordinates, thus we map subtract the chosen coordinate from every other.
We reach <code>˜◡</code>, which preserves all arguments to the previous function.
We do so <code>backward</code> as you can see from <code>˜</code>, which is necessary to keep our values in correct order.
Finally, we do <code>⌵</code>, which is just absolute value in case our subtraction left negatives.</p>
<p>Back to the stack after this operation:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">╭─       ╭─
╷ 0 0    ╷ 0 0
  0 1      0 1           ╭─
  0 2      0 2           ╷ 3 4 3
  1 0      1 0    [0 0]    3 4 3
  1 1      1 1                   ╯
  1 2      1 2
      ╯        ╯</code></pre><p>In this case, since we subtracted [0 0], most of those operations did not matter, they would in deeper cases though.</p>
<p>Next step is more fun:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">▽≠2≡/++=1⤙=0</code></pre><p><code>=0</code> runs on all coordinates, and <code>⤙</code> saves the coordinates for <code>=1</code> as well, this time by putting it ahead instead of behind like <code>⊸</code> does.
<code>+</code> runs between the results giving us a mask of all coordinates equal to 0 or 1.
We then do another row reduce <code>≡/+</code>, this time without specifying a rank.
This sums the x and y coordinate masks together into one coordinate mask.
We can then filter <code>▽≠2</code> which takes a mask (the coordinate mask we had and <code>≠2</code> to remove coordinates where x and y are either 1 or 0)
as well as the original pre-subtraction coordinates which we still have on the stack.</p>
<p>Let us check up on the stack after all of that:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">╭─
╷ 3 4 3
  3 4 3
        ╯
[0 0]
╭─
╷ 0 2
  1 2
      ╯</code></pre><p>Remember, as a stack language, it is both right-to-left and top-to-bottom.
It does look quite small now that we filtered so many neighbors away.
Oh and most of this work so far was just filtering neighbors of the coordinate.
If it seems like a lot, remember that it would likely be a lot of cases/long if statements in most other languages.</p>
<p>Now we enter an even more fun part, forking!</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">+⊃(⊡⋅⊙∘|Dyn⊙⋅∘)</code></pre><p>As you can see by <code>Dyn</code>, this is where we finally have recursion.
Anyway, let us do this step by step.
Well a <code>⊃</code> (<code>fork</code>) is a bit special.
Essentially it copies the inputs to both the left and right sides of the bar <code>|</code>.
We will do the right side first.
This is the &ldquo;planet notation&rdquo; as the Uiua tutorial calls it.
Think of the stack from above and map it to the &ldquo;planets&rdquo; from left to right.
<code>⊙</code> tells us that we use the argument, <code>⋅</code> tells us that we ignore the argument.
We must end these planets in either <code>∘</code> (<code>identity</code>) which keeps the last value or <code>◌</code> (<code>pop</code>), which ignores it.
All of these can be used elsewhere, but this planet notation kind of usage, especially in forks, is very nice.</p>
<p>Thus, what we get on the right side is &ldquo;use the first&rdquo;, &ldquo;skip the second&rdquo;, and &ldquo;use the last&rdquo;, which then gets fed into <code>Dyn</code>.
On the left side it is &ldquo;skip the first&rdquo;, &ldquo;use the second&rdquo;, &ldquo;use the last&rdquo;, with which we call <code>⊡</code> (<code>pick</code>).
The end result is that the left side picks the summed square by our coordinate,
and the right side gives us the maximum value from that branch.
Both of them are now on the stack, and we trivially add them with <code>+</code>.</p>
<p>The output is:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">6</code></pre><p>Now we truly reduced.</p>
<p>Still, per my plans, we are not yet finished:</p>
<blockquote>
<p>On each step either visiting the coordinate or <strong>skipping it</strong>,</p></blockquote>
<p>We have to also skip the coordinate instead of using it.
There could be a better combination down the line that we would otherwise filter away.
Thankfully, we already have a really useful tool for considering that:</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">Dyn ← |2 memo⍣(
  ↥⊃(
    Dyn↘1
  | ⌵˜◡≡₁-⊸⊢
    ▽≠2≡/++=1⤙=0
    +⊃(⊡⋅⊙∘|Dyn⊙⋅∘))
| 0)</code></pre><p>You can see the part we went over, now it is in the right side of the fork.
This fork copies the original input to the function <code>Dyn</code>.
New addition is the left side, where we do <code>Dyn↘1</code>,
that is we skip the top coordinate and call <code>Dyn</code> again.
This gives the maximum value of that branch.
Afterwards, we just take the maximum of the two <code>↥</code>.
And the function is complete.</p>
<p>It would be nice to combine <code>Prep</code> and <code>Dyn</code>, so we can use them more easily too.</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">F ← Dyn Prep</code></pre><p>Thus, calling <code>F</code> on the original example from the problem description gives us:</p>





<pre tabindex="0"><code>6</code></pre><p>Well, in this case taking the first branch every time was the correct answer.
But, we did the work to make sure of it.</p>
<h2 id="the-first-hurdle-passed">The First Hurdle Passed</h2>
<p>Barely any words (in the actual code) and we get a beautiful terse Uiua result.</p>





<pre tabindex="0"><code class="language-uiua" data-lang="uiua">Prep ← ◴⊸⊚≡₂(/+/+)⧈∘2_2
Dyn ← |2 memo⍣(
  ↥⊃(
    Dyn↘1
  | ⌵˜◡≡₁-⊸⊢
    ▽≠2≡/++=1⤙=0
    +⊃(⊡⋅⊙∘|Dyn⊙⋅∘))
| 0)
F ← Dyn Prep</code></pre><p>You can see, run, and play with the code and the example <a href="https://uiua.org/pad?src=0_19_0-dev_4__eJxzLEpNDM7MLchJVXjUNkHhUdt6BeN4E4VoAwVDBUMFAy4FLAAkY6hgiFUOqi-WK6AotQBi5PQtj7p2POqa9ahz4aOmJg19bX1tzUfLOx51zDCKN-JyqcwDK6sxUshNzc1XfNS7WINLQeFR29JHXc0gloKCS2Xeo7YZIPtqFB71bD0959H0hWDDGnXBJi8Cq3o0be-jzgVGjzoX6mtr2xo-WjLTFuJ8bZBBj7oWPupufdQ181HHjBqQgV0zQfyOGZqaXDUKBppcbmBXgFwDcjkXl5uCIzxouAAkKm33">here in the Uiua online interpreter</a>.</p>
<p>Now some might realize that I originally stated:</p>
<blockquote>
<p>we must return the optimal packing of non-overlapping 2×2 squares</p></blockquote>
<p>While this code would only return the optimal value of that packing.
However, since we already track coordinates, it is a trivial change to fix it.
I shall leave it as an exercise for the reader (after some Uiua tutorial though).</p>
<p>One bigger problem though, Mindustry has belts.</p>
<p>That means adding routing on top of our NP-hard problem.
This, however, will have to wait until the next part.</p>
]]></content:encoded></item></channel></rss>