<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>lynn&apos;s house</title>
    <description>look!! a strange bug</description>
    <link>https://lynn.github.io/</link>
    <atom:link href="https://lynn.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 30 Jun 2026 15:34:46 +0000</pubDate>
    <lastBuildDate>Tue, 30 Jun 2026 15:34:46 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Late acceptance hill-climbing, a snake with a camera</title>
        <description>&lt;style&gt; body { max-width: 44em; }
pre span.k, pre span.kn { font-weight: bolder; }
.c1 { opacity: 0.6; font-family: var(--font-body); }
.sa,.sh,.s { color: light-dark(#2367cd, #789abc); }
p:has(img) { text-align: center; }
.box { border: 1px solid #8888; padding: 0.5em 1em; }
.apology { background: light-dark(#e0f7ff, #2a1a7a); }
.tab-bar { display: flex; gap: 0.25em; }
.tab-button {
  font: inherit; cursor: pointer; padding: 0.2em 0.8em;
  border: 1px solid #8888; border-bottom: none;
  border-radius: 0.4em 0.4em 0 0;
  background: light-dark(#0001, #fff1); color: inherit;
}
.tab-button.active { background: light-dark(#0002, #fff2); font-weight: bolder; }
.tabs pre { margin-top: 0; }
.tabs .tabbed { margin-top: 0; }
.hll { display: block; background-color: light-dark(#fec, #255); margin: 0 -1.4em; padding: 0 1.4em; }
.hll::before { width: 3px; height: 1.4em; background-color: light-dark(#ec9, #144); position: absolute; left: -3px; content: &quot;&quot;; }
figure.highlight { margin: 0; position: relative; }

&lt;/style&gt;

&lt;p&gt;It’s a Saturday morning, and suddenly I wonder: what’s the most compressible list of eight distinct 8-letter English words? Let’s write some &lt;a href=&quot;https://www.python.org/&quot;&gt;Python&lt;/a&gt; code.&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/usr/share/dict/words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^[a-z]{8}$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# How small can this get?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Minimizing the “cost function” &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;len(compress(w))&lt;/code&gt; is an “optimization problem” with a large search space: there are 12,529 candidate words, which means over 6 × 10&lt;sup&gt;32&lt;/sup&gt; lists-of-eight-words to consider.&lt;/p&gt;

&lt;p&gt;We’re going to compare some increasingly clever approaches for exploring this search space:&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#trying-random-stuff&quot; id=&quot;markdown-toc-trying-random-stuff&quot;&gt;Trying random stuff&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#undirected-local-search&quot; id=&quot;markdown-toc-undirected-local-search&quot;&gt;Undirected local search&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#hill-climbing&quot; id=&quot;markdown-toc-hill-climbing&quot;&gt;Hill climbing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#simulated-annealing&quot; id=&quot;markdown-toc-simulated-annealing&quot;&gt;Simulated annealing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#late-acceptance-hill-climbing&quot; id=&quot;markdown-toc-late-acceptance-hill-climbing&quot;&gt;Late-acceptance hill climbing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;box&quot;&gt;

  &lt;p&gt;Each code blog in this post is a standalone Python script, so you can follow along at home easily. In fact, I encourage you to &lt;strong&gt;type out&lt;/strong&gt; the code examples by hand: the ideas in the post will stick with you better than if you were to simply read along, or clicked “play” on a notebook.&lt;/p&gt;

  &lt;p&gt;On Arch, I had to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yay -S words&lt;/code&gt;, but other Linuxes (and Macs, I think) come with &lt;nobr&gt;&lt;code&gt;/usr/share/dict/words&lt;/code&gt;&lt;/nobr&gt; pre-installed. If not, you can get the word list &lt;a href=&quot;https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words&quot;&gt;here&lt;/a&gt; and update the path.&lt;/p&gt;

&lt;/div&gt;

&lt;h2 id=&quot;trying-random-stuff&quot;&gt;Trying random stuff&lt;/h2&gt;

&lt;p&gt;We might try random sets of words and see how well they compress. Whenever we beat our record, we’ll print the result.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/usr/share/dict/words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^[a-z]{8}$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;):&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;best&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;):&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;On my computer, this prints something like…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(68): puniness reverted scavenge critical haymaker cowlicks hankered relaunch
(67): stranger platters betiding skincare advanced resuming bourbons seizures
(66): crisping sleekest lovelorn stuffily pirating estrogen gargoyle vagrants
(65): shebangs tarragon doorpost coleslaw prequels japanned dispirit maternal
(64): adducing splodges motorman weddings enticing collages darlings pervades
(61): poisoned suspense tousling elisions discuses sheepish ogresses snippier
(60): blocking trucking booklets trekking billings quipping preciser bombings
(59): caroling scooting spooning surefire clouting flouting cackling wonkiest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then stalls. I guess the many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ing&lt;/code&gt; words are working well for compressibility, but it’s hard to arrive at such a set by pure chance.&lt;/p&gt;

&lt;h2 id=&quot;undirected-local-search&quot;&gt;Undirected local search&lt;/h2&gt;

&lt;p&gt;Instead of creating new attempts from scratch every time — picture throwing darts into the search space at random — we can “walk around” the search space, constantly moving from the current state to a “nearby” state in some sense. This idea is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Local_search_(optimization)&quot;&gt;local search&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What might that mean for us? If we have a candidate list of compressible words &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt;, how do we tweak it to find another candidate? Let’s try replacing one word at a time, swapping words between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt; and the rest of the dictionary. To make this convenient, we can permute the big &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;words&lt;/code&gt; list itself, and say that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;words[:N]&lt;/code&gt; is current list-of-eight under consideration, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;words[N:]&lt;/code&gt; contains all other words. Then a “step” involves simply swapping elements between these two sides of the array:&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s our full code again. Try running it yourself.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/usr/share/dict/words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^[a-z]{8}$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;):&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Randomly swap out a word
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Did that improve things?
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;best&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cost&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;):&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this change, instead of “throwing darts” at search space, we are stumbling through it randomly. We can see words sticking around between improvements now, especially at the beginning.&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;68&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tattooed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smirches&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;revivify&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hoarding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;squarest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowlings&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unfasten&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locators&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;67&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tattooed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smirches&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;revivify&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hoarding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;squarest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowlings&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cabochon&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locators&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;66&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tattooed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mediates&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trenches&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hoarding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;squarest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowlings&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;misusing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locators&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;overhung&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mediates&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;consoles&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hoarding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;probates&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retained&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;misusing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;centrism&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;derriere&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acrobats&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;marketer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;balsamic&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slighted&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crappers&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threaten&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;62&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;milepost&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scantest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snugging&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paranoid&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parasols&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plotting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;spoiling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unburden&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;61&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pointing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rambling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bleeping&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eruption&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sureness&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nosiness&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prairies&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mistreat&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;singular&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pureeing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;choicest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;squarest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grasping&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chroming&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testiest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mechanic&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;59&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pleasing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grapples&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gritters&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relaters&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;toasting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resoling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paneling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shoehorn&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;58&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defiling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tricking&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;swagging&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;heisting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sledding&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;haunting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mounting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dentists&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;releases&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tracking&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shacking&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stapling&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;snacking&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stemming&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;demising&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;primness&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But can we really expect this approach to do much better than the “fully random” approach? We are &lt;em&gt;printing&lt;/em&gt; results that are better than anything we’ve seen, but we’re not letting that comparison guide our search at all yet. We may stumble back &lt;em&gt;out&lt;/em&gt; of a promising region of search space just as easily as we stumbled into it. Let’s find some way to improve on this.&lt;/p&gt;

&lt;h2 id=&quot;hill-climbing&quot;&gt;Hill climbing&lt;/h2&gt;

&lt;p&gt;A simple way to guide a local search is called &lt;strong&gt;hill climbing&lt;/strong&gt;: only commit to those random steps that are direct improvements. If we imagine the problem space as a landscape where better solutions (lower cost) are “higher up”, we only take steps uphill.&lt;/p&gt;

&lt;p&gt;To implement it, we only need to append this little bit to our code…&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# If this swap *isn&apos;t* an improvement...
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# undo it
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now the behavior of our program is very different! Here are three different runs:&lt;/p&gt;

&lt;div class=&quot;language-plaintext tabbed highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(66): bendable beginner cesspool northern selloffs showoffs massacre druggies
(65): bendable beginner asphodel northern selloffs showoffs massacre druggies
(64): bendable beginner asphodel northern selloffs fusspots massacre druggies
(63): bendable beginner asphodel northern selloffs fusspots massacre embroils
(62): bendable occupies asphodel northern selloffs fusspots massacre embroils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext tabbed highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(66): eremites sciences nuisance spawning polecats lacrosse globulin chancels
(65): eremites sciences nuisance spawning polecats lacrosse trapdoor chancels
(64): eremites sciences nuisance spawning polecats lacrosse trapdoor mealiest
(62): eremites sciences nuisance screened polecats lacrosse trapdoor mealiest
(61): eremites sciences nuisance screened neurosis lacrosse trapdoor mealiest
(60): eremites sciences nuisance screened neurosis lacrosse landmine mealiest
(59): eremites sciences nuisance screened neurosis lacrosse landmine addendum
(58): eremites sciences nuisance screened neurosis lacrosse landmine lecterns
(57): eremites sciences nuisance screened schussed lacrosse landmine lecterns
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext tabbed highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(67): emerging insignia bronzing diazepam berating discoing shakeups killjoys
(65): emerging diuretic bronzing diazepam berating discoing shakeups killjoys
(63): emerging diuretic bronzing diazepam berating discoing shakeups iambuses
(62): emerging diuretic bronzing diazepam berating discoing gestated iambuses
(61): emerging diuretic bronzing diabetes berating discoing gestated iambuses
(60): emerging diuretic bronzing diabetes berating discoing disorder iambuses
(59): emerging diuretic bronzing diabetes berating discoing disorder bartered
(58): emerging diuretic denuding diabetes berating discoing disorder bartered
(57): emerging guarding denuding diabetes berating discoing disorder bartered
(56): deriding guarding denuding diabetes berating discoing disorder bartered
(55): deriding guarding denuding recorder berating discoing disorder bartered
(54): deriding acceding denuding recorder berating discoing disorder bartered
(53): receding acceding denuding recorder berating discoing disorder bartered
(52): receding acceding denuding recorder berating discoing disorder beriberi
(49): receding acceding aerating recorder berating discoing disorder beriberi
(48): receding acceding aerating recorder berating seceding disorder beriberi
(47): receding acceding aerating suborder berating seceding disorder beriberi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;script&gt;
// Combine the three `.tabbed` code blocks into one widget with Run 1/2/3 tabs.
(function () {
  var blocks = Array.prototype.slice.call(document.querySelectorAll(&quot;.tabbed&quot;));
  if (blocks.length &lt; 2) return;

  var wrap = document.createElement(&quot;div&quot;);
  wrap.className = &quot;tabs&quot;;

  var bar = document.createElement(&quot;div&quot;);
  bar.className = &quot;tab-bar&quot;;
  wrap.appendChild(bar);

  // Insert the wrapper where the first block currently sits.
  blocks[0].parentNode.insertBefore(wrap, blocks[0]);

  var buttons = [];
  blocks.forEach(function (block, i) {
    var btn = document.createElement(&quot;button&quot;);
    btn.type = &quot;button&quot;;
    btn.className = &quot;tab-button&quot;;
    btn.textContent = &quot;Run &quot; + (i + 1);
    bar.appendChild(btn);
    buttons.push(btn);

    wrap.appendChild(block); // move the code block into the widget

    btn.addEventListener(&quot;click&quot;, function () {
      buttons.forEach(function (b, j) {
        b.classList.toggle(&quot;active&quot;, j === i);
        blocks[j].style.display = j === i ? &quot;&quot; : &quot;none&quot;;
      });
    });

    block.style.display = i === 0 ? &quot;&quot; : &quot;none&quot;;
  });

  buttons[0].classList.add(&quot;active&quot;);
})();
&lt;/script&gt;

&lt;p&gt;The third time, we beat our fully-random attempts by a wide margin. But the first two times, we were unlucky, and we got stuck at a &lt;strong&gt;local optimum&lt;/strong&gt;. The hill we randomly decided to climb was not the highest hill in the landscape.&lt;/p&gt;

&lt;p&gt;Even in the third run, we have the “odd pair” of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-order&lt;/code&gt; words in the result we land on, breaking the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-erating&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ceding&lt;/code&gt; pattern. Improving further on the whole set means first making the backwards step of getting rid of one of them. This is the big problem with “naive” hill climbing.&lt;/p&gt;

&lt;p&gt;So, we’d like a less headstrong local-search algorithm, that &lt;em&gt;will&lt;/em&gt; take steps downhill sometimes: often enough to help it &lt;em&gt;discover&lt;/em&gt; higher hills in the landscape, but not &lt;em&gt;so&lt;/em&gt; often that it fails to be sufficiently attentive or attracted to them.&lt;/p&gt;

&lt;h2 id=&quot;simulated-annealing&quot;&gt;Simulated annealing&lt;/h2&gt;

&lt;p&gt;I really like this animated GIF from &lt;a href=&quot;https://en.wikipedia.org/wiki/Simulated_annealing#Overview&quot;&gt;the Wikipedia article on simulated annealing&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/d/d5/Hill_Climbing_with_Simulated_Annealing.gif&quot; alt=&quot;simulated annealing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It shows a one-dimensional “hilly landscape” with plenty of local optima to get stuck on. What is this algorithm doing to find the highest hill? From looking at the GIF, we can already try to form an intuition. A &lt;strong&gt;temperature&lt;/strong&gt; value is ticking down steadily. When it’s high, the red line moves erratically, accepting almost any step towards a new state. As the temperature goes down, the red line chills out, and becomes less likely to jump away.&lt;/p&gt;

&lt;p&gt;Indeed, simulated annealing boils down to this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set some initial &lt;strong&gt;temperature&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;, and slowly tick it down to 0 as you search.&lt;/li&gt;
  &lt;li&gt;Always accept “uphill” steps with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cost &amp;lt; best_cost&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Accept a step “downhill”, i.e. with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cost &amp;gt; best_cost&lt;/code&gt;, randomly with probability &lt;nobr&gt;&lt;code&gt;exp(-(cost - best_cost) / T)&lt;/code&gt;&lt;/nobr&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why this function? The graph of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f(x) = exp(-x / T)&lt;/code&gt; slopes down from 1 at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x = 0&lt;/code&gt; to 0 as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x → ∞&lt;/code&gt;, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; controlling the steepness of the slope. (Play around with it in &lt;a href=&quot;https://www.desmos.com/calculator&quot;&gt;Desmos&lt;/a&gt;!) This gives us what we want: an infinitesimally small step down &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; is acceptable at any temperature, but as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; goes up, we become quicker and quicker to reject a step down by any larger &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The name &lt;em&gt;annealing&lt;/em&gt; comes from metallurgy: it refers to treating a material by heating it up a bunch and then slowly cooling it down at just the right pace.&lt;/p&gt;

&lt;p class=&quot;box apology&quot;&gt;This box is blue, which is the color of apologies. I apologize for mixing metaphors! People write about optimization problems in two directions: maximization and minimization. There’s the “hill climbing” metaphor, like in the GIF, which makes the most intuitive sense when you’re &lt;strong&gt;maximizing a score&lt;/strong&gt;: “higher” means “better”. But simulated annealing is often described in terms of &lt;strong&gt;minimizing a cost&lt;/strong&gt;, as we have it in our word-compressing problem: going “uphill” in the language of hill-climbing means going &lt;em&gt;down&lt;/em&gt; in cost.&lt;/p&gt;

&lt;p&gt;Here’s simulated annealing in a Python script:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;math&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/usr/share/dict/words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^[a-z]{8}$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Randomly swap out a word
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Accept the swap if it&apos;s uphill, or the temperature randomly allows it.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;):&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Otherwise, undo the swap
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.000001&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It’s immensely successful, reliably finding solutions about as good as this one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
(37): winching pinching cinching parching munching patching punching bunching
(36): hunching munching mulching matching hatching patching punching mulcting
(35): latching patching bitching watching pitching witching hatching ditching
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or this one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
(37): clinging flinging slinging bringing fringing tingling bridling ringings
(36): slinging clinging swagging clinking clogging swinging slogging slinking
(34): slinging clinging cringing clinking stinking stinging clonking slinking
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The sad news is that to get the program to this point, I had to tweak the initial temperature (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt;) and step size  (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.000001&lt;/code&gt;) until the results were any good. This is called &lt;strong&gt;hyperparameter optimization&lt;/strong&gt; and nobody likes it, especially when there are many parameters to finetune before you get any promising results — which is sadly the case for simulated annealing.&lt;/p&gt;

&lt;h2 id=&quot;late-acceptance-hill-climbing&quot;&gt;Late-acceptance hill climbing&lt;/h2&gt;

&lt;p&gt;There is a brilliant alternative with a &lt;a href=&quot;https://en.wikipedia.org/wiki/Late_acceptance_hill_climbing&quot;&gt;very short Wikipedia article&lt;/a&gt; called &lt;em&gt;late-acceptance hill climbing&lt;/em&gt;. It’s so simple: we keep track of the last &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt; costs, and accept a step if it’s at least as good as the current state &lt;em&gt;or&lt;/em&gt; as what we had &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt; steps ago. We can use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Circular_buffer&quot;&gt;circular buffer&lt;/a&gt; (let’s call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;history&lt;/code&gt;) to implement this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/usr/share/dict/words&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^[a-z]{8}$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# the last L costs; starts all-equal
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Randomly swap out a word
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Accept if it beats where we are now, or where we were L steps ago.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_cost&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best_cost&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;):&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;best&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# undo
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_cost&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# remember where we are now
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There is only one hyperparameter to tweak, namely the size of history &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt;. This choice of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;40&lt;/code&gt; gives me excellent results:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
(35): wracking slacking stinging sticking stinking snacking stacking slinging
(34): blacking slacking stinging sticking stinking swinging stacking slinging
(33): blacking slacking stinging sticking slicking slinging stacking stinking
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Try running it yourself and tweaking the parameter yourself.&lt;/p&gt;

&lt;p&gt;I don’t have a great intuition for why this works well, but I did think of a nice visualization. In the video below, the &lt;strong&gt;state space&lt;/strong&gt; is a blobby image, and a “snake” is wriggling around, looking for the brightest pixel. The yellowest blob contains the global optimum. The white circle is the snake’s head, the white square is a “camera” attached to the tip of its tail.&lt;/p&gt;

&lt;video src=&quot;/images/lahc.mp4&quot; alt=&quot;LAHC visualization&quot; style=&quot;max-width:100%;&quot; controls=&quot;&quot;&gt;&lt;/video&gt;

&lt;p&gt;The snake’s head will move to a new nearby spot if it’s at least as bright as what either its head or the tail camera sees; otherwise it stays in place. (The little red lines are where it’s deciding &lt;em&gt;not&lt;/em&gt; to move.) The parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L&lt;/code&gt; is the length of the snake.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I just really like late-acceptance hill climbing. If you have a search problem with a big state space, give it a try!&lt;/p&gt;

&lt;p&gt;Oh, and the most compressible word list is probably something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blacking slacking stinging sticking slicking slinging stacking stinking&lt;/code&gt;, which compresses to 33 bytes. But who knows? An important takeaway is that none of these approaches promise to find &lt;em&gt;the&lt;/em&gt; perfectly optimal result. Often, an approximate optimum is good enough.&lt;/p&gt;

&lt;p&gt;Can you find a better list of eight words? Let me know &lt;a href=&quot;https://bsky.app/profile/chordbug.bsky.social&quot;&gt;on Bluesky&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sat, 13 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/lahc/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/lahc/</guid>
        
        
      </item>
    
      <item>
        <title>Adding compose key rules from the clipboard</title>
        <description>&lt;style&gt;
kbd { border: 1px solid black; padding: 2px 4px; border-radius: 2px; }
&lt;/style&gt;

&lt;p&gt;I wrote a small Bash (plus Python) script for adding my current clipboard contents to my &lt;a href=&quot;https://en.wikipedia.org/wiki/Compose_key&quot;&gt;compose key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s made for my combination of GNOME + Wayland + XCompose + ibus, so you may have to tweak it. But here’s the idea:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;wl-paste&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;zenity &lt;span class=&quot;nt&quot;&gt;--entry&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Adding compose rule from clipboard&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Which keys should make &apos;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$symbol&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&apos;?&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$code&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$code&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | python &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;names=[&quot;error&quot;]*32 + &quot;space exclam quotedbl numbersign dollar percent ampersand apostrophe parenleft parenright asterisk plus comma minus period slash 0 1 2 3 4 5 6 7 8 9 colon semicolon less equal greater question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore grave a b c d e f g h i j k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde&quot;.split(); print(*[&quot;&amp;lt;&quot; + names[ord(c)] + &quot;&amp;gt;&quot; for c in input()])&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;Multi_key&amp;gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$keys&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$symbol&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.XCompose

ibus restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That nasty-looking Python command translates ASCII symbols to their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.XCompose&lt;/code&gt;-safe key names. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;o/&lt;/code&gt; becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;o&amp;gt; &amp;lt;slash&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I put this script at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.local/bin/addcompose&lt;/code&gt; and hooked it up to Super+Shift+A in GNOME Settings:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/compose1.png&quot; alt=&quot;GNOME Keyboard shortcut settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here it is in action:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/compose2.png&quot; alt=&quot;Adding a mapsto arrow to my Compose key&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now I can hit &lt;kbd&gt;CapsLock&lt;/kbd&gt; &lt;kbd&gt;m&lt;/kbd&gt; &lt;kbd&gt;t&lt;/kbd&gt; to type ↦. Yay!&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/compose/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/compose/</guid>
        
        
      </item>
    
      <item>
        <title>NetHack 5.0.0 (is still guidecore)</title>
        <description>&lt;p&gt;&lt;strong&gt;NetHack&lt;/strong&gt; is the strangest game that I regularly get sucked back into. Recently, the DevTeam released version 5.0.0. The version I’m most familiar with is 3.4.3, which seemed to be the “definitive” version when I started playing as a teenager (c. 2008) and development had already long slowed to a crawl.&lt;/p&gt;

&lt;p&gt;I long considered the changes in 3.6.0 and beyond to be a bit disappointing: they mostly served to remove cheesy strategies from 3.4.3 that I found fun. So I lost interest for a while, until the recent &lt;a href=&quot;https://nethack.org/common/index.html&quot;&gt;release of a major version&lt;/a&gt; (the first since the 1980s!) made me check in with the DevTeam’s progress.&lt;/p&gt;

&lt;p&gt;Many of the recent changes diversify winning strategies, make the late game more interesting, and remove “interface screw”. For example, the game now prevents you from instantly dying if you accidentally typo your way into a pool of lava. Mind flayers’ attacks no longer cause you to forget maps and items, which encouraged the highly un-fun chore of making backup notes outside of the game.&lt;/p&gt;

&lt;h2 id=&quot;goodbye-chordbug-the-demigoddess&quot;&gt;Goodbye, chordbug the Demigoddess&lt;/h2&gt;

&lt;p&gt;I won as an &lt;a href=&quot;https://archive.alt.org/dumplog/chordbug/1778664962.nh500.txt&quot;&gt;orcish Barbarian&lt;/a&gt;, playing on &lt;a href=&quot;https://www.alt.org/nethack/&quot;&gt;nethack.alt.org&lt;/a&gt;, a Telnet server that has hosted NetHack since 2001. Apparently, the last game I played on this account, in 2019, was also an orcish Barbarian win. Oops! I should have changed it up.&lt;/p&gt;

&lt;p&gt;Some of the most obvious changes in NetHack 5 are interface modernizations. Dungeon branches are colored now, which is lovely. Hell is wrought of fiery rock and iron, and Sokoban is blue, the color of thinking. I messed up a Sokoban level because they are randomly mirrored now, which screws with my muscle memory. I’m not sure what the point of the change is but I think it might have worked on me.&lt;/p&gt;

&lt;p&gt;I wonder how the DevTeam reasons about exposing vs. hiding information from the player. For example, NetHack has a weirdly “secret” defensive stat called &lt;em&gt;magic cancellation&lt;/em&gt;, not to be confused with &lt;em&gt;magic resistance&lt;/em&gt;. Roughly speaking, MC defends against magical “touches” whereas MR wards you from ranged spells. It used to be that the cloak of MR also provided maximal MC, but this is no longer true. (I got level-drained many, many times.) This well-intentioned balance change seems to make it much more important to think about MC… but its existence remains well hidden. I think it would have been good to present MC next to AC in the status bar. Or would that ruin the magic?&lt;/p&gt;

&lt;p&gt;Throughout my adventure, I realized the &lt;a href=&quot;https://nethackwiki.com/wiki/Main_Page&quot;&gt;NetHack wiki&lt;/a&gt; is a bit of a mess. It was written for 3.4.3, and now contains a bunch of outdated information, with updates for later versions straddled between callout boxes and actual edits to the body text. But really, it has always contained a bunch of ultra-detailed cruft that is not so relevant to most players. (The article about &lt;a href=&quot;https://nethackwiki.com/wiki/Vlad_the_Impaler&quot;&gt;Vlad&lt;/a&gt; has an entire section dedicated to taming him, which is something roughly zero percent of players will ever try to do.)&lt;/p&gt;

&lt;style&gt;article img { max-width: 100%; max-height: 100%; }&lt;/style&gt;

&lt;p&gt;&lt;img src=&quot;/images/nethack.png&quot; alt=&quot;Screenshot of NetHack&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;on-guidecore&quot;&gt;On “guidecore”&lt;/h2&gt;

&lt;p&gt;I find NetHack hard to recommend to anyone, but it has a special place in my heart. To master NetHack is to memorize reams of dumb little facts and interactions that you can use to your advantage. This ascension marked the first time I ever used the nugget of wisdom that you can dip an &lt;a href=&quot;https://nethackwiki.com/wiki/Amethyst_stone&quot;&gt;amethyst&lt;/a&gt; into &lt;a href=&quot;https://nethackwiki.com/wiki/Potion_of_booze&quot;&gt;booze&lt;/a&gt; to make &lt;a href=&quot;https://nethackwiki.com/wiki/Potion_of_fruit_juice&quot;&gt;fruit juice&lt;/a&gt; — because, see, fruit juice was randomly &lt;a href=&quot;https://nethackwiki.com/wiki/Smoky_and_milky_potions&quot;&gt;smoky&lt;/a&gt; in my game, and drinking a smoky potion has a 1/13 chance of summoning a &lt;a href=&quot;https://nethackwiki.com/wiki/Djinni&quot;&gt;djinni&lt;/a&gt;, which in turn rolls for a 4/5 chance of getting to &lt;a href=&quot;https://nethackwiki.com/wiki/Wish&quot;&gt;wish&lt;/a&gt; for any item in the game — as long as the potion is &lt;a href=&quot;https://nethackwiki.com/wiki/BUC&quot;&gt;blessed&lt;/a&gt;, of course. Its baroque complexity makes the original Rogue look like Spider Solitaire.&lt;/p&gt;

&lt;p&gt;Winning NetHack without a guide or wiki seems unrealistic. Then again, NetHack inspired Minecraft, which is rather like that too, and it’s the best-selling video game of all time. The harsh unknowability of these games becomes their appeal, inspiring all sorts of walkthroughs and tutorials. I’m reminded of &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Tower_of_Druaga&quot;&gt;The Tower of Druaga&lt;/a&gt;, an arcade RPG that asks the player to master its obtuse secrets: Japanese players &lt;a href=&quot;https://x.com/obskyr/status/1316111754682748928&quot;&gt;exchanged hints in notebooks&lt;/a&gt; to beat the game, granting it cult status in the progress.&lt;/p&gt;

&lt;p&gt;There ought to be a name for this school of game design. Guidecore, maybe? &lt;a href=&quot;https://bsky.app/profile/chordbug.bsky.social&quot;&gt;Tell me&lt;/a&gt; about your favorite guidecore games.&lt;/p&gt;

</description>
        <pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/nethack/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/nethack/</guid>
        
        
      </item>
    
      <item>
        <title>Some Strudel tips</title>
        <description>&lt;p&gt;I’ve been doing a bunch of livecoding/algorave stuff in &lt;a href=&quot;https://strudel.cc&quot;&gt;Strudel&lt;/a&gt;, and it’s great fun. You can make all kinds of fun sounds through experimentation, and the docs are fantastic, but I’ve been itching to make a kind of “guide” that catalogues certain winning formulas for electronic music that I frequently turn to.&lt;/p&gt;

&lt;p&gt;Here are some scattered thoughts that would make it into such a guide. Each code block is executable: press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+Enter&lt;/code&gt; to start audio and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+.&lt;/code&gt; (period) to stop.&lt;/p&gt;

&lt;style&gt;.cm-editor { font-size: 13px; }&lt;/style&gt;

&lt;h2 id=&quot;hi-hats&quot;&gt;Hi-hats&lt;/h2&gt;

&lt;p&gt;Many “house-y” rhythms sound good with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hh*16&lt;/code&gt;. Just don’t make them too loud. It sounds good to play them “softly, then a bit louder” on each beat.&lt;/p&gt;

&lt;script src=&quot;https://unpkg.com/@strudel/repl@latest&quot;&gt;&lt;/script&gt;

&lt;strudel-editor&gt;
  &lt;!--
// Press Ctrl+Enter. Then uncomment the
// &quot;.gain(...)&quot; and press Ctrl+Enter again.
// Finally, press Ctrl+. (period) to stop.

$: s(&quot;bd [bd,cp] bd [bd,cp]&quot;)
$: s(&quot;hh*16&quot;) // .gain(&quot;[0.2 0.6]*4&quot;)
--&gt;
&lt;/strudel-editor&gt;

&lt;p&gt;(Also, claps and snares like to be on beats 2 and 4.)&lt;/p&gt;

&lt;h2 id=&quot;ribbon&quot;&gt;Ribbon&lt;/h2&gt;

&lt;p&gt;If you play random notes in a scale, it sounds, well, random. But if you loop a single random measure, it will start to sound like music! In the words of Adam Neely: &lt;em&gt;repetition legitimizes.&lt;/em&gt; In Strudel, the &lt;em&gt;ribbon&lt;/em&gt; function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rib&lt;/code&gt; can slice out a bit of a (random) pattern and loop it.&lt;/p&gt;

&lt;strudel-editor&gt;
&lt;!--
// Uncomment the .rib(52,1) to repeat the 52th
// random measure over and over again.
// Try changing 52 (for a different pattern), or
// 19 (for a different range of random notes)

$: s(&quot;- sh - [sh - - sh]&quot;)
$: n(irand(19).seg(16)).scale(&quot;e:minor&quot;)
   .delay(0.2) // .rib(52,1)
$: note(&quot;&lt;c2 d2 e2!2&gt;&quot;)

// Remember: press Ctrl+. to stop playback.
--&gt;
&lt;/strudel-editor&gt;

&lt;p&gt;You can even do things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rib(&quot;52 52 52 &amp;lt;52 3&amp;gt;&quot;, 1)&lt;/code&gt; to change the pattern up every other bar.&lt;/p&gt;

&lt;h2 id=&quot;randomize-everything&quot;&gt;Randomize everything&lt;/h2&gt;

&lt;p&gt;Most parameters sound fun when randomized over a small range. It can make even a single note sound interesting. Also, randomly pitch notes up or down by an octave! This kind of “accent” with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rarely&lt;/code&gt; sounds fun.&lt;/p&gt;

&lt;strudel-editor&gt;
  &lt;!--
$: sound(&quot;tr909_bd:3*4&quot;)

// Try replacing $: by _$: here,
$: note(&quot;f2*16&quot;).sound(&quot;saw&quot;)
     .fm(1)
     .lpf(1500)
     .lpenv(8)
     .lpdecay(0.02)
     .clip(0.5)
     .gain(0.9)

// and _$: by $: here, then Ctrl-Enter.
_$: note(&quot;f2*16&quot;).sound(&quot;saw&quot;)
     .fm(rand.range(0, 2))
     .lpf(rand.range(1000,2000))
     .lpenv(rand.range(4,14))
     .lpdecay(rand.range(0, 0.04))
     .clip(rand.range(0.3, 0.9))
     .gain(rand.range(0.8, 1.0))
     .swingBy(0.1, 8)
     .rarely(x=&gt;x.transpose(choose(-12, 12)))
--&gt;
&lt;/strudel-editor&gt;

&lt;p&gt;You can combine this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rib&lt;/code&gt;, but you don’t have to.&lt;/p&gt;

&lt;h2 id=&quot;chords-bass-drums-melody&quot;&gt;Chords, bass, drums, melody&lt;/h2&gt;

&lt;p&gt;This is a solid four-ingredient recipe for making a tune sound “full.”&lt;/p&gt;

&lt;p&gt;Try muting parts with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_$&lt;/code&gt; to see what it feels like to have
“no chords” or “no bass” for a bit. It’s fun to DJ like this.&lt;/p&gt;

&lt;strudel-editor&gt;
&lt;!--
// Chords
// Muting me sounds &quot;dry&quot;.
$: chord(&quot;&lt;Dm9 Em9&gt;&quot;).anchor(&quot;70&quot;).voicing()
    .pan(0.7).clip(0.90).gain(0.4).lp(3000)
$: chord(&quot;&lt;Dm9 Em9&gt;&quot;).anchor(&quot;80&quot;).voicing()
    .pan(0.3).clip(0.95).gain(0.4).lp(2000)

// Bass
// Muting me sounds &quot;floaty&quot;.
$: note(&quot;&lt;[d2 e2]*4 [a1 c2]*4&gt;&quot;)
    .struct(&quot;x - x(1,4,1) x*2&quot;).sound(&quot;gm_koto,saw&quot;)

// Drums
// Muting me sounds &quot;contemplative&quot;.
$: s(&quot;sh*16&quot;).swingBy(0.1,8).gain(&quot;[0.5 0.7]*4&quot;)
$: s(&quot;bd:6*4, - rim - rim, -!7 hh:3 -!8&quot;).clip(1)

// Melody
$: note(&quot;-!8 - c6 b5 g5 c6 b5 g5 c6 b5 g5 e5@2 -!12&quot;).slow(2)
    .vib(0.2).swingBy(0.1,8).clip(rand.range(0.5, 1))
    .pan(0.4).fm(rand).delay(0.2)
--&gt;
&lt;/strudel-editor&gt;

&lt;p&gt;(Oh! I like this trick for making nice and “full” chord voicings: play the same chords at two different anchors, then pan them left and right.)&lt;/p&gt;

&lt;h2 id=&quot;fun-samples&quot;&gt;Fun samples&lt;/h2&gt;

&lt;p&gt;There are loads of fun samples in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github:tidalcycles/dirt-samples&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github:yaxu/clean-breaks&lt;/code&gt;.&lt;/p&gt;

&lt;strudel-editor&gt;
&lt;!--
samples(&apos;github:tidalcycles/dirt-samples&apos;)
setcpm(33)

// A cool pad
$: note(&quot;db1&quot;).sound(&quot;ade:3&quot;).begin(0.2).clip(1).gain(0.3)

// Nice bass
$: note(&quot;ab1 - - eb2 - - &lt;bb1 bb2&gt;? -&quot;).sound(&quot;bass:0&quot;)

// Many drums, e.g.
$: sound(&quot;house*4&quot;).gain(0.7)

// I found this great little house chord!
$: note(&quot;c2 - - &lt;- - - g2&gt;&quot;).s(&quot;house:5&quot;).delay(1)

// Lots and lots of weird little one-shots.
$: n(irand(100)).struct(&quot;x*16&quot;).sound(&quot;yeah,subroc3d&quot;).clip(1).gain(0.2)
--&gt;
&lt;/strudel-editor&gt;

&lt;p&gt;Meanwhile, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github:yaxu/clean-breaks&lt;/code&gt; catalogs some nice drum loops for breakbeats.&lt;/p&gt;

&lt;p&gt;I like using Vocaloid samples in Strudel… There are some excellent stems licensed under BY-NC-SA on &lt;a href=&quot;https://jamies.page/stems&quot;&gt;Jamie Paige’s stems page&lt;/a&gt;, from which I took the &lt;em&gt;Strawberry&lt;/em&gt; vocal for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;teto&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github:lynn/sounds/main/vocaloid&lt;/code&gt;.&lt;/p&gt;

&lt;strudel-editor&gt;
&lt;!--
samples(&apos;github:lynn/sounds/main/vocaloid&apos;)
samples(&apos;github:yaxu/clean-breaks&apos;)
setcpm(28)

// Teto sounds good when chopped up.
// Always use clip() or she&apos;ll keep singing!
// I use begin() to make her sing random ribboned notes.
// What happens if you rand.seg(4)?
$: sound(&quot;teto&quot;).struct(&quot;x - x x - - x*2 x x x@3 x x x x&quot;)
    .begin(rand.seg(16).rib(53,1)).clip(0.9).swingBy(0.1,8).late(0.005)

// The breaks are incredibly generous ingredients, and
// basically anything you do with them sounds cool.
// Use slow()+fit() to match your bpm.
_$: sound(&quot;movement&quot;).slow(2).fit()
    .compressor(&quot;-20:20:10:.002:.02&quot;).postgain(1.1)

// Some of the gm_ sounds can sound pretty good in context.
_$: chord(&quot;&lt;AM7 EM7&gt;&quot;).voicing().sound(&quot;gm_string_ensemble_1&quot;).gain(0.6)

// Random twinkles~
_$: note(&quot;e5*16,b4*7,e2*5&quot;).swingBy(0.1,8).late(0.005).fm(rand).gain(0.9).clip(0.9).room(0.2)
--&gt;
&lt;/strudel-editor&gt;

&lt;h2 id=&quot;more&quot;&gt;More?&lt;/h2&gt;

&lt;p&gt;Maybe that’s plenty of tips for now – I can feel myself veering off track. But I might write more about Strudel in the future. Let me know what you think &lt;a href=&quot;https://bsky.app/profile/chordbug.bsky.social&quot;&gt;on Bluesky&lt;/a&gt; or email me (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lynn@foldr.moe&lt;/code&gt;).&lt;/p&gt;

</description>
        <pubDate>Sun, 15 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/strudel/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/strudel/</guid>
        
        
      </item>
    
      <item>
        <title>Days Since `pacman -Syu`</title>
        <description>&lt;p&gt;&lt;strong&gt;Sometimes I wake&lt;/strong&gt; up in a cold sweat and realize it’s been three weeks since I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pacman -Syu&lt;/code&gt;. No more. I use GNOME (btw), so I made &lt;a href=&quot;https://github.com/lynn/days-since-update&quot;&gt;a little GNOME shell extension&lt;/a&gt; for myself, constantly showing how long it’s been in the top bar.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/days-since-update.png&quot; alt=&quot;Screenshot of the indicator in the top bar&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That &lt;strong&gt;1d&lt;/strong&gt; means I updated my packages one day ago. If it’s been ten days or more, the number turns yellow.&lt;/p&gt;

&lt;p&gt;As far as engineering goes, the process was pretty unremarkable. LLMs make it easier than ever to turn the sparkle of an idea into a single-hour project. The GNOME extensions project has a reasonable &lt;a href=&quot;https://www.theverge.com/news/844655/gnome-linux-ai-shell-extensions-ban&quot;&gt;no-vibe-coding policy&lt;/a&gt;, so I took care to tend to the business logic by hand and trim down the generated fluff.&lt;/p&gt;

&lt;p&gt;In this age, a not-too-serious project like this feels like an opportunity to leave a human mark on a program: a silly variable name, or a funny code golf trick that a machine would never generate. Did you know there are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;864e5&lt;/code&gt; milliseconds in a day?&lt;/p&gt;

&lt;p&gt;You can install &lt;em&gt;Days Since Update&lt;/em&gt; &lt;a href=&quot;https://extensions.gnome.org/extension/9222/days-since-update/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Thu, 29 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/days-since-update/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/days-since-update/</guid>
        
        
      </item>
    
      <item>
        <title>Cute beeps for your terminal</title>
        <description>&lt;p&gt;&lt;strong&gt;I think&lt;/strong&gt; computers should beep. Put this in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.local/bin/bip&lt;/code&gt; if you agree:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#/usr/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=()&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;arg &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;notes+&lt;span class=&quot;o&quot;&gt;=(&lt;/span&gt;sine &lt;span class=&quot;nv&quot;&gt;$arg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;times&lt;/span&gt;+&lt;span class=&quot;o&quot;&gt;=(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;fi
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;t &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3500&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done

&lt;/span&gt;play &lt;span class=&quot;nt&quot;&gt;-qn&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c1&lt;/span&gt; synth 1 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;notes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[*]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
	fade 0.01 0.1 0.06 delay &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;times&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[*]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; gain &lt;span class=&quot;nt&quot;&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;play&lt;/code&gt; command is provided by &lt;a href=&quot;https://en.wikipedia.org/wiki/SoX&quot;&gt;sox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;long_command &amp;amp;&amp;amp; bip C4 D4 G4&lt;/code&gt;, for example. I use it in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.claude/settings.json&lt;/code&gt; to have Claude chirp at me, with different little melodies depending on what it wants.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Stop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bip D5 F4&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Notification&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;matcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;permission_prompt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bip D5 E5&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;matcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;idle_prompt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bip A4 B4 A4&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;PreToolUse&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;matcher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;AskUserQuestion&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;hooks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bip D5 E5&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Tue, 27 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/bip/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/bip/</guid>
        
        
      </item>
    
      <item>
        <title>Reader-style Uiua</title>
        <description>&lt;p&gt;&lt;strong&gt;Lately I’ve been&lt;/strong&gt; playing with &lt;a href=&quot;https://www.uiua.org/&quot;&gt;Uiua&lt;/a&gt;, an APL-like programming language that has no local variables on purpose. Instead, you describe how data flows through your function using combinators. One can also think of the language as stack-based, though the official documentation has recently started to discourage this perspective.&lt;/p&gt;

&lt;p&gt;I appreciate the die-hard commitment to tacit, point-free programming on an aesthetic level, but on a practical level I find it often gets in the way. I’ve landed on a nice way to make programming in Uiua feel more like a regular stack-based language. We can write functions that keep the top-of-stack fixed to some ambient passed-in &lt;strong&gt;context&lt;/strong&gt;, that we constantly either read parts from, or dip past.&lt;/p&gt;

&lt;p&gt;Here’s an example of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Quadratic_equation#Discriminant&quot;&gt;quadratic discriminant&lt;/a&gt; function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Disc(c,b,a) = b²−4ac&lt;/code&gt;, side-by-side with what we might write if Uiua had local variables:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-ua&quot;&gt;Disc ← (           # Disc(C_B_A) ← (
  ~ {C B A}        #
  ⊙ⁿ₂ ⟜B           #   ⁿ₂ B
  ⊙× ⊙4 ⊙× ⟜A ⟜C   #   × 4 × A C
  ⊙-               #   -
  ◌                #
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We define the shape of the context using a local &lt;a href=&quot;https://www.uiua.org/tutorial/Data%20Definitions&quot;&gt;data definition&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~&lt;/code&gt;. This context stays on top, and everything underneath it is the &lt;strong&gt;effective stack&lt;/strong&gt;, which starts empty.&lt;/p&gt;

&lt;p&gt;Now here’s how we write our code:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Every time we want to read from our context, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⟜F&lt;/code&gt; to push &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F(context)&lt;/code&gt; to the top of the effective stack.&lt;/li&gt;
  &lt;li&gt;To manipulate the effective stack any other way, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⊙&lt;/code&gt; to dip past the context.&lt;/li&gt;
  &lt;li&gt;At the end, we pop &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;◌&lt;/code&gt; the context, leaving only the effective stack values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I want to call this style &lt;strong&gt;reader style&lt;/strong&gt;, because in a way, this is like programming in a Reader monad: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⟜f&lt;/code&gt; is like &lt;a href=&quot;https://hackage-content.haskell.org/package/mtl-2.3.2/docs/Control-Monad-Reader.html#v:asks&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asks f&lt;/code&gt;&lt;/a&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⊙&lt;/code&gt; is like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pure&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fmap&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;liftA2&lt;/code&gt;. For comparison, here is an equivalent Haskell definition of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disc&lt;/code&gt;, which is also “point-free without really being point-free.”&lt;/p&gt;

&lt;div class=&quot;language-hs highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Control.Monad.Reader&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Control.Applicative&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Quadratic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;Quadratic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Float&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;deriving&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;disc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Reader&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Quadratic&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Float&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;disc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;liftA2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asks&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;liftA2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pure&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;liftA2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asks&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asks&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This style can and should be mixed freely with regular flavors of Uiua. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⊙× ⊙4 ⊙× ⟜A ⟜C&lt;/code&gt; could be written &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;⟜(×₄×⊃A C)&lt;/code&gt;, where we use a small, easy-to-read tacit function as our accessor function.&lt;/p&gt;

&lt;script&gt;
  const uiua = document.getElementsByClassName(&quot;language-ua&quot;)[0];
  uiua.innerHTML = uiua.innerHTML.replace(/#.*/g, &quot;&lt;span style=opacity:0.5&gt;$&amp;&lt;/span&gt;&quot;)
&lt;/script&gt;

</description>
        <pubDate>Sat, 24 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://lynn.github.io/blog/reader-style-uiua/</link>
        <guid isPermaLink="true">https://lynn.github.io/blog/reader-style-uiua/</guid>
        
        
      </item>
    
  </channel>
</rss>
