<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="/blog/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-02-18T16:13:43+00:00</updated><id>/blog/feed.xml</id><title type="html">Ankit Sinha</title><subtitle>A personal blog site.
</subtitle><author><name>Ankit Sinha</name><email>ankitsinha@myyahoo.com</email></author><entry><title type="html">MENACE Part 2 - Generating all board states and optimizing by removing invalid and symmetric states</title><link href="/blog/2026-02-18-menace-part-2/" rel="alternate" type="text/html" title="MENACE Part 2 - Generating all board states and optimizing by removing invalid and symmetric states" /><published>2026-02-18T00:00:00+00:00</published><updated>2026-02-18T16:12:35+00:00</updated><id>/blog/menace-part-2</id><content type="html" xml:base="/blog/2026-02-18-menace-part-2/"><![CDATA[<p>Part 1 - <a href="/2026-02-13-menace-part-1/">Introduction to MENACE and setting up the project</a><br /></p>

<p>We need to generate all possible board states for tic-tac-toe and then remove the invalid and symmetric states to optimize our AI’s learning process. These will act as labels on the matchboxes so we can change the number of beads (the moves we play next time on the current state) based on the result of the game.</p>

<p>This is the general plan for the whole project:</p>
<ol>
  <li>We create an AI class for all the AI logic.</li>
  <li>Save every move played in a game (to reward/punish later) using lists.</li>
  <li>Create all possible board states and initialize matchboxes for them</li>
  <li>After each game, reward/punish the moves played based on win/draw/loss</li>
  <li>Integrate the AI to play against human</li>
  <li>Make the AI play against itself to train</li>
</ol>

<p>And somewhere in the middle we need a system to save whatever it learnt and load back on startup; also need to optimize the matchbox storage so that we dont store symmetric board states separately (since they are equivalent).</p>

<p>Let us start with item 3.</p>

<h2 id="generating-all-possible-board-states">Generating all possible board states</h2>
<p>I will show 3 methods</p>

<p>Need to create all possible combinations of X, O, and empty spaces in a 3x3 grid = (3 possibilities)^(9 positions) = 19683 states</p>

<h3 id="method-1-iterative-approach-using-loops">Method 1: Iterative approach using loops</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">generate_all_boards</span><span class="p">():</span>
    <span class="n">boards</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="o">**</span><span class="mi">9</span><span class="p">):</span> <span class="c1"># total 3^9 combinations
</span>        <span class="c1"># you remember how you did this modulus and floor division thingy to convert decimal to other bases? This is the same, we are converting i to base 3 and mapping 0-&gt;X, 1-&gt;O, 2-&gt;" "
</span>        <span class="n">board</span> <span class="o">=</span> <span class="sh">""</span>
        <span class="n">num</span> <span class="o">=</span> <span class="n">i</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">):</span>
            <span class="n">rem</span> <span class="o">=</span> <span class="n">num</span> <span class="o">%</span> <span class="mi">3</span>
            <span class="k">if</span> <span class="n">rem</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">board</span> <span class="o">+=</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span>
            <span class="k">elif</span> <span class="n">rem</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
                <span class="n">board</span> <span class="o">+=</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">board</span> <span class="o">+=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span>
            <span class="n">num</span> <span class="o">//=</span> <span class="mi">3</span>
        <span class="n">boards</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">boards</span>
</code></pre></div></div>
<h3 id="method-2-recursive-approach">Method 2: Recursive approach</h3>
<p>Here we will use recursion to fill the board position by position. At each position we will try all 3 possibilities and then call the function recursively for the next position.<br /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">fill_board</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">boards</span><span class="p">):</span>
    <span class="n">board</span> <span class="o">=</span> <span class="n">board</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">index</span> <span class="o">==</span> <span class="mi">9</span><span class="p">:</span> <span class="c1"># index 9 is out of bounds. we had 0-8 index. So we have permuted all positions
</span>        <span class="k">return</span>
    <span class="k">for</span> <span class="n">symbol</span> <span class="ow">in</span> <span class="p">[</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">]:</span>
        <span class="n">board</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">symbol</span> <span class="c1"># change current index to symbol (create a permutation)
</span>        <span class="n">boards</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="sh">""</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">board</span><span class="p">))</span> <span class="c1"># make it into string and add to boards permutation lists
</span>        <span class="nf">fill_board</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">boards</span><span class="p">)</span> <span class="c1"># now we permute at the next index
</span>
<span class="k">def</span> <span class="nf">generate_all_boards</span><span class="p">():</span>
    <span class="n">boards</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">board</span> <span class="o">=</span> <span class="p">[</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">]</span> <span class="o">*</span> <span class="mi">9</span> <span class="c1"># we init with empty, then at index we permute the possibilities
</span>    <span class="nf">fill_board</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">boards</span><span class="p">)</span> <span class="c1"># permute at index 0 initially
</span>    <span class="k">return</span> <span class="n">boards</span>
</code></pre></div></div>

<p>We start with index 0, try X, O, “ “ at index 0 one by one. For each of these, we call the function recursively for index 1.<br />
At index 1, we again try X, O, “ “ one by one. For each of these, we call the function recursively for index 2.<br />
BUT it doesnt work the same way as it looks.</p>

<p>This is how all the permutations are generated:<br />
<code class="language-plaintext highlighter-rouge">"        "</code> -&gt; <code class="language-plaintext highlighter-rouge">"X       "</code> -&gt; <code class="language-plaintext highlighter-rouge">"XX      "</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXX     "</code> -&gt; … -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXXX"</code> -&gt; (then here index 9 so stop)<br />
<code class="language-plaintext highlighter-rouge">"XXXXXXXXX"</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXXO"</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXX "</code> -&gt; (then here index 9 so stop)<br />
<code class="language-plaintext highlighter-rouge">"XXXXXXXX "</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXO "</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXOX"</code>” -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXOO"</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXXO "</code> -&gt; (then here index 9 so stop)<br />
<code class="language-plaintext highlighter-rouge">"XXXXXXXO "</code> -&gt; <code class="language-plaintext highlighter-rouge">"XXXXXXX  "</code> -&gt; … and so on<br /></p>

<p>It’s fine if you weren’t able to follow. Just look at the following simplified animation: (simplification = 2 positions instead of 9, and I’ve marked blank as <code class="language-plaintext highlighter-rouge">B</code> for better visibility)<br />
<img src="https://raw.githubusercontent.com/Quantum-Codes/MENACE/refs/heads/main/images/generator_animation.gif" alt="Animation of a recursion tree" loop="infinite" /><br />
These are the generated states (return value of the leaves) 
<img src="http://raw.githubusercontent.com/Quantum-Codes/MENACE/refs/heads/main/images/recursion-tree-return.png" alt="Generateed states from recursion" /><br /></p>

<h3 id="method-3-iterative-using-recursive-relationships">Method 3: Iterative using recursive relationships</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Recurrence relation: (T = string of gamestates, n = number of moves made) 
Base case: T(0) = "" 
T(n) = T(n-1) + " " 
     = T(n-1) + "X" 
     = T(n-1) + "O" 
We end at n = 9. So loop with 9 iterations
</code></pre></div></div>
<p>Code: <br />
Technically this is recursion too, just another version that uses loops instead of function calls. Also more memory efficient than method 2. (i like this one)<br /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">generator</span><span class="p">(</span><span class="n">possibilities</span><span class="p">):</span>
    <span class="n">states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">possibility</span> <span class="ow">in</span> <span class="n">possibilities</span><span class="p">:</span>
        <span class="n">states</span><span class="p">.</span><span class="nf">extend</span><span class="p">([</span><span class="n">possibility</span> <span class="o">+</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="n">possibility</span> <span class="o">+</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">,</span> <span class="n">possibility</span> <span class="o">+</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">])</span> <span class="c1"># for each existing state, just add 3 possibilities of the new next character. 
</span>    <span class="k">return</span> <span class="n">states</span> 

<span class="k">def</span> <span class="nf">generate_all_states</span><span class="p">():</span>
    <span class="n">all_states</span> <span class="o">=</span> <span class="p">[</span><span class="sh">""</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">):</span>
        <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generator</span><span class="p">(</span><span class="n">all_states</span><span class="p">)</span> <span class="c1"># run 9 times to add 9 characters
</span>    <span class="k">return</span> <span class="n">all_states</span>
</code></pre></div></div>
<p><br /></p>

<h2 id="removing-invalid-and-symmetric-states">Removing invalid and symmetric states</h2>
<p>Note that this is simply an optimization step to reduce the number of matchboxes we need to store and to make the AI learn a lot quicker. We can skip this step and just have MENACE learn all states but that would be inefficient. MENACE will anyway skip invalid boards since it will never encounter them.<br />
You can skip this section and still have the AI work, just slower.<br /></p>

<p>Note that doing anything with these roations is a bit tricky so take your time to understand it.<br /></p>

<h3 id="invalid-states">Invalid states</h3>
<p>Out of these 3^9 states, there are many that we will never encounter. The following are invalid states:</p>

<ol>
  <li>States where the difference between number of X’s and O’s is more than 1 (since players alternate turns)</li>
  <li>States where O has more X’s than O’s (since X always starts first)</li>
  <li>States where both X and O have winning lines (impossible in a real game)</li>
  <li>Already won. Nothing to learn in here and game stops at this.</li>
</ol>

<p>Here are some examples of invalid states:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X | X | X      O | X | X
---------      ---------
  | O |          | O |  
---------      ---------
  |   |          |   | O
</code></pre></div></div>

<p>Condition 1 and 2 can be checked together by the condition: <br /><code class="language-plaintext highlighter-rouge">if ((count_X - count_O) &gt; 1 OR (count_O &gt; count_X)) then reject</code><br />
Condition 3 can be checked by using the <code class="language-plaintext highlighter-rouge">check_winner</code> function modified to count how many conditions within it are met. Note that 2 of X or O winning lines are possible.<br />
BUT 2 of X parallel winning lines are impossible since the game would have ended on the first winning line. AND if you do that even then you have 6X and at max 3Os which anywaty violates condition 1.<br />
But if we simlpy implement condition 4 then we never had to think all this, nothing to learn in already won states. No double win needed to be considered.<br /></p>

<p>Code for 1 and 2:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">filter_game_states</span><span class="p">():</span>
    <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generate_all_states</span><span class="p">()</span>
    <span class="c1">#remove all where (number of X) - (number of O) &gt; 1 since we can only have alternating moves and X always starts
</span>    <span class="c1"># we can just make a new list that only has valid states
</span>    <span class="n">new_all_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="k">if</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">))</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">new_all_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
</code></pre></div></div>

<p>Code for 3 and 4:<br />
I will just add this to check if any of the state is a winner</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="k">if</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
            <span class="k">continue</span>
</code></pre></div></div>
<p>Like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">filter_game_states</span><span class="p">():</span>
    <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generate_all_states</span><span class="p">()</span>
    <span class="c1">#remove all where (number of X) - (number of O) &gt; 1 since we can only have alternating moves and X always starts
</span>    <span class="c1"># we can just make a new list that only has valid states
</span>    <span class="n">new_all_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="k">if</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">))</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">new_all_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>

    <span class="n">all_states</span> <span class="o">=</span> <span class="n">new_all_states</span>
    <span class="n">unique_states</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># we add all non winning valid states here
</span>    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="k">if</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
            <span class="k">continue</span> <span class="c1"># skip already won states
</span>        <span class="n">unique_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="c1"># add if not already won
</span>    
    <span class="k">return</span> <span class="n">unique_states</span>
</code></pre></div></div>

<h3 id="why-remove-symmetric-states">Why remove symmetric states?</h3>
<p>Many board states are symmetric to one another (rotations and mirror images). We can remove these symmetric states by picking a state one at a time, generating all its rotations and mirrored versions, and then checking if any of those already exist in new_states. IF not we add them.<br /></p>

<p>but why even care?<br />
Would you agree all these states are equivalent?<br /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X | O | O      X |   | X      O | O | X
---------      ---------      ---------
  | O |        X | O | O        | O |
---------      ---------      ---------
X | X |          |   | O        | X | X
</code></pre></div></div>
<p>2nd one is a clockwise 90 deg rotation of 1st one<br />
3rd one is a mirror image of 1st one along y axis<br />
But in all of them, player X just need to play the same move (same to us humans - complete the X line).<br />
Wouldn’t you be mad if the AI wins in one of them but then plays dumb in the other? I would (especially after all this coding).<br />
So just training the AI on one of these should teach it how to handle ALL of them.<br />
This is why we remove equivalent symmetric states. And later when we find these during a game, we convert these states by rotations or reflections to match one of these for picking the move and later training.<br /></p>

<h3 id="removing-symmetric-states">Removing symmetric states</h3>
<p>We first need to find out a way to rotate a board and mirror it too.<br />
Let us see how the rotated board looks like:<br /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Original indexes: 0 1 2
                  3 4 5
                  6 7 8

Rotated indexes:  6 3 0
(Clockwise)       7 4 1
                  8 5 2

Mirror along y-axis indexes:
                2 1 0
                5 4 3
                8 7 6

Mirror along x-axis indexes:
                6 7 8
                3 4 5
                0 1 2
</code></pre></div></div>
<p>We can have mathematical fomulaes here too, like you can just transpose+reverse every row to rotate 90deg anticlockwise but that is more complex to code and understand. (especially combining the fact that we are not working with a 2D array but a 1D string so we need to use that <code class="language-plaintext highlighter-rouge">index = f(i,j)</code> formula earlier)<br /></p>

<p>The simplest way of rotating i can see is to create an index map for each transformation and then use that to create the new string.<br />
What do i mean by that? look at this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sample</span> <span class="o">=</span> <span class="sh">"</span><span class="s">ABCDE</span><span class="sh">"</span> <span class="c1"># original index: 01234
</span><span class="n">index_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># this is reversed original index
</span><span class="n">reversed_string</span> <span class="o">=</span> <span class="sh">''</span>
<span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">:</span>
    <span class="n">reversed_string</span> <span class="o">+=</span> <span class="n">sample</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> 
    <span class="c1"># we are getting indexes from index_mapm getting the corresponding character from sample string and adding to new string
</span><span class="nf">print</span><span class="p">(</span><span class="n">reversed_string</span><span class="p">)</span> <span class="c1"># prints EDCBA
</span>
<span class="c1"># another example, with the compressed for loop and a &lt;str&gt;.join(&lt;iterable&gt;) method
</span><span class="n">index_map2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span> <span class="c1"># interchange first 2 characters
</span><span class="n">swapped_string</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">sample</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map2</span><span class="p">])</span>
<span class="nf">print</span><span class="p">(</span><span class="n">swapped_string</span><span class="p">)</span> <span class="c1"># prints BACDE
</span></code></pre></div></div>
<p>We can use the same for rotations. We already have the index maps from the rotated board I showed above.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">original_board</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span>
<span class="n">rotated_board</span> <span class="o">=</span>  <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
<span class="n">x_mirrored_board</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span>
<span class="n">y_mirrored_board</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">6</span><span class="p">]</span>
</code></pre></div></div>
<p>We can now use these index maps to generate all similar states (rotations and mirror images) of a given board state.<br />
Why didnt i make maps for 180 and 270 deg rotations? Because we can just keep rotating the 90 deg rotated board again and again to get those.<br /></p>

<p>Rotation of board again and again:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">original_board</span> <span class="o">=</span> <span class="sh">"</span><span class="s">XOX OX   </span><span class="sh">"</span>
<span class="n">to_rotate</span> <span class="o">=</span> <span class="n">original_board</span> <span class="c1"># just renaming. this does not make a copy and it will change original_board too. (remember pointers from C? both of these vars point to the same list in memory)
</span><span class="nf">print</span><span class="p">(</span><span class="n">to_rotate</span><span class="p">)</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
    <span class="n">to_rotate</span> <span class="o">=</span> <span class="p">[</span><span class="n">to_rotate</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">rotated_board</span><span class="p">]</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">to_rotate</span><span class="p">)</span>
</code></pre></div></div>
<p>This prints all 4 rotations of the original board.<br />
We similarly can do for mirrored boards too.<br /></p>

<p>Let us implement this. For every state, we generate all its similar states and check if any of those already exist in unique_states. If not we add them.<br /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">filter_game_states</span><span class="p">():</span>
    <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generate_all_states</span><span class="p">()</span>

    <span class="c1"># ---
</span>    <span class="n">new_all_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="k">if</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">new_all_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>

    <span class="n">all_states</span> <span class="o">=</span> <span class="n">new_all_states</span>
    <span class="c1"># ---
</span>
    <span class="c1"># By the way, all the code in the above block wrapped by these "---" can be written in one line as:
</span>    <span class="c1"># all_states = [state for state in all_states if 0 &lt;= state.count("X") - state.count("O") &lt;= 1]
</span>    <span class="c1"># I wont actually use that in the explanation further so you can ignore this fact if too complex
</span>
    <span class="c1">#remove rotations and mirrored states. 
</span>    <span class="c1"># For this we pick a state one at a time, generate all its rotations and mirrored versions, and then check if any of those already exist in new_states. IF not we add them.
</span>    <span class="n">unique_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">index_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># 90 deg rotated indexes. we can repeat this rotation again and again for every rotation.
</span>    <span class="n">mirror_y_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">6</span><span class="p">]</span> <span class="c1"># mirrored indexes along y axis
</span>    <span class="n">mirror_x_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># mirrored indexes along x axis
</span>    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="c1"># If already won, skip. Dont waste processing time.
</span>        <span class="k">if</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
            <span class="k">continue</span>
        
        <span class="c1"># make mirror images
</span>        <span class="n">y_mirror</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">state</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">mirror_y_map</span><span class="p">])</span>
        <span class="n">x_mirror</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">state</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">mirror_x_map</span><span class="p">])</span>
        <span class="n">similar_states</span> <span class="o">=</span> <span class="p">[</span><span class="n">state</span><span class="p">,</span> <span class="n">x_mirror</span><span class="p">,</span> <span class="n">y_mirror</span><span class="p">]</span>

        <span class="c1"># generate rotations (90 deg at a time)
</span>        <span class="n">to_rotate</span> <span class="o">=</span> <span class="n">state</span>
        <span class="n">to_rotate_y</span> <span class="o">=</span> <span class="n">y_mirror</span>
        <span class="n">to_rotate_x</span> <span class="o">=</span> <span class="n">x_mirror</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
            <span class="n">to_rotate</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">])</span>
            <span class="n">to_rotate_y</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate_y</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">])</span>
            <span class="n">to_rotate_x</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate_x</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">])</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate</span><span class="p">)</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate_y</span><span class="p">)</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate_x</span><span class="p">)</span>

        <span class="c1"># check if any of the similar states already exist in unique_states
</span>        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">similar_states</span><span class="p">:</span>  
            <span class="k">if</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">unique_states</span><span class="p">:</span>
                <span class="k">break</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">unique_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="c1">#if none matches then for loop is not broken and the state is added (for-else loop)
</span>    
    <span class="k">return</span> <span class="n">unique_states</span>
</code></pre></div></div>

<h2 id="how-do-we-know-this-is-right">How do we know this is right?</h2>
<p>We do know that the original guy got 304 matchboxes after all this. Let us try to get that number after applying his extra conditions. Conditions he had that we did not implement:</p>

<ol>
  <li>Only X plays. (we added both X and O)</li>
  <li>Ignore states where we only have 1 empty cell left (since game would end before that) [we didnt do this because we would anyway have only 1 bead in there so it wouldnt matter at all]</li>
</ol>

<p>For 1, we say that after O plays, there is always equal number of Xs and Os.<br />
For 2, we can make sure that number of <code class="language-plaintext highlighter-rouge">" "</code> (blanks) are more than 1.<br /></p>

<p>We change</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="mi">1</span> <span class="o">&gt;=</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">:</span>
</code></pre></div></div>
<p>to</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">if </span><span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
</code></pre></div></div>

<details>
  <summary>Full code for filtering invalid states with these 2 extra conditions (i will run this, you can run this too in a whole new file)</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">generator</span><span class="p">(</span><span class="n">possibilities</span><span class="p">):</span>
    <span class="n">states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">possibility</span> <span class="ow">in</span> <span class="n">possibilities</span><span class="p">:</span>
        <span class="n">states</span><span class="p">.</span><span class="nf">extend</span><span class="p">([</span><span class="n">possibility</span> <span class="o">+</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="n">possibility</span> <span class="o">+</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">,</span> <span class="n">possibility</span> <span class="o">+</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">])</span>
    <span class="k">return</span> <span class="n">states</span> 

<span class="k">def</span> <span class="nf">generate_all_states</span><span class="p">():</span>
    <span class="c1"># all game states in string format
</span>    
    <span class="c1"># Recurrence relation: (T = string of gamestates, n = number of moves made)
</span>    <span class="c1"># Base case: T(0) = ""
</span>    <span class="c1"># T(n) = T(n-1) + " "
</span>    <span class="c1">#      = T(n-1) + "X"
</span>    <span class="c1">#      = T(n-1) + "O"
</span>    <span class="c1"># We end at n = 9. So loop with 9 iterations
</span>    <span class="c1"># this will give us all possible combinations of X, O, and empty spaces in a 3x3 grid = (3 possibilities)^(9 positions) = 19683 states
</span>    <span class="n">all_states</span> <span class="o">=</span> <span class="p">[</span><span class="sh">""</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">):</span>
        <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generator</span><span class="p">(</span><span class="n">all_states</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">all_states</span>

<span class="k">def</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="c1"># Check rows and columns
</span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        
    <span class="c1"># Check diagonals
</span>    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
    <span class="k">return</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">filter_game_states</span><span class="p">():</span>
    <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generate_all_states</span><span class="p">()</span>

    <span class="n">new_all_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="nf">if </span><span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
            <span class="n">new_all_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>

    <span class="n">all_states</span> <span class="o">=</span> <span class="n">new_all_states</span>
    
    <span class="c1">#remove rotations and mirrored states. 
</span>    <span class="c1"># For this we pick a state one at a time, generate all its rotations and mirrored versions, and then check if any of those already exist in new_states. IF not we add them.
</span>    <span class="n">unique_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">index_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># 90 deg rotated indexes. we can repeat this rotation again and again for every rotation.
</span>    <span class="n">mirror_y_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">6</span><span class="p">]</span> <span class="c1"># mirrored indexes along y axis
</span>    <span class="n">mirror_x_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># mirrored indexes along x axis
</span>
    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="c1"># If already won, skip. Dont waste processing time.
</span>        <span class="k">if</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
            <span class="k">continue</span>
        
        <span class="c1"># make mirror images
</span>        <span class="n">y_mirror</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">state</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">mirror_y_map</span><span class="p">])</span>
        <span class="n">x_mirror</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">state</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">mirror_x_map</span><span class="p">])</span>
        <span class="n">similar_states</span> <span class="o">=</span> <span class="p">[</span><span class="n">state</span><span class="p">,</span> <span class="n">x_mirror</span><span class="p">,</span> <span class="n">y_mirror</span><span class="p">]</span>

        <span class="c1"># generate rotations (90 deg at a time)
</span>        <span class="n">to_rotate</span> <span class="o">=</span> <span class="n">state</span>
        <span class="n">to_rotate_y</span> <span class="o">=</span> <span class="n">y_mirror</span>
        <span class="n">to_rotate_x</span> <span class="o">=</span> <span class="n">x_mirror</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
            <span class="n">to_rotate</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">])</span>
            <span class="n">to_rotate_y</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate_y</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">])</span>
            <span class="n">to_rotate_x</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate_x</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">index_map</span><span class="p">])</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate</span><span class="p">)</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate_y</span><span class="p">)</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate_x</span><span class="p">)</span>

        <span class="c1"># check if any of the similar states already exist in unique_states
</span>        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">similar_states</span><span class="p">:</span>  
            <span class="k">if</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">unique_states</span><span class="p">:</span>
                <span class="k">break</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">unique_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="c1">#if none matches then for loop is not broken and the state is added (for-else loop)
</span>    
    <span class="k">return</span> <span class="n">unique_states</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="nf">filter_game_states</span><span class="p">()))</span> 
</code></pre></div>  </div>
</details>
<p><br />
AND… The output:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>menace-py3.12<span class="o">)</span> ankit@LAPTOP-T90NGO2F:~/python/MENACE<span class="nv">$ </span>python3 menace.py 
304
</code></pre></div></div>
<p>Yay!<br />
And actually adding condition 2 is pretty nice; i couldnt think about it before i went around searching why my count was <code class="language-plaintext highlighter-rouge">338</code> prior to this, so i will keep the condition 2, the blanks &gt; 1, (see the filtering code below to see) but then reintroduce both X and Os<br /></p>

<details>
  <summary>Here is my current code for filtering invalid states</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">filter_game_states</span><span class="p">():</span>
    <span class="n">all_states</span> <span class="o">=</span> <span class="nf">generate_all_states</span><span class="p">()</span>
    <span class="c1">#remove all where (number of X) - (number of O) &gt; 1 since we can only have alternating moves and X always starts
</span>    <span class="c1"># same as generating a new list with only valid states
</span>    <span class="n">all_states</span> <span class="o">=</span> <span class="p">[</span><span class="n">state</span> <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span> <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">X</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">state</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">]</span>

    <span class="c1">#remove rotations and mirrored states. 
</span>    <span class="c1"># For this we pick a state one at a time, generate all its rotations and mirrored versions, and then check if any of those already exist in new_states. IF not we add them.
</span>    <span class="n">unique_states</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">index_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># 90 deg rotated indexes. we can repeat this rotation again and again for every rotation.
</span>    <span class="n">mirror_y_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">6</span><span class="p">]</span> <span class="c1"># mirrored indexes along y axis
</span>    <span class="n">mirror_x_map</span> <span class="o">=</span> <span class="p">[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># mirrored indexes along x axis
</span>
    <span class="k">for</span> <span class="n">state</span> <span class="ow">in</span> <span class="n">all_states</span><span class="p">:</span>
        <span class="c1"># If already won, skip. Dont waste processing time.
</span>        <span class="k">if</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">state</span><span class="p">):</span>
            <span class="k">continue</span>
        
        <span class="c1"># make mirror images
</span>        <span class="n">y_mirror</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">state</span><span class="p">[</span><span class="n">mirror_y_map</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">)])</span>
        <span class="n">x_mirror</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">state</span><span class="p">[</span><span class="n">mirror_x_map</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">)])</span>
        <span class="n">similar_states</span> <span class="o">=</span> <span class="p">[</span><span class="n">state</span><span class="p">,</span> <span class="n">x_mirror</span><span class="p">,</span> <span class="n">y_mirror</span><span class="p">]</span>

        <span class="c1"># generate rotations (90 deg at a time)
</span>        <span class="n">to_rotate</span> <span class="o">=</span> <span class="n">state</span>
        <span class="n">to_rotate_y</span> <span class="o">=</span> <span class="n">y_mirror</span>
        <span class="n">to_rotate_x</span> <span class="o">=</span> <span class="n">x_mirror</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
            <span class="n">to_rotate</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate</span><span class="p">[</span><span class="n">index_map</span><span class="p">[</span><span class="n">j</span><span class="p">]]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">)])</span>
            <span class="n">to_rotate_y</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate_y</span><span class="p">[</span><span class="n">index_map</span><span class="p">[</span><span class="n">j</span><span class="p">]]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">)])</span>
            <span class="n">to_rotate_x</span> <span class="o">=</span> <span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">([</span><span class="n">to_rotate_x</span><span class="p">[</span><span class="n">index_map</span><span class="p">[</span><span class="n">j</span><span class="p">]]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">9</span><span class="p">)])</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate</span><span class="p">)</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate_y</span><span class="p">)</span>
            <span class="n">similar_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">to_rotate_x</span><span class="p">)</span>

        <span class="c1"># check if any of the similar states already exist in unique_states
</span>        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">similar_states</span><span class="p">:</span>  
            <span class="k">if</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">unique_states</span><span class="p">:</span>
                <span class="k">break</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">unique_states</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="c1">#if none matches then for loop is not broken and the state is added (for-else loop)
</span>    
    <span class="k">return</span> <span class="n">unique_states</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="nf">filter_game_states</span><span class="p">()))</span> 
</code></pre></div>  </div>

</details>
<p><br /></p>

<p>Now we need to map it to matchboxes and initialize beads in them.<br />
We can use a dictionary for this purpose. Also accessing a dictionary item is O(1) on average due to hashmaps (meaning fast regardless of number of items stored) unlike lists where searching is O(n) (meaning time taken increases linearly with number of items stored).<br />
Also rehashing cost does not negate the O(1) since we max only add approx 600 items and we access its items more than that.<br />
If we want to exploit this speed in the filtering thing too then we could’ve made a dict rather than a list for unique_states and then just do <code class="language-plaintext highlighter-rouge">if item in unique_states_dict:</code>and also we would already have a dict so no need to convert to dict later; but since this is a one time operation and we have only 304 items, it doesnt matter much. I would do it but imagine spending a minute on this optimisation that saves a little time once a while<br /></p>

<p>Actually let me do that. If we time our current code with:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="kn">import</span> <span class="n">time</span>
    <span class="n">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">time</span><span class="p">()</span>
    <span class="nf">print</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="nf">filter_game_states</span><span class="p">()))</span> 
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Time taken: </span><span class="si">{</span><span class="n">time</span><span class="p">.</span><span class="nf">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span><span class="si">}</span><span class="s"> seconds</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p>We get this time:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>menace-py3.12<span class="o">)</span> ankit@LAPTOP-T90NGO2F:~/python/MENACE<span class="nv">$ </span>python3 menace.py 
593
Time taken: 0.3060119152069092 seconds
</code></pre></div></div>

<p>AND.. if we change the definition of <code class="language-plaintext highlighter-rouge">unique_states = []</code> to <code class="language-plaintext highlighter-rouge">unique_states = {}</code> and then change the code to add items to dict rather than list like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">similar_states</span><span class="p">:</span>  
            <span class="k">if</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">unique_states</span><span class="p">:</span>
                <span class="k">break</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">unique_states</span><span class="p">[</span><span class="n">state</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span> <span class="c1">#if none matches then for loop is not broken and the state is added (for-else loop)
</span></code></pre></div></div>
<p>Then we get this time:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>menace-py3.12<span class="o">)</span> ankit@LAPTOP-T90NGO2F:~/python/MENACE<span class="nv">$ </span>python3 menace.py 
593
Time taken: 0.06501245498657227 seconds
</code></pre></div></div>
<p>NICE!<br /> I wonder what happens when we use sets for both <code class="language-plaintext highlighter-rouge">unique_states</code> and <code class="language-plaintext highlighter-rouge">similar_states</code> since we dont care about order and we just want to check if any of the similar states are in unique states. I will leave that as an exercise for you. (hint: you can use set intersection to check if any of the similar states are in unique states and get rid of one of the for loops.)<br />
Answer: You do get another speedup! Time now was <code class="language-plaintext highlighter-rouge">0.015 seconds</code> for one of the runs.<br />
You can message me if you need the code and cant change yourself</p>

<p>I will keep the dictionary version since it is convenient for the next steps.</p>

<h2 id="hotfix">Hotfix:</h2>
<p>Forgot to add detection for draw games. Modify check_winner() function to return “Draw” if there are no blank spaces left and no winner. And then in the main loop, we check for draw too and break the loop if it is a draw.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="c1"># Check rows and columns
</span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        
    <span class="c1"># Check diagonals
</span>    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
    
    <span class="k">if</span> <span class="n">board</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="sh">"</span><span class="s">draw</span><span class="sh">"</span>
    
    <span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>

<p>Main loop:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="c1">#game always starts with X
</span>
    <span class="n">ai</span> <span class="o">=</span> <span class="nc">AI</span><span class="p">()</span>
    <span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span>
    <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># 1 = X, 0 = O
</span> 
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">result</span> <span class="o">=</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">result</span><span class="p">:</span>
            <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">result</span> <span class="o">==</span> <span class="sh">"</span><span class="s">draw</span><span class="sh">"</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">draw!</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">result</span><span class="si">}</span><span class="s"> wins!</span><span class="sh">"</span><span class="p">)</span>

            <span class="k">break</span>

        <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">player</span> <span class="c1"># Switch player
</span></code></pre></div></div>]]></content><author><name>Ankit Sinha</name><email>ankitsinha@myyahoo.com</email></author><summary type="html"><![CDATA[Programming MENACE (A Reinforcement Learning AI) that learns to play tic-tac-toe using matchboxes and beads. Generation of states and optimization by removing invalid and symmetric states.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://i0.wp.com/cdn.makezine.com/uploads/2009/11/menace_tic_tac_toe.jpg?resize=600%2C450&amp;ssl=1" /><media:content medium="image" url="https://i0.wp.com/cdn.makezine.com/uploads/2009/11/menace_tic_tac_toe.jpg?resize=600%2C450&amp;ssl=1" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">MENACE Part 1: An Introduction to Reinforcement Learning with Matchboxes and Beads</title><link href="/blog/2026-02-13-menace-part-1/" rel="alternate" type="text/html" title="MENACE Part 1: An Introduction to Reinforcement Learning with Matchboxes and Beads" /><published>2026-02-13T00:00:00+00:00</published><updated>2026-02-18T16:12:35+00:00</updated><id>/blog/menace-part-1</id><content type="html" xml:base="/blog/2026-02-13-menace-part-1/"><![CDATA[<p>In this series of posts, we will recreate the worlds first reinforcement learning through a hands-on project: programming MENACE (Matchbox Educable Noughts and Crosses Engine), a simple AI that learns to play tic-tac-toe using matchboxes and beads.</p>

<p>Also in this i will show some basic python too for the 1st year students. Others can just skip those parts.<br />
To the first years, there are some references to higher level topics too in the later parts (not this one), don’t get demotivated if you don’t get it, it doesn’t matter too much to code this up without knowing them fully. Send me doubts if needed.</p>

<p>Created by:</p>
<ul>
  <li><a href="https://github.com/Quantum-Codes">@Quantum-Codes</a> (Ankit Sinha, IIT Tirupati)</li>
  <li><a href="https://github.com/AnmolSinha42">@AnmolSinha42</a> (Anmol Sinha, NIT Puducherry)</li>
</ul>

<h2 id="behaviour">Behaviour</h2>
<p>MENACE doesn’t know the rules of Tic-Tac-Toe. It doesn’t know that three-in-a-row wins. It only picks out of a learned whitelist moves from each game board possibility.
We need to teach it this whitelist (The ideal moves on every game board possible)</p>

<p>This is done by adding beads to a matchbox as it was done originally, and then pick a random bead corresponding to the move it needs to play (so there will be 9 coloured beads)
Each matchbox in the system represents a unique state of the Tic-Tac-Toe board.
The Memory: Inside each box are beads of different colours. Each colour corresponds to a potential move (an empty square) on the grid. There can be multiple beads of same colour indicating this move has been responsible for a lot of wins and the random picking will most likely pick that colour again.</p>

<ul>
  <li>
    <p><strong>On a Win</strong>: MENACE is rewarded. Each matchbox used in that game receives 3 additional beads of the colour that was played. This makes it much more likely to repeat those winning moves in the future.</p>
  </li>
  <li>
    <p><strong>On a Draw</strong>: It receives a small reward of 1 additional bead.</p>
  </li>
  <li>
    <p><strong>On a Loss</strong>: It is punished. The beads used in that game are removed from their boxes. This decreases the chance of the machine making that losing mistake again.</p>
  </li>
</ul>

<p>There is a possibility that a box empties itself in the process - this means that the game state always leads to a loss and there is no way to recover OR it was in early game and the bot had too much of a skill issue to win (and so we might either have to restart training or make sure each early game state always has 1 of each bead, we will decide which to choose later)</p>

<p>Also typically we always let the bot start so it only learns X’s moves or Y’s moves (these are disjoint to one another). I would want them to play against each other and improve each other, and due to this disjoint property I can just use the same set of matchboxes and in the end have a singular model that can play both X and O.</p>

<p>If you did not understand fully then watch this <a href="https://www.youtube.com/watch?v=R9c-_neaxeU">video</a> by Stand-Up Maths youtube channel.
<br /></p>

<h1 id="creating-the-tictactoe-game-itself">Creating the tictactoe game itself</h1>
<p>We need to create a standard tictactoe game first and then put this system on it. In this part, we will just be making the 2 player game, and in the next parts we will add the AI to it.</p>

<h2 id="1-storing-the-game-state">1. Storing the game state</h2>
<blockquote>
  <p>skim through this part if you already know basic python<br /></p>
</blockquote>

<p>So first we will decide how to store the game - 2D lists seem like a natural choice because of the 3x3 board. We can also use a length 9 string (and that will be better as we figure out later). So we will use the string method but for a few functions i will also share the 2D list method (but not use it in the main code)</p>

<p>If you are using strings then this is how you index: “012345678” and that corresponds to: <br /></p>

<table>
  <thead>
    <tr>
      <th></th>
      <th><b>col0</b></th>
      <th><b>col1</b></th>
      <th><b>col2</b></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><b>row0</b></td>
      <td>0</td>
      <td>1</td>
      <td>2</td>
    </tr>
    <tr>
      <td><b>row1</b></td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
    </tr>
    <tr>
      <td><b>row2</b></td>
      <td>6</td>
      <td>7</td>
      <td>8</td>
    </tr>
  </tbody>
</table>

<p>You can see that <br />
<code class="language-plaintext highlighter-rouge">index = row * 3 + col</code> <br />
<code class="language-plaintext highlighter-rouge">col = index % 3</code><br />
<code class="language-plaintext highlighter-rouge">row = index // 3</code></p>

<p>Also we will fill the table with X, O and “ “ (a space for empty cell)</p>

<p>Let us first create the board and printing function:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span>  <span class="c1"># This creates a string with 9 spaces representing an empty board
</span>
<span class="c1"># this is a print board function for string version
</span><span class="k">def</span> <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"""</span><span class="s">
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="si">}</span><span class="s">
---------
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="si">}</span><span class="s">
---------
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"""</span><span class="p">)</span>
</code></pre></div></div>
<details>
  <summary>For the 2D list version: (which we will not use in main code)</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">board</span> <span class="o">=</span> <span class="p">[[</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)]</span>
<span class="c1"># This is list comprehension. This is the shorter version of:
</span><span class="n">board</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
    <span class="n">board</span><span class="p">.</span><span class="nf">append</span><span class="p">([</span><span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">()</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s"> | </span><span class="sh">"</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">-</span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s"> | </span><span class="sh">"</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">-</span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s"> | </span><span class="sh">"</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>
</code></pre></div>  </div>
</details>
<p><br /></p>

<p>Now let us create a function to get user input AND play the move (Standard tictactoe, no AI yet. See explanation below this code block for how it works):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span> <span class="c1"># string version
</span>    <span class="c1"># returns 2 values = updated board and position played
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">player</span><span class="si">}</span><span class="s">, enter the cell number (0-8): </span><span class="sh">"</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span> <span class="c1"># check if cell is empty
</span>                <span class="n">board</span> <span class="o">=</span> <span class="n">board</span><span class="p">[:</span><span class="n">num</span><span class="p">]</span> <span class="o">+</span> <span class="n">player</span> <span class="o">+</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">num</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">That position is already taken. Try again.</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">except </span><span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="nb">IndexError</span><span class="p">):</span> <span class="c1"># valueeerror= not an int, indexerror= not in 0-8
</span>            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter numbers between 0 and 8.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="explanation-of-how-the-above-function-works">Explanation of how the above function works:</h3>
<p>Let us start with getting user input (make a new file and follow along):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sh">"</span><span class="s">Enter a number between 0 and 8: </span><span class="sh">"</span><span class="p">))</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">You entered: </span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p>This is simple, asks input with the <code class="language-plaintext highlighter-rouge">input()</code> function and converts it to an integer using <code class="language-plaintext highlighter-rouge">int()</code>. <code class="language-plaintext highlighter-rouge">input()</code> returns a string so i need to convert to an integer for greaterthan (<code class="language-plaintext highlighter-rouge">&gt;</code>) or lesserthan (<code class="language-plaintext highlighter-rouge">&lt;</code>) operations.</p>

<p>BUT what if i enter <code class="language-plaintext highlighter-rouge">10</code> or <code class="language-plaintext highlighter-rouge">100</code> or <code class="language-plaintext highlighter-rouge">-1</code>? These are invalid. I need to ask user again if i get these values. <br />
So what we will do is that we will keep asking user for input until we get a valid input. For this we can use a <code class="language-plaintext highlighter-rouge">while True:</code> loop which will run forever until we break out of it. <br /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sh">"</span><span class="s">Enter a number between 0 and 8: </span><span class="sh">"</span><span class="p">))</span>
    <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">num</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">You entered: </span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">break</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please try again.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p>We have caught invalid integer inputs now. What if someone enters <code class="language-plaintext highlighter-rouge">abc</code> though? <code class="language-plaintext highlighter-rouge">int()</code> function cannot handle that, it will throw an error (try it, it gives a <code class="language-plaintext highlighter-rouge">ValueError</code>). So we need to catch that error. For this we use <code class="language-plaintext highlighter-rouge">try-except</code> blocks. <br /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sh">"</span><span class="s">Enter a number between 0 and 8: </span><span class="sh">"</span><span class="p">))</span>
        <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">num</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">You entered: </span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">break</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please try again.</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">ValueError</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter a valid integer.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p>We have handled all bad inputs! While we are at try-except, try out what error will this code give in this input: <code class="language-plaintext highlighter-rouge">abc</code> <br /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]</span>
<span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sh">"</span><span class="s">Enter a number between 0 and 8: </span><span class="sh">"</span><span class="p">))</span>
<span class="nf">print</span><span class="p">(</span><span class="n">arr</span><span class="p">[</span><span class="n">num</span><span class="p">])</span>
</code></pre></div></div>
<p>This gives an <code class="language-plaintext highlighter-rouge">IndexError</code> since the index is out of range (range is 0-2, both 0 and 2 included). So we can catch both <code class="language-plaintext highlighter-rouge">ValueError</code> and <code class="language-plaintext highlighter-rouge">IndexError</code> in the same except block like this: <br /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sh">"</span><span class="s">Enter a number between 0 and 8: </span><span class="sh">"</span><span class="p">))</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">arr</span><span class="p">[</span><span class="n">num</span><span class="p">])</span>
        <span class="k">break</span>
    <span class="nf">except </span><span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="nb">IndexError</span><span class="p">):</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter numbers between 0 and 8.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p>There was nothing wrong with the <code class="language-plaintext highlighter-rouge">if</code> loop earlier. I would say that previous one was better too! I just randomly wanted to show you how to catch multiple exceptions in one except block.<br />
Now rather than just printing that number, we need to update the gameboard. Go back to your main file, and now let us think how to update the board. <br />
We need to check if the cell is empty first. If it is empty, we put the player’s symbol there. If not, we ask for input again. We check that by <code class="language-plaintext highlighter-rouge">if board[num] == " "</code> <br />
<br /></p>
<h3 id="updating-the-string-"><strong>Updating the string:</strong> <br /></h3>
<p>String are immutable, cannot be changed. So something like <code class="language-plaintext highlighter-rouge">board[num] = player</code> will not work. We need to create a new string with the updated value. <br />
Try this out:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s">XOXX OOXX</span><span class="sh">"</span>
<span class="nf">print</span><span class="p">(</span><span class="n">board</span><span class="p">[:</span><span class="mi">4</span><span class="p">])</span> <span class="c1"># prints indexes 0,1,2,3 (4 at end index, so prints upto that. Start index is not given so it just starts from 0 in this case)
</span><span class="nf">print</span><span class="p">(</span><span class="n">board</span><span class="p">[</span><span class="mi">5</span><span class="p">:])</span> <span class="c1"># prints indexes 5,6,7,8 (start index is 5, end index not given so goes till end)
</span></code></pre></div></div>
<p>This is called string slicing. Experiment more with this if you want. We can use this for creating that new string. We can slice upto the index, then put the symbol, and put the rest of the characters.<br /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s">XOXX OOXX</span><span class="sh">"</span>
<span class="n">board</span> <span class="o">=</span> <span class="n">board</span><span class="p">[:</span><span class="n">num</span><span class="p">]</span> <span class="o">+</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="o">+</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span> <span class="c1"># this gives "XOXXXOOXX"
</span></code></pre></div></div>
<p>Lets implement all this:<br />
Also we make this a function that takes in the board and the player (X or O) that needs to be inserted. Finally we return the updated board and the position played. <br /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">):</span> 
    <span class="c1"># returns 2 values = updated board and position played
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">player</span><span class="si">}</span><span class="s">, enter the cell number (0-8): </span><span class="sh">"</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span> <span class="c1"># check if cell is empty
</span>                <span class="n">board</span> <span class="o">=</span> <span class="n">board</span><span class="p">[:</span><span class="n">num</span><span class="p">]</span> <span class="o">+</span> <span class="n">player</span> <span class="o">+</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">num</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">That position is already taken. Try again.</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">except </span><span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="nb">IndexError</span><span class="p">):</span> <span class="c1"># valueeerror= not an int, indexerror= not in 0-8
</span>            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter numbers between 0 and 8.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>
<p><br /></p>

<details>
  <summary>2D list version: (which we will not use in main code)</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]],</span> <span class="n">player</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">player</span><span class="si">}</span><span class="s">, enter the cell number (0-8): </span><span class="sh">"</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span> <span class="o">//</span> <span class="mi">3</span><span class="p">][</span><span class="n">num</span> <span class="o">%</span> <span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
                <span class="n">board</span><span class="p">[</span><span class="n">num</span> <span class="o">//</span> <span class="mi">3</span><span class="p">][</span><span class="n">num</span> <span class="o">%</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">player</span>
                <span class="k">return</span> <span class="n">num</span> <span class="c1"># this function does changes in place on the list, so no need to return board
</span>            <span class="k">else</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">That position is already taken. Try again.</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">except </span><span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="nb">IndexError</span><span class="p">):</span>
            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter numbers between 0 and 8.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div>  </div>
</details>

<h2 id="2-game-loop-and-player-switching-logic">2. Game loop and player switching logic</h2>
<blockquote>
  <p>skim if you already know basic python<br /></p>
</blockquote>

<p>Now we will create a game loop function that will print the board, get user input and return the move played</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">):</span>
    <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
    <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">move</span>



<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="c1"># game always starts with X
</span>    <span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span>
    <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># 1 = X, 0 = O
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span>     
        <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">player</span> <span class="c1"># Switch player (1-1=0, 1-0=1)
</span></code></pre></div></div>

<details>
  <summary>compressed if loops</summary>

  <p>These two code snippets are equivalent:</p>
  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
    <span class="n">symbol</span> <span class="o">=</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">symbol</span> <span class="o">=</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span>
<span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">symbol</span><span class="p">)</span>

<span class="c1"># shortened:
</span><span class="n">symbol</span> <span class="o">=</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span>
<span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">symbol</span><span class="p">)</span>
</code></pre></div>  </div>
</details>

<details>
  <summary> If you are wondering how `board, move = player_turn(board, player)` works, click here </summary>

  <p>This is called tuple unpacking. When a function returns multiple values separated by commas, it is actually returning a tuple containing those values. You can unpack this tuple into separate variables in one line.</p>
  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">example_function</span><span class="p">():</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">,</span> <span class="sh">"</span><span class="s">hello</span><span class="sh">"</span><span class="p">,</span> <span class="mf">3.14</span>

<span class="nf">print</span><span class="p">(</span><span class="nf">type</span><span class="p">(</span><span class="nf">example_function</span><span class="p">()))</span> <span class="c1"># prints &lt;class 'tuple'&gt; (type function returns the type of the data passed to it)
</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span> <span class="o">=</span> <span class="nf">example_function</span><span class="p">()</span>
<span class="nf">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="c1"># prints 1
</span><span class="nf">print</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="c1"># prints hello
</span><span class="nf">print</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="c1"># prints 3.14
</span></code></pre></div>  </div>

</details>

<h3 id="checkpoint-1">Checkpoint 1</h3>
<p>The code now should run a basic tictactoe game between 2 players. Next we will add win checking logic.</p>
<details>
  <summary>Full code until now (click this to reveal)</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"""</span><span class="s">
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="si">}</span><span class="s">
---------
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="si">}</span><span class="s">
---------
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"""</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
    <span class="c1"># returns 2 values = updated board and position played
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">player</span><span class="si">}</span><span class="s">, enter the cell number (0-8): </span><span class="sh">"</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span> <span class="c1"># check if cell is empty
</span>                <span class="n">board</span> <span class="o">=</span> <span class="n">board</span><span class="p">[:</span><span class="n">num</span><span class="p">]</span> <span class="o">+</span> <span class="n">player</span> <span class="o">+</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">num</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">That position is already taken. Try again.</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">except </span><span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="nb">IndexError</span><span class="p">):</span> <span class="c1"># valueeerror= not an int, indexerror= not in 0-8
</span>            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter numbers between 0 and 8.</span><span class="sh">"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">):</span>
    <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
    <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">move</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="c1"># game always starts with X
</span>    <span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span>
    <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># 1 = X, 0 = O
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span>     
        <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">player</span> <span class="c1"># Switch player (1-1=0, 1-0=1)
</span></code></pre></div>  </div>

</details>

<h2 id="3-win-checking-logic">3. Win checking logic</h2>
<blockquote>
  <p>skim if you already know basic python<br /></p>
</blockquote>

<p>For checking the winner we need to check all possible winning combinations:</p>
<ol>
  <li>Line among rows (3 combinations)</li>
  <li>Line among columns (3 combinations)</li>
  <li>Line among diagonals (2 combinations)</li>
</ol>

<p>Note: <code class="language-plaintext highlighter-rouge">index = row * 3 + col</code> <br /></p>

<p>So for 1, we can check on each row if all 3 cells are same <br />
On ith row, cells are: <code class="language-plaintext highlighter-rouge">i*3+0, i*3+1, i*3+2</code> and they all should be same. <br /></p>

<p>For 2, on ith column, cells are: <code class="language-plaintext highlighter-rouge">0*3+i, 1*3+i, 2*3+i</code> and they all should be same. <br /></p>

<p>Test these formulae/indexes out on these if you need to:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                                j=0 j=1 j=2
X | X | X      O | X | X    i=0  0 | 1 | 2
---------      ---------         ---------
  | O | O        | O |      i=1  3 | 4 | 5
---------      ---------         ---------
  |   |        X |   | O    i=2  6 | 7 | 8
</code></pre></div></div>

<p>Now we will create a function to check if there is a winner after each move</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="c1"># Check rows and columns
</span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        
    <span class="c1"># Check diagonals
</span>    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
    <span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>
<details>
  <summary>2D list version (which we will not use in main code)</summary>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">):</span>
    <span class="c1"># Check rows and columns
</span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="n">i</span><span class="p">]</span>
        
    <span class="c1"># Check diagonals
</span>    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span>
    <span class="k">return</span> <span class="bp">None</span>
</code></pre></div>  </div>
</details>

<p><br />
2 player tictactoe game is now complete!!</p>

<p>Now we need to add the AI itself to play against us. (later against itself)</p>

<h3 id="checkpoint-2">Checkpoint 2</h3>
<p>We have our 2 player tictactoe game completed!
This is how our main loop looks like now:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="c1">#game always starts with X
</span>    
    <span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span>
    <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># 1 = X, 0 = O
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span>

        <span class="n">winner</span> <span class="o">=</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">winner</span><span class="p">:</span>
            <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">winner</span><span class="si">}</span><span class="s"> wins!</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">break</span>
        
        <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">player</span> <span class="c1"># Switch player
</span></code></pre></div></div>

<p>And this is the whole code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="c1"># Check rows and columns
</span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        
    <span class="c1"># Check diagonals
</span>    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
    <span class="k">return</span> <span class="bp">None</span>


<span class="k">def</span> <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"""</span><span class="s">
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="si">}</span><span class="s">
---------
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span><span class="si">}</span><span class="s">
---------
</span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span><span class="si">}</span><span class="s"> | </span><span class="si">{</span><span class="n">board</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span><span class="si">}</span><span class="sh">"""</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">player</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
    <span class="c1"># returns 2 values = updated board and position played
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">num</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="nf">input</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">player</span><span class="si">}</span><span class="s">, enter the cell number (0-8): </span><span class="sh">"</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="p">]</span> <span class="o">==</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span><span class="p">:</span> <span class="c1"># check if cell is empty
</span>                <span class="n">board</span> <span class="o">=</span> <span class="n">board</span><span class="p">[:</span><span class="n">num</span><span class="p">]</span> <span class="o">+</span> <span class="n">player</span> <span class="o">+</span> <span class="n">board</span><span class="p">[</span><span class="n">num</span><span class="o">+</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">num</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">That position is already taken. Try again.</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">except </span><span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="nb">IndexError</span><span class="p">):</span> <span class="c1"># valueeerror= not an int, indexerror= not in 0-8
</span>            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Invalid input. Please enter numbers between 0 and 8.</span><span class="sh">"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">):</span>
    <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
    <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">player_turn</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">player</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">board</span><span class="p">,</span> <span class="n">move</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="c1">#game always starts with X
</span>    
    <span class="n">board</span> <span class="o">=</span> <span class="sh">"</span><span class="s"> </span><span class="sh">"</span> <span class="o">*</span> <span class="mi">9</span>
    <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># 1 = X, 0 = O
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">board</span><span class="p">,</span> <span class="n">move</span> <span class="o">=</span> <span class="nf">game_loop</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="sh">"</span><span class="s">X</span><span class="sh">"</span> <span class="k">if</span> <span class="n">player</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="sh">"</span><span class="s">O</span><span class="sh">"</span><span class="p">)</span>

        <span class="n">winner</span> <span class="o">=</span> <span class="nf">check_winner</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">winner</span><span class="p">:</span>
            <span class="nf">print_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Player </span><span class="si">{</span><span class="n">winner</span><span class="si">}</span><span class="s"> wins!</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">break</span>
        
        <span class="n">player</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">player</span> <span class="c1"># Switch player
</span></code></pre></div></div>
<p><br /><br /></p>
<h1 id="next-steps">Next Steps</h1>

<p>In the next part, we will be writing some code to represent the matchboxes and beads, which means we will be generating all the 3^9 possible game states and then realising that we really only need 593 states (and get to the 304 matchbox count too that you might have read/seen)!<br />
Also next part will be significantly harder than this.</p>

<p>Part 2  -  <a href="/2026-02-18-menace-part-2/">MENACE Part 2: Building the Matchbox System and Generating Game States</a></p>]]></content><author><name>Ankit Sinha</name><email>ankitsinha@myyahoo.com</email></author><summary type="html"><![CDATA[Programming MENACE (A Reinforcement Learning AI) that learns to play tic-tac-toe using matchboxes and beads.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://i0.wp.com/cdn.makezine.com/uploads/2009/11/menace_tic_tac_toe.jpg?resize=600%2C450&amp;ssl=1" /><media:content medium="image" url="https://i0.wp.com/cdn.makezine.com/uploads/2009/11/menace_tic_tac_toe.jpg?resize=600%2C450&amp;ssl=1" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>