Thicket data repository for the EEG
at main 21 kB view raw
1{ 2 "id": "https://ryan.freumh.org/nim.html", 3 "title": "Nim", 4 "link": "https://ryan.freumh.org/nim.html", 5 "updated": "2022-08-30T00:00:00", 6 "published": "2022-08-30T00:00:00", 7 "summary": "<div>\n \n <span>Published 30 Aug 2022.</span>\n \n \n <span>Last update 30 Aug 2022.</span>\n \n </div>\n \n <div> Tags: <a href=\"/projects.html\" title=\"All pages tagged 'projects'.\">projects</a>. </div>\n \n \n\n \n<p><span>I recently had the good fortune to attended a talk\nby Simon Peyton Jones titled “Immutability changes everything: 40 years\nof functional programming” which chronologic Simon’s life and research\ncareer. Simon is a self-described one trick pony, where that trick is\nfunctional programming. However, this trick has taken him from being a\nmajor instigator in Haskell - he remains a lead developer of the Glasgow\nHaskell Compiler to this day - to Microsoft research, and more recently\nto Epic Games working on a new programming language dubbed ‘<a href=\"https://discourse.haskell.org/t/an-epic-future-for-spj/3573\">Verse</a>’.\nIn this talk Simon described how he first got interested in Computer\nScience through a problem posed to him: the game Nim.</span></p>\n\n\n<img alt=\", \" src=\"./images/nim-dalle2.webp\">\n\n<a href=\"#fn1\">1</a>\n\n<h3>The game</h3>\n<p><span>Picture the scene: you and your opponent\nstare each other down across stacks of gold doubloons. You place a\nwager, you’ll each take turns taking 1 or more coins from a single pile,\nand whoever takes the last coin wins.</span></p>\n<p><span>Let’s think through your\nstrategy…</span></p>\n<p><span>To start with a very simple scenario\nconsider two stacks; one with 2 coins, and one with 3 coins.</span></p>\n<p><img src=\"./images/nim1.svg\"></p>\n<p><span>If you take all of one pile:</span></p>\n<p><img src=\"./images/nim2.svg\"></p>\n<p><span>Then your opponent can take all off the\nother, leaving you penniless:</span></p>\n<p><img src=\"./images/nim3.svg\"></p>\n<p><span>Let’s rethink…</span></p>\n<p><span>Instead, if you take one from the pile of\nthree to make the piles equal:</span></p>\n<p><img src=\"./images/nim4.svg\"></p>\n<p><span>Then if your opponent takes all of either\npile, you can just take the remaining pile. But let’s imagine they are\nslightly more canny and they only take one from a pile:</span></p>\n<p><img src=\"./images/nim5.svg\"></p>\n<p><span>It seemed to have worked last time, so\nlet’s repeat our trick of making the piles equal:</span></p>\n<p><img src=\"./images/nim6.svg\"></p>\n<p><span>Your opponent will be forced to take one\ncoin:</span></p>\n<p><img src=\"./images/nim7.svg\"></p>\n<p><span>And then fortunes are yours!</span></p>\n<p><img src=\"./images/nim8.svg\"></p>\n<h3>A strategy</h3>\n<p><span>Nim is a solved game for an arbitrary\nnumber of stacks. We can use a a <em>binary digital sum</em>, denoted\nthe ‘’nim-sum’ to come up with an optimal strategy. That is, a sum –\nignoring carrying – of the binary values for every stack size. Another\nword for this operation is ‘exclusve or’ (⊕). For stacks A size 5, B\nsize 2, and C size 3, this is equal to A⊕B⊕C:</span></p>\n<pre>A⊕B⊕C\n=510⊕210⊕310\n=1012⊕0102⊕0112\n=1112⊕0112\n=1002\n=410\n</pre>\n<p><span>Where subscript numbers denote\nbase.</span></p>\n<p><span>The key to Nim is to always finish your\nturn with a num-sum of 0. This is always possible as long as the nim-sum\nisn’t 0 at the start of your turn. The nim-sum will always be less than\nthe value of the largest stack, so you can take the decimal value of the\nnim-sum from this (or another) stack - which will result in a nim-sum of\nzero. If the nim-sum is already zero, taking any coin will make that no\nlonger the case.</span></p>\n<p><span>The player who can win the game will be\nthe player with a zero nim-sum on their last turn, which is predicated\non them having a zero nim-sum (and their opponent always having a\nnon-zero nim-sum) on their turn from the beginning. You will note, that\nthis makes the outcome of the game entirely deterministic on the\ninitially state of the board and whoever has the first turn.</span></p>\n<p><span>A formal verison of this hand-waving\ninductive proof can be found <a href=\"https://en.wikipedia.org/wiki/Nim#Proof_of_the_winning_formula\">here</a>.</span></p>\n<h3>Some reflections</h3>\n<p><span>This talk was not my first\nencounter with Nim. In fact, as my S6<a href=\"#fn2\">2</a> advanced higher\ncomputing project I implemented this game.</span></p>\n<p><span>As part of this I implemented a\ncrude AI opponent which played optimally using the nim-sum with some\nprobability based on a difficulty:</span></p>\n<div><pre><code><span><a href=\"#cb1-1\"></a><span>private</span> <span>void</span> <span>AITurn</span><span>()</span> <span>{</span></span>\n<span><a href=\"#cb1-2\"></a> <span>int</span> nim_sum <span>=</span> <span>0x0</span><span>;</span></span>\n<span><a href=\"#cb1-3\"></a> <span>int</span><span>[]</span> Stacks_binary_values <span>=</span> <span>new</span> <span>int</span><span>[</span>number_of_stacks<span>];</span></span>\n<span><a href=\"#cb1-4\"></a> <span>for</span> <span>(</span><span>int</span> i <span>=</span> <span>0</span><span>;</span> i <span>&lt;</span> number_of_stacks<span>;</span> i<span>++)</span> <span>{</span></span>\n<span><a href=\"#cb1-5\"></a> Stacks_binary_values<span>[</span>i<span>]</span> <span>=</span> Convert<span>.</span><span>ToByte</span><span>(</span>Stacks<span>[</span>i<span>].</span><span>Text</span><span>);</span></span>\n<span><a href=\"#cb1-6\"></a> nim_sum <span>=</span> nim_sum <span>^</span> Stacks_binary_values<span>[</span>i<span>];</span></span>\n<span><a href=\"#cb1-7\"></a> <span>}</span></span>\n<span><a href=\"#cb1-8\"></a> <span>if</span> <span>(</span>nim_sum <span>==</span> <span>0x0</span> <span>||</span> random<span>.</span><span>NextDouble</span><span>()</span> <span>&gt;</span> AIdifficulty<span>)</span> <span>{</span></span>\n<span><a href=\"#cb1-9\"></a> <span>int</span> stack_index <span>=</span> random<span>.</span><span>Next</span><span>(</span>number_of_stacks<span>);</span></span>\n<span><a href=\"#cb1-10\"></a> Stacks<span>[</span>stack_index<span>].</span><span>Text</span> <span>=</span> random<span>.</span><span>Next</span><span>(</span><span>int</span><span>.</span><span>Parse</span><span>(</span>Stacks<span>[</span>stack_index<span>].</span><span>Text</span><span>)-</span><span>1</span><span>).</span><span>ToString</span><span>();</span></span>\n<span><a href=\"#cb1-11\"></a> <span>if</span> <span>(</span>Stacks<span>[</span>stack_index<span>].</span><span>Text</span> <span>==</span> <span>&quot;0&quot;</span><span>)</span> <span>{</span></span>\n<span><a href=\"#cb1-12\"></a> Stacks<span>[</span>stack_index<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-13\"></a> Disks<span>[</span>stack_index<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-14\"></a> <span>}</span></span>\n<span><a href=\"#cb1-15\"></a> current_stack <span>=</span> stack_index <span>+</span> <span>1</span><span>;</span></span>\n<span><a href=\"#cb1-16\"></a> <span>}</span> <span>else</span> <span>{</span></span>\n<span><a href=\"#cb1-17\"></a> <span>for</span> <span>(</span><span>int</span> i <span>=</span> <span>0</span><span>;</span> i <span>&lt;</span> number_of_stacks<span>;</span> i<span>++)</span> <span>{</span></span>\n<span><a href=\"#cb1-18\"></a> <span>if</span> <span>((</span>nim_sum <span>^</span> Stacks_binary_values<span>[</span>i<span>])</span> <span>&lt;</span> Stacks_binary_values<span>[</span>i<span>])</span> <span>{</span></span>\n<span><a href=\"#cb1-19\"></a> Stacks_binary_values<span>[</span>i<span>]</span> <span>=</span> nim_sum <span>^</span> Stacks_binary_values<span>[</span>i<span>];</span></span>\n<span><a href=\"#cb1-20\"></a> Stacks<span>[</span>i<span>].</span><span>Text</span> <span>=</span> Convert<span>.</span><span>ToString</span><span>(</span>Stacks_binary_values<span>[</span>i<span>]);</span></span>\n<span><a href=\"#cb1-21\"></a> <span>if</span> <span>(</span>Stacks<span>[</span>i<span>].</span><span>Text</span> <span>==</span> <span>&quot;0&quot;</span><span>)</span> <span>{</span></span>\n<span><a href=\"#cb1-22\"></a> Stacks<span>[</span>i<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-23\"></a> Disks<span>[</span>i<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-24\"></a> <span>}</span></span>\n<span><a href=\"#cb1-25\"></a> current_stack <span>=</span> i <span>+</span> <span>1</span><span>;</span></span>\n<span><a href=\"#cb1-26\"></a> <span>break</span><span>;</span></span>\n<span><a href=\"#cb1-27\"></a> <span>}</span></span>\n<span><a href=\"#cb1-28\"></a> <span>}</span></span>\n<span><a href=\"#cb1-29\"></a> <span>}</span></span>\n<span><a href=\"#cb1-30\"></a> <span>TurnTaken</span><span>();</span></span>\n<span><a href=\"#cb1-31\"></a><span>}</span></span></code></pre></div>\n<p><span>At the time I only really knew how\nto program in Visual Studio, and wanting to move away from visual basic\nC# was the natural choice. I believe they even use the same intermediate\nrepresentation.</span></p>\n<p><span>The source code and binary of this\ngame can be found <a href=\"https://github.com/RyanGibb/nim\">here</a>\n(but build instructions are left as an exercise for the\nreader).</span></p>\n<p><span>Reflecting on this project, while\nit had some inane requirements - like writing out the entire program in\npseuocode beforehand and using a strict waterful methodology, all of\nwhich was done retrospectivly of course - it really got me to consider\nstudying computer science further, which led me to where I am\ntoday.</span></p>\n\n\n\n\n<ol>\n<li><p><span>Generated by <a href=\"https://openai.com/dall-e-2/\">DALL.E 2</a>.</span><a href=\"#fnref1\">↩︎</a></p></li>\n<li><p><span>S6 meaning senior\nphase 6, as I just found out.</span><a href=\"#fnref2\">↩︎</a></p></li>\n</ol>", 8 "content": "<div>\n \n <span>Published 30 Aug 2022.</span>\n \n \n <span>Last update 30 Aug 2022.</span>\n \n </div>\n \n <div> Tags: <a href=\"/projects.html\" title=\"All pages tagged 'projects'.\">projects</a>. </div>\n \n \n\n \n<p><span>I recently had the good fortune to attended a talk\nby Simon Peyton Jones titled “Immutability changes everything: 40 years\nof functional programming” which chronologic Simon’s life and research\ncareer. Simon is a self-described one trick pony, where that trick is\nfunctional programming. However, this trick has taken him from being a\nmajor instigator in Haskell - he remains a lead developer of the Glasgow\nHaskell Compiler to this day - to Microsoft research, and more recently\nto Epic Games working on a new programming language dubbed ‘<a href=\"https://discourse.haskell.org/t/an-epic-future-for-spj/3573\">Verse</a>’.\nIn this talk Simon described how he first got interested in Computer\nScience through a problem posed to him: the game Nim.</span></p>\n\n\n<img alt=\", \" src=\"./images/nim-dalle2.webp\">\n\n<a href=\"#fn1\">1</a>\n\n<h3>The game</h3>\n<p><span>Picture the scene: you and your opponent\nstare each other down across stacks of gold doubloons. You place a\nwager, you’ll each take turns taking 1 or more coins from a single pile,\nand whoever takes the last coin wins.</span></p>\n<p><span>Let’s think through your\nstrategy…</span></p>\n<p><span>To start with a very simple scenario\nconsider two stacks; one with 2 coins, and one with 3 coins.</span></p>\n<p><img src=\"./images/nim1.svg\"></p>\n<p><span>If you take all of one pile:</span></p>\n<p><img src=\"./images/nim2.svg\"></p>\n<p><span>Then your opponent can take all off the\nother, leaving you penniless:</span></p>\n<p><img src=\"./images/nim3.svg\"></p>\n<p><span>Let’s rethink…</span></p>\n<p><span>Instead, if you take one from the pile of\nthree to make the piles equal:</span></p>\n<p><img src=\"./images/nim4.svg\"></p>\n<p><span>Then if your opponent takes all of either\npile, you can just take the remaining pile. But let’s imagine they are\nslightly more canny and they only take one from a pile:</span></p>\n<p><img src=\"./images/nim5.svg\"></p>\n<p><span>It seemed to have worked last time, so\nlet’s repeat our trick of making the piles equal:</span></p>\n<p><img src=\"./images/nim6.svg\"></p>\n<p><span>Your opponent will be forced to take one\ncoin:</span></p>\n<p><img src=\"./images/nim7.svg\"></p>\n<p><span>And then fortunes are yours!</span></p>\n<p><img src=\"./images/nim8.svg\"></p>\n<h3>A strategy</h3>\n<p><span>Nim is a solved game for an arbitrary\nnumber of stacks. We can use a a <em>binary digital sum</em>, denoted\nthe ‘’nim-sum’ to come up with an optimal strategy. That is, a sum –\nignoring carrying – of the binary values for every stack size. Another\nword for this operation is ‘exclusve or’ (⊕). For stacks A size 5, B\nsize 2, and C size 3, this is equal to A⊕B⊕C:</span></p>\n<pre>A⊕B⊕C\n=510⊕210⊕310\n=1012⊕0102⊕0112\n=1112⊕0112\n=1002\n=410\n</pre>\n<p><span>Where subscript numbers denote\nbase.</span></p>\n<p><span>The key to Nim is to always finish your\nturn with a num-sum of 0. This is always possible as long as the nim-sum\nisn’t 0 at the start of your turn. The nim-sum will always be less than\nthe value of the largest stack, so you can take the decimal value of the\nnim-sum from this (or another) stack - which will result in a nim-sum of\nzero. If the nim-sum is already zero, taking any coin will make that no\nlonger the case.</span></p>\n<p><span>The player who can win the game will be\nthe player with a zero nim-sum on their last turn, which is predicated\non them having a zero nim-sum (and their opponent always having a\nnon-zero nim-sum) on their turn from the beginning. You will note, that\nthis makes the outcome of the game entirely deterministic on the\ninitially state of the board and whoever has the first turn.</span></p>\n<p><span>A formal verison of this hand-waving\ninductive proof can be found <a href=\"https://en.wikipedia.org/wiki/Nim#Proof_of_the_winning_formula\">here</a>.</span></p>\n<h3>Some reflections</h3>\n<p><span>This talk was not my first\nencounter with Nim. In fact, as my S6<a href=\"#fn2\">2</a> advanced higher\ncomputing project I implemented this game.</span></p>\n<p><span>As part of this I implemented a\ncrude AI opponent which played optimally using the nim-sum with some\nprobability based on a difficulty:</span></p>\n<div><pre><code><span><a href=\"#cb1-1\"></a><span>private</span> <span>void</span> <span>AITurn</span><span>()</span> <span>{</span></span>\n<span><a href=\"#cb1-2\"></a> <span>int</span> nim_sum <span>=</span> <span>0x0</span><span>;</span></span>\n<span><a href=\"#cb1-3\"></a> <span>int</span><span>[]</span> Stacks_binary_values <span>=</span> <span>new</span> <span>int</span><span>[</span>number_of_stacks<span>];</span></span>\n<span><a href=\"#cb1-4\"></a> <span>for</span> <span>(</span><span>int</span> i <span>=</span> <span>0</span><span>;</span> i <span>&lt;</span> number_of_stacks<span>;</span> i<span>++)</span> <span>{</span></span>\n<span><a href=\"#cb1-5\"></a> Stacks_binary_values<span>[</span>i<span>]</span> <span>=</span> Convert<span>.</span><span>ToByte</span><span>(</span>Stacks<span>[</span>i<span>].</span><span>Text</span><span>);</span></span>\n<span><a href=\"#cb1-6\"></a> nim_sum <span>=</span> nim_sum <span>^</span> Stacks_binary_values<span>[</span>i<span>];</span></span>\n<span><a href=\"#cb1-7\"></a> <span>}</span></span>\n<span><a href=\"#cb1-8\"></a> <span>if</span> <span>(</span>nim_sum <span>==</span> <span>0x0</span> <span>||</span> random<span>.</span><span>NextDouble</span><span>()</span> <span>&gt;</span> AIdifficulty<span>)</span> <span>{</span></span>\n<span><a href=\"#cb1-9\"></a> <span>int</span> stack_index <span>=</span> random<span>.</span><span>Next</span><span>(</span>number_of_stacks<span>);</span></span>\n<span><a href=\"#cb1-10\"></a> Stacks<span>[</span>stack_index<span>].</span><span>Text</span> <span>=</span> random<span>.</span><span>Next</span><span>(</span><span>int</span><span>.</span><span>Parse</span><span>(</span>Stacks<span>[</span>stack_index<span>].</span><span>Text</span><span>)-</span><span>1</span><span>).</span><span>ToString</span><span>();</span></span>\n<span><a href=\"#cb1-11\"></a> <span>if</span> <span>(</span>Stacks<span>[</span>stack_index<span>].</span><span>Text</span> <span>==</span> <span>&quot;0&quot;</span><span>)</span> <span>{</span></span>\n<span><a href=\"#cb1-12\"></a> Stacks<span>[</span>stack_index<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-13\"></a> Disks<span>[</span>stack_index<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-14\"></a> <span>}</span></span>\n<span><a href=\"#cb1-15\"></a> current_stack <span>=</span> stack_index <span>+</span> <span>1</span><span>;</span></span>\n<span><a href=\"#cb1-16\"></a> <span>}</span> <span>else</span> <span>{</span></span>\n<span><a href=\"#cb1-17\"></a> <span>for</span> <span>(</span><span>int</span> i <span>=</span> <span>0</span><span>;</span> i <span>&lt;</span> number_of_stacks<span>;</span> i<span>++)</span> <span>{</span></span>\n<span><a href=\"#cb1-18\"></a> <span>if</span> <span>((</span>nim_sum <span>^</span> Stacks_binary_values<span>[</span>i<span>])</span> <span>&lt;</span> Stacks_binary_values<span>[</span>i<span>])</span> <span>{</span></span>\n<span><a href=\"#cb1-19\"></a> Stacks_binary_values<span>[</span>i<span>]</span> <span>=</span> nim_sum <span>^</span> Stacks_binary_values<span>[</span>i<span>];</span></span>\n<span><a href=\"#cb1-20\"></a> Stacks<span>[</span>i<span>].</span><span>Text</span> <span>=</span> Convert<span>.</span><span>ToString</span><span>(</span>Stacks_binary_values<span>[</span>i<span>]);</span></span>\n<span><a href=\"#cb1-21\"></a> <span>if</span> <span>(</span>Stacks<span>[</span>i<span>].</span><span>Text</span> <span>==</span> <span>&quot;0&quot;</span><span>)</span> <span>{</span></span>\n<span><a href=\"#cb1-22\"></a> Stacks<span>[</span>i<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-23\"></a> Disks<span>[</span>i<span>].</span><span>Hide</span><span>();</span></span>\n<span><a href=\"#cb1-24\"></a> <span>}</span></span>\n<span><a href=\"#cb1-25\"></a> current_stack <span>=</span> i <span>+</span> <span>1</span><span>;</span></span>\n<span><a href=\"#cb1-26\"></a> <span>break</span><span>;</span></span>\n<span><a href=\"#cb1-27\"></a> <span>}</span></span>\n<span><a href=\"#cb1-28\"></a> <span>}</span></span>\n<span><a href=\"#cb1-29\"></a> <span>}</span></span>\n<span><a href=\"#cb1-30\"></a> <span>TurnTaken</span><span>();</span></span>\n<span><a href=\"#cb1-31\"></a><span>}</span></span></code></pre></div>\n<p><span>At the time I only really knew how\nto program in Visual Studio, and wanting to move away from visual basic\nC# was the natural choice. I believe they even use the same intermediate\nrepresentation.</span></p>\n<p><span>The source code and binary of this\ngame can be found <a href=\"https://github.com/RyanGibb/nim\">here</a>\n(but build instructions are left as an exercise for the\nreader).</span></p>\n<p><span>Reflecting on this project, while\nit had some inane requirements - like writing out the entire program in\npseuocode beforehand and using a strict waterful methodology, all of\nwhich was done retrospectivly of course - it really got me to consider\nstudying computer science further, which led me to where I am\ntoday.</span></p>\n\n\n\n\n<ol>\n<li><p><span>Generated by <a href=\"https://openai.com/dall-e-2/\">DALL.E 2</a>.</span><a href=\"#fnref1\">↩︎</a></p></li>\n<li><p><span>S6 meaning senior\nphase 6, as I just found out.</span><a href=\"#fnref2\">↩︎</a></p></li>\n</ol>", 9 "content_type": "html", 10 "categories": [], 11 "source": "https://ryan.freumh.org/atom.xml" 12}