<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://mattdodge.codes/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mattdodge.codes/" rel="alternate" type="text/html" /><updated>2022-07-19T03:45:47+00:00</updated><id>https://mattdodge.codes/feed.xml</id><title type="html">Matt Dodge</title><subtitle>More than you want to know about Matt Dodge. No, not the punter.
</subtitle><entry><title type="html">Building a Wordle Bot In Under 100 Lines of Python</title><link href="https://mattdodge.codes/wordle-bot/" rel="alternate" type="text/html" title="Building a Wordle Bot In Under 100 Lines of Python" /><published>2022-01-26T15:14:00+00:00</published><updated>2022-01-26T15:14:00+00:00</updated><id>https://mattdodge.codes/wordle-bot</id><content type="html" xml:base="https://mattdodge.codes/wordle-bot/">&lt;p&gt;If you don’t know what Wordle is, get out from under your rock and check it out. It’s a simple game at surface level but does end up needing some strategy to master it. It didn’t take more than a couple of rounds of playing it before my curiosity piqued and I felt the need to build a bot to play the game and learn more about it. Let’s get going!&lt;/p&gt;

&lt;h2 id=&quot;the-objective&quot;&gt;The Objective&lt;/h2&gt;

&lt;p&gt;Build a Python program that plays Wordle. It should make guesses of words and try and figure out the ultimate answer word in as few guesses as possible.&lt;/p&gt;

&lt;h2 id=&quot;the-basics&quot;&gt;The Basics&lt;/h2&gt;

&lt;p&gt;The basics of the game are pretty straightforward. We need to be able to keep a list of valid words, make guesses, collect responses, and update our word list based on the data in those responses. This high-level Python code can be the baseline of our solving application.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ALL_WORDS&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;guess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&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;s&quot;&gt;&quot;Guess: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;collect_result&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;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CORRECT&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;s&quot;&gt;&quot;I won!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;update_valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&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;We really only have 3 methods to build here with this simple structure. The first is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make_guess&lt;/code&gt;. This method should take a list of valid words and guess a word out of that list. To start, let’s just choose randomly. We’ll make this smarter later. For now we just want to get the basic gameplay in place.&lt;/p&gt;

&lt;div class=&quot;language-python 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;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; Given a list of valid words, make a guess as to the next word &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&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;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&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;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collect_result&lt;/code&gt; method just needs to collect some input from the user about the guess. The user of our program will input the guess into the Wordle website and that will tell us what is right and wrong. The user needs to input those values into our program. We’ll use 3 different characters to denote the 3 possible results for each letter:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; = Miss - this letter does not appear in the word&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt; = Wrong Spot - this letter is in the word but not in this location&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; = Correct - this letter appears in the word in this spot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some straightforward Python user input collection along with some regex matching for data validation gets us on our way.&lt;/p&gt;

&lt;div class=&quot;language-python 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;re&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;collect_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; Collect the result of our guess from the user &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;What's the result? (_/?/!) &quot;&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;o&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;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'^[!?_]{5}'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&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;ow&quot;&gt;not&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;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid response string, try again&quot;&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;collect_result&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;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we need to update our valid words based on our guess and the result, that’s what the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_valid_words&lt;/code&gt; method is for. We’ll start with a trivial implementation of this that isn’t very performant but should get the job done. We’ll iterate through all remaining valid words and see if they would have yielded the same result had we guessed our guess. In order to do this, we’ll need a new method called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_result&lt;/code&gt; that will return what the result for a guess would be if we knew the answer.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; Given a guess and a known answer, return what the result would be &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch_answer&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;zip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&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;ch_guess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch_answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;!&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch_guess&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_&quot;&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;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;?&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; Return a list of valid words based on previously valid words and a new guess/result &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&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;result&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;There are a few issues with this, specifically around words with duplicate letters (e.g. ROBOT, SPEED, etc). But we’ll sort those out later. At this point we have enough to play the game! We can put all of these methods together and play. Let’s see how our game plays with a target word of SNAKE.&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;$ python solve_wordle.py
Guess: WHISK
What's the result? (_/?/!) ___??
Guess: DUSKY
What's the result? (_/?/!) __?!_
Guess: SPOKE
What's the result? (_/?/!) !__!!
Guess: STAKE
What's the result? (_/?/!) !_!!!
Guess: SNAKE
What's the result? (_/?/!) !!!!!
I won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It worked! In 5 guesses even, not too shabby considering the simple approach for a lot of those methods. Since we’re relying on a random choice here this will likely be different each time too, 5 seemed to be the median solve amount for SNAKE given this algorithm. Let’s see if we can do better.&lt;/p&gt;

&lt;h2 id=&quot;smarter-guesses&quot;&gt;Smarter Guesses&lt;/h2&gt;

&lt;p&gt;A good Wordle player is one who can guess a good word. Astute analysis, am I right? To start we just randomly picked a word from our remaining words, but that’s not very smart. What makes a good guess though?&lt;/p&gt;

&lt;p&gt;My initial theory was that a good guess would be one that had the most common letters in it. This is a fairly safe assumption, especially early on in the guessing. With many words left we want to learn as much about them all as we can, so guessing common letters is a natural instinct. Surely a first guess of “RATED” would likely teach us more about the word than a guess of “JAZZY” would. Let’s enhance our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make_guess&lt;/code&gt; method to employ this strategy.&lt;/p&gt;

&lt;p&gt;To do this, the first step is to figure out which letters are the most frequent. We should only consider valid words for these character counts too, no sense counting words we know are not correct. Python’s built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Counter&lt;/code&gt; object can help with this.&lt;/p&gt;

&lt;p&gt;After we get the counts of each character in the remaining words, we want to find the words that have the most of these characters. We also don’t want to count duplicates here either. Remember, our goal is to find out about as many letters as possible. So while E may be the most common letter in words, it doesn’t make sense to guess EERIE. We don’t want to waste those other letter slots.&lt;/p&gt;

&lt;div class=&quot;language-python 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;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Given a list of valid words, make a guess as to the next word
&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Figure out the counts first
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Now score the words based on their counts
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# This will be a list of tuples (score, word)
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;word_scores&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;unique_chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;word_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;word_scores&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Sort the list and pick the highest score
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word_scores&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&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;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;Not a terribly complicated method. We can probably make a few enhancements, like ignore letters that we already know are in the word, but this should do for now. Let’s play again against the word SNAKE and see if we do better.&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;$ python solve_wordle.py
Guess: LATER
What's the result? (_/?/!) _?_?_
Guess: SHADE
What's the result? (_/?/!) !_!_!
Guess: SUAVE
What's the result? (_/?/!) !_!_!
Guess: SPACE
What's the result? (_/?/!) !_!_!
Guess: SNAKE
What's the result? (_/?/!) !!!!!
I won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We still got the word but it still took us 5 tries. Also, since our guessing algorithm is deterministic now, this will be the result every single time we play, at least for a target word of SNAKE.&lt;/p&gt;

&lt;p&gt;One interesting observation here is that this algorithm thinks the best possible starting word is LATER (or any of its anagrams i.e., ALTER, ALERT). If you’re looking for a good starting word, these might be good ones to try.&lt;/p&gt;

&lt;p&gt;This algorithm will get some words in just a few tries, but 5 tries doesn’t cut it for me. I can do that on my own, I want my computer to be smarter than me. Let’s do better.&lt;/p&gt;

&lt;h2 id=&quot;put-this-machine-to-work&quot;&gt;Put This Machine To Work&lt;/h2&gt;

&lt;p&gt;Linear-time frequency counting isn’t much of an algorithm to write home about. We might even be able to roughly eyeball that ourselves as humans. If we think about what a good guess is, it is one that will tell us the most information based on the response. In other words, it is the one that will yield, on average, the smallest next list of valid words.&lt;/p&gt;

&lt;p&gt;We have a computer in front of us, we can simulate this! For each valid word remaining, what would happen if we guessed it? We can run each valid word as a guess against every other valid word as an answer and see what the resulting word list would be. This comes out to an O(N^2) algorithm against a relatively small problem space of just a few thousand words. Let’s go!&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; Given a list of valid words, make a guess as to the next word &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;lowest_worst_score&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;valid_words&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;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;lowest_total_score&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;valid_words&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_guess&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;worst_score&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;n&quot;&gt;total_score&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;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_answer&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# reuse our old get_result method from before, nice
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;possible_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_answer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;num_new_valid_words&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;update_valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;worst_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_new_valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;total_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_new_valid_words&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# If this possble guess has a lower total/average number of words remaining, use it instead
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lowest_worst_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_guess&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lowest_worst_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst_score&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lowest_total_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_score&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# If it's a tie in average words remaining, choose the one that doesn't have the worst case
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lowest_worst_score&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lowest_total_score&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_guess&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lowest_worst_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst_score&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;lowest_total_score&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_score&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guess&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This doesn’t look too bad, basically we’re just seeing how many words will be remaining after every possible guess and every possible answer. It will definitely be slower, but I think it will be smarter too. However, there’s a small gotcha here. We thought our algorithm would run in O(N^2) time, which isn’t too bad. But our rather simple implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_valid_words&lt;/code&gt; comes back to bite us here. That method actually has its own O(N) runtime, so really this guess method will now run in O(N^3). It’s still a small data set, but cubic runtime gets big quickly.&lt;/p&gt;

&lt;p&gt;Rather than trying to optimize either of those methods, let’s just make this strategy the one that runs once we’re down to less than 500 words in our valid word list. We can keep the frequency counting algorithm for longer word lists but once we’re closer to the end we’ll do the deep search.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_guess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&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;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&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;mi&quot;&gt;500&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;make_guess_exhaustive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make_guess_frequency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&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;Time to try it out against the word SNAKE again!&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;$ python solve_wordle.py
Guess: LATER
What's the result? (_/?/!) _?_?_
Guess: SHADE
What's the result? (_/?/!) !_!_!
Guess: SNAKE
What's the result? (_/?/!) !!!!!
I won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nice! Only 3 guesses! We might be getting a bit lucky here though. If we know the S, A, and E are in the right places then SNAKE, SUAVE, and SPACE should all be roughly equal. But this algorithm should still be more performant in the general case. Can we test that theory though?&lt;/p&gt;

&lt;h2 id=&quot;the-hardest-wordle-word&quot;&gt;The Hardest Wordle Word&lt;/h2&gt;

&lt;p&gt;At this point I got a bit curious, how would this bot do against other wordle words? I had spot-checked a few of the recent games and the bot was consistently beating my mere human brain, exactly what I wanted! Would this be the case for any word? Was there a theoretical maximum number of guesses a bot with this strategy would need to guess a word?&lt;/p&gt;

&lt;p&gt;I decided to run a simulator, much like we do for the exhaustive guess algorithm. For every word in the word list, run the bot and see how many guesses it would take to guess it. The code for this wasn’t really challenging, but it’s a bit beside the point so I won’t share it here.&lt;/p&gt;

&lt;p&gt;As expected, most of the words were solved in 2 or 3 guesses. The occasional 4 would slip in there too. But there was one word that took 8, yes 8, guesses to get. Without further ado, I present to you the hardest possible Wordle word:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CATCH&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Catch? Really? Why is that hard? Pretty easy word with pretty common letters. What’s going on here? Let’s play with our bot and see what happens.&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;$ python solve_wordle.py
Guess: LATER
What's the result? (_/?/!) _!!__
Guess: FATTY
What's the result? (_/?/!) _!!?_
Guess: HATCH
What's the result? (_/?/!) _!!!!
Guess: BATCH
What's the result? (_/?/!) _!!!!
Guess: MATCH
What's the result? (_/?/!) _!!!!
Guess: WATCH
What's the result? (_/?/!) _!!!!
Guess: PATCH
What's the result? (_/?/!) _!!!!
Guess: CATCH
What's the result? (_/?/!) !!!!!
I won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Interesting! The bot quickly got to the _ATCH suffix, but then was stuck spinning its wheels trying to guess the first letter. Because it didn’t have that much information, it was really only able to guess one letter at a time and hope that it was right. If we could somehow learn about more than one letter here we could probably solve it faster.&lt;/p&gt;

&lt;p&gt;And there it is, a truly marvelous realization after I looked at this for a while:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is sometimes advantageous to guess a word that you &lt;em&gt;know&lt;/em&gt; is wrong in order to make a better guess on your next word.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The rules just state the guess has to be a real word, not a word that is valid given the current set of known information. In other words, if we have 6 words that end in _ATCH left in our list, don’t just guess them one by one. Instead, guess another word that &lt;strong&gt;isn’t&lt;/strong&gt; in the remaining valid word list to try and get as much info as possible.&lt;/p&gt;

&lt;p&gt;What does this look like in code? It’s surprisingly easy. Instead of iterating through valid words looking for possible guesses, we’ll iterate through &lt;strong&gt;all&lt;/strong&gt; words. This will have an obvious performance hit, so we’ll only run this when we’re down to 50 words or less.&lt;/p&gt;

&lt;p&gt;Here’s the relevant part of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make_guess_exhaustive&lt;/code&gt; method now&lt;/p&gt;
&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_guess_exhaustive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;guessable_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ALL_WORDS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&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;valid_words&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;mi&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_words&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible_guess&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guessable_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# rest of the method goes here...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now what happens if we run against CATCH?&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;$ python solve_wordle.py
Guess: LATER
What's the result? (_/?/!) _!!__
Guess: CRIMP
What's the result? (_/?/!) !____
Guess: CATCH
What's the result? (_/?/!) !!!!!
I won!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Look at that! After finding out that the A and T were correct, the bot guesses CRIMP with zero intention of winning but instead gathering information. As a result, it knows which letter is the first letter and is able to guess CATCH correctly on the 3rd try. Much better than the 8th guess like before. I call that a win!&lt;/p&gt;

&lt;h2 id=&quot;wrap-up-and-other-enhancements&quot;&gt;Wrap Up and Other Enhancements&lt;/h2&gt;

&lt;p&gt;This was a fun little experiment and weekend project. I think it also demonstrates how an idea can start small and with some pretty simple pseudo-code and eventually turn into a rather complex and impactful algorithm. The code is still concise, but it ends up getting the job done.&lt;/p&gt;

&lt;p&gt;There were a few gotchas I discovered along the way while building this that I didn’t get into too much here, for the sake of keeping the article focused. The final code, shared below, includes these enhancements and realizations though.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Multiple Letters - Wordle is kind of weird (smart?) about how it handles multiple letters in a word. If the answer is ROUND and your guess is ROBOT, the first O would be green (right spot) but the second O would be blank, rather than yellow. This is a bit of a gotcha and needs to be accounted for in the code&lt;/li&gt;
  &lt;li&gt;Concept of “knowns” - The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_valid_words&lt;/code&gt; method’s algorithm and performance wasn’t satisfactory to me. I ended up implementing a concept of “knowns” about the answer word. These are basically things that we know about the word, where letters can and can’t be, what letters are missing, etc. In the final code you’ll see these knowns references flying around.&lt;/li&gt;
  &lt;li&gt;Word list - If you’ve played Wordle you will notice that most words are pretty common. Most of the examples here only referenced a common word list (2,314 5-letter words). The list of 5-letter words in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/dict/words&lt;/code&gt; file was 9,330, a much larger list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To check out the finished code for this project, head on over to the &lt;a href=&quot;https://github.com/mattdodge/wordle_bot&quot;&gt;mattdodge/wordle_bot GitHub repo&lt;/a&gt;. Feel free to fork, report issues, and/or submit PRs there.&lt;/p&gt;

&lt;p&gt;The ultimate file ended up being over 100 lines of code, but that’s mainly because it includes the enhancements and other features I mentioned here.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post and this journey of making a fun little bot for a fun little game. Of course, don’t use this bot to cheat, that’s not cool. But learning and tinkering with Python for real-world projects like this is always fun - happy Wordle-ing!&lt;/p&gt;</content><author><name></name></author><summary type="html">If you don’t know what Wordle is, get out from under your rock and check it out. It’s a simple game at surface level but does end up needing some strategy to master it. It didn’t take more than a couple of rounds of playing it before my curiosity piqued and I felt the need to build a bot to play the game and learn more about it. Let’s get going!</summary></entry><entry><title type="html">The Hardest and Easiest New York Times Crossword Puzzles of 2021</title><link href="https://mattdodge.codes/hardest-easiest-nyt-puzzles-2021/" rel="alternate" type="text/html" title="The Hardest and Easiest New York Times Crossword Puzzles of 2021" /><published>2022-01-01T15:14:00+00:00</published><updated>2022-01-01T15:14:00+00:00</updated><id>https://mattdodge.codes/hardest-easiest-nyt-puzzles-2021</id><content type="html" xml:base="https://mattdodge.codes/hardest-easiest-nyt-puzzles-2021/">&lt;p&gt;2021 is officially behind us. Let’s take a look back at the 365 daily New York Times crossword puzzles from the year and see which ones were the hardest and which ones were the easiest. Using the crowd-sourced solve times from hundreds of solvers at &lt;a href=&quot;https://xwstats.com&quot; target=&quot;_blank&quot;&gt;XW Stats&lt;/a&gt;, I was able to rank the puzzles in terms of difficulty. I’ll describe the method below, but for you with short attention spans here are those rankings, along with links to the puzzles at XWordInfo.&lt;/p&gt;

&lt;h2 id=&quot;hardest-puzzles-of-2021&quot;&gt;Hardest Puzzles of 2021&lt;/h2&gt;

&lt;p&gt;Here are the 5 most difficult/slowest puzzles of 2021, based on comparing solve times to a solver’s average solve time on that day of the week.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=12/10/2021&quot; target=&quot;_blank&quot;&gt;Friday, Dec 10&lt;/a&gt; - Joe DiPietro, Themeless (68.1% harder)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=3/19/2021&quot; target=&quot;_blank&quot;&gt;Friday, Mar 19&lt;/a&gt; - Kameron Austin Collins, Themeless (60.3% harder)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=10/6/2021&quot; target=&quot;_blank&quot;&gt;Wednesday, Oct 6&lt;/a&gt; - Jules Markey, Pigeon Holes (49.9% harder)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=6/8/2021&quot; target=&quot;_blank&quot;&gt;Tuesday, June 8&lt;/a&gt; - Christopher Adams,  Added H (49.4% harder)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=5/16/2021&quot; target=&quot;_blank&quot;&gt;Sunday, May 16&lt;/a&gt; - Joe DiPietro, A Shot in the Dark (48.2% harder)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Joe DiPietro, a veteran constructor with hundreds of published puzzles, appears twice on the list. His Friday December 10 puzzle had some tricky crosses and difficult cluing. 19% of XW Stats solvers needed to cheat on that puzzle, compared to the 10% average cheat rate on Fridays. Jules Markey clearly caught the solvers off-guard with the rare Wednesday rebus puzzle.&lt;/p&gt;

&lt;h2 id=&quot;easiest-puzzles-of-2021&quot;&gt;Easiest Puzzles of 2021&lt;/h2&gt;

&lt;p&gt;Here are the 5 easiest/fastest puzzles of 2021, based on comparing solve times to a solver’s average solve time on that day of the week.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=11/18/2021&quot; target=&quot;_blank&quot;&gt;Thursday, Nov 18&lt;/a&gt; - Ori Brian, First Second Middle… (46.2% easier)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=9/16/2021&quot; target=&quot;_blank&quot;&gt;Thursday, Sep 16&lt;/a&gt; - Kevin Patterson, Silver Lining (45.8% easier)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=10/29/2021&quot; target=&quot;_blank&quot;&gt;Friday, Oct 29&lt;/a&gt; - Aimee Lucido, Themeless (43.69% easier)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=12/5/2021&quot; target=&quot;_blank&quot;&gt;Sunday, Dec 5&lt;/a&gt; - Chase Dittrich and Jeff Chen, Come Again? (43.67% easier)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=10/27/2021&quot; target=&quot;_blank&quot;&gt;Wednesday, Oct 27&lt;/a&gt; - Johanna Fenimore, SNL (43.5% easier)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A couple of Thursdays lead the easy list this year. Neither involved any kind of rebus or typical Thursday trickery which probably led to their faster than average solve times. Johanna Fenimore’s inclusion of several famous Saturday Night Live quotes also seemed to be a crowd favorite.&lt;/p&gt;

&lt;h2 id=&quot;calculation-method&quot;&gt;Calculation Method&lt;/h2&gt;

&lt;p&gt;To compute these lists, I define a puzzle’s difficulty to be how much longer a solver takes to solve that puzzle compared to his/her average on that day of the week. Let’s get into some specifics.&lt;/p&gt;

&lt;p&gt;The first concept to understand that drives this calculation is something I call “Adjusted Solve Time.” You can read more about &lt;a href=&quot;https://xwstats.com/help#adjusted-solve-time&quot; target=&quot;_blank&quot;&gt;Adjusted Solve Time on the XW Stats help page&lt;/a&gt;, but it is basically an adjusted time it takes to solve a puzzle that takes into account any squares that were checked or revealed. This metric adds a time penalty for checking and revealing squares so that puzzles that require “cheating” show up as harder than those that do not.&lt;/p&gt;

&lt;p&gt;Once I have a solver’s adjusted solve time, the goal is to get their “average solve time” too. This value is intended to represent an individual solver’s average time to solve a puzzle &lt;strong&gt;on that day of the week&lt;/strong&gt;. Monday puzzles are obviously easier than Thursdays, so a solver will have different averages for each. But solvers also sometimes get better at puzzles over time. It is relatively safe to assume that a solver, especially a newer one, is probably solving puzzles faster at the end of the year than they were at the beginning of the year. To account for this, the average solve time metric uses a 10 week rolling average for each day of the week. In other words, a solver’s average solve time on a random Tuesday is the average of their solve times on the previous 10 Tuesday puzzles before that.&lt;/p&gt;

&lt;p&gt;Now I can compare a solver’s adjusted solve time for a given puzzle and compare it to their “expected” adjusted solve time (their 10 day average) for that puzzle. I can do this for every solver and for every puzzle throughout the year, then attempt to aggregate this data together. There are some different ways to do this, but I ended up settling on taking the &lt;strong&gt;median&lt;/strong&gt; percentage difference of adjusted solve times for each puzzle. Median makes more sense than mean, as the latter can be heavily skewed by outliers or strange performances. I opted for median over a different percentile (e.g., 75th percentile) because I believed it would best capture the experience of both faster and slower solvers.&lt;/p&gt;

&lt;p&gt;So, putting this all together gives me percentages of how much slower/harder or faster/easier a puzzle was. When I say that Joe DiPietro’s puzzle was 68.1% harder, that means that the median solver took 68.1% longer to solve that puzzle than they did the 10 previous Friday puzzles before that. This includes the time penalties for checking/revealing.&lt;/p&gt;

&lt;h2 id=&quot;raw-data&quot;&gt;Raw Data&lt;/h2&gt;

&lt;p&gt;If you’d like to conduct some of your own analysis, I’ve provided some of the raw data here. This data has been normalized and anonymized.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/files/Crossword Difficulty 2021 Full Data.csv&quot; target=&quot;_blank&quot;&gt;Crossword Difficulty 2021 Full Data.csv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a brief description of each field in the CSV:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Puzzle Date - the date the puzzle was published&lt;/li&gt;
  &lt;li&gt;Constructor - the puzzle’s constructor&lt;/li&gt;
  &lt;li&gt;Day of Week - the day of the week the puzzle was constructed&lt;/li&gt;
  &lt;li&gt;Cheat Pct - the percentage of solvers that checked or revealed a square in the puzzle&lt;/li&gt;
  &lt;li&gt;Adjusted Mean - the average/mean of all adjusted solve times compared to average adjusted solve time&lt;/li&gt;
  &lt;li&gt;Adjusted Median - the median of all adjusted solve times compared to average adjusted solve time&lt;/li&gt;
  &lt;li&gt;Adjusted P75 - the 75th percentile of all adjusted solve times compared to average adjusted solve time&lt;/li&gt;
  &lt;li&gt;Adjusted P90 - the 90th percentile of all adjusted solve times compared to average adjusted solve time&lt;/li&gt;
  &lt;li&gt;Actual Mean - the average/mean of all non-adjusted solve times compared to average solve time&lt;/li&gt;
  &lt;li&gt;Actual Median - the median of all non-adjusted solve times compared to average solve time&lt;/li&gt;
  &lt;li&gt;Actual P75 - the 75th percentile of all non-adjusted solve times compared to average solve time&lt;/li&gt;
  &lt;li&gt;Actual P90 - the 90th percentile of all non-adjusted solve times compared to average solve time&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">2021 is officially behind us. Let’s take a look back at the 365 daily New York Times crossword puzzles from the year and see which ones were the hardest and which ones were the easiest. Using the crowd-sourced solve times from hundreds of solvers at XW Stats, I was able to rank the puzzles in terms of difficulty. I’ll describe the method below, but for you with short attention spans here are those rankings, along with links to the puzzles at XWordInfo.</summary></entry><entry><title type="html">FiveThirtyEight Riddler - Prolog to the Rescue!</title><link href="https://mattdodge.codes/prolog-to-the-rescue/" rel="alternate" type="text/html" title="FiveThirtyEight Riddler - Prolog to the Rescue!" /><published>2019-02-22T15:14:00+00:00</published><updated>2019-02-22T15:14:00+00:00</updated><id>https://mattdodge.codes/prolog-to-the-rescue</id><content type="html" xml:base="https://mattdodge.codes/prolog-to-the-rescue/">&lt;p&gt;One of my weekly routines is to solve the &lt;a href=&quot;https://fivethirtyeight.com/tag/the-riddler/&quot;&gt;FiveThirtyEight Riddler&lt;/a&gt; puzzle. If you’ve never done them before, they are a set of weekly riddles that tend to involve math, logic, and statistics. Sometimes, though, they make you think. That’s silly, thinking is what computers are for!&lt;/p&gt;

&lt;p&gt;This week’s puzzle is a perfect example of a puzzle that can be solved with one of my favorite programming languages, &lt;a href=&quot;https://en.wikipedia.org/wiki/Prolog&quot;&gt;Prolog&lt;/a&gt;. I believe that in order to be a good software engineer or computer scientist you must be versed in many different programming languages. There simply isn’t one “best” programming language, they come in all shapes and sizes and some are better than others for a certain problem.&lt;/p&gt;

&lt;p&gt;One rather niche programming language is Prolog. If you’ve never seen it or worked with it before I highly recommend checking out &lt;a href=&quot;https://learnxinyminutes.com/docs/prolog/&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;https://en.wikibooks.org/wiki/Prolog/Introduction&quot;&gt;tutorials&lt;/a&gt; and getting familiar with the basics. Essentially, it’s a language that allows you to define a set of rules within a problem space and then find the answers, if they exist, that satisfy all of those rules.&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through how we can use Prolog to quickly and succinctly arrive at an answer for this week’s Riddler puzzle. I’ll be using &lt;a href=&quot;http://www.swi-prolog.org&quot;&gt;SWI-Prolog&lt;/a&gt; for this, but I believe it will work in any flavor of Prolog that you choose.&lt;/p&gt;

&lt;h2 id=&quot;problem-statement&quot;&gt;Problem Statement&lt;/h2&gt;

&lt;p&gt;First, let’s take a look at &lt;a href=&quot;https://fivethirtyeight.com/features/dont-trust-anyone-older-than-l/&quot;&gt;this week’s puzzle&lt;/a&gt; description:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We are marooned on an island that has the following curious property: Everyone over a certain age lies all the time. More specifically, there is an age limit L — a positive integer — and all islanders who are younger than age L only tell the truth, while islanders who are at least L years old only tell lies.&lt;/p&gt;

  &lt;p&gt;We are greeted by five islanders who make the following statements:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;A: “B is more than 20 years old.”&lt;/li&gt;
    &lt;li&gt;B: “C is more than 18 years old.”&lt;/li&gt;
    &lt;li&gt;C: “D is less than 22 years old.”&lt;/li&gt;
    &lt;li&gt;D: “E is not 17 years old.”&lt;/li&gt;
    &lt;li&gt;E: “A is more than 21 years old.”&lt;/li&gt;
    &lt;li&gt;A: “D is more than 16 years old.”&lt;/li&gt;
    &lt;li&gt;B: “E is less than 20 years old.”&lt;/li&gt;
    &lt;li&gt;C: “A is 19 years old.”&lt;/li&gt;
    &lt;li&gt;D: “B is 20 years old.”&lt;/li&gt;
    &lt;li&gt;E: “C is less than 18 years old.”&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;What is L? And what did we just learn about the ages of the islanders?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So basically we have a group of 5 islanders with certain ages. Some of them are liars and some are not, and they are giving us facts about the other islanders’ ages. These “facts” make great rules in a Prolog program. Let’s get to work.&lt;/p&gt;

&lt;h2 id=&quot;starting-small&quot;&gt;Starting Small&lt;/h2&gt;

&lt;p&gt;We’ll start off with a very small subset of this puzzle. I’m going to show some Prolog syntax below. If you’ve never seen it before it may look a little strange, I encourage you to read through &lt;a href=&quot;https://en.wikibooks.org/wiki/Prolog/Introduction&quot;&gt;this wiki tutorial&lt;/a&gt; first, it will give you the basics.&lt;/p&gt;

&lt;p&gt;I’m going to start with some simple claims about peoples ages.&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;c1&quot;&gt;% Claim that a Person is older than Age&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&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;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;% Claim that a Person is younger than Age&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&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;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&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 sets up 2 types of “claims” that we can make about an islander’s age. A claim that a person is older than an age (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;claim_older&lt;/code&gt;) is true &lt;strong&gt;only if&lt;/strong&gt; that person’s age is greater than the specified age. A similar rule is declared for younger. We can load this Prolog program into our interpreter and start making queries against it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: We need to specify some “bounds” for the person’s age, otherwise Prolog won’t know how to assign someone’s age and will try infinite possibilities. This is the purpose of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;between&lt;/code&gt; fact in our query.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, we’ll bound A’s age between 10 and 40 (reasonable boundaries given the problem statement). Then we’ll specify a claim that A is older than 15 and a claim that A is younger than 18. Prolog will tell us possibilities for how old A is.&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;17&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;Prolog has determined that A is either 16 or 17 years old - that makes sense! When querying in the interpreter, you can press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt; to see more possible results. In this case, after we hit the 17 result there were no more valid options, so Prolog returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What happens if we give it an impossible claim like A is older than 18 and younger than 15?&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;We can even specify claims about multiple people - this time the query is broken up into multiple lines to make it easier to read:&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;36&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;37&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;38&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;39&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;Prolog has spit out all of the possible pairs of ages that are valid for this scenario, neat!&lt;/p&gt;

&lt;h2 id=&quot;the-invention-of-lying&quot;&gt;The Invention of Lying&lt;/h2&gt;

&lt;p&gt;In our first runs we were able to make claims but we always assumed those claims were true. Per our problem statement, claims are only true if the claimant is under a certain age. Let’s define some additional rules for when a claim is made but the claimant is lying.&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;c1&quot;&gt;% A claimant is a liar if they are at least a certain age&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;% We'll hard code this to 20 years old for now&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;% We make two declarations for an older claim, one from a lying&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;% claimant and one from one telling the truth&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&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;ss&quot;&gt;between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&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;There are a few things to notice about our program now.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We are hard-coding the age of lying to 20 for now, we’ll variablize this soon.&lt;/li&gt;
  &lt;li&gt;We’ve added the ability for a claim to have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Claimant&lt;/code&gt; as well - this is the person, potentially a liar, making the claim.&lt;/li&gt;
  &lt;li&gt;We’ve added a second set of rules for each claim, now with one that supports a liar. Notice the inverted comparison operators in each set of claims. When Prolog looks through its rules it will find one that matches. We can create an “or” condition by specifying multiple possibilities for each claim - one for a lying claimant and one for one who tells the truth.&lt;/li&gt;
  &lt;li&gt;We have added our claims and bounds to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt; rule, this saves us some repetitive typing in the interpreter. Now we can just query &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run(A, B)&lt;/code&gt; and see the results. Let’s do that now!&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;Prolog has given us some pairs of ages that are valid given the rules we provided. The possibilities boil down to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15 &amp;lt;= A &amp;lt;= 18&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B = 20&lt;/code&gt;. If we look back to our original claims we basically had B claiming that A is older than 18 and younger than 15. Since that is impossible, Prolog determined that B must be a liar. But since liars are only at least 20 years old (since we hard coded that rule), B must be at least 20. However, we also had A claiming that B was younger than 21. Since we know A is between 15 and 18 (since B was a liar), we know A must be telling the truth (younger than 20). So B indeed is younger than 21 and at least 20, or, in other words, B is 20.&lt;/p&gt;

&lt;h2 id=&quot;sexier-ranges&quot;&gt;Sexier Ranges&lt;/h2&gt;

&lt;p&gt;Bet that’s the first time you’ve seen those two words next to each other.&lt;/p&gt;

&lt;p&gt;Prolog is great at giving us multiple solutions to the problem. But we’re going to be working a lot with ranges of ages. For the previous example, it would have been great if rather than iterating through every possible set of ages Prolog could have told us the range of ages that were possible in one go. Fortunately, Prolog provides a way to define “constraints” for values using something called &lt;a href=&quot;https://en.wikibooks.org/wiki/Prolog/Constraint_Logic_Programming&quot;&gt;Constraint Logic Programming&lt;/a&gt;. Let’s adapt our previous code to use constraints instead now.&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;ss&quot;&gt;use_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;library&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;clpfd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;lt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#=&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&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;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&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;Check out what’s new&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We import the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clpfd&lt;/code&gt; module at the top of our program rules now, to get access to the constraint logic predicates.&lt;/li&gt;
  &lt;li&gt;We prefix our comparison operators with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; symbol. So it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person #&amp;lt; Age&lt;/code&gt; now instead of just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person &amp;lt; Age&lt;/code&gt;. These operators are documented &lt;a href=&quot;http://www.swi-prolog.org/pldoc/man?section=clpfd#sec:A.9.2&quot;&gt;here&lt;/a&gt; if you’d like to read more about them.&lt;/li&gt;
  &lt;li&gt;We added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;not_liar&lt;/code&gt; rule instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;not(liar)&lt;/code&gt;. This is most likely due to my lack of complete understanding of Prolog, but I kept having trouble inverting the CLP constraint operators so I wrote it out explicitly.&lt;/li&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A in 10..40&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;between(10, 40, A)&lt;/code&gt; in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt; rule.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now check out our output when we run — much more readable!&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;h2 id=&quot;variable-liars&quot;&gt;Variable Liars&lt;/h2&gt;

&lt;p&gt;Up until now, we’ve been assuming that everyone over 20 is a liar. In reality, the age of that lying threshold is something we need to solve for as well. We’ll do this by making another variable for the lying age and pass that through with our claims.&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;ss&quot;&gt;use_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;library&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;clpfd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#=&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&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;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&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;We’ve pretty much just added an argument to each of our rules that will keep track of the lying age threshold. We can test this by running again with our known threshold of 20.&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;Cool, same result, that makes sense! But what if we want a variable lie threshold?&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#=&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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 syntax is a little weirder but it makes sense if you parse it out. I won’t go into too much detail (it won’t really matter when we plug in our actual claims) but here’s the gist. With a completely variable lie threshold (well, between 10 and 40 per our constraint) there are two solution possibilities. We know B is a liar because of the impossible claim. But what if A is a liar too.&lt;/p&gt;

&lt;h2 id=&quot;tying-it-all-together&quot;&gt;Tying it all Together&lt;/h2&gt;

&lt;p&gt;We’ve got a pretty slick claiming system in a rather succinct format (imagine writing this code in another language!), now it’s just time to fill out the actual claims from the problem statement and see what our solution is! (obviously, spoilers ahead…)&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;ss&quot;&gt;use_module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;library&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;clpfd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;nv&quot;&gt;Claimant&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#=&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&amp;lt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;claim_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
	
&lt;span class=&quot;ss&quot;&gt;claim_not_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;claim_not_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&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;ss&quot;&gt;not_liar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Claimant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LieThreshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Islanders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&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;nv&quot;&gt;Islanders&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ins&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&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;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;E&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;nv&quot;&gt;Islanders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_not_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;ss&quot;&gt;claim_younger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&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;A few quick things to note about the changes here:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I’ve converted the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt; arguments to a list of islanders now, rather than explicitly saying A, B, etc. Also, notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ins&lt;/code&gt; syntax for a list rather than the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in&lt;/code&gt; syntax for a single variable.&lt;/li&gt;
  &lt;li&gt;I’ve added claims for “equal” and “not equal” ages. I probably could have gotten away with re-writing D’s claim that E’s age is not 17 in a different way, but I figured I’d be explicit.&lt;/li&gt;
  &lt;li&gt;I’ve loaded in the 10 claims from the problem statement.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And now if we run it, with our ages and lying threshold as variables, we will see our solution:&lt;/p&gt;

&lt;div class=&quot;language-prolog 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;o&quot;&gt;?-&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;D&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;E&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;ss&quot;&gt;false&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;There you have it! The age threshold is 19, making &lt;strong&gt;A&lt;/strong&gt; (age 19), &lt;strong&gt;B&lt;/strong&gt; (age 20) and &lt;strong&gt;E&lt;/strong&gt; (age &amp;gt;= 20) all liars. &lt;strong&gt;C&lt;/strong&gt; (age 18) and &lt;strong&gt;D&lt;/strong&gt; (age &amp;lt;= 16) are truth tellers.&lt;/p&gt;

&lt;p&gt;I hope this post has at least minimally encouraged you to go explore some other programming languages or to scrounge up some interest in Prolog. If you’re curious or want to explore more, the completed and commented code from this tutorial is available on a &lt;a href=&quot;https://gist.github.com/mattdodge/bd2a2a0f885c732328054e9fac6a05c0&quot;&gt;GitHub gist here&lt;/a&gt;. Also feel free to reach out to me with any questions, suggestions, or Prolog tips!&lt;/p&gt;</content><author><name></name></author><summary type="html">One of my weekly routines is to solve the FiveThirtyEight Riddler puzzle. If you’ve never done them before, they are a set of weekly riddles that tend to involve math, logic, and statistics. Sometimes, though, they make you think. That’s silly, thinking is what computers are for!</summary></entry><entry><title type="html">Creating and Publishing My First Crossword</title><link href="https://mattdodge.codes/creating-a-crossword/" rel="alternate" type="text/html" title="Creating and Publishing My First Crossword" /><published>2019-02-19T15:14:00+00:00</published><updated>2019-02-19T15:14:00+00:00</updated><id>https://mattdodge.codes/creating-crossword</id><content type="html" xml:base="https://mattdodge.codes/creating-a-crossword/">&lt;p&gt;My latest hobby is solving crossword puzzles. I’ve been doing the New York Times puzzle daily for the last 8 months or so and absolutely love it. I am constantly impressed and amazed by what the creators of these crosswords can do — build a complete and connected puzzle while still integrating humor, trivia, creativity, and puns.&lt;/p&gt;

&lt;p&gt;I’ve been so impressed, in fact, that I decided to give it a go myself. What goes into building — colloquially referred to as “constructing” — a crossword puzzle? Can I do it myself? Can I end up constructing a puzzle that makes its way into the New York Times, the gold standard of crosswords? Let’s find out!&lt;/p&gt;

&lt;h2 id=&quot;puzzle-construction-goals&quot;&gt;Puzzle Construction Goals&lt;/h2&gt;

&lt;p&gt;My first step in the process was to set up some guidelines. After completing enough crosswords I learned things that I liked seeing in puzzles and things that I didn’t.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;No “double whammies”&lt;/strong&gt; - I never knew what these were called but I would see them occasionally. I call it a double whammy when two clues cross that involve somewhat obscure “trivia” that you either know or you don’t. I’ve since learned this is referred to as a “&lt;a href=&quot;https://www.urbandictionary.com/define.php?term=Natick&quot;&gt;Natick&lt;/a&gt;” in the crossword community. They can be annoying when you’re solving and I wanted my puzzle to avoid them.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;No “auto-fill” help&lt;/strong&gt; - Probably the most obvious challenge in constructing a crossword is getting all of the letters (known as the “fill”) to line up and create words in both directions. Perhaps unsurprisingly, there are &lt;a href=&quot;http://www.crossword-studio.com/helpContent/autoFill.html&quot;&gt;computer programs&lt;/a&gt; that can help build puzzles that have clean fill. I wanted to avoid using these and slog through it the old fashioned way.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Clean fill&lt;/strong&gt; - This is a bit harder to explicitly quantify but I really wanted my puzzle to not have anything too obscure or lucky. After doing a few puzzles you realize that there are &lt;a href=&quot;https://en.wikipedia.org/wiki/Crosswordese&quot;&gt;many clues&lt;/a&gt; that end up being somewhat “convenient” for the constructor. I wanted to make sure every clue of my puzzle was there deliberately and not just there because I got lucky that LEV happens to be the currency of Bulgaria, for example.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Have fun&lt;/strong&gt; - I did this to have fun and to learn - I am not trying to make a career out of crossword constructing. I needed to remind myself of this goal several times throughout the process when I started to get frustrated.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lastly, before I get into too many details, this post is going to detail my process of constructing an entire puzzle. There will obviously be “spoilers” ahead. If you’d like to solve my puzzle first, you can find it here, in several formats: [&lt;a href=&quot;https://mattdodge.codes/xword/ocean-breeze.puz&quot;&gt;AcrossLite&lt;/a&gt;] [&lt;a href=&quot;https://mattdodge.codes/xword/ocean-breeze.pdf&quot;&gt;PDF&lt;/a&gt;] [&lt;a href=&quot;https://mattdodge.codes/xword/ocean-breeze-solution.pdf&quot;&gt;Solution&lt;/a&gt;]&lt;/p&gt;

&lt;h2 id=&quot;the-theme&quot;&gt;The Theme&lt;/h2&gt;

&lt;p&gt;Most NYT crossword puzzles are centered around some type of “theme.” These themes range from clues that contain colors to Denzel Washington movie quotes and even to “&lt;a href=&quot;https://www.nytimes.com/2017/06/01/crosswords/yes-you-can-write-more-than-one-letter-in-a-square.html&quot;&gt;rebuses&lt;/a&gt;” - boxes that can contain more than one letter or a non-letter. Virtually all constructors agree that the first step to building a themed puzzle is to come up with your theme idea and the theme clues. Theme clues tend to be the longest answers in a puzzle and thus are the hardest to place, so you want to get them out of the way first.&lt;/p&gt;

&lt;p&gt;I actually started down this constructing journey while I was sitting on the beach during my Babymoon in Hawaii. And being a California boy, a beach theme seemed appropriate. Looking around for inspiration, I saw a happy little kiddo building a sand castle and wondered whether I could incorporate that into my puzzle. &lt;strong&gt;SANDCASTLE&lt;/strong&gt; is 10 letters long, a reasonable length for a theme clue, and also has some alternate meanings. SAND can be a verb, and CASTLE can refer to a chess piece. Maybe I could make this work.&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/beach.png&quot; alt=&quot;The beach where the inspiration happened&quot; width=&quot;300px&quot; class=&quot;align-center&quot; /&gt;
The beach where the construction inspiration struck&lt;/p&gt;

&lt;p&gt;Theme clues need to be consistent across the puzzle, so I started trying to come up with other two-word beach-related clues that could have alternate meanings. I ended up jotting down 5 total theme clues, knowing I would probably only use 3 or 4, that met these criteria.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;SANDCASTLE&lt;/strong&gt; (10 letters) - could also refer to sanding down a chess piece&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;CATCHRAYS&lt;/strong&gt; (9 letters) - could also refer to catching baseballs from Tampa Bay Rays players&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;OPENUMBRELLAS&lt;/strong&gt; (13 letters) - could also refer to opening up umbrella insurance policies&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;SURFBOARDS&lt;/strong&gt; (10 letters) - could also refer to surfing the web and browsing message boards&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;COASTLINE&lt;/strong&gt; (9 letters) - could also refer to coasting on a bike on a straight line&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also may need to have a “theme revealer” in my puzzle. A theme revealer is a clue that tends to be towards the bottom right of a puzzle that ties together all of the theme entries. For mine, a simple revealer of &lt;strong&gt;BEACH&lt;/strong&gt; (5) (“Locale of the clues of this puzzle”) would probably suffice.&lt;/p&gt;

&lt;p&gt;I also thought it’d be fun to incorporate &lt;strong&gt;BABYMOON&lt;/strong&gt; (8 letters) into my puzzle if I could make it work, given the fun-ness of the term and its relevance to my puzzle. We’ll see…&lt;/p&gt;

&lt;h2 id=&quot;the-grid&quot;&gt;The Grid&lt;/h2&gt;

&lt;p&gt;Now that I had my theme clues it was time to start laying out the structure of the puzzle. The grid basically refers to the placement of the black squares in the crossword puzzle. That ends up defining word lengths for every answer and where the theme answers will go as well.&lt;/p&gt;

&lt;p&gt;I didn’t think this would be that difficult of a task but it ended up being one of the hardest parts of the entire process. What you have to know before doing this is that there are a few simple rules about a “valid” crossword grid:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Diagonal Symmetry - All (or almost all) crosswords are symmetric across the diagonal - this means if you put a black square in the top-left corner, one has to go in the bottom-right corner as well.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;No 1 or 2 letter words - The minimum word length for an answer is 3 letters - seems obvious, right? This ends up being trickier than you’d think because a lot of times you want to terminate a word going across but that causes a 1 or 2 letter word going down.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;No more than 78 words - Tossing black squares all over the place makes a bunch of short words, that’s a no-no.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Grid connectivity - The entire grid must be connected, you can’t close off one section from another.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;tools&quot;&gt;Tools&lt;/h3&gt;

&lt;p&gt;This seems like a good time to discuss the tools I used for constructing this sucker. I started off drawing by hand. That lasted about 5 minutes before I realized how many times I’d be erasing. I then moved on to Google Sheets but was getting frustrated with formatting and dealing with symmetry. I then found a cool free tool online called &lt;a href=&quot;http://www.keiranking.com/phil/&quot;&gt;Phil&lt;/a&gt;. This kept track of symmetry for me and would also let me fill my clues in later. I ended up using Phil for most of the grid construction phase but ultimately sprung for &lt;a href=&quot;http://beekeeperlabs.com/crossfire/&quot;&gt;CrossFire&lt;/a&gt; - a full-featured app that has a bunch of features including auto-fill (which I won’t use—see rule #2 above!), word counts, and output formats. If you’re serious about constructing I recommend using a comprehensive tool like CrossFire or &lt;a href=&quot;https://www.crossword-compiler.com/&quot;&gt;Crossword Compiler&lt;/a&gt; - it makes your life so much easier.&lt;/p&gt;

&lt;p&gt;Due to the symmetric constraints, it’s imperative to have symmetric theme entries too. Fortunately, two sets of my theme entries had the same number of letters. SANDCASTLE and SURFBOARDS both with 10, CATCHRAYS and COASTLINE both with 9.&lt;/p&gt;

&lt;h3 id=&quot;biting-off-more-than-i-could-chew&quot;&gt;Biting off more than I could chew&lt;/h3&gt;

&lt;p&gt;I also had a 5th theme entry of OPENUMBRELLAS which had 13 letters and no partner. I started off trying to include this, but since there’s no clue to use symmetrically, it had to go in the center of my grid. However, since it’s 13 letters, and the grid is 15 letters wide, that forces 1 black square on each side of it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/openumbrellas.png&quot; alt=&quot;Already looking bad...&quot; width=&quot;400px&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was already looking weird with those two black squares. I tried to cram my other theme entries into the puzzle but it got crowded quickly and it was quite difficult (at least for a rookie like me) to create a good grid. I ended up getting them all to kind of fit and adhere to the rules with the following setup.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/openumbrellas-with-theme.png&quot; alt=&quot;The rest of the theme entries are in&quot; width=&quot;400px&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was so excited once it all actually fit and validated but then I quickly started noticing a lot of problems. First of all, I had two 15-letter words I would going to have to work in there now (3-D and 11-D). Not only did that seem difficult, but your theme entries should also typically be the longest words in your puzzle. There are also some other patterns that would undoubtedly prove to be problematic later — the 6-letter word ending in U at 5-D — two 10-letter words that will have to fit between a 15-letter and a 7-letter word at 2-D and 31-D. Plus it was just kind of an ugly grid overall.&lt;/p&gt;

&lt;p&gt;I spent at least 3 hours trying to work this grid — it was way more difficult than I imagined it would be. As painful as it was, I ended up scrapping it and starting over, this time not expecting to get my OPENUMBRELLAS clue in the puzzle.&lt;/p&gt;

&lt;h3 id=&quot;round-2&quot;&gt;Round 2&lt;/h3&gt;

&lt;p&gt;Starting over on the grid felt like a step backward but it was much smoother the second time through. At first, I was really trying to work a BEACH revealer in there too, but wasn’t having much luck. Adding any kind of 5 letter word into a grid where your theme answers are 9 and 10 letters long ended up being a big challenge. Just like the umbrella clue, I abandoned the idea of trying to get BEACH in there…for now at least…&lt;/p&gt;

&lt;p&gt;I did end up moving my theme answers to be more on top of each other—this prevented me from having a lot of long words which I figured would make my life easier in the future. After about 30 minutes I ended up with this grid that I think will work well enough.&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/grid.png&quot; alt=&quot;The grid is done!&quot; width=&quot;400px&quot; class=&quot;align-center&quot; /&gt;
Grid is complete!&lt;/p&gt;

&lt;h2 id=&quot;the-fill&quot;&gt;The Fill&lt;/h2&gt;

&lt;p&gt;Now that the grid was ready came the “fun” part—completing the rest of the letters of the puzzle so that everything is a word. Constructors call this the “fill” because it fills in the rest of the puzzle. There are a few rules about the fill, of course. I have my personal goals like no Naticks and common knowledge only. There is also no profanity allowed and no duplicate clues either.&lt;/p&gt;

&lt;p&gt;This was the step that I assumed would be the hardest part of the entire process. I was quite correct in that assumption. Within the first few minutes I realized I was in for quite the challenge.&lt;/p&gt;

&lt;p&gt;The longest remaining words in my puzzle are the two 10-letter words at 11-D and 28-D. I figured I’d start there so I started tackling the left-center and bottom-left sections first. I didn’t really know exactly where to start so I just looked up 10 letter words starting with A (since it goes under COASTLINE). There is, unsurprisingly, a lot of them.&lt;/p&gt;

&lt;p&gt;I tried to choose words that were fun or would let me be creative with the clues. My method was pretty much to try to plug a word in, fill in around it until I got stuck, and then start over. This took a while and got pretty demoralizing. I was also only doing this by hand right now, no tools or word lists involved yet. I needed a better strategy.&lt;/p&gt;

&lt;h3 id=&quot;word-lists&quot;&gt;Word Lists&lt;/h3&gt;

&lt;p&gt;I would often encounter a situation like the following: I need a 5 letter word that ends in A, starts with a vowel, and has a consonant as the 2nd to last letter. Fortunately, my knowledge of &lt;a href=&quot;https://en.wikipedia.org/wiki/Grep&quot;&gt;grep&lt;/a&gt;, a simple command-line tool to parse text with regular expressions, and a local dictionary file with all of the English words helped a bit. The problem with this approach though was that I would ignore any proper nouns (OBAMA), abbreviations (ASPCA), or compound words (UPONA). This is quite a large list to be ignoring. I needed to find a list of all possible words for a crossword - to Google I went!&lt;/p&gt;

&lt;p&gt;It turned out I wasn’t the only constructor with this problem. There’s actually a whole economy of fellow crossword constructors selling their word lists. They even “score” the word lists so you can prioritize better/more fun words over other less common words. I wasn’t ready to spring for a premium word list quite yet, but it did make me realize that if you wanted to be a serious constructor, the word list you work off of is probably the most important tool in your toolbox.&lt;/p&gt;

&lt;p&gt;If you want to use any of these scored word lists though then you’ll need some robust crossword construction software. As I mentioned earlier, I ended up downloading a Mac app called &lt;a href=&quot;http://beekeeperlabs.com/crossfire/&quot;&gt;CrossFire&lt;/a&gt;. They offer a free trial that includes a word list as well as some cool lookup features. It ended up working great for me so I shelled out the 50 smackers to get the full version which lets you save progress and work for more than 1 hour.&lt;/p&gt;

&lt;p&gt;I also stumbled across an amazing website called &lt;a href=&quot;https://www.xwordinfo.com/&quot;&gt;XWordInfo&lt;/a&gt; which contains a premium word list maintained by legendary constructor Jeff Chen. His clue lookup tool along with my dictionary grepping and CrossFire word list would get me on my way.&lt;/p&gt;

&lt;h3 id=&quot;fill-progress&quot;&gt;Fill Progress&lt;/h3&gt;

&lt;p&gt;I ended up getting the left-center and bottom-left sections filled in, working off the (kind of lame) word ASSOCIATED, so I moved on to the bottom-center. Then I got completely stuck. I tried and tried and tried to work clues in there but couldn’t ever get anything to fit.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/bottom-stuck.png&quot; alt=&quot;That pesky bottom section&quot; width=&quot;400px&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I had sunk hours into getting that bottom left section to work, so going back and redoing it sounded dreadful, but I didn’t think I had any other choice.&lt;/p&gt;

&lt;p&gt;After erasing and trying out some other options I had a bit of a breakthrough. I had been relying pretty heavily on words that existed in my word lists and in previous puzzles. There is no rule that says a word has to have existed before though—creativity is allowed! I ended up using YEAHDUH (7) going across the top of the bottom-left section. This is an NYT debut word and a fun one that could be clued with something like “Sarcastic response from a teenager.” It wasn’t easy, but after about 30-40 more minutes I had a fill on the bottom-center that worked with the whole left side. Success!&lt;/p&gt;

&lt;p&gt;I also had a realization midway through working that bottom section. I had left out my BEACH revealer clue but there was a way for me to work in a related revealer at 34-D with something like BEACHBALL or BEACHBAGS. I ended up going with BEACHBODS, a fun clue that is also an NYT debut. I could clue with something like “Physiques seen at the locale of the themed clues of this puzzle.” Things were starting to come together!&lt;/p&gt;

&lt;p&gt;I’m not sure if it was coincidence or if I was just getting better at navigating the word lists, but the rest of the puzzle filled in a lot smoother than the bottom left. I would never describe it as easy, but I didn’t have any major regressions or problems. After a few more hours of grepping, Googling, searching, filling, and crying, I had a complete fill.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/fill-1.png&quot; alt=&quot;All filled in!&quot; width=&quot;400px&quot; class=&quot;align-center&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-clues&quot;&gt;The Clues&lt;/h2&gt;

&lt;p&gt;The last step in the crossword construction process is writing the clues. This is a pretty fun step and is where the constructor’s creativity really gets to shine. In order to properly write the clues though, you have to know what level of difficulty you are expecting your puzzle to be. For example, the word APPLE could be clued as “iPhone Maker” for an &lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=2/18/2019&amp;amp;g=68&amp;amp;d=A&quot;&gt;easy puzzle&lt;/a&gt; but clued as “Jobs creation” for a &lt;a href=&quot;https://www.xwordinfo.com/Crossword?date=6/7/2007&amp;amp;g=50&amp;amp;d=D&quot;&gt;harder puzzle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To decide what difficulty you want your puzzle to be it’s important to understand the general levels of difficulty of the publisher you are submitting to. For the New York Times, 15x15 themed puzzles (like mine) run on Monday through Thursday, with Mondays being the easiest and Thursdays being the hardest. Thursday puzzles also tend to involve a bit more trickery in the theme with things like rebuses or missing letters. You definitely get a feel for it the longer you do the crosswords, but this theme that I had constructed felt like a Monday or Tuesday theme. Ultimately it’s up to the editors which day a puzzle will run, but I would try to clue my puzzle to be a hard-Monday to an easy-Tuesday difficulty.&lt;/p&gt;

&lt;p&gt;Realistically when you’re working on the fill you are coming up with some clue ideas in your head as you go. Therefore, this process went pretty quickly. Like the other steps though, there are a few rules about the clues. Again the obvious ones apply such as no profanity or obscure knowledge. The most interesting rule though is that none of your clues can have any of the words from your puzzle or other clues in them. In other words, a word may not appear more than once in a puzzle or its clues.&lt;/p&gt;

&lt;p&gt;Looking up how answers have been clued in previous puzzles was extremely helpful for this step. Almost too helpful honestly. With tools like XWordInfo, I could see every way that a word had been clued in the past. This ended up making me realize that previous constructors and editors had found much more interesting ways to write clues than I had. I tried to avoid copying old clues as much as I could and rather used them as inspiration for an interesting way to write a clue.&lt;/p&gt;

&lt;p&gt;For example, seeing EAR clued as “It may pop on a plane” led me to try to use the fact that an ear can pop in my clue. I ended up going with “Poppable body part.”&lt;/p&gt;

&lt;p&gt;Writing the clues was really fun. Especially letting them sit for a few days and sleeping on some of them for a while. I won’t list all of them out here but here are a few of my favorites:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;“Anti-liquid agcy.” (&lt;strong&gt;TSA&lt;/strong&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“Sub-par” (&lt;strong&gt;BIRDIE&lt;/strong&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“Like kangaroos and platypuses” (&lt;strong&gt;AUSTRALIAN&lt;/strong&gt; - a (surprising) NYT debut in the Shortz-era)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“Source of sin” (&lt;strong&gt;EDEN&lt;/strong&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“Kids take them down from Ladders” (&lt;strong&gt;CHUTES&lt;/strong&gt;)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;final-adjustments&quot;&gt;Final Adjustments&lt;/h2&gt;

&lt;p&gt;I sent the completed puzzle to a few friends and family members who do crosswords on a regular basis. They gave me great feedback about the sections they liked and the clues that they thought were a stretch. One area, in particular, stood out in many of the responses - the bottom-left. Specifically the crossing of NENA (German pop star) and ELEVE (French word for student). I think I knew deep down that this was a Natick and violated my rule of no double-whammies. This section had given me so much trouble that I tried to ignore it, but after a few people brought it up, I knew I had to change it. Another painful erasure of clues, but it ended up not being too tricky to fix and not impacting other sections too much.&lt;/p&gt;

&lt;p&gt;I also received some common feedback that the clue for INNY (Concave navel) was actually spelled INNIE. Another one that I knew was a bit of a stretch but couldn’t find a better fill over in that section. I ended up re-cluing INNY as “In New York” with “What Buff. and Brkln. have in common.” This is still a stretch and not the best clue, but it’s better than a misspelling.&lt;/p&gt;

&lt;p&gt;After my last tweaks, I was left with my final puzzle:&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;&lt;img src=&quot;https://mattdodge.codes/img/xword/fill-2.png&quot; alt=&quot;The final puzzle&quot; width=&quot;400px&quot; class=&quot;align-center&quot; /&gt;
All done!&lt;/p&gt;

&lt;h2 id=&quot;publishing&quot;&gt;Publishing&lt;/h2&gt;

&lt;p&gt;Now that the clues were written and the puzzle was finished up it was time to submit for publishing! The CrossFire app makes it super easy to conform to the NYT submission guidelines, which (for some reason) still require hard-copy submissions. I printed out my PDF puzzle and mailed it over, along with a huge sense of accomplishment. Then the waiting game began.&lt;/p&gt;

&lt;p&gt;Due to most of the publishers’ rules, once a puzzle is accepted the publisher owns the full rights to it. As a result, you can’t just submit your puzzle to as many publishers as you want and see which one replies first. I waited for a few months and eventually got back the following reply from the NYT editing crew, via email.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Thanks for sending us your SURFBOARDS 15x. This theme idea was nice, but the puns weren’t exciting Will quite enough to say yes. Sorry! We appreciated the chance to take a look here, though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Drats! They didn’t like it. After reading some other message board posts from people who have published many puzzles, I learned that this response basically meant the theme wasn’t interesting enough for them, so there’s pretty much no chance of fixing it. Starting over is a better option than repairing.&lt;/p&gt;

&lt;p&gt;After the NYT rejection came through I also submitted the puzzle to another publisher, the Universal Crossword. Their editor, David Steinberg, got back to me more quickly (about 6 weeks later) with a similar response:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Anyway, what an impressive first construction!  I especially got a chuckle out of the reparsed SURFBOARDS and COASTLINE.  The other two don’t grab me quite as much, though—in SAND CASTLE, I wish the meaning of “sand” changed as well; as for CATCH RAYS, the clue feels a little stretchy to me.
While I’m afraid this one is a near miss for me, I hope you’ll try again with another puzzle!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Womp! 0 for 2. At this point, I started to come to terms with the fact that this puzzle wouldn’t make it into a major publication. However, the silver lining is that I still own the rights to it and can share the details with you all!&lt;/p&gt;

&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;

&lt;p&gt;What a fun process this was! While I didn’t officially time it, the full construction process took several weeks. I’d estimate about 40 or 50 hours total. And I now have even more appreciation and respect for the people who do this for a living. Here are a few takeaways about what goes into constructing a puzzle:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;It’s all about the word list&lt;/strong&gt; - Having a thorough and complete word list is so so important. Especially if you want to be churning out puzzles at a decent pace. I can’t imagine coming up with a good, clean fill without one.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The grid is tough&lt;/strong&gt; - Building the grid seems like an easy task but it is quite challenging. A bad grid can also set you up for a lot of trouble and headache down the road.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Undoing is ok&lt;/strong&gt; - It was so painful to undo a bunch of work or erase a completed section, especially when said section took hours to begin with. But whenever I did undo a section, the next time through was a lot easier and I ended up being happy with it in the end.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Trust your gut&lt;/strong&gt; - If a clue or word feels iffy or like a stretch, it probably is.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Take notes&lt;/strong&gt; - I took tons of notes on different sections, wrote down which words worked, which words almost worked, why words didn’t work, etc. That ended up being very useful when working on a tough section.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m already working on my next puzzle and can’t wait to share it with you all when it’s done — hopefully this time it will be an NYT crossword link!&lt;/p&gt;</content><author><name></name></author><summary type="html">My latest hobby is solving crossword puzzles. I’ve been doing the New York Times puzzle daily for the last 8 months or so and absolutely love it. I am constantly impressed and amazed by what the creators of these crosswords can do — build a complete and connected puzzle while still integrating humor, trivia, creativity, and puns.</summary></entry><entry><title type="html">Interfaces for the Internet of Things</title><link href="https://mattdodge.codes/interfaces-for-internet-of-things/" rel="alternate" type="text/html" title="Interfaces for the Internet of Things" /><published>2018-11-07T15:14:00+00:00</published><updated>2018-11-07T15:14:00+00:00</updated><id>https://mattdodge.codes/interfaces-for-internet-of-things</id><content type="html" xml:base="https://mattdodge.codes/interfaces-for-internet-of-things/">&lt;p&gt;It’s undeniable that we are entering a new era of computing. Whether you call it the &lt;a href=&quot;https://www.wired.co.uk/article/internet-of-things-what-is-explained-iot&quot;&gt;Internet of Things&lt;/a&gt;, the &lt;a href=&quot;https://www.goldmansachs.com/insights/pages/iot-video.html&quot;&gt;Third Wave&lt;/a&gt;, the &lt;a href=&quot;https://www.nextplatform.com/2017/08/28/rise-fourth-wave-computing/&quot;&gt;Fourth Wave&lt;/a&gt;, distributed computing, whatever it is, the technology landscape and the machines around us are starting to look different.&lt;/p&gt;

&lt;p&gt;Plenty has been written about this shift but one aspect that is often overlooked is how interfaces evolve as we move into new technical eras. An interface, for the purposes of this post, refers to how humans interact with machines. You use different interfaces every day, whether you realize it or not. As the hardware and software that surrounds us change to embrace this new generation, new interfaces will be born.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;the-past&quot;&gt;The Past&lt;/h3&gt;

&lt;p&gt;Early computers did not have monitors that displayed the output of the machines. Instead, users would enter commands into a terminal, known as a teletype, and the results would get printed out on paper. Yes, real paper. Eventually, as computers became more mainstream, new interfaces like blinking lights, the monitor, and the mouse were born to allow humans to better interact with machines.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/interfaces/teletype.jpeg&quot; alt=&quot;img&quot; title=&quot;Teletype Model 33&quot; /&gt;
&lt;em&gt;An early interface for computers – a Teletype Model 33&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The 1990s brought a new era to the personal computer space, one where actions taken on a machine often depended on the state of a different machine across the network. This era introduced concepts like the &lt;a href=&quot;https://en.wikipedia.org/wiki/World_Wide_Web&quot;&gt;World Wide Web&lt;/a&gt; and it undoubtedly changed the world of computing as we know it. It also meant that our interface for machines had to change. We needed a way to visualize and easily digest the state of a different machine and, as a result, the web browser was born.&lt;/p&gt;

&lt;p&gt;Eventually, we entered the next generation of computing — mobile. This led to a plethora of new interfaces from touch screens to responsive design to fingerprint sensors that allow us to interact with these tiny machines that we carry in our pockets.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;the-present&quot;&gt;The Present&lt;/h3&gt;

&lt;p&gt;Which brings us to today. We are in a new time. One where devices are more powerful than ever and contain their own application logic. One where there are so many connected things talking to each other that we, as humans, aren’t able to keep track of all of the connections. One where we expect things to “just work together” without any effort and somehow be secure as well. One where connectivity and reliability are (often incorrectly) assumed.&lt;/p&gt;

&lt;p&gt;The environment of technology around us is extremely complex. And, like always, the development of the infrastructure has predated the development of our interfaces. There are, however, a handful of different interfaces that are starting to emerge, ready to tackle this massive challenge and become the interfaces of the future.&lt;/p&gt;

&lt;h3 id=&quot;voice&quot;&gt;Voice&lt;/h3&gt;

&lt;p&gt;Voice is now becoming a commonplace interface thanks to an elegant convergence of several different technologies. Natural language processing is finally starting to understand what we’re trying to say with pretty high accuracy. Text-to-speech algorithms kind of actually sound like humans now and not Microsoft Sam robots. And there are more microphones around us than ever before (this one’s a bit more creepy than elegant I suppose) with mobile phones and Amazon Echos.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/interfaces/assistants.png&quot; alt=&quot;img&quot; title=&quot;Virtual Assistants&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As a result of this, virtual assistants are popping up everywhere. The obvious ones are Siri, OK Google (or Hey Google…or Google Assistant…or whatever they call it now), and Alexa (maybe also Cortana, does anyone actually use that?).&lt;/p&gt;

&lt;p&gt;There are less obvious virtual assistants too. Bank of America has an assistant, &lt;a href=&quot;https://promo.bankofamerica.com/erica/&quot;&gt;Erica&lt;/a&gt;, that lets you interact with your bank account using voice. At this point, we’ve all probably interacted with a phone tree virtual assistant that (supposedly) helps us get in touch with the right customer service representative.&lt;/p&gt;

&lt;p&gt;Not every voice interface is a virtual assistant though. Cable TV remotes can now be controlled (with impressive accuracy I have found) with your voice. Identity verification on some banking phone calls can now be done by repeating a phrase with your voice. Turn-by-turn navigation and you car stereo are controlled by voice.&lt;/p&gt;

&lt;p&gt;While some of these technologies are still very limited in terms of their function and reliability, they do indicate a trend toward more voice interfaces in the future.&lt;/p&gt;

&lt;h3 id=&quot;low-code&quot;&gt;Low-Code&lt;/h3&gt;

&lt;p&gt;While voice is becoming a great way to interact with a set of partially pre-determined functions, it doesn’t excel when any variability is introduced into the system. You can’t tell Alexa to “run the sprinklers for 15 minutes if it reaches 90 degrees” for example. Even if you could, that would presumably only be because a developer had programmed that “recipe” into the Alexa dialect.&lt;/p&gt;

&lt;p&gt;However, users continue to demand more control over their devices. They want to explore and utilize their creativity. They need an interface to do that. And they don’t want to have to write code or know programming to make it happen. We’re seeing control over data and devices expand into a realm of users who are technical but don’t program. This is where low-code or no-code interfaces are starting to shine.&lt;/p&gt;

&lt;p&gt;Software solutions like &lt;a href=&quot;https://ifttt.com/&quot;&gt;IFTTT&lt;/a&gt; and &lt;a href=&quot;https://nodered.org/&quot;&gt;Node-RED&lt;/a&gt; allow general consumers and technical hobbyists to connect their different services, apps, and devices together in creative and intelligent ways, all without writing any code. This puts users in control of their own world and not always reliant on what functionality already exists inside of an app or product.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/interfaces/nodered.jpeg&quot; alt=&quot;img&quot; title=&quot;Node-RED Screenshot&quot; /&gt;
&lt;em&gt;A screenshot from Node-RED — allowing workflows to be built without writing any code&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the enterprise side, products like the &lt;a href=&quot;https://niolabs.com/product/system-designer&quot;&gt;niolabs System Designer&lt;/a&gt; and &lt;a href=&quot;https://www.mendix.com/&quot;&gt;Mendix&lt;/a&gt; allow companies to define complex applications that power their businesses (disclaimer: I work at &lt;a href=&quot;https://niolabs.com&quot;&gt;niolabs&lt;/a&gt;). These tools, which don’t require software developers to operate them, are opening up “intelligence” and “logic” to a much broader audience. This will almost certainly shape the way teams within companies are structured in the future.&lt;/p&gt;

&lt;p&gt;Operating early computers meant working in the terminal and understanding various shell commands, something that the layman was not able to do. Eventually, through newer interfaces and better software, computers became accessible to the masses. The same will be true for distributed systems, which are quickly becoming more accessible to non-developers.&lt;/p&gt;

&lt;h3 id=&quot;proximity-wireless&quot;&gt;Proximity Wireless&lt;/h3&gt;

&lt;p&gt;While this progress is exciting, many of the newer interfaces don’t seem poised to gracefully handle the immense scale that will come with this new computing era. Launching an app or talking to your virtual assistant to deploy hundreds or thousands of devices is unreasonable. This is where we need devices to do a little bit of self-negotiation and pairing without too much human involvement.&lt;/p&gt;

&lt;p&gt;The fundamental idea of this type of interface is if two things are supposed to connect with each other, they are both expecting a connection, and they are close by, then they should just connect. The only human requirement should be to put the system into a “discoverable” or ready-to-pair state.&lt;/p&gt;

&lt;p&gt;One of the earliest examples I can think of this is the Wi-Fi Protected Setup feature that exists on most modern wireless routers. According to &lt;a href=&quot;https://en.wikipedia.org/wiki/Wi-Fi_Protected_Setup&quot;&gt;Wikipedia&lt;/a&gt;, this was introduced in 2006. In case you aren’t familiar, the way it works is that in order to join a new wireless network you push a button on the router to automatically connect your device to it within a short period of time. This technology relied on the assumption that if you had physical access to the router then you could probably be trusted.&lt;/p&gt;

&lt;p&gt;There are many more wireless-proximity-based interfaces that are in use today. Bluetooth pairing is one that almost everyone has used at this point. Put at least one of the devices in “pairing mode” and another device can connect to it. NFC and close-range RFID allow us to make payments and unlock doors. Even scanning QR codes is a form of “visual” proximity wireless technology.&lt;/p&gt;

&lt;p&gt;When talking about large numbers of devices or broad audiences of non-technical users, device interaction will have to be somewhat automatic and not require too much human involvement. The growth of some of these wireless technologies may be an indicator that this interface is just right for that type of problem.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Up until now, humans have interacted with computers one machine at a time. When you type on your keyboard or tap on your phone you are, for the most part, controlling one and only one machine. However, we are quickly becoming in need of interfaces that allow us to interact with systems — multiple devices and machines that work together. It’s difficult to say for sure what these interfaces will be — some combination of the above, voice, low-code, and/or proximity wireless, seem like reasonable expectations at the moment. What’s certain, however, is that they will be novel and different compared to the past.&lt;/p&gt;</content><author><name></name></author><summary type="html">It’s undeniable that we are entering a new era of computing. Whether you call it the Internet of Things, the Third Wave, the Fourth Wave, distributed computing, whatever it is, the technology landscape and the machines around us are starting to look different.</summary></entry><entry><title type="html">The Two Questions You Should Never Ask Anyone</title><link href="https://mattdodge.codes/two-questions-never-ask/" rel="alternate" type="text/html" title="The Two Questions You Should Never Ask Anyone" /><published>2017-10-20T20:56:25+00:00</published><updated>2017-10-20T20:56:25+00:00</updated><id>https://mattdodge.codes/two-questions-never-ask</id><content type="html" xml:base="https://mattdodge.codes/two-questions-never-ask/">&lt;p&gt;Ok, there are probably more than two questions that fit into this category. How much money do you make? What’s that thing on your face? Did you have to kill anyone in Iraq? But these are two I’ve had recent personal experiences with that may not be as obvious, so I thought I’d share.&lt;/p&gt;

&lt;p&gt;So, without further ado, the two questions you should never ask anyone are:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How is the job hunt going?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;When are you going to have kids?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first one was totally my bad. I recently asked this to a friend of mine who had been unemployed for some time and who I knew was sending out resumes. As soon as I said it I regretted it. I know this person pretty well, and if there was good news, they would have told me. Instead, I had to make them remind me of the fact that they were still in the midst of the dreaded job hunt. What a great friend I am!&lt;/p&gt;

&lt;p&gt;It’s obvious in hindsight, but a simple rule applies here—don’t ask a question that you know doesn’t have a good answer.&lt;/p&gt;

&lt;p&gt;The second question is related, but in this case, the tables are turned for me. This is the question I really want to talk about here.&lt;/p&gt;

&lt;h3 id=&quot;when-are-you-going-to-have-kids&quot;&gt;When are you going to have kids?&lt;/h3&gt;

&lt;p&gt;First of all, not everyone wants to have children. And they likely don’t have a strong desire to constantly defend such a personal decision. Then there is the whole other group of people who want to, but can’t.&lt;/p&gt;

&lt;p&gt;My wife recently shared on social media the struggles we’ve been having with infertility over the last couple years. Throughout that time, I cannot tell you how many times we’ve been asked that seemingly innocent question.&lt;/p&gt;

&lt;p&gt;Believe me, no one dealing with infertility actually thinks this question is in any way intended to be hurtful. Sometimes it comes from family members who are just excited to have another baby around. Sometimes it’s from colleagues who are just asking the typical small-talk questions they always ask a semi-newly-married couple. Regardless, we know the question is not meant to harm.&lt;/p&gt;

&lt;p&gt;The problem is it sometimes does.&lt;/p&gt;

&lt;p&gt;It’s not so much that it saddens you or angers you to hear that question, it’s more that it reminds you that you are in an awful situation and then forces you to decide whether you want to address that situation or not.&lt;/p&gt;

&lt;p&gt;Do I really want to launch into the whole explanation right now? I could flip the awkward situation back around on the question-asker.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Not sure, we’ve been trying for two years. Dozens of medications don’t seem to be working, any suggestions?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While that likely achieves the objective of preventing this person from asking the question again, it’s probably not the most delicate way to respond.&lt;/p&gt;

&lt;p&gt;Instead, I often just end up avoiding the conflict altogether and responding with something ambiguous like the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Oh, I don’t know, one of these days, we’re starting to talk about it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem with a response like that is that it doesn’t really address the problem. And I think the root of the problem is simple—people don’t talk about infertility.&lt;/p&gt;

&lt;h3 id=&quot;lets-talk-about-it&quot;&gt;Let’s talk about it&lt;/h3&gt;

&lt;p&gt;Almost everyone who has seriously battled infertility has probably asked themselves something along the lines of “why is this happening to me.” It feels like such a lonely and hidden experience. AND I AM A GUY! I can’t even begin to imagine how it feels for a woman experiencing it when our society has put so much unfair pressure on women to raise families.&lt;/p&gt;

&lt;p&gt;The thing is, it’s not a lonely experience. Tons of people deal with it and, while every journey is different, there are a lot of others out there who can relate to your grief. There are countless different estimates on the number (1 in 4 couples, 1 in 6, etc.) but whatever the actual number is one thing is certain: someone you know, maybe even someone you consider yourself close to, has struggled with infertility.&lt;/p&gt;

&lt;p&gt;I know this because as my wife and I started to gradually open up and share what we’ve been experiencing, people often responded that they had a similar experience or knew someone who had. Given so many people around us that can relate, why does it still feel so shameful to share it? The perfectly-manicured lives we tend to portray on social media probably don’t help. Neither do Hollywood films where quick (and often inconvenient) pregnancies are a common trope.&lt;/p&gt;

&lt;p&gt;This lack of awareness around the topic can cause otherwise well-intended questions to have unfortunate consequences. Hopefully this post can do its small part in raising awareness about a difficult and all-too-common subject.&lt;/p&gt;</content><author><name></name></author><summary type="html">Ok, there are probably more than two questions that fit into this category. How much money do you make? What’s that thing on your face? Did you have to kill anyone in Iraq? But these are two I’ve had recent personal experiences with that may not be as obvious, so I thought I’d share.</summary></entry><entry><title type="html">A Statistical Look at Liar’s Dice</title><link href="https://mattdodge.codes/statistical-look-at-liars-dice/" rel="alternate" type="text/html" title="A Statistical Look at Liar’s Dice" /><published>2017-10-09T20:56:25+00:00</published><updated>2017-10-09T20:56:25+00:00</updated><id>https://mattdodge.codes/liars-dice</id><content type="html" xml:base="https://mattdodge.codes/statistical-look-at-liars-dice/">&lt;p&gt;Liar’s dice is a great game. Easy to learn, difficult to master. A good balance of luck and strategy. If you don’t know the game, I encourage you to &lt;a href=&quot;https://en.wikipedia.org/wiki/Liar%27s_dice&quot;&gt;check it out&lt;/a&gt;, it’s great for a group.&lt;/p&gt;

&lt;p&gt;In our last session, we encountered an interesting situation when we got down to the final two. One player had all 6 of his remaining dice while his opponent was down to one. Of course this led to much discussion and side betting. Mainly, we set out (read: argued) to answer the following questions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Can the player with all six dice devise an optimal strategy to guarantee a victory?&lt;/li&gt;
  &lt;li&gt;If not, what are the odds for the player with only one dice?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And just like that, a bet was born. A bet not even involving the two players actually playing the game.&lt;/p&gt;

&lt;h3 id=&quot;the-strategy&quot;&gt;The Strategy&lt;/h3&gt;

&lt;p&gt;Before we get to the bet, we needed to agree on what the strategy of the player with all 6 dice would do. We pretty quickly arrived that, in order to avoid ever “bluffing,” the person with 6 should just call the highest thing they can possibly call and hope that the opponent with 1 die won’t be able to beat it. So, given dice &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1, 1, 3, 4, 4, 5&lt;/code&gt;, he should call four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;s are wild).&lt;/p&gt;

&lt;h3 id=&quot;the-bettors&quot;&gt;The Bettors&lt;/h3&gt;

&lt;p&gt;After taking minimal game theory courses in college, I tend to assume there is always some kind of strange optimal strategy. I was unable to find said strategy but I of course immediately started trying. My goal was to find some consistent strategy (one that did not allow for variable bluffing, despite it being a key component of the game) and then calculate the odds of how often it would win. A statistical (and thus, sure fire, right?) approach to the problem.&lt;/p&gt;

&lt;p&gt;On the other side of this bet was my friend Kurt. Kurt, a long time card-player, doesn’t think about the math right away. Instead, he rapidly starts running through thousands of iterations in his head of how the game might play out. His intuition about odds and seeing the game so many times allows him to determine how often each player will win.&lt;/p&gt;

&lt;h3 id=&quot;the-bet&quot;&gt;The Bet&lt;/h3&gt;

&lt;p&gt;My quick math showed me that the person with all 6 dice would only lose if the opponent had the same number they called, or had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; (wild card). Given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1, 1, 3, 4, 4, 5&lt;/code&gt; and a call of four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s, the underdog only wins when he has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt; or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;, thus allowing him to call five &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s and win. Rolling a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; and another specific roll will happen 1 in 3 times and it needs to happen 6 times in a row in order to lose. In other words, only 1 in 729 times will the underdog win.&lt;/p&gt;

&lt;p&gt;Kurt did some other kind of gambler’s voodoo magic in his head and boldly exclaimed “if you give me 1 dice against your 6 15 times in a row, I bet you I win one.” Me having just calculated the odds with 100% certainty (maybe not…) saw an opportunity to strike. I replied “you’re on” so quickly that Kurt second-guessed himself based on my confidence. I balked a bit at his request for 2:1 odds (he’s already getting odds by running it 15 times) but eventually gave in, thinking I was still a statistical lock.&lt;/p&gt;

&lt;p&gt;6 dice vs 1 die. 15 times in a row.&lt;/p&gt;

&lt;p&gt;Time to play.&lt;/p&gt;

&lt;h3 id=&quot;the-result&quot;&gt;The Result&lt;/h3&gt;

&lt;p&gt;The first few turns went exactly according to plan. I’d call something like three &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;s, Kurt has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;, game over.&lt;/p&gt;

&lt;p&gt;For the most part, the first 10 games went great. Kurt rolled a couple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;s in there but would pretty much lose on the next turn every time. But then, somewhere around the 10th game, something happened.&lt;/p&gt;

&lt;p&gt;I rolled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 1 3 4 4 5&lt;/code&gt; and, per the strategy, called four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s, the most I could safely call. Kurt then comes back with four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;s, having rolled his own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Oh, crap.&lt;/p&gt;

&lt;p&gt;I lost, and he didn’t roll a duplicate or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;. I ended up winning that game anyways, but he did get me down to three dice. I went through the next few games no problem and won the bet. But my interest had been piqued. My assumptions, and thus my odds, weren’t correct. I must find out what they actually are.&lt;/p&gt;

&lt;h3 id=&quot;simulation&quot;&gt;Simulation&lt;/h3&gt;

&lt;p&gt;Now realizing the problem is possibly more complicated than simple statistics can solve, I set out to write a simple simulation script that could run many many trials to get an estimate of the odds. It also seemed to be a good opportunity to test out Jupyter Notebooks, something I had been meaning to do for a while. All of the code and iPython notebooks for this project is &lt;a href=&quot;https://github.com/mattdodge/liars-dice&quot;&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building out the dice randomization, general gameplay flow, and my simple rule-based-no-bluffing strategy wasn’t too tricky. The real variable in this situation is the strategy that Kurt should use.&lt;/p&gt;

&lt;h3 id=&quot;run-1---the-one-upper&quot;&gt;Run 1 - The One-Upper&lt;/h3&gt;
&lt;p&gt;As a sanity check, I wanted to check my original odds and give Kurt a basic strategy. Whatever I bid, Kurt bids one more of that and hopes that he has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; or whatever number I called. Not smart, but a good starting point.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_kurts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&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;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# If Matt has not bid yet start off with 1 of my dice
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&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;# Return one more of what Matt said
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;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;Simulation Complete
-------------------
Num Simulations: 10,000,000
Took:            247.9 seconds
Kurt Wins:       13,742
Win Percent:     0.14%
Wins 1 in 727.70 times
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sure enough, 10 million simulations show that my odds of 1 in 729 were likely right. It also, perhaps more importantly, shows that the code is working. Time to start optimizing.&lt;/p&gt;

&lt;h3 id=&quot;run-2---the-good-guesser&quot;&gt;Run 2 - The Good Guesser&lt;/h3&gt;

&lt;p&gt;Maybe Kurt can do better by trying to trap Matt on the initial bid. Rather than letting Matt drive the action, Kurt can try and guess the number that Matt would have called anyway. An initial guess of three of whatever die Kurt rolls seems like a good guess.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_kurts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&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;my_dice&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;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&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;k&quot;&gt;else&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;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;Simulation Complete
-------------------
Num Simulations: 10,000,000
Took:            243.0 seconds
Kurt Wins:       14,365
Win Percent:     0.14%
Wins 1 in 696.14 times
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not much improvement, dropped to 1 in 696 times for Kurt to win. This actually makes sense, mostly because of gameplay. Once Kurt wins the first turn, Matt gets to go first in subsequent turns. So really this strategy only affects the very first turn of the game.&lt;/p&gt;

&lt;h3 id=&quot;run-3---the-end-gamer&quot;&gt;Run 3 - The End Gamer&lt;/h3&gt;

&lt;p&gt;Since the previous strategy only affected the first turn, let’s see if Kurt can be smarter towards the end of the game, if he can get there.&lt;/p&gt;

&lt;p&gt;When it gets down to Matt having two or less dice Kurt wants to guess two of whatever his dice is.  The hope is Matt rolls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1, 3&lt;/code&gt; and Kurt rolls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;. Matt guesses two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;s, Kurt comes back with two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s and wins.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_kurts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# When Matt has 2 or less dice, guess 2 of whatever I have if I can, otherwise guess 3 of them
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&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;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&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;p&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;n&quot;&gt;my_dice&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&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;my_dice&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;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&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;k&quot;&gt;else&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;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;Simulation Complete
-------------------
Num Simulations: 10,000,000
Took:            243.5 seconds
Kurt Wins:       14,831
Win Percent:     0.15%
Wins 1 in 674.26 times
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A little better, but we’re still not doing that much damage. Kurt is only getting to the final two dice about 1 in 80 times anyway. Any strategy we apply there doesn’t do too much to help overall.&lt;/p&gt;

&lt;h3 id=&quot;run-4---the-finger-crosser&quot;&gt;Run 4 - The Finger Crosser&lt;/h3&gt;

&lt;p&gt;Let’s apply the situation that happened during the real game. As a refresher, Matt rolled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 1 3 4 4 5&lt;/code&gt; and called four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s. Then Kurt then came back with four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;s, having rolled his own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;. So Kurt’s strategy is to guess the same quantity that Matt bids, but of the number he has instead. The hope is that Matt has only a few natural rolls of his number and the rest &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;s.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_kurts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# When Matt has 2 or less dice, guess 2 of whatever I have if I can, otherwise guess 3 of them
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&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;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&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;p&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;n&quot;&gt;my_dice&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&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;my_dice&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;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&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;k&quot;&gt;else&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Might as well try and trap him
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Hoping for [1, 1, 3] against [4]
&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Hope he has all 1s?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;Simulation Complete
-------------------
Num Simulations: 10,000,000
Took:            294.1 seconds
Kurt Wins:       89,389
Win Percent:     0.89%
Wins 1 in 111.87 times
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ooooh, progress. Kurt wins 1 in 112 times now. This situation of having all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;s and one natural actually happens a lot, especially towards the end of the game when there are fewer dice.&lt;/p&gt;

&lt;p&gt;In the last case, if Matt calls three &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;s and I have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;, for example, Kurt would call four &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;s. From what I can tell, this only happens when Matt rolls all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;s, so probably not a whole lot.&lt;/p&gt;

&lt;h3 id=&quot;run-5---the-adjuster&quot;&gt;Run 5 - The Adjuster&lt;/h3&gt;

&lt;p&gt;Upon thinking more about the last strategy, I realize it’s essentially the same as the original end game strategy. Actually, it’s an even better end game strategy! The previous end game strategy of guessing 2 of whatever Kurt has (if possible) doesn’t account for a Matt roll of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3, 4&lt;/code&gt; and a Kurt roll of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;. Matt guesses one 4, Kurt should guess one 5 but would previously guess two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;Let’s apply the general game strategy to the end game also.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_kurts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_dice_matt_has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&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;my_dice&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;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&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;k&quot;&gt;else&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Might as well try and trap him
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matts_bid&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Hoping for [1, 1, 3] against [4]
&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matts_bid&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;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_dice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Hope he has all 1s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;Simulation Complete
-------------------
Num Simulations: 10,000,000
Took:            301.5 seconds
Kurt Wins:       354,633
Win Percent:     3.55%
Wins 1 in 28.20 times
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whaaa!! Kurt wins this way 1 in 28 times!&lt;/p&gt;

&lt;p&gt;It kind of makes sense maybe. Kurt just needs to get through a couple turns by rolling a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; or the same as me and then has to count on me rolling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;s. It definitely happens, and it turns out it happens about 3.5% of the time.&lt;/p&gt;

&lt;h3 id=&quot;outcome&quot;&gt;Outcome&lt;/h3&gt;

&lt;p&gt;So, 15 runs at 2:1 odds are actually looking pretty good right now.&lt;/p&gt;

&lt;p&gt;Even more importantly, it shows that Kurt’s intuition was nearly spot on. This is truly fascinating to me, just by drawing on past experiences and seeing so many dice rolls, Kurt was able to almost exactly predict the odds of this complex scenario.&lt;/p&gt;

&lt;p&gt;At least I still won the bet.&lt;/p&gt;</content><author><name></name></author><summary type="html">Liar’s dice is a great game. Easy to learn, difficult to master. A good balance of luck and strategy. If you don’t know the game, I encourage you to check it out, it’s great for a group.</summary></entry><entry><title type="html">Tweet-2-RSS has been shut down</title><link href="https://mattdodge.codes/tweet-2-rss-has-been-shut-down/" rel="alternate" type="text/html" title="Tweet-2-RSS has been shut down" /><published>2013-10-03T09:56:25+00:00</published><updated>2013-10-03T09:56:25+00:00</updated><id>https://mattdodge.codes/tweet-2-rss-has-been-shut-down</id><content type="html" xml:base="https://mattdodge.codes/tweet-2-rss-has-been-shut-down/">&lt;p&gt;A few months back I created a service that would allow people to get tweets or other content from the Twitter API in RSS or XML format. It ended up growing a moderate amount and amassed over 2,500 users. Well, some frustrating and disappointing news came through yesterday; the Twitter API team has suspended the Tweet-2-RSS application for violation of the Terms of Service. They did so without any warning and it was certainly a surprise to me. Here is the relevant quote from their &lt;a title=&quot;Twitter API TOS&quot; href=&quot;https://dev.twitter.com/terms/api-terms&quot; target=&quot;_blank&quot;&gt;API Terms&lt;/a&gt; (Section I.4):&lt;!--more--&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You will not attempt or encourage others to sell, rent, lease, sublicense, redistribute, or syndicate access to the Twitter API or Twitter Content to any third party without prior written approval from Twitter. If you provide downloadable datasets of Twitter Content or an API that returns Twitter Content, you may only return IDs (including tweet IDs and user IDs).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They consider returning the body of the tweet in any form “re-syndication” of their content. Their “solution” is to return a tweet ID and have the end user make their own API call to obtain the actual tweet. Obviously this is a horrible solution for current users as it would require you to register your own Application and make a custom API call. If you could do that, you wouldn’t be using this service at all.&lt;/p&gt;

&lt;p&gt;There are dozens of uses for getting RSS/XML Twitter data; use in existing RSS readers, XML-only plugins, and more. Unfortunately, it seems that the Twitter API team doesn’t recognize them or care. Their lowly customer service rep who was on my case specifically called it out (and even tried to get me to rat out some similar services):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Services which provide XML/RSS output of Twitter Content are disallowed per our API Terms of Service, unless they only return IDs. If you have encountered other services which engage in this activity please let us know and we will investigate them for compliance with our Rules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I “politely” responded that I would not snitch on fellow developers who are simply trying to make the web a better place and that they could do their own homework to find them.&lt;/p&gt;

&lt;p&gt;To the users of Tweet-2-RSS, I apologize for the sudden suspension. It came as a surprise to me as well. Even though Twitter can’t seem to do some simple Googling, there are other services out there that will accomplish the same thing. However, be warned that they are most likely in violation of the same terms. For now, Tweet-2-RSS will just return some empty XML results so your plugins won’t “break” or throw exceptions. It will just look like there is no content. To those of you who donated and helped out with the hosting costs: I greatly appreciate the support and help. I will be refunding your donations considering the service is no longer operating. Wait for an email from me.&lt;/p&gt;

&lt;p&gt;This isn’t the end of the road though. I understand the need for a service like this and I am currently working with similar RSS services to come up with a workaround. It may take a few weeks but I’m confident that a solution will arise, it may just be a hacky one. If you are interested in being involved let me know via email or a comment below.&lt;/p&gt;</content><author><name></name></author><summary type="html">A few months back I created a service that would allow people to get tweets or other content from the Twitter API in RSS or XML format. It ended up growing a moderate amount and amassed over 2,500 users. Well, some frustrating and disappointing news came through yesterday; the Twitter API team has suspended the Tweet-2-RSS application for violation of the Terms of Service. They did so without any warning and it was certainly a surprise to me. Here is the relevant quote from their API Terms (Section I.4):</summary></entry><entry><title type="html">Realtime Stock Analyst Opinions</title><link href="https://mattdodge.codes/realtime-stock-analyst-opinions/" rel="alternate" type="text/html" title="Realtime Stock Analyst Opinions" /><published>2013-07-24T16:11:33+00:00</published><updated>2013-07-24T16:11:33+00:00</updated><id>https://mattdodge.codes/realtime-stock-analyst-opinions</id><content type="html" xml:base="https://mattdodge.codes/realtime-stock-analyst-opinions/">&lt;p&gt;Two words: WolframAlpha. Or is that one word? I don’t know, but what I do know is that it is a lifesaver for any engineering student these days. Unfortunately for me, it only really existed during my senior year of college, which happened to be the year I got the highest GPA. Coincidence? Probably.&lt;/p&gt;

&lt;p&gt;Until now, I had really only used &lt;a title=&quot;WolframAlpha&quot; href=&quot;http://wolframalpha.com&quot; target=&quot;_blank&quot;&gt;WolframAlpha&lt;/a&gt; for its scientific and mathematical solving abilities. But it can do so much more! Featured in this post will be a snippet of its financial knowledge. Head on over and try &lt;a title=&quot;WolframAlpha AAPL&quot; href=&quot;http://www.wolframalpha.com/input/?i=AAPL&quot; target=&quot;_blank&quot;&gt;searching for your favorite stock symbol&lt;/a&gt;. You will notice a plethora of information from realtime trade data to price projections. You will also notice that they provide a breakdown of analyst opinions on the stock from buy to sell. Wouldn’t it be cool if we could see this breakdown for all stocks in the market and how they compare to one another? I’m glad you asked.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Queue the &lt;a title=&quot;WolframAlpha API&quot; href=&quot;http://products.wolframalpha.com/api/&quot; target=&quot;_blank&quot;&gt;WolframAlpha API&lt;/a&gt;. Thanks to that API, a realtime backend provided by &lt;a title=&quot;Firebase&quot; href=&quot;https://www.firebase.com/&quot; target=&quot;_blank&quot;&gt;Firebase&lt;/a&gt;, and some nifty visuals courtesy of &lt;a title=&quot;HighCharts&quot; href=&quot;http://www.highcharts.com/&quot; target=&quot;_blank&quot;&gt;HighCharts&lt;/a&gt;, I was able to put together a basic visual of the overall opinion of all 500 of the stocks on the S&amp;amp;P 500. &lt;a title=&quot;Stocky Node&quot; href=&quot;http://stockynode.aws.af.cm&quot; target=&quot;_blank&quot;&gt;So go check it out&lt;/a&gt;! More on how it works below, but first a screenshot!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://mattdodge.codes/img/stock_opinions.png&quot; alt=&quot;Stock Analyst Output Screenshot&quot; title=&quot;Stock Analyst Output Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The way it works is rather simple, on my command it takes the current list of the 500 S&amp;amp;P 500 stock companies (&lt;a title=&quot;Wikipedia S&amp;amp;P 500&quot; href=&quot;http://en.wikipedia.org/wiki/List_of_S%26P_500_companies&quot; target=&quot;_blank&quot;&gt;courtesy of Wikipedia&lt;/a&gt;) and one-by-one (in parallel of course) asks WolframAlpha for the analyst opinions of the stock. It collects and parses that information, then pushes it up to a Firebase. Firebase is nice and will keep all of this data for me and even notify existing connections if any of the data has changed. So if you happen to be looking at the chart when the analyst opinions get updated, the chart will get updated too. The whole app is written in Node and hosted on &lt;a title=&quot;AppFog&quot; href=&quot;https://www.appfog.com/&quot; target=&quot;_blank&quot;&gt;AppFog&lt;/a&gt;, another first for me. I have to say, their deployment and management process is really straight-forward.&lt;/p&gt;

&lt;p&gt;It was a fun little project, I got to experiment with AppFog, and I think it’s actually a somewhat useful and meaningful visual. Hope you enjoy! Check it out here: &lt;a title=&quot;Stocky Node&quot; href=&quot;http://stockynode.aws.af.cm&quot; target=&quot;_blank&quot;&gt;http://stockynode.aws.af.cm&lt;/a&gt;; you can leave feedback in the comments below.&lt;/p&gt;</content><author><name></name></author><summary type="html">Two words: WolframAlpha. Or is that one word? I don’t know, but what I do know is that it is a lifesaver for any engineering student these days. Unfortunately for me, it only really existed during my senior year of college, which happened to be the year I got the highest GPA. Coincidence? Probably.</summary></entry><entry><title type="html">24 : A childhood game taken to the next level</title><link href="https://mattdodge.codes/24-a-childhood-game-taken-to-the-next-level/" rel="alternate" type="text/html" title="24 : A childhood game taken to the next level" /><published>2013-07-19T15:27:49+00:00</published><updated>2013-07-19T15:27:49+00:00</updated><id>https://mattdodge.codes/24-a-childhood-game-taken-to-the-next-level</id><content type="html" xml:base="https://mattdodge.codes/24-a-childhood-game-taken-to-the-next-level/">&lt;p&gt;Remember that math game 24 from middle school? It’s the one where you are given 4 numbers and you have to do basic math operations (+,-,*,/) to make the 4 numbers equal 24. For example, if I had the numbers 2,3,4,5 I could get to 24 by doing 2*(5+4+3) or maybe by doing 4*(5+3-2). You get the idea.&lt;/p&gt;

&lt;p&gt;So the game itself is pretty simple and for some reason I find myself playing it with the numbers on a clock or credit card or something else when I am bored. And I was finding that it was normally pretty easy to find a solution given any set of numbers. That made me start to ask a couple of questions.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Exactly how many solutions exist for solving 24, given a set of 4 integers each between 1 and 9?&lt;/li&gt;
  &lt;li&gt;Is there a reason 24 was chosen? Does it have more solutions than another number, say 20?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step was to figure out exactly how many sets of 4 numbers there were. We need unique sets here, the set [1,3,3,7] is fundamentally equivalent to [3,1,3,7] for the purposes of the game. Sadly, this took me longer than it should have, my combinatorics must be rusty. But by using the &lt;a href=&quot;http://en.wikipedia.org/wiki/Multiset_coefficient#Counting_multisets&quot;&gt;Multiset Coefficient&lt;/a&gt; (basically, combinations with replacement) I used the following formula to determine that there are only &lt;strong&gt;495 unique sets&lt;/strong&gt;. Much less than I expected given that there are 6,561 total sets of numbers (9^4).&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;(n+k−1)!
--------
k!(n−1)!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next step is to bust out the IDE and get to coding. Fortunately, this part didn’t take as long as the math (my coding isn’t rusty yet, phew). &lt;a href=&quot;https://gist.github.com/mattdodge/6043083&quot;&gt;Here is a quick little Python 24-solver (really an N-solver…we’ll get there) I threw together&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-python 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;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;solve&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;nums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solveStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'x'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;''' Play 24 with nums 1,3,4,6 like so:  
        &amp;gt;&amp;gt;&amp;gt; solve(24, [1,3,4,6])
        (6/(1-(3/4)))                       '''&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;nb&quot;&gt;float&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;solved&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&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;nums&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;1&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;solveStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'x'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&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;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nums&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;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;possibles&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;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'+'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&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;num&lt;/span&gt;&lt;span class=&quot;o&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;s&quot;&gt;'-'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;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;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'-'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&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;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&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;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;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&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;else&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&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;s&quot;&gt;'*'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&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;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&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;s&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&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;wo&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;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possibles&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;possible&lt;/span&gt;&lt;span class=&quot;p&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;n&quot;&gt;solved&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solved&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;possible&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;wo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solveStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'x'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'('&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&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;possible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;s&quot;&gt;'x)'&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;solved&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solved&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;possible&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;wo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solveStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'x'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'(x'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;possible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&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;s&quot;&gt;')'&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;solved&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So now I’ve got a solver and a list of 495 unique sets, time to find out how many of those actually have a solution for 24. Ready? Drumroll….&lt;/p&gt;

&lt;center&gt;
  &lt;strong&gt;381&lt;/strong&gt; out of 495 have a solution!
&lt;/center&gt;

&lt;p&gt;381?! Wayyyy more than I was guessing. No wonder this game is so easy, pick 4 numbers and you pretty much have a 3 out of 4 shot that there is a solution. So on to part 2, is there possibly an easier or more difficult number to use as a target? I ran it for target solutions from 1 to 100, and the winner (meaning the number with the most solutions) is….more drumroll…&lt;/p&gt;

&lt;center&gt;
  &lt;strong&gt;2&lt;/strong&gt;- with 479 solutions out of 495 possible
&lt;/center&gt;

&lt;p&gt;2 has the most, followed up by the rest of the single-digit numbers interestingly. 24 came in 17th place. 94 and 97 tied for last with 40 solutions each (remember, only checking from 1-100). Here is the &lt;a href=&quot;https://gist.github.com/mattdodge/efea6ca919f6f0522afe023fa80cd31c&quot;&gt;full list as a gist&lt;/a&gt; for your viewing pleasure.&lt;/p&gt;

&lt;p&gt;One more fun thing; there are actually 2 (and only 2!) sets of 4 that can produce solutions for a target of 314. Here they are, can you come up with the formulas to get there?&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;1 5 7 9
5 6 8 8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><summary type="html">Remember that math game 24 from middle school? It’s the one where you are given 4 numbers and you have to do basic math operations (+,-,*,/) to make the 4 numbers equal 24. For example, if I had the numbers 2,3,4,5 I could get to 24 by doing 2*(5+4+3) or maybe by doing 4*(5+3-2). You get the idea.</summary></entry></feed>