{"@attributes":{"version":"2.0"},"channel":{"title":{},"description":"Bit pusher at Spotify. Previously Interactive News at the New York Times, \nU.S. Digital Service, and Code for America.\n","link":"https:\/\/bensmithgall.com","item":[{"title":"Visualizing a generalized maximum-weight-matching graph algorithm","category":{},"description":"<p>I have a small side project I\u2019ve been tinkering with over the past few years: a chess tournament\nmanagement application. It\u2019s been nice as a sort of open-ended exploration sandbox for all sorts\nof different things, like <a href=\".\/2023-09-10-phoenix-hamburger.md\">learning Phoenix<\/a>, and getting to\n<a href=\".\/2024-05-31-live-view-chess-the-rules.md\">implement the rules of chess<\/a>.<\/p>\n\n<p>The core of the application involves tournament matchmaking. I\u2019ve added support for various \ntournament formats over the years, but I started with the most common chess format: <a href=\"https:\/\/en.wikipedia.org\/wiki\/Swiss-system_tournament\">swiss<\/a>.\nThe application for matchmaking in Swiss tournaments can be understood best as a form of a\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Maximum-weight_matching\">maximum-weighted matching<\/a> problem. The players form a graph (often bipartite, but\nnot necessarily!), and we use the state of the tournament to determine the weight of the edges \nbetween them. Internally, the application <a href=\"https:\/\/github.com\/bsmithgall\/elswisser\/blob\/main\/lib\/elswisser\/pairings\/pair_weight.ex\">assigns weights<\/a> based on rules from\nthe USCF rulebook.<\/p>\n\n<p>For a very long time, the application used a very-slightly modified version of Joris van Rantwijk\u2019s\n<a href=\"https:\/\/git.jorisvr.nl\/joris\/maximum-weight-matching\/\">implementation<\/a>. Elixir makes it pretty easy to shell out calls to other languages and get\nthe results back, and I just used this. The algorithm itself is quite complicated and I always had\ntrouble reading and deeply comprehending it. Joris has a <a href=\"https:\/\/git.jorisvr.nl\/joris\/maximum-weight-matching\/src\/branch\/master\/doc\/Algorithm.md\">great writeup<\/a> of how it works.<\/p>\n\n<p>I had tried a couple of times to port the algorithm directly from Python to Elixir. I tried by hand\nand wasn\u2019t able to make much progress. I also tried using LLMs to help with this transformation, but I had never been able to make much headway.<\/p>\n\n<p>However, with the release of Opus 4.5 and Opus 4.6, I decided to give it another go. I asked Claude\nto read and <a href=\"https:\/\/github.com\/bsmithgall\/elswisser\/pull\/144\/changes\/2a1139fdfeae93195f3fdc10c274f86e99e35c7f\">build a spec<\/a> and implementation plan to move things over, and it pretty much\njust worked. This was the first time an LLM was able to really tackle this problem in a serious way.<\/p>\n\n<p>So for this, I wanted to go a bit further into the verification and validation. The algorithm is\nvery complicated with many moving pieces. I had a lot of trouble deeply understanding it. So I put\ntogether <a href=\"https:\/\/elswisser.casita.zone\/matching-viz\">this visualization<\/a> with Claude.<\/p>\n\n<p>Working with Claude on this was interesting. It can produce visual and written artifacts very\nquickly, but the comprehensibility of those artifacts is wildly hit-or-miss. Working from the\noriginal spec documents and the writeup had steered Claude into a spot where the words produced\nassumed a pretty deep familiarity with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linear_programming\">linear programming<\/a> and some algorithm-specific jargon.\nGiven that the goal was to produce something somewhat comprehensible, this didn\u2019t really work out. I\nwas trying to thread a needle here: existing explanatory resources I had found (like from <a href=\"https:\/\/demonstrations.wolfram.com\/TheBlossomAlgorithmForMaximumMatching\/\">wolfram<\/a>\nor <a href=\"https:\/\/algorithms.discrete.ma.tum.de\/graph-algorithms\/matchings-blossom-algorithm\/index_en.html\">TUM in munich<\/a>) operated at either too high or too low of a level; I wanted something that\nwas a lot more explicit, but still approachable. For the top-level algorithmic explanation, I had to\nthrow away basically everything it wrote. After a lot of turns, though, I got something I was pretty\nhappy with:<\/p>\n\n<p><img src=\"..\/images\/matching-viz\/viz.png\" alt=\"A visualization of the blossom algorithm with matches partially\ncompleted\" \/><\/p>\n\n<p>This visualization is backed by a Phoenix LiveView that uses the actual implementation that runs in\nthe application with some <a href=\"https:\/\/github.com\/bsmithgall\/elswisser\/blob\/main\/lib\/max_weight_matching\/stepper.ex\">additional observability attached<\/a>. This allows for a lot of\ncontrol over what data is visualized, including specific details about internals of the algorithm:<\/p>\n\n<p><img src=\"..\/images\/matching-viz\/stepper.png\" alt=\"Detailed information an individual node dequeing\" \/><\/p>\n\n<p>Because it\u2019s built directly into the application, one other nice thing is the visualization also\nserves as a nice debugging tool: I added some code to deserialize graphs out of the URL state, which\nlets me walk through how the pairing works for basically any tournament:<\/p>\n\n<p><img src=\"..\/images\/matching-viz\/tournament.png\" alt=\"An in-progress tournament with mock data\" \/><\/p>\n\n<h2 id=\"clauding\">Clauding<\/h2>\n\n<p>I\u2019m not going to talk too much about the rise of Anthropic\u2019s flagship model for generating code as\nthere is a ton of pre-existing writing about its capabilities. For me, if you can gain this level of\ncoding capability, the main interesting question is around what do you <em>do<\/em> with the time that you\nhave gained? I think that using \u201cagentic LLMs\u201d (something like Claude code) are a bit of a manic\ntechnology: they produce lines that often compile and work very quickly! So what to do about this? I\nsee basically two paths: spend tokens to make things quickly (aka slop), or spend tokens on deep\nverification and learning.<\/p>\n\n<p>A lot has been written about building novel systems without really having an understanding about how\nthey work, and whether or not that\u2019s actually a good thing. Perhaps you can build <a href=\"https:\/\/steve-yegge.medium.com\/welcome-to-gas-town-4f25ee16dd04\">giant towers of\nabstraction<\/a>, but that isn\u2019t <em>necessarily<\/em> required from the tools. I think that instead\ngoing in this direction of using the capability for improving what existings or learning more deeply\nis much more interesting. Of course, in a personal project it\u2019s much easier to take this time; I am\nmore incentivized to build these sorts of tools for learning and understanding: the process of\nbuilding out the visualization did help me more deeply understand the algorithm.<\/p>\n\n<p>Working at the edge of my own understanding with LLMs is also quite strange; it\u2019s easier to pilot\nClaude through familiar domains and problems. Looking at its output is helpful but because those\nproblems have a familiar shape it\u2019s more akin to working with a colleague. In this scenario, though,\nI knew that the output was correct (one of the original implementation details was building out a\nbig validation suite that tested tens of thousands of graphs against the reference implementation)\nbut didn\u2019t really know why. The process of building this visual artifact helped me to understand why\nin a way that I don\u2019t think would have been possible previously.<\/p>\n\n","pubDate":"Tue, 10 Mar 2026","link":"https:\/\/bensmithgall.com\/\/blog\/claude-blossom-viz","guid":"https:\/\/bensmithgall.com\/\/blog\/claude-blossom-viz"},{"title":"Making a big button to answer FaceTime calls","category":{},"description":"<p>A family member of mine struggles to accept FaceTime calls from the computer. \nImagine if you had limited mobility or physical dexterity and wanted to answer a\nFaceTime call on a Mac. You need to be able to physically move a mouse or\ntrackpad cursor over to a (pretty small) \u201cAccept\u201d button and click on it. I\nmean, just look at the tiny size of this thing:<\/p>\n\n<p><img src=\"..\/..\/images\/a-big-button\/answer.png\" alt=\"A tiny click target\" \/><\/p>\n\n<p>I took this screencap <a href=\"https:\/\/support.apple.com\/en-us\/102215#answer\">directly from Apple!<\/a><\/p>\n\n<p>What if instead you could just mash a giant physical button? Here\u2019s one sort-of\nridiculous way of making this work.<\/p>\n\n<h1 id=\"step-1-automate-the-click\">Step 1: Automate the click<\/h1>\n\n<p>Something I didn\u2019t know but probably should have is that MacOS comes with an\napplication called <a href=\"https:\/\/support.apple.com\/guide\/shortcuts-mac\/intro-to-shortcuts-apdf22b0444c\/mac\">\u201cShortcuts\u201d<\/a>, a pretty powerful automation tool.\nYou can wire it up to do all sorts of different things with different MacOS\napplications with its somewhat convoluted if-this-then-that interface. You can\ncontrol a lot of the built-in applications with Shortcuts with varying levels of\nbuilt-in control. The Notes app, for example, supports Shortcuts for all sorts\nof things like <code class=\"language-plaintext highlighter-rouge\">Append to a Note<\/code> or <code class=\"language-plaintext highlighter-rouge\">Move a Note to a Folder<\/code>. FaceTime,\nhowever, only supports making outbound calls via Shortcuts. This could be\nuseful, but we\u2019re trying to get to the opposite: answering a call.<\/p>\n\n<p>However, Shortcuts can also be used to trigger arbitrary AppleScript commands.\n<a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/AppleScript\/Conceptual\/AppleScriptLangGuide\/introduction\/ASLR_intro.html\">AppleScript<\/a> is a language included on MacOS that provides a way\nof automating tasks. It has a sort of goofy english-esque syntax that you use to\nboss your computer around. Here\u2019s the sample from the documentation that counts\nup the number of files in the <code class=\"language-plaintext highlighter-rouge\">Applications<\/code> directory:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>tell application \"Finder\"\n    if folder \"Applications\" of startup disk exists then\n        return count files in folder \"Applications\" of startup disk\n    else\n        return 0\n    end if\nend tell\n<\/code><\/pre><\/div><\/div>\n\n<p>Some MacOS applications have pretty nice integrations with AppleScript, but the\nproject seems to have largely been abandoned in favor of things like Automator\nand Shortcuts. <a href=\"https:\/\/en.wikipedia.org\/wiki\/AppleScript\">Wikipedia<\/a> has a nice history. In any case,\nFacetime does not have nice integrations with AppleScript, so we have to abandon\nthat and just attempt to automate clicking on buttons on the display instead.\nWithout further ado, here\u2019s an AppleScript to click on that tiny <code class=\"language-plaintext highlighter-rouge\">Accept<\/code>\nbutton:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>tell application \"Shortcuts\"\n  tell application \"System Events\"\n    delay 0.1\n    set acceptButton to a reference to button \"Accept\" of group 1 of UI element 1 of scroll area 1 of group 1 of window 1 of application process \"NotificationCenter\"\n    if acceptButton exists then\n      click acceptButton\n      delay 2\n      repeat while exists (window 1 of process \"FaceTime\")\n        delay 30\n      end repeat\n    end if\n  end tell\nend tell\n<\/code><\/pre><\/div><\/div>\n\n<p>What in the world does this do? Well:<\/p>\n\n<ol>\n  <li><code class=\"language-plaintext highlighter-rouge\">tell application \"System Events\"<\/code> scopes our commands to the \u201cSystem Events\u201d\napplication, a place where non-GUI functions can be executed (like entering a\nkeystroke, etc)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">delay 0.1<\/code> waits for 100ms to give FaceTime time to render the button<\/li>\n  <li>\n    <p>The next big giant block finds the <code class=\"language-plaintext highlighter-rouge\">Accept<\/code> button. It lives in a big element\nhierarchy:\n<code class=\"language-plaintext highlighter-rouge\">NotificationCenter &gt; window &gt; group &gt; scroll area &gt; UI element &gt; button<\/code>.\nThe way I figured this out was by using the <a href=\"https:\/\/developer.apple.com\/documentation\/accessibility\/accessibility-inspector\">XCode Accessability\nInspector<\/a> and working backwards through the display hierarchy:<\/p>\n\n    <p><img src=\"..\/..\/images\/a-big-button\/hierarchy.png\" alt=\"The FaceTime answer button hierarchy for Sonoma\" \/><\/p>\n\n    <p>One big caveat here is that this definitely not future-proof. This element\nhierarchy worked for Sonoma, but inspecting it again for Sequoia shows some\npretty large differences in the layout. In this case, the people I am\nbuilding the button for aren\u2019t planning on upgrading, but it is something to\nkeep in mind.<\/p>\n  <\/li>\n  <li>The rest is just waiting for the button to exist, clicking on it, and then\nkeeping the script running for as long as FaceTime is open. For some reason I\nhad to do that or FaceTime would just close randomly which was very\nfrustrating.<\/li>\n  <li>We also wrap up the whole thing up in a <code class=\"language-plaintext highlighter-rouge\">tell application \"Shortcuts\"<\/code> to try\nand scope down what applications we allow to control our computer via\nAccessibility settings.<\/li>\n<\/ol>\n\n<p>So now that we have our AppleScript, we can bind it into our Shortcuts\napplication and <a href=\"https:\/\/support.apple.com\/guide\/shortcuts-mac\/launch-a-shortcut-from-another-app-apd163eb9f95\/mac#apd94a0e7c32\">give it a keyboard shortcut<\/a>! I chose\n\u2318\u2303\u23258 basically at random.<\/p>\n\n<h1 id=\"step-2-automate-the-keyboard-command\">Step 2: Automate the keyboard command<\/h1>\n\n<p>Now that we have a (terrible) way of clicking on a tiny button by instead\npressing a ton of keys together at the same time, let\u2019s automate the pressing of\nthe keys!<\/p>\n\n<p>You can use a microcontroller to emulate a USB keyboard and have it send the\nkeys in question based on a relevant signal. Since I knew basically nothing\nabout hardware going into this mess, I used an <a href=\"https:\/\/store.arduino.cc\/products\/arduino-micro\">Arduino Micro<\/a>, though\nyou could probably do the same thing with something a bit smaller and cheaper.<\/p>\n\n<p>One nice thing about using Arduino is that there are good instructions and\nguides for getting started and writing your first Arduino sketches. The Arduino\nwebsite has some <a href=\"https:\/\/docs.arduino.cc\/learn\/programming\/sketches\/\">good tutorials<\/a>. Here\u2019s what I came up with\nfor sending keystrokes programatically to a computer:<\/p>\n\n<div class=\"language-cpp highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cp\">#include<\/span> <span class=\"cpf\">&lt;Keyboard.h&gt;<\/span><span class=\"cp\">\n<\/span><span class=\"kt\">int<\/span> <span class=\"n\">btnPin<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">2<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kt\">bool<\/span> <span class=\"n\">currentlyPlaying<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">false<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kt\">void<\/span> <span class=\"nf\">setup<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"n\">pinMode<\/span><span class=\"p\">(<\/span><span class=\"n\">btnPin<\/span><span class=\"p\">,<\/span> <span class=\"n\">INPUT_PULLUP<\/span><span class=\"p\">);<\/span>\n  <span class=\"n\">Keyboard<\/span><span class=\"p\">.<\/span><span class=\"n\">begin<\/span><span class=\"p\">();<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kt\">void<\/span> <span class=\"n\">loop<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">digitalRead<\/span><span class=\"p\">(<\/span><span class=\"n\">btnPin<\/span><span class=\"p\">)<\/span> <span class=\"o\">==<\/span> <span class=\"n\">HIGH<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"n\">currentlyPlaying<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">false<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"n\">digitalRead<\/span><span class=\"p\">(<\/span><span class=\"n\">btnPin<\/span><span class=\"p\">)<\/span> <span class=\"o\">==<\/span> <span class=\"n\">LOW<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"o\">!<\/span><span class=\"n\">currentlyPlaying<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"n\">currentlyPlaying<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">true<\/span><span class=\"p\">;<\/span>\n    <span class=\"n\">Keyboard<\/span><span class=\"p\">.<\/span><span class=\"n\">press<\/span><span class=\"p\">(<\/span><span class=\"n\">KEY_LEFT_GUI<\/span><span class=\"p\">);<\/span>\n    <span class=\"n\">Keyboard<\/span><span class=\"p\">.<\/span><span class=\"n\">press<\/span><span class=\"p\">(<\/span><span class=\"n\">KEY_LEFT_ALT<\/span><span class=\"p\">);<\/span>\n    <span class=\"n\">Keyboard<\/span><span class=\"p\">.<\/span><span class=\"n\">press<\/span><span class=\"p\">(<\/span><span class=\"n\">KEY_LEFT_CTRL<\/span><span class=\"p\">);<\/span>\n    <span class=\"n\">Keyboard<\/span><span class=\"p\">.<\/span><span class=\"n\">press<\/span><span class=\"p\">(<\/span><span class=\"sc\">'8'<\/span><span class=\"p\">);<\/span>\n    <span class=\"n\">Keyboard<\/span><span class=\"p\">.<\/span><span class=\"n\">releaseAll<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"n\">delay<\/span><span class=\"p\">(<\/span><span class=\"mi\">500<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Basically, we:<\/p>\n\n<ol>\n  <li>Set up the <a href=\"https:\/\/docs.arduino.cc\/language-reference\/en\/functions\/usb\/Keyboard\/#functions\">Keyboard library<\/a><\/li>\n  <li>Wait for a signal to come in on pin 2<\/li>\n  <li>Press all of the keys needed at the same time<\/li>\n  <li>Stop once the button is released.<\/li>\n<\/ol>\n\n<p>With this, you can automatically send whatever combination of keys to the\ncomputer whenever you get a signal on a given pin.<\/p>\n\n<h1 id=\"step-3-make-a-big-button\">Step 3: Make a big button<\/h1>\n\n<p>Now that we\u2019ve carefully set up all the pieces of our machine, we can bring them\ntogether using a giant button. I got <a href=\"https:\/\/www.sparkfun.com\/big-dome-pushbutton-green.html\">one of these<\/a>. It makes an\nextremely satisfying \u201cchunk\u201d sound when pressed. The next step was to wire\neverything up. Here\u2019s a small diagram of how this button works:<\/p>\n\n<p><img src=\"..\/..\/images\/a-big-button\/button-diagram.png\" alt=\"A small wiring diagram\" \/><\/p>\n\n<p>Essentially, we want to run a wire from the \u201cpush to make\u201d into the Arduino at\npin 2, and then a wire from the arduino ground pin to the \u201ccommon\u201d terminal on\nthe button. Here\u2019s what that looks like all wired up (sorry for the extremely\nblurry photo):<\/p>\n\n<p><img src=\"..\/..\/images\/a-big-button\/wired-up.jpg\" alt=\"Wired up\" \/><\/p>\n\n<p>One note here is that the button itself has an LED on it, so if you were\ninterested you could also wire in some LED stuff but I just didn\u2019t in the end.<\/p>\n\n<p>I was also very lucky to get a beautiful wooden enclosure built and stained by\nmy wonderful and talented father.<\/p>\n\n<p><img src=\"..\/..\/images\/a-big-button\/enclosed.jpg\" alt=\"The electronics in their enclosure\" \/><\/p>\n\n<p>And here it is all finished:<\/p>\n\n<p><img src=\"..\/..\/images\/a-big-button\/da-button.jpg\" alt=\"Da button\" \/><\/p>\n\n<h1 id=\"recap\">Recap<\/h1>\n\n<p>So, what happens when you push this button?<\/p>\n\n<ol>\n  <li>The <code class=\"language-plaintext highlighter-rouge\">digitalRead<\/code> signal fires on the Arduino, sending \u2318\u2303\u23258\nas a keyboard signal.<\/li>\n  <li>That keyboard combination starts a MacOS \u201cShortcut\u201d which triggers an\nAppleScript<\/li>\n  <li>The AppleScript searches for the \u201cAccept\u201d button on an inbound FaceTime call\nand clicks on it.<\/li>\n  <li>You talk on FaceTime.<\/li>\n<\/ol>\n\n<p>Is it brittle? Yes. Is it hacky? Yes. Does it work? Yes.<\/p>\n\n","pubDate":"Wed, 26 Mar 2025","link":"https:\/\/bensmithgall.com\/\/blog\/a-big-button","guid":"https:\/\/bensmithgall.com\/\/blog\/a-big-button"},{"title":"Self-hosting Obsidian LiveSync on Kubernetes with Tailscale","category":{},"description":"<p>Recently, I\u2019ve been using <a href=\"https:\/\/obsidian.md\/\">Obsidian<\/a> for note taking. It\u2019s fast, has a bunch of\nnice features, a decent vim mode, and rich support for community plugins. I also\nreally like the \u201cfile over app\u201d philosophy that the current CEO <a href=\"https:\/\/stephango.com\/file-over-app\">has written\nabout<\/a>.<\/p>\n\n<p>I also have been running Kubernetes on a cluster of RaspberryPi computers in my\nhouse that I have been using for all sorts of odds and ends. It hosts my <a href=\"https:\/\/elswisser.casita.zone\/\">chess\ntournament website<\/a> that I have <a href=\".\/2023-09-10-phoenix-hamburger-menu.md\">written<\/a> <a href=\".\/2024-05-31-live-view-chess-the-rules.md\">about<\/a>\n<a href=\".\/2024-06-05-live-view-chess-the-board.md\">previously<\/a>. I also use it as a sync server for the incredible\n<a href=\"https:\/\/atuin.sh\/\">atuin<\/a>, host a handful of internal-to-the-house apps, and\nsome other things. The cluster runs <a href=\"https:\/\/k3s.io\/\">k3s<\/a> and uses four Pi\ncomputers that I have accumulated over the years, going as far back as model 2B.<\/p>\n\n<p>One thing I like quite a bit about it is that nothing is exposed to the public\ninternet, except the aforementioned tournament website, which is hidden behind a\n<a href=\"https:\/\/www.cloudflare.com\/products\/tunnel\/\">Cloudflare tunnel<\/a>. Instead, I use a combination of <a href=\"https:\/\/github.com\/blake\/external-mdns\">external-mdns<\/a> and\n<a href=\"https:\/\/tailscale.com\/\">Tailscale<\/a> to make things accessible outside the\ncluster as needed.<\/p>\n\n<p>Obsidian has a plugin for <a href=\"https:\/\/github.com\/vrtmrz\/obsidian-livesync\">self-hosted LiveSync<\/a>, which I really\nappreciate. I like to host these things myself when possible, especially when\nthe cost of failure is low, like it is here. I had some \u201cfun\u201d getting this set\nup with my setup and wanted to write about wiring pieces together.<\/p>\n\n<h1 id=\"couchdb\">CouchDB<\/h1>\n\n<p>I know absolutely nothing about CouchDB. In fact, I hadn\u2019t heard of it before\nlearning that the plugin in based on it, but I did learn that it is written in\nErlang, which is great. So to start off, I had to get CouchDB running in\nkubernetes.<\/p>\n\n<p>CouchDB has a <a href=\"https:\/\/artifacthub.io\/packages\/helm\/couchdb\/couchdb\">helm chart<\/a>, but I opted to write my own deployment\ninstead. In general, I prefer to have my own yaml lying around so I can put it\nin source control and make tweaks later.<\/p>\n\n<p>In any case, the deployment is pretty straightforward. However, there are a\ncouple of gotchas when setting this up from scratch, especially since the\ndocumentation is quite sparse.<\/p>\n\n<p>First, you need to have an admin user and password ready to go via environment\nvariables. I created them as a kubernetes secret and referenced them via\n<code class=\"language-plaintext highlighter-rouge\">secretKeyRef<\/code>.<\/p>\n\n<p>Second, after everything has started, you need to go into the running pod and\nmanually create the required databases. This is discussed in the <a href=\"https:\/\/docs.couchdb.org\/en\/stable\/setup\/single-node.html\">setup\ndocumentation<\/a>, but there are a few additional caveats:<\/p>\n\n<ol>\n  <li>The curl commands in the setup documentation will not work as written because\nCouchDB will only allow you to run them if you are an administrator. You need to\nsign your requests. I used the <a href=\"https:\/\/docs.couchdb.org\/en\/stable\/api\/server\/authn.html#api-auth-session\"><code class=\"language-plaintext highlighter-rouge\">_session<\/code> auth method<\/a> and managed to\ncreate the required databases.<\/li>\n  <li>Setting up CouchDB to operate as a single-node installation isn\u2019t as\nstraightforward as I had hoped. The <a href=\"https:\/\/github.com\/apache\/couchdb\/discussions\/2948\">recommended way<\/a> to do it\nis to publish your own Docker image that has a <code class=\"language-plaintext highlighter-rouge\">local.ini<\/code> file bundled inside\nof it, with the <code class=\"language-plaintext highlighter-rouge\">single_node=true<\/code> flag set in the CouchDB property. This is\nprobably the best way to do it, but I just configured a service and local <code class=\"language-plaintext highlighter-rouge\">mdns<\/code>\ningress and used the management GUI to do it manually.<\/li>\n<\/ol>\n\n<h1 id=\"tailscale\">Tailscale<\/h1>\n\n<p>Now that we\u2019ve got CouchDB set up, we need to have some way to communicate with\nit from Obsidian. One thing that is quite important is that we are able to do so\nvia https; without it, I wouldn\u2019t be able to sync to and from my phone.\n<a href=\"https:\/\/tailscale.com\/\">Tailscale<\/a> is an amazing piece of technology. It wraps\naround <a href=\"https:\/\/www.wireguard.com\/\">WireGuard<\/a> and can make a mesh network out\nof any number of devices. My pi cluster, computer, and phone are all part of the\nsame Tailscale mesh network, so I can do all sorts of cool stuff like <a href=\"https:\/\/tailscale.com\/kb\/1106\/taildrop\">send\nfiles between devices<\/a> or <a href=\"https:\/\/tailscale.com\/kb\/1193\/tailscale-ssh\">ssh from anywhere<\/a> without\nhaving machines exposed to the public internet.<\/p>\n\n<p>One relatively recent feature added to Tailscale was the <a href=\"https:\/\/tailscale.com\/kb\/1236\/kubernetes-operator\">kubernetes\noperator<\/a>. This does a couple of nice things for us, like being able to\nexpose a service directly as a \u201cmachine\u201d in your Tailscale mesh network (aka\ntailnet), or even an entire subnet router. I have found that selectively\nexposing services has been the most straightforward since it can be done just\nwith <a href=\"https:\/\/tailscale.com\/kb\/1236\/kubernetes-operator#exposing-a-cluster-workload-by-annotating-an-existing-service\">annotations<\/a>.<\/p>\n\n<p>Unlike with CouchDB, I used the helm chart for this one. The kubernetes setup is\nquite complicated with a lot of moving parts (service accounts, roles and\nrolebindings, custom resource definitions, etc.). I had tried using the raw yaml\ndirectly for awhile, but it\u2019s still somewhat early and things have moved around\na bit since I started trying it out.<\/p>\n\n<p>In any case, the documentation for the simple case is pretty straightforward\nfrom Tailscale\u2019s website. There are some <a href=\"https:\/\/tailscale.com\/kb\/1153\/enabling-https\">additional steps<\/a> needed in\norder to get HTTPS set up for a tailnet. The main thing for us is that in order\nto use HTTPS, we have to know the <a href=\"https:\/\/tailscale.com\/kb\/1217\/tailnet-name\">full tailnet name<\/a>.  This is\nimportant when actually setting up the LiveSync, since the <code class=\"language-plaintext highlighter-rouge\">tailscale cert<\/code>\ncommand will automatically generate a certificate with the full name and https\nrequests will work as expected.<\/p>\n\n<p>Additionally, annotating a kubernetes service wasn\u2019t sufficient to generate a\ncertificate. In order to get HTTPS traffic working, I had to use a dedicated\ningress, as is outlined <a href=\"https:\/\/tailscale.com\/kb\/1236\/kubernetes-operator?q=kubernetes#exposing-a-service-using-ingress\">in Tailscale documentation<\/a>. I also had to\nmanually ssh into the pod running tailscale for the given ingress and run\n<code class=\"language-plaintext highlighter-rouge\">tailscale cert<\/code> manually in order to generate a certificate. My guess is that I\nhad spent too much time mucking around, and something had gone sideways.<\/p>\n\n<h1 id=\"obsidian-livesync\">Obsidian LiveSync<\/h1>\n\n<p>At this point, there\u2019s a CouchDB instance running in our cluster that is\naccessible over HTTPS to all nodes in our tailnet, so we are ready to set up the\nLiveSync plugin. One really nice thing about the plugin is that once you connect\nit to the CouchDB instance, it can automatically configure the database to have\nthe appropriate configuration and CORS settings etc.<\/p>\n\n<p>At this point, everything should be set up and sync can be enabled. The URI will\nbe the full name given to our CouchDB ingress. There\u2019s a couple of other really\nthings about this setup:<\/p>\n\n<ul>\n  <li>Each vault goes to its own CouchDB database, which means that it is possible\nto sync multiple vaults simultaneously through the same instance<\/li>\n  <li>\u201cFile over app\u201d mentality means that the files are distributed onto each\ndevice without being dependent on the database directly. If something goes\nsideways with CouchDB, it just be deleted and rebuilt from the Obsidian vault\ndirectly.<\/li>\n<\/ul>\n\n","pubDate":"Mon, 17 Jun 2024","link":"https:\/\/bensmithgall.com\/\/blog\/obsidian-self-hosted-livesync","guid":"https:\/\/bensmithgall.com\/\/blog\/obsidian-self-hosted-livesync"},{"title":"Building a chess application with Phoenix LiveView (Part 2)","category":{},"description":"<p>Now that we have a <a href=\"..\/_posts\/2024-05-31-live-view-chess-the-rules.md\">working\nAPI<\/a> to create and validate\nmoves on a chess board, we can go ahead and turn that into a little chess\napplication using Phoenix LiveView. In this part, we\u2019ll get pieces onto a\nrepresentation of the board and enable moving them via <code class=\"language-plaintext highlighter-rouge\">phx-click<\/code> events. From\nthere (and possibly in future posts) we can explore other features, like\ndisplaying the game\u2019s moves, piece drag and drop, implementing a live game\nserver, or even generating an engine to play against.<\/p>\n\n<p>Quick note: syntax highlighting is a <a href=\"https:\/\/github.com\/rouge-ruby\/rouge\/issues\/2051\">bit broken<\/a> for HEEx templates, so\nI\u2019ve disabled it for now. I have a proposed fix but it may take some time to\ntrickle all the way down to Jekyll and into the blog.<\/p>\n\n<h1 id=\"1-the-board\">1: The board<\/h1>\n\n<p>Before anything else, we\u2019ll need an empty board state. Tailwind and CSS grid\nmake this pretty straightforward:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>defmodule ElchesserWeb.LiveBoard do\n  use ElchesserWeb, :live_view\n\n  def render(assigns) do\n    ~H\"\"\"\n    &lt;div class=\"w-[388px] m-auto left-0 right-0\"&gt;\n      &lt;div class=\"border border-2 border-zinc-700 grid grid-rows-8 grid-cols-8\"&gt;\n        &lt;%= for rank &lt;- Elchesser.ranks() |&gt; Enum.reverse() do %&gt;\n          &lt;%= for file &lt;- Elchesser.files() do %&gt;\n            &lt;div class={[\"w-12 h-12\", background(file, rank)]}&gt;&lt;\/div&gt;\n          &lt;% end %&gt;\n        &lt;% end %&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  defp background(file, rank) when rem(file + rank, 2) != 0, do: \"bg-boardwhite\"\n  defp background(_, _), do: \"bg-boardblack\"\n\nend\n<\/code><\/pre><\/div><\/div>\n\n<p>The main interesting thing going on here is <code class=\"language-plaintext highlighter-rouge\">bg-boardwhite<\/code> and <code class=\"language-plaintext highlighter-rouge\">bg-boardblack<\/code>\nin the <code class=\"language-plaintext highlighter-rouge\">background<\/code> function. Tailwind allows for custom colors to be added to\nthe <a href=\"https:\/\/tailwindcss.com\/docs\/theme#colors\">theme<\/a>, so we can go ahead and pick some out so that we can be sure to have\nsome visual consistency (I am using <code class=\"language-plaintext highlighter-rouge\">#f2f5f3<\/code> for <code class=\"language-plaintext highlighter-rouge\">boardwhite<\/code> and <code class=\"language-plaintext highlighter-rouge\">#71828f<\/code> for\n<code class=\"language-plaintext highlighter-rouge\">boardblack<\/code>):<\/p>\n\n<p><img src=\"..\/..\/images\/elchesser\/empty-board.png\" alt=\"Empty board\" \/><\/p>\n\n<h1 id=\"2-the-pieces\">2. The pieces<\/h1>\n\n<p>Now, we\u2019ll need to put some pieces on the board. To start, we\u2019ll need some SVG\nshapes for the pieces. <a href=\"https:\/\/simple.wikipedia.org\/wiki\/Chess_piece\">These pieces<\/a> from Wikipedia by Colin Burnett\nare very well-known and have a friendly license, so those are good to start.<\/p>\n\n<h2 id=\"making-them-accessible-as-icons\">Making them accessible as icons<\/h2>\n\n<p>A fresh install of Phoenix 1.7+ comes with a handy <code class=\"language-plaintext highlighter-rouge\">CoreComponents<\/code> module,\nwhich includes a bunch of nice functional components for getting started. We can\nuse the <code class=\"language-plaintext highlighter-rouge\">icon\/1<\/code> function as inspiration. Let\u2019s take a look at how that works:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  <span class=\"nv\">@doc<\/span> <span class=\"sd\">\"\"\"\n  Renders a [Hero Icon](https:\/\/heroicons.com).\n\n  Hero icons come in three styles \u2013 outline, solid, and mini.\n  By default, the outline style is used, but solid an mini may\n  be applied by using the `-solid` and `-mini` suffix.\n\n  You can customize the size and colors of the icons by setting\n  width, height, and background color classes.\n\n  Icons are extracted from your `assets\/vendor\/heroicons` directory and bundled\n  within your compiled app.css by the plugin in your `assets\/tailwind.config.js`.\n\n  ## Examples\n\n      &lt;.icon name=\"hero-x-mark-solid\" \/&gt;\n      &lt;.icon name=\"hero-arrow-path\" class=\"ml-1 w-3 h-3 animate-spin\" \/&gt;\n  \"\"\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">icon<\/span><span class=\"p\">(%{<\/span><span class=\"ss\">name:<\/span> <span class=\"s2\">\"hero-\"<\/span> <span class=\"o\">&lt;&gt;<\/span> <span class=\"n\">_<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">assigns<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"sx\">~H\"\"<\/span><span class=\"s2\">\"\n    &lt;span class={[@name, @class]} \/&gt;\n    \"\"\"<\/span>\n  <span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>When examining one of the icons in the browser, we see that the <code class=\"language-plaintext highlighter-rouge\">hero-<\/code> classes\nare registered in tailwind as an svg <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/mask\">mask<\/a>:<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ tailwind.config.js<\/span>\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"na\">plugins<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n    <span class=\"c1\">\/\/ Embeds Hero Icons (https:\/\/heroicons.com) into your app.css bundle<\/span>\n    <span class=\"c1\">\/\/ See your `CoreComponents.icon\/1` for more information.<\/span>\n    <span class=\"c1\">\/\/<\/span>\n    <span class=\"nx\">plugin<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span> <span class=\"p\">({<\/span> <span class=\"nx\">matchComponents<\/span><span class=\"p\">,<\/span> <span class=\"nx\">theme<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n      <span class=\"kd\">let<\/span> <span class=\"nx\">iconsDir<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"nx\">__dirname<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/vendor\/heroicons\/optimized<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n      <span class=\"kd\">let<\/span> <span class=\"nx\">values<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{};<\/span>\n      <span class=\"kd\">let<\/span> <span class=\"nx\">icons<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n        <span class=\"p\">[<\/span><span class=\"dl\">\"\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">\/24\/outline<\/span><span class=\"dl\">\"<\/span><span class=\"p\">],<\/span>\n        <span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">-solid<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">\/24\/solid<\/span><span class=\"dl\">\"<\/span><span class=\"p\">],<\/span>\n        <span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">-mini<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">\/20\/solid<\/span><span class=\"dl\">\"<\/span><span class=\"p\">],<\/span>\n      <span class=\"p\">];<\/span>\n      <span class=\"nx\">icons<\/span><span class=\"p\">.<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">(([<\/span><span class=\"nx\">suffix<\/span><span class=\"p\">,<\/span> <span class=\"nx\">dir<\/span><span class=\"p\">])<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">fs<\/span><span class=\"p\">.<\/span><span class=\"nx\">readdirSync<\/span><span class=\"p\">(<\/span><span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"nx\">iconsDir<\/span><span class=\"p\">,<\/span> <span class=\"nx\">dir<\/span><span class=\"p\">)).<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">file<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n          <span class=\"kd\">let<\/span> <span class=\"nx\">name<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">basename<\/span><span class=\"p\">(<\/span><span class=\"nx\">file<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.svg<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">suffix<\/span><span class=\"p\">;<\/span>\n          <span class=\"nx\">values<\/span><span class=\"p\">[<\/span><span class=\"nx\">name<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">name<\/span><span class=\"p\">,<\/span> <span class=\"na\">fullPath<\/span><span class=\"p\">:<\/span> <span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"nx\">iconsDir<\/span><span class=\"p\">,<\/span> <span class=\"nx\">dir<\/span><span class=\"p\">,<\/span> <span class=\"nx\">file<\/span><span class=\"p\">)<\/span> <span class=\"p\">};<\/span>\n        <span class=\"p\">});<\/span>\n      <span class=\"p\">});<\/span>\n      <span class=\"nx\">matchComponents<\/span><span class=\"p\">(<\/span>\n        <span class=\"p\">{<\/span>\n          <span class=\"na\">hero<\/span><span class=\"p\">:<\/span> <span class=\"p\">({<\/span> <span class=\"nx\">name<\/span><span class=\"p\">,<\/span> <span class=\"nx\">fullPath<\/span> <span class=\"p\">})<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n            <span class=\"kd\">let<\/span> <span class=\"nx\">content<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">fs<\/span>\n              <span class=\"p\">.<\/span><span class=\"nx\">readFileSync<\/span><span class=\"p\">(<\/span><span class=\"nx\">fullPath<\/span><span class=\"p\">)<\/span>\n              <span class=\"p\">.<\/span><span class=\"nx\">toString<\/span><span class=\"p\">()<\/span>\n              <span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"sr\">\/<\/span><span class=\"se\">\\r?\\n<\/span><span class=\"sr\">|<\/span><span class=\"se\">\\r<\/span><span class=\"sr\">\/g<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">);<\/span>\n            <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n              <span class=\"p\">[<\/span><span class=\"s2\">`--hero-<\/span><span class=\"p\">${<\/span><span class=\"nx\">name<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">]:<\/span> <span class=\"s2\">`url('data:image\/svg+xml;utf8,<\/span><span class=\"p\">${<\/span><span class=\"nx\">content<\/span><span class=\"p\">}<\/span><span class=\"s2\">')`<\/span><span class=\"p\">,<\/span>\n              <span class=\"dl\">\"<\/span><span class=\"s2\">-webkit-mask<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">`var(--hero-<\/span><span class=\"p\">${<\/span><span class=\"nx\">name<\/span><span class=\"p\">}<\/span><span class=\"s2\">)`<\/span><span class=\"p\">,<\/span>\n              <span class=\"na\">mask<\/span><span class=\"p\">:<\/span> <span class=\"s2\">`var(--hero-<\/span><span class=\"p\">${<\/span><span class=\"nx\">name<\/span><span class=\"p\">}<\/span><span class=\"s2\">)`<\/span><span class=\"p\">,<\/span>\n              <span class=\"dl\">\"<\/span><span class=\"s2\">background-color<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">currentColor<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n              <span class=\"dl\">\"<\/span><span class=\"s2\">vertical-align<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">middle<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n              <span class=\"na\">display<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">inline-block<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n              <span class=\"na\">width<\/span><span class=\"p\">:<\/span> <span class=\"nx\">theme<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">spacing.5<\/span><span class=\"dl\">\"<\/span><span class=\"p\">),<\/span>\n              <span class=\"na\">height<\/span><span class=\"p\">:<\/span> <span class=\"nx\">theme<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">spacing.5<\/span><span class=\"dl\">\"<\/span><span class=\"p\">),<\/span>\n            <span class=\"p\">};<\/span>\n          <span class=\"p\">},<\/span>\n        <span class=\"p\">},<\/span>\n        <span class=\"p\">{<\/span> <span class=\"nx\">values<\/span> <span class=\"p\">},<\/span>\n      <span class=\"p\">);<\/span>\n    <span class=\"p\">}),<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>So, when you include a <code class=\"language-plaintext highlighter-rouge\">&lt;.icon name=\"hero-arrow-path\"\/&gt;<\/code> component, it generates\na <code class=\"language-plaintext highlighter-rouge\">&lt;span&gt;&lt;\/span&gt;<\/code> tag that has a mask made up of the contents of the file\nlocated at <code class=\"language-plaintext highlighter-rouge\">.\/vendor\/heroicons\/optimized\/24\/outline\/arrow-path.svg<\/code>.<\/p>\n\n<p>We can use this same exact technique to generate similar <code class=\"language-plaintext highlighter-rouge\">&lt;span&gt;&lt;\/span&gt;<\/code> tags\nthat have the contents of our piece SVGs. First, we need to add a new plugin to\nour tailwind configuration (this plugin assumes that our piece svgs are stored\nin <code class=\"language-plaintext highlighter-rouge\">assets\/pieces<\/code>):<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">\/\/ tailwind.config.js<\/span>\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"na\">plugins<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\n    <span class=\"nx\">plugin<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span> <span class=\"p\">({<\/span> <span class=\"nx\">matchComponents<\/span><span class=\"p\">,<\/span> <span class=\"nx\">theme<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n      <span class=\"kd\">let<\/span> <span class=\"nx\">piecesDir<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"nx\">__dirname<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.\/pieces<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n      <span class=\"kd\">let<\/span> <span class=\"nx\">values<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{};<\/span>\n\n      <span class=\"kd\">let<\/span> <span class=\"nx\">colors<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">white<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">black<\/span><span class=\"dl\">\"<\/span><span class=\"p\">];<\/span>\n\n      <span class=\"nx\">colors<\/span><span class=\"p\">.<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">((<\/span><span class=\"nx\">color<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">fs<\/span><span class=\"p\">.<\/span><span class=\"nx\">readdirSync<\/span><span class=\"p\">(<\/span><span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"nx\">piecesDir<\/span><span class=\"p\">,<\/span> <span class=\"nx\">color<\/span><span class=\"p\">)).<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">file<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n          <span class=\"kd\">let<\/span> <span class=\"nx\">name<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">color<\/span><span class=\"p\">}<\/span><span class=\"s2\">-<\/span><span class=\"p\">${<\/span><span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">basename<\/span><span class=\"p\">(<\/span><span class=\"nx\">file<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">.svg<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)}<\/span><span class=\"s2\">`<\/span><span class=\"p\">;<\/span>\n          <span class=\"nx\">values<\/span><span class=\"p\">[<\/span><span class=\"nx\">name<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">name<\/span><span class=\"p\">,<\/span> <span class=\"na\">fullPath<\/span><span class=\"p\">:<\/span> <span class=\"nx\">path<\/span><span class=\"p\">.<\/span><span class=\"nx\">join<\/span><span class=\"p\">(<\/span><span class=\"nx\">piecesDir<\/span><span class=\"p\">,<\/span> <span class=\"nx\">color<\/span><span class=\"p\">,<\/span> <span class=\"nx\">file<\/span><span class=\"p\">)<\/span> <span class=\"p\">};<\/span>\n        <span class=\"p\">});<\/span>\n      <span class=\"p\">});<\/span>\n\n      <span class=\"nx\">matchComponents<\/span><span class=\"p\">(<\/span>\n        <span class=\"p\">{<\/span>\n          <span class=\"na\">piece<\/span><span class=\"p\">:<\/span> <span class=\"p\">({<\/span> <span class=\"nx\">fullPath<\/span> <span class=\"p\">})<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n            <span class=\"kd\">let<\/span> <span class=\"nx\">content<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">fs<\/span>\n              <span class=\"p\">.<\/span><span class=\"nx\">readFileSync<\/span><span class=\"p\">(<\/span><span class=\"nx\">fullPath<\/span><span class=\"p\">)<\/span>\n              <span class=\"p\">.<\/span><span class=\"nx\">toString<\/span><span class=\"p\">()<\/span>\n              <span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"sr\">\/<\/span><span class=\"se\">\\r?\\n<\/span><span class=\"sr\">|<\/span><span class=\"se\">\\r<\/span><span class=\"sr\">\/g<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">);<\/span>\n\n            <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n              <span class=\"dl\">\"<\/span><span class=\"s2\">background-image<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"s2\">`url('data:image\/svg+xml;base64,<\/span><span class=\"p\">${<\/span><span class=\"nx\">Buffer<\/span><span class=\"p\">.<\/span><span class=\"k\">from<\/span><span class=\"p\">(<\/span>\n                <span class=\"nx\">content<\/span><span class=\"p\">,<\/span>\n              <span class=\"p\">).<\/span><span class=\"nx\">toString<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">base64<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)}<\/span><span class=\"s2\">')`<\/span><span class=\"p\">,<\/span>\n              <span class=\"na\">display<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">inline-block<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n              <span class=\"na\">width<\/span><span class=\"p\">:<\/span> <span class=\"nx\">theme<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">spacing.5<\/span><span class=\"dl\">\"<\/span><span class=\"p\">),<\/span>\n              <span class=\"na\">height<\/span><span class=\"p\">:<\/span> <span class=\"nx\">theme<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">spacing.5<\/span><span class=\"dl\">\"<\/span><span class=\"p\">),<\/span>\n              <span class=\"dl\">\"<\/span><span class=\"s2\">background-size<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">contain<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n            <span class=\"p\">};<\/span>\n          <span class=\"p\">},<\/span>\n        <span class=\"p\">},<\/span>\n        <span class=\"p\">{<\/span> <span class=\"nx\">values<\/span> <span class=\"p\">},<\/span>\n      <span class=\"p\">);<\/span>\n    <span class=\"p\">}),<\/span>\n  <span class=\"p\">],<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This will slurp in all of the pieces under <code class=\"language-plaintext highlighter-rouge\">pieces\/white<\/code> and <code class=\"language-plaintext highlighter-rouge\">pieces\/black<\/code> and\ngenerate mappings for them. Note that we use <code class=\"language-plaintext highlighter-rouge\">background-image<\/code> along with the\n<code class=\"language-plaintext highlighter-rouge\">base64<\/code> encoding for these.<\/p>\n\n<p>From here, we can add a new <code class=\"language-plaintext highlighter-rouge\">piece\/1<\/code> function to our live board:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  <span class=\"n\">attr<\/span><span class=\"p\">(<\/span><span class=\"ss\">:piece<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:atom<\/span><span class=\"p\">)<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">piece<\/span><span class=\"p\">(<\/span><span class=\"n\">assigns<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"sx\">~H\"\"<\/span><span class=\"s2\">\"\n    &lt;span class={[piece_name(@piece), @class]} \/&gt;\n    \"\"\"<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:P<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-white-pawn\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:p<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-black-pawn\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:N<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-white-knight\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:n<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-black-knight\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:B<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-white-bishop\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:b<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-black-bishop\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:R<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-white-rook\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:r<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-black-rook\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:Q<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-white-queen\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:q<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-black-queen\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:K<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-white-king\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"ss\">:k<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"piece-black-king\"<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">piece_name<\/span><span class=\"p\">(<\/span><span class=\"no\">nil<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"\"<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now, we should be able to load in the pieces onto our board based on our game:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>defmodule ElchesserWeb.LiveBoard do\n  use ElchesserWeb, :live_view\n\n  alias Elchesser.Game\n\n  def mount(_, _, socket) do\n    {:ok, socket |&gt; assign(game: Game.new())}\n  end\n\n  attr(:game, Game)\n\n  def render(assigns) do\n    ~H\"\"\"\n    &lt;div class=\"w-[388px] m-auto left-0 right-0\"&gt;\n      &lt;div class=\"border border-2 border-zinc-700 grid grid-rows-8 grid-cols-8\"&gt;\n        &lt;%= for rank &lt;- Elchesser.ranks() |&gt; Enum.reverse() do %&gt;\n          &lt;%= for file &lt;- Elchesser.files() do %&gt;\n            &lt;.square square={Game.get_square(@game, {file, rank})}&gt;\n          &lt;% end %&gt;\n        &lt;% end %&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  attr(:square, :map)\n\n  def square(assigns) do\n    ~H\"\"\"\n    &lt;div class={[\"w-12 h-12 text-center\", background(@square.file, @square.rank)]}&gt;\n      &lt;.square_contents piece={@square.piece} \/&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  attr(:piece, :atom)\n\n  def square_contents(%{piece: nil} = assigns), do: ~H\"\"\n\n  def square_contents(assigns) do\n    ~H\"\"\"\n    &lt;.piece piece={@piece} class=\"w-[45px] h-[45px] mt-[1.5px]\" \/&gt;\n    \"\"\"\n  end\n\n  defp background(file, rank) when rem(file + rank, 2) != 0, do: \"bg-boardwhite\"\n  defp background(_, _), do: \"bg-boardblack\"\nend\n<\/code><\/pre><\/div><\/div>\n\n<p>There\u2019s a few changes here to highlight. First, we added a <a href=\"https:\/\/hexdocs.pm\/phoenix_live_view\/Phoenix.LiveView.html#c:mount\/3\"><code class=\"language-plaintext highlighter-rouge\">mount\/3<\/code><\/a>\ncall. This is the entrypoint to our live view, and we use it to initialize our\nnew game. Second, we broke out the square into a function component. This will\nbecome more important later when we add click handlers to the square itself.\nFinally, we include a <code class=\"language-plaintext highlighter-rouge\">square_contents\/1<\/code> function component to include what\nshould go into the square itself. All this together looks like this:<\/p>\n\n<p><img src=\"..\/..\/images\/elchesser\/starting-position.png\" alt=\"Starting position\" \/><\/p>\n\n<h1 id=\"3-selecting-a-piece-to-move\">3. Selecting a piece to move<\/h1>\n\n<p>Now we can really take advantage of Phoenix LiveView by tracking clicks on\nsquares and changing our board state. The first thing to do is to add click\ntracking on the squares themselves:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  attr(:square, Square)\n  attr(:click_type, :atom, values: [:select, :move])\n\n  def square(assigns) do\n    ~H\"\"\"\n    &lt;div\n      class={[\"w-12 h-12 text-center\", background(@square.file, @square.rank)]}\n      phx-click=\"square-click\"\n      phx-value-file={@square.file}\n      phx-value-rank={@square.rank}\n      phx-value-type={@click_type}\n    &gt;\n      &lt;.square_contents piece={@square.piece} \/&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n<\/code><\/pre><\/div><\/div>\n\n<p>When clicked, each square will now send an event up to our live view server with\nthe name <code class=\"language-plaintext highlighter-rouge\">square-click<\/code> and data in the form of something like <code class=\"language-plaintext highlighter-rouge\">%{\"file\" =&gt; \"a\",\n\"rank\" =&gt; \"1\", \"type\" =&gt; \"select\"}<\/code> (for more, see <a href=\"https:\/\/hexdocs.pm\/phoenix_live_view\/bindings.html#click-events\"><code class=\"language-plaintext highlighter-rouge\">phx-click<\/code><\/a>\ndocumentation). Now, we need to set up an event handler for this event. Since we\naren\u2019t going to be implementing drag-and-drop functionality in this post, we\u2019ll\nwant to have two separate handlers: one for selecting a piece, and one for\nactually moving it.<\/p>\n\n<p>When we select a piece, we\u2019ll want to get all legal moves for that piece and\nhighlight them on the board. Then, when we move the piece, we\u2019ll need to update\nthe board state as expected and remove the highlights.<\/p>\n\n<p>Let\u2019s start with piece selection:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  <span class=\"k\">def<\/span> <span class=\"n\">handle_event<\/span><span class=\"p\">(<\/span><span class=\"s2\">\"square-click\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">%{<\/span><span class=\"s2\">\"file\"<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"n\">f<\/span><span class=\"p\">,<\/span> <span class=\"s2\">\"rank\"<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"n\">r<\/span><span class=\"p\">,<\/span> <span class=\"s2\">\"type\"<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s2\">\"select\"<\/span><span class=\"p\">},<\/span> <span class=\"n\">socket<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">loc<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"no\">String<\/span><span class=\"o\">.<\/span><span class=\"n\">to_integer<\/span><span class=\"p\">(<\/span><span class=\"n\">f<\/span><span class=\"p\">),<\/span> <span class=\"no\">String<\/span><span class=\"o\">.<\/span><span class=\"n\">to_integer<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">)}<\/span>\n    <span class=\"p\">{<\/span><span class=\"ss\">:noreply<\/span><span class=\"p\">,<\/span> <span class=\"n\">socket<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"n\">on_select_click<\/span><span class=\"p\">(<\/span><span class=\"n\">loc<\/span><span class=\"p\">)}<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"k\">defp<\/span> <span class=\"n\">on_select_click<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">square<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"o\">.<\/span><span class=\"n\">assigns<\/span><span class=\"o\">.<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">socket<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span>\n      <span class=\"ss\">move_map:<\/span>\n        <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">legal_moves<\/span><span class=\"p\">(<\/span><span class=\"n\">loc<\/span><span class=\"p\">,<\/span> <span class=\"n\">socket<\/span><span class=\"o\">.<\/span><span class=\"n\">assigns<\/span><span class=\"o\">.<\/span><span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">reduce<\/span><span class=\"p\">(%{},<\/span> <span class=\"k\">fn<\/span> <span class=\"n\">move<\/span><span class=\"p\">,<\/span> <span class=\"n\">acc<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">put<\/span><span class=\"p\">(<\/span><span class=\"n\">acc<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n    <span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span><span class=\"ss\">click_type:<\/span> <span class=\"ss\">:move<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span>\n      <span class=\"ss\">active_square:<\/span>\n        <span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"n\">color_match?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">socket<\/span><span class=\"o\">.<\/span><span class=\"n\">assigns<\/span><span class=\"o\">.<\/span><span class=\"n\">game<\/span><span class=\"o\">.<\/span><span class=\"n\">active<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"k\">else<\/span><span class=\"p\">:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">)<\/span>\n    <span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now we need to update our board state based based on the new <code class=\"language-plaintext highlighter-rouge\">active_square<\/code> and\n<code class=\"language-plaintext highlighter-rouge\">move_map<\/code> attributes that we\u2019ve calculated from our click:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  attr(:game, Elchesser.Game)\n  attr(:click_type, :atom, values: [:select, :move])\n  attr(:active_square, Elchesser.Square, default: nil)\n\n  def render(assigns) do\n    ~H\"\"\"\n    &lt;div class=\"w-[388px] m-auto left-0 right-0\"&gt;\n      &lt;div class=\"border border-2 border-zinc-700 grid grid-rows-8 grid-cols-8\"&gt;\n        &lt;%= for rank &lt;- Elchesser.ranks() |&gt; Enum.reverse() do %&gt;\n          &lt;%= for file &lt;- Elchesser.files() do %&gt;\n            &lt;.square\n              square={Elchesser.Game.get_square(@game, {file, rank})}\n              click_type={@click_type}\n              highlight={Map.has_key?(@move_map, {file, rank})}\n              active={not is_nil(@active_square) &amp;&amp; @active_square.loc == {file, rank}}\n            \/&gt;\n          &lt;% end %&gt;\n        &lt;% end %&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n<\/code><\/pre><\/div><\/div>\n\n<p>Now, we can re-style our squares after a piece gets selected. First, let\u2019s\nhighlight our active square and forward the <code class=\"language-plaintext highlighter-rouge\">highlight<\/code> attribute down to the\n<code class=\"language-plaintext highlighter-rouge\">square_contents\/1<\/code> component.<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  attr(:square, Square)\n  attr(:click_type, :atom, values: [:select, :move])\n  attr(:highlight, :boolean, default: false)\n  attr(:active, :boolean, default: false)\n\n  def board_square(assigns) do\n    ~H\"\"\"\n    &lt;div\n      class={[\n        \"w-12 h-12 text-center\",\n        background(@square.file, @square.rank),\n        @active &amp;&amp; \"bg-purple-200\/60\"\n      ]}\n      phx-click=\"square-click\"\n      phx-value-file={@square.file}\n      phx-value-rank={@square.rank}\n      phx-value-type={@click_type}\n    &gt;\n      &lt;.square_contents piece={@square.piece} highlight={@highlight} \/&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n<\/code><\/pre><\/div><\/div>\n\n<p>Next, let\u2019s highlight squares in our <code class=\"language-plaintext highlighter-rouge\">move_map<\/code>. We\u2019ll need to handle cases\n when the target square is empty, and when it already has a piece:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  attr(:piece, :atom)\n  attr(:highlight, :boolean, default: false)\n\n  # Yes piece, no highlight\n  def square_contents(%{piece: _, highlight: false} = assigns) do\n    ~H\"\"\"\n    &lt;.piece piece={@piece} class=\"w-[45px] h-[45px] mt-[1.5px]\" \/&gt;\n    \"\"\"\n  end\n\n  # No piece, yes highlight\n  def square_contents(%{piece: nil, highlight: true} = assigns) do\n    ~H\"\"\"\n    &lt;span class=\"w-[45px] h-[45px] mt-[1.5px] inline-block rounded-full border-2 border-zinc-600\" \/&gt;\n    \"\"\"\n  end\n\n  # Yes piece, yes highlight\n  def square_contents(%{piece: _, highlight: true} = assigns) do\n    ~H\"\"\"\n    &lt;div class=\"relative\"&gt;\n      &lt;span class=\"absolute w-[45px] h-[45px] mt-[1.5px] inline-block rounded-full border-2 border-zinc-600\" \/&gt;\n      &lt;.piece piece={@piece} class=\"w-[45px] h-[45px] mt-[1.5px]\" \/&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  # No piece, no highlight\n  def square_contents(%{piece: nil} = assigns), do: ~H\"\"\n<\/code><\/pre><\/div><\/div>\n\n<p>With all of this, clicking on a square that has a piece with legal moves should\nhighlight those moves on our board:<\/p>\n\n<p><img src=\"..\/..\/images\/elchesser\/highlighted-moves.png\" alt=\"A highlighted piece\" \/><\/p>\n\n<h1 id=\"4-making-moves\">4. Making moves<\/h1>\n\n<p>Finally, we can implement making moves on our chess board. In <a href=\"..\/_posts\/2024-05-31-live-view-chess-the-rules.md\">part\none<\/a>, we wrote a\n<code class=\"language-plaintext highlighter-rouge\">Game.move\/2<\/code> function which returns <code class=\"language-plaintext highlighter-rouge\">{:ok, game}<\/code>, or <code class=\"language-plaintext highlighter-rouge\">{:error, reason}<\/code>. We\ncan use the results of this function to update our board. We\u2019ll use the\nfollowing logic:<\/p>\n\n<ul>\n  <li>Valid moves should update the board state properly<\/li>\n  <li>If we try to move a piece onto a square that is occupied by a friendly piece,\nwe should instead select that new piece<\/li>\n  <li>Otherwise, we should clear out our \u201cselected\u201d state and put the board back to\nnormal<\/li>\n<\/ul>\n\n<p>Fortunately, pattern matching makes this fairly straightforward:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  <span class=\"k\">def<\/span> <span class=\"n\">handle_event<\/span><span class=\"p\">(<\/span><span class=\"s2\">\"square-click\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">%{<\/span><span class=\"s2\">\"file\"<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"n\">f<\/span><span class=\"p\">,<\/span> <span class=\"s2\">\"rank\"<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"n\">r<\/span><span class=\"p\">,<\/span> <span class=\"s2\">\"type\"<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s2\">\"move\"<\/span><span class=\"p\">},<\/span> <span class=\"n\">socket<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">loc<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"no\">String<\/span><span class=\"o\">.<\/span><span class=\"n\">to_integer<\/span><span class=\"p\">(<\/span><span class=\"n\">f<\/span><span class=\"p\">),<\/span> <span class=\"no\">String<\/span><span class=\"o\">.<\/span><span class=\"n\">to_integer<\/span><span class=\"p\">(<\/span><span class=\"n\">r<\/span><span class=\"p\">)}<\/span>\n\n    <span class=\"n\">socket<\/span> <span class=\"o\">=<\/span>\n      <span class=\"k\">case<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"o\">.<\/span><span class=\"n\">assigns<\/span><span class=\"o\">.<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"o\">.<\/span><span class=\"n\">assigns<\/span><span class=\"o\">.<\/span><span class=\"n\">move_map<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">))<\/span> <span class=\"k\">do<\/span>\n        <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span>\n          <span class=\"n\">socket<\/span>\n          <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span><span class=\"ss\">game:<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n          <span class=\"o\">|&gt;<\/span> <span class=\"n\">on_move_click<\/span><span class=\"p\">()<\/span>\n\n        <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"n\">err<\/span><span class=\"p\">}<\/span> <span class=\"ow\">when<\/span> <span class=\"n\">err<\/span> <span class=\"ow\">in<\/span> <span class=\"p\">[<\/span><span class=\"ss\">:invalid_to_color<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:invalid_from_color<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:no_move_provided<\/span><span class=\"p\">]<\/span> <span class=\"o\">-&gt;<\/span>\n          <span class=\"n\">on_select_click<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">)<\/span>\n\n        <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"n\">_<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span>\n          <span class=\"n\">on_move_click<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"p\">)<\/span>\n      <span class=\"k\">end<\/span>\n\n    <span class=\"p\">{<\/span><span class=\"ss\">:noreply<\/span><span class=\"p\">,<\/span> <span class=\"n\">socket<\/span><span class=\"p\">}<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"k\">defp<\/span> <span class=\"n\">on_move_click<\/span><span class=\"p\">(<\/span><span class=\"n\">socket<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">socket<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span><span class=\"ss\">move_map:<\/span> <span class=\"p\">%{})<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span><span class=\"ss\">click_type:<\/span> <span class=\"ss\">:select<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"n\">assign<\/span><span class=\"p\">(<\/span><span class=\"ss\">active_square:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>And that should do it! Now we have a fully working game of chess implemented\nwith Elixir and playable via LiveView.<\/p>\n\n<p><img src=\"..\/..\/images\/elchesser\/tiny-game.gif\" alt=\"Everything works\" \/><\/p>\n\n<p>From here there are a lot of things that can be done, like hooking up a\n<code class=\"language-plaintext highlighter-rouge\">GenServer<\/code> to actually play games, adding additional features to our board like\ndrag and drop to move, showing the game history, and more.<\/p>\n\n","pubDate":"Wed, 05 Jun 2024","link":"https:\/\/bensmithgall.com\/\/blog\/live-view-chess-the-board","guid":"https:\/\/bensmithgall.com\/\/blog\/live-view-chess-the-board"},{"title":"Building a chess application with Phoenix LiveView (Part 1)","category":{},"description":"<p>Recently I have been doing some more\n<a href=\"..\/_posts\/2023-09-10-phoenix-hamburger-menu.md\">explorations<\/a> of\n<a href=\"https:\/\/elixir-lang.org\/\">Elixir<\/a> and <a href=\"https:\/\/www.phoenixframework.org\/\">Phoenix<\/a>. After watching this <a href=\"https:\/\/www.youtube.com\/watch?v=U4ogK0MIzqk\">great video<\/a> about\nhand-implementing a chess engine, I got inspired to try and write my own\nimplementations for the rules of chess and my own Chessboard layout for the web\nwith <a href=\"https:\/\/hexdocs.pm\/phoenix_live_view\/Phoenix.LiveView.html\">Phoenix LiveView<\/a>.  I want to note that I\u2019m not a great chess\nprogrammer or Elixir expert at this point. For expert takes on things, you might\ntry the <a href=\"https:\/\/www.chessprogramming.org\/Main_Page\">Chess programming wiki<\/a>, or\nthe <a href=\"https:\/\/elixirforum.com\/\">Elixir forums<\/a>. I wanted to share my progress\nwith that here in an occasionally recurring series as I add more bits and pieces\nto this implementation, such as writing my own (comparatively weak) engine, or\nadding live play functionality. But before we get to any of that interesting\nstuff, we first need to get a mostly working model for the rules of chess. There\nare a few existing libraries (most notably <a href=\"https:\/\/hexdocs.pm\/chex\/api-reference.html\"><code class=\"language-plaintext highlighter-rouge\">chex<\/code><\/a>), but I still wanted\nto roll my own for easier integration with LiveView. You can play around with\nthe progress of this <a href=\"https:\/\/elswisser.casita.zone\/live-board\">here<\/a>.<\/p>\n\n<h1 id=\"1-representing-the-game\">1. Representing the game<\/h1>\n\n<p>The first need we need is a game state, and we can reach naturally for the\nElixir <a href=\"https:\/\/hexdocs.pm\/elixir\/structs.html\"><code class=\"language-plaintext highlighter-rouge\">struct<\/code><\/a> to encode our information. The <code class=\"language-plaintext highlighter-rouge\">Game<\/code> struct needs to\nkeep track of a number of things that represent the game state which we will get\nto, but the most important is the current board state.<\/p>\n\n<p>One notable thing about Elixir is that its <code class=\"language-plaintext highlighter-rouge\">List<\/code> implementations are in fact\nlinked lists, as opposed to arrays. This means that random access is effectively\n<code class=\"language-plaintext highlighter-rouge\">O(n)<\/code> as opposed to <code class=\"language-plaintext highlighter-rouge\">O(1)<\/code> that you might find in other languages. There are\nsome good reasons for this in the language, but it does mean that some standard\nthings about board state (like representing the board as a 64-length array) is\nnot really optimal for our case.<\/p>\n\n<p>Instead, I opted to use an elixir <code class=\"language-plaintext highlighter-rouge\">Map<\/code> of the form <code class=\"language-plaintext highlighter-rouge\">%{{number(),\nnumber()} =&gt; Square.t()}<\/code>. This means that each key is the location\nof each square, represented as a tuple of <code class=\"language-plaintext highlighter-rouge\">file, rank<\/code>, and each value is\nrepresented as a <code class=\"language-plaintext highlighter-rouge\">Square<\/code> struct.<\/p>\n\n<h2 id=\"the-square-struct\">The <code class=\"language-plaintext highlighter-rouge\">Square<\/code> struct<\/h2>\n\n<p>Each <code class=\"language-plaintext highlighter-rouge\">Square<\/code> contains not only the piece sitting on it (or <code class=\"language-plaintext highlighter-rouge\">nil<\/code>) but also\nanother struct called <code class=\"language-plaintext highlighter-rouge\">Sees<\/code>, which contains all the other squares that can be\n\u201cseen\u201d from this square, keyed by the direction (along with knight moves and \ncombination of all of them):<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elswisser<\/span><span class=\"o\">.<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"no\">Sees<\/span> <span class=\"k\">do<\/span>\n  <span class=\"k\">defstruct<\/span> <span class=\"ss\">up:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">down:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">left:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">right:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">up_right:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">up_left:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">down_left:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">down_right:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">knight:<\/span> <span class=\"p\">[],<\/span>\n            <span class=\"ss\">all:<\/span> <span class=\"no\">MapSet<\/span><span class=\"o\">.<\/span><span class=\"n\">new<\/span><span class=\"p\">()<\/span>\n<span class=\"k\">end<\/span>\n\n<span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Square<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">alias<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"no\">Sees<\/span>\n\n  <span class=\"k\">defstruct<\/span> <span class=\"ss\">file:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">rank:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">loc:<\/span> <span class=\"p\">{},<\/span>\n            <span class=\"ss\">piece:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">sees:<\/span> <span class=\"p\">%<\/span><span class=\"no\">Sees<\/span><span class=\"p\">{}<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Additionally, I implemented the <a href=\"https:\/\/hexdocs.pm\/elixir\/1.16.3\/Inspect.html\">Inspect<\/a> protocol for the square for\ndebugging purposes to draw the square on the board along with all the squares\nthat it sees. Using unicode pipe characters allows us to draw a nice\nrepresentation of each square in the terminal. For example:<\/p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>e4 - ' '\n  \u250c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2510\n8 \u2502 \u2715 \u2502   \u2502   \u2502   \u2502 \u2715 \u2502   \u2502   \u2502   \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n7 \u2502   \u2502 \u2715 \u2502   \u2502   \u2502 \u2715 \u2502   \u2502   \u2502 \u2715 \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n6 \u2502   \u2502   \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502   \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n5 \u2502   \u2502   \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502   \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n4 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u25ef \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n3 \u2502   \u2502   \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502   \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n2 \u2502   \u2502   \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502 \u2715 \u2502   \u2502\n  \u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524\n1 \u2502   \u2502 \u2715 \u2502   \u2502   \u2502 \u2715 \u2502   \u2502   \u2502 \u2715 \u2502\n  \u2514\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2518\n    a   b   c   d   e   f   g   h\n<\/code><\/pre><\/div><\/div>\n\n<h2 id=\"the-move-struct\">The <code class=\"language-plaintext highlighter-rouge\">Move<\/code> struct<\/h2>\n\n<p>Along with <code class=\"language-plaintext highlighter-rouge\">Square<\/code>, another key component is the <code class=\"language-plaintext highlighter-rouge\">Move<\/code>. This struct contains\nrelevant information needed for computing and encoding an individual chess move.\nMost importantly, it contains <code class=\"language-plaintext highlighter-rouge\">from<\/code> and <code class=\"language-plaintext highlighter-rouge\">to<\/code> board locations, the <code class=\"language-plaintext highlighter-rouge\">piece<\/code> being\nmoved, and additional information to be figured out later (like castling,\npromotions, etc.):<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Move<\/span> <span class=\"k\">do<\/span>\n  <span class=\"k\">defstruct<\/span> <span class=\"ss\">from:<\/span> <span class=\"p\">{},<\/span>\n            <span class=\"ss\">to:<\/span> <span class=\"p\">{},<\/span>\n            <span class=\"ss\">piece:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">capture:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">promotion:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">castle:<\/span> <span class=\"no\">false<\/span><span class=\"p\">,<\/span>\n            <span class=\"ss\">checking:<\/span> <span class=\"no\">nil<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"2-representing-pieces\">2. Representing pieces<\/h1>\n\n<p>Now that we have the outlines our game board, move, and square structs, we can\nget started with actually implementing pieces.  For each piece, we need a way to\nget all of squares that the given piece can move to based on where it is right\nnow.  There are really four different types of pieces to consider. Rooks,\nbishops, and queens can be implemented the same way. Knights are similar to\nthose but have different move patterns. Then there are pawns and kings.<\/p>\n\n<p>First, we can outline a <code class=\"language-plaintext highlighter-rouge\">@behaviour<\/code> that each piece will follow:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">alias<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"p\">{<\/span><span class=\"no\">Square<\/span><span class=\"p\">,<\/span> <span class=\"no\">Game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"p\">}<\/span>\n\n  <span class=\"nv\">@doc<\/span> <span class=\"sd\">\"\"\"\n  Generates psuedo-legal moves for a given piece type.\n  \"\"\"<\/span>\n  <span class=\"nv\">@callback<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">(),<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">())<\/span> <span class=\"p\">::<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">()]<\/span>\n  <span class=\"nv\">@callback<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">(),<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">())<\/span> <span class=\"p\">::<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">()]<\/span>\n<span class=\"k\">end<\/span>\n\n<\/code><\/pre><\/div><\/div>\n\n<h2 id=\"rooks-bishops-queens\">Rooks, bishops, queens<\/h2>\n\n<p>All three of these pieces move in effectively the same way: they slide along\nwhatever directions they are allowed to move until they bump into the edge of\nthe board, an opposing piece, or a friendly piece. For this, we can take\nadvantage of the relevant pre-computed <code class=\"language-plaintext highlighter-rouge\">sees<\/code> fields:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>  <span class=\"nv\">@spec<\/span> <span class=\"n\">move_range<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{},<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{},<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"no\">Sees<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">())<\/span> <span class=\"p\">::<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">()]<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">move_range<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">direction<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">get_in<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">sees<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span><span class=\"no\">Access<\/span><span class=\"o\">.<\/span><span class=\"n\">key!<\/span><span class=\"p\">(<\/span><span class=\"n\">direction<\/span><span class=\"p\">)])<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">reduce_while<\/span><span class=\"p\">([],<\/span> <span class=\"k\">fn<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"n\">acc<\/span> <span class=\"o\">-&gt;<\/span>\n      <span class=\"n\">s<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">})<\/span>\n\n      <span class=\"k\">case<\/span> <span class=\"n\">friendly?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n        <span class=\"no\">true<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:halt<\/span><span class=\"p\">,<\/span> <span class=\"n\">acc<\/span><span class=\"p\">}<\/span>\n        <span class=\"no\">false<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:halt<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"ss\">capture:<\/span> <span class=\"no\">true<\/span><span class=\"p\">)<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]}<\/span>\n        <span class=\"no\">nil<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:cont<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">rank<\/span><span class=\"p\">})<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]}<\/span>\n      <span class=\"k\">end<\/span>\n    <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">reverse<\/span><span class=\"p\">()<\/span>\n  <span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>From here, implemeting each of the sliding pieces becomes fairly\nstraightforward. For example, with a bishop:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">Bishop<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">alias<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"p\">{<\/span><span class=\"no\">Square<\/span><span class=\"p\">,<\/span> <span class=\"no\">Game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Piece<\/span><span class=\"p\">}<\/span>\n\n  <span class=\"nv\">@behaviour<\/span> <span class=\"no\">Piece<\/span>\n\n  <span class=\"nv\">@impl<\/span> <span class=\"no\">true<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">for<\/span> <span class=\"n\">d<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"p\">[<\/span><span class=\"ss\">:up_right<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:up_left<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:down_left<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:down_right<\/span><span class=\"p\">],<\/span> <span class=\"ss\">reduce:<\/span> <span class=\"p\">[]<\/span> <span class=\"k\">do<\/span>\n      <span class=\"n\">acc<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">[<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">move_range<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">d<\/span><span class=\"p\">)<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]<\/span>\n    <span class=\"k\">end<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">List<\/span><span class=\"o\">.<\/span><span class=\"n\">flatten<\/span><span class=\"p\">()<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"nv\">@impl<\/span> <span class=\"no\">true<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Rooks and queens follow the some logic with different directions. Because these\npieces also attack all the squares they can move to, attacks and moves are the same.<\/p>\n\n<h2 id=\"knights\">Knights<\/h2>\n\n<p>Knights are similar to sliding pieces, but they can \u201cjump\u201d over pieces as well.\nSo instead of scanning along a direction, we check all of the <code class=\"language-plaintext highlighter-rouge\">knight<\/code> sees\nsquares from our struct, and use that to generate moves and attacks:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">Knight<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">alias<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"p\">{<\/span><span class=\"no\">Square<\/span><span class=\"p\">,<\/span> <span class=\"no\">Game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Piece<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"p\">}<\/span>\n\n  <span class=\"nv\">@behaviour<\/span> <span class=\"no\">Piece<\/span>\n\n  <span class=\"nv\">@impl<\/span> <span class=\"no\">true<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">reduce<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">sees<\/span><span class=\"o\">.<\/span><span class=\"n\">knight<\/span><span class=\"p\">,<\/span> <span class=\"p\">[],<\/span> <span class=\"k\">fn<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"n\">acc<\/span> <span class=\"o\">-&gt;<\/span>\n      <span class=\"n\">s<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"o\">.<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">})<\/span>\n\n      <span class=\"k\">case<\/span> <span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"n\">friendly?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n        <span class=\"no\">true<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"n\">acc<\/span>\n        <span class=\"no\">false<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"ss\">capture:<\/span> <span class=\"no\">true<\/span><span class=\"p\">)<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]<\/span>\n        <span class=\"no\">nil<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">rank<\/span><span class=\"p\">})<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]<\/span>\n      <span class=\"k\">end<\/span>\n    <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"nv\">@impl<\/span> <span class=\"no\">true<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h2 id=\"pawns\">Pawns<\/h2>\n\n<p>Pawns are more difficult than the pieces that have been implemented so far for a\nfew reasons:<\/p>\n\n<ol>\n  <li>They can move two squares from their starting position and one square\notherwise.<\/li>\n  <li>They attack diagonally instead of straight ahead.<\/li>\n  <li>They have to respect the rules of <a href=\"https:\/\/en.wikipedia.org\/wiki\/En_passant\">en passant<\/a>.<\/li>\n  <li>They can promote to another piece if they reach the back rank.<\/li>\n<\/ol>\n\n<p>Elixir\u2019s function head pattern matching makes this easier to handle. We can\nstart by generating the squares that the pawns attack (we use the <code class=\"language-plaintext highlighter-rouge\">:P<\/code> atom to\nrepresent white pawns and <code class=\"language-plaintext highlighter-rouge\">:p<\/code> to represent black pawns to be consistent with\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Forsyth%E2%80%93Edwards_Notation\">FEN notation<\/a>):<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">Pawn<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">alias<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"p\">{<\/span><span class=\"no\">Square<\/span><span class=\"p\">,<\/span> <span class=\"no\">Game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"p\">,<\/span> <span class=\"no\">Piece<\/span><span class=\"p\">}<\/span>\n\n  <span class=\"nv\">@behaviour<\/span> <span class=\"no\">Piece<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">,<\/span> <span class=\"n\">_<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">[]<\/span>\n\n  <span class=\"nv\">@impl<\/span> <span class=\"no\">true<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">_<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">)<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"nv\">&amp;1<\/span><span class=\"p\">))<\/span>\n\n  <span class=\"k\">defp<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:P<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"n\">rank<\/span><span class=\"p\">,<\/span> <span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n    <span class=\"p\">[{<\/span><span class=\"n\">file<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}]<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">valid?<\/span><span class=\"o\">\/<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"k\">defp<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:p<\/span><span class=\"p\">,<\/span> <span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"n\">rank<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n    <span class=\"p\">[{<\/span><span class=\"n\">file<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}]<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">valid?<\/span><span class=\"o\">\/<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now, we need to make moves. Depending on where the pawn is on the board, we can\nuse a number of candidate moves and then whittle them down based on various\nconditions. We can use pattern matching once again to generate candidate moves,\nalong with a flag for whether or not the pawn should be promoted:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">Pawn<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ... <\/span>\n  <span class=\"nv\">@impl<\/span> <span class=\"no\">true<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:P<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">),<\/span>\n    <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">[{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"mi\">3<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">}],<\/span> <span class=\"no\">false<\/span><span class=\"p\">)<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:P<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"n\">rank<\/span><span class=\"p\">,<\/span> <span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">),<\/span>\n    <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">[{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}],<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">1<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">8<\/span><span class=\"p\">)<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:p<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"mi\">7<\/span><span class=\"p\">,<\/span> <span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">),<\/span>\n    <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">[{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"mi\">6<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"mi\">5<\/span><span class=\"p\">}],<\/span> <span class=\"no\">false<\/span><span class=\"p\">)<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:p<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"n\">rank<\/span><span class=\"p\">,<\/span> <span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">),<\/span>\n    <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">[{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}],<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"c1\"># ... <\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Then, once we have the candidate moves, we can whittle them down based on the\nrules described above:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">Pawn<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">candidates<\/span><span class=\"p\">,<\/span> <span class=\"n\">promotion<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">m<\/span> <span class=\"o\">=<\/span>\n      <span class=\"n\">candidates<\/span>\n      <span class=\"c1\"># moves are only valid if the candidate square is empty<\/span>\n      <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"k\">fn<\/span> <span class=\"n\">candidate<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">candidate<\/span><span class=\"p\">)<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">empty?<\/span><span class=\"p\">()<\/span> <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n      <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"k\">fn<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span>\n        <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"ss\">promotion:<\/span> <span class=\"n\">promote<\/span><span class=\"p\">(<\/span><span class=\"n\">promotion<\/span><span class=\"p\">,<\/span> <span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">))<\/span>\n      <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">a<\/span> <span class=\"o\">=<\/span>\n      <span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">)<\/span>\n      <span class=\"c1\"># attacks are only valid if the candidate square is occupied by an enemy<\/span>\n      <span class=\"c1\"># piece or the current en passant square of the given game state<\/span>\n      <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"k\">fn<\/span> <span class=\"n\">s<\/span> <span class=\"o\">-&gt;<\/span>\n        <span class=\"n\">sq<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"p\">)<\/span>\n        <span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"n\">enemy?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">sq<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">)<\/span> <span class=\"o\">||<\/span> <span class=\"n\">en_passant?<\/span><span class=\"p\">(<\/span><span class=\"n\">sq<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n      <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n      <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"k\">fn<\/span> <span class=\"n\">s<\/span> <span class=\"o\">-&gt;<\/span>\n        <span class=\"n\">sq<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">ep?<\/span> <span class=\"o\">=<\/span> <span class=\"n\">en_passant?<\/span><span class=\"p\">(<\/span><span class=\"n\">sq<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n\n        <span class=\"n\">capture<\/span> <span class=\"o\">=<\/span>\n          <span class=\"k\">cond<\/span> <span class=\"k\">do<\/span>\n            <span class=\"n\">ep?<\/span> <span class=\"ow\">and<\/span> <span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span> <span class=\"o\">==<\/span> <span class=\"ss\">:P<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"ss\">:p<\/span>\n            <span class=\"n\">ep?<\/span> <span class=\"ow\">and<\/span> <span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span> <span class=\"o\">==<\/span> <span class=\"ss\">:p<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"ss\">:P<\/span>\n            <span class=\"no\">true<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"n\">sq<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span>\n          <span class=\"k\">end<\/span>\n\n        <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"p\">,<\/span> <span class=\"ss\">capture:<\/span> <span class=\"n\">capture<\/span><span class=\"p\">,<\/span> <span class=\"ss\">promotion:<\/span> <span class=\"n\">promote<\/span><span class=\"p\">(<\/span><span class=\"n\">promotion<\/span><span class=\"p\">,<\/span> <span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">))<\/span>\n      <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">concat<\/span><span class=\"p\">(<\/span><span class=\"n\">m<\/span><span class=\"p\">,<\/span> <span class=\"n\">a<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n\n  <span class=\"k\">defp<\/span> <span class=\"n\">en_passant?<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">empty?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">)<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">eq?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"o\">.<\/span><span class=\"n\">en_passant<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This does rely on the en passant information stored in the game state. That will\nbe discussed in much more detail in part three when we update the game state\nafter making moves.<\/p>\n\n<h3 id=\"kings\">Kings<\/h3>\n\n<p>Kings are even more complex than pawns:<\/p>\n\n<ol>\n  <li>Kings can possibly castle either kingside or queenside depending on the state\nof the game.<\/li>\n  <li>Kings cannot move into check.<\/li>\n<\/ol>\n\n<p>Both of these rely on game state, which again will be discussed a bit later. To\nstart, we can generate a list of all possible squares that the king can attack.\nkings can move one square in any direction, so an Elixir for comprehension is a\ngood fit for candidate move generation. From there, we can apply similar logic\nto our previous piece moves to check for friendly pieices, captures, etc:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">King<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">file:<\/span> <span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"ss\">rank:<\/span> <span class=\"n\">rank<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">for<\/span><span class=\"p\">(<\/span><span class=\"n\">f<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"o\">..<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"n\">r<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"o\">..<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"ow\">not<\/span> <span class=\"p\">(<\/span><span class=\"n\">f<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">0<\/span> <span class=\"ow\">and<\/span> <span class=\"n\">r<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">0<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span> <span class=\"o\">+<\/span> <span class=\"n\">f<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span> <span class=\"o\">+<\/span> <span class=\"n\">r<\/span><span class=\"p\">})<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">valid?<\/span><span class=\"o\">\/<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">reduce<\/span><span class=\"p\">([],<\/span> <span class=\"k\">fn<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"n\">acc<\/span> <span class=\"o\">-&gt;<\/span>\n      <span class=\"n\">s<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"o\">.<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">rank<\/span><span class=\"p\">})<\/span>\n\n      <span class=\"k\">case<\/span> <span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"n\">friendly?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">piece<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n        <span class=\"no\">true<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"n\">acc<\/span>\n        <span class=\"no\">false<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">rank<\/span><span class=\"p\">},<\/span> <span class=\"ss\">capture:<\/span> <span class=\"no\">true<\/span><span class=\"p\">)<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]<\/span>\n        <span class=\"no\">nil<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">file<\/span><span class=\"p\">,<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">rank<\/span><span class=\"p\">})<\/span> <span class=\"o\">|<\/span> <span class=\"n\">acc<\/span><span class=\"p\">]<\/span>\n      <span class=\"k\">end<\/span>\n    <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n<span class=\"k\">end<\/span> \n<\/code><\/pre><\/div><\/div>\n\n<p>From there, we need to add kingside and queenside castling (in the event that\ncastling is legal from the given position), and then remove any moves that might\nput the king in check.<\/p>\n\n<p>First, castling. There are three conditions that have to be met in order for the\nking to be able to castle in a given direction:<\/p>\n\n<ol>\n  <li>It must be legal according the game state (i.e. the king or relevant rook\nmust not have moved)<\/li>\n  <li>All squares between the king and relevant rook must be empty<\/li>\n  <li>No squares between the king and relevant rook can be attacked by an enemy\npiece (the king cannot move through or into check)<\/li>\n<\/ol>\n\n<p>In order to do this, we\u2019ll need to have a way to check if a given square is\nbeing attacked. To do this, we can figure out where all the pieces are for a\ngiven color, and then figure out which squares those pieces attack.<\/p>\n\n<p>First, let\u2019s figure out where all the pieces for a given color are located:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Board<\/span> <span class=\"k\">do<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">white_occupied<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n    <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"k\">fn<\/span> <span class=\"p\">{<\/span><span class=\"n\">_<\/span><span class=\"p\">,<\/span> <span class=\"n\">square<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">white?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">)<\/span> <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">values<\/span><span class=\"p\">()<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">black_occupied<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n    <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">filter<\/span><span class=\"p\">(<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"k\">fn<\/span> <span class=\"p\">{<\/span><span class=\"n\">_<\/span><span class=\"p\">,<\/span> <span class=\"n\">square<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">black?<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">)<\/span> <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">values<\/span><span class=\"p\">()<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Then, let\u2019s get all the unique locations attacked by those pieces:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Board<\/span> <span class=\"k\">do<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">white_attacks<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">white_occupied<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"nv\">&amp;1<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">))<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">List<\/span><span class=\"o\">.<\/span><span class=\"n\">flatten<\/span><span class=\"p\">()<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">uniq<\/span><span class=\"p\">()<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span> <span class=\"nv\">&amp;1<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n\n  <span class=\"k\">def<\/span> <span class=\"n\">black_attacks<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">black_occupied<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"nv\">&amp;1<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">))<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">List<\/span><span class=\"o\">.<\/span><span class=\"n\">flatten<\/span><span class=\"p\">()<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">uniq<\/span><span class=\"p\">()<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span> <span class=\"nv\">&amp;1<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span><span class=\"p\">)<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Then, using this information, we can evaluate the legality of all of our\ncastling rules. This snippet is for castling kingside for the white king, but\ncan be extended to go queenside, and for the black king:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">King<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"c1\"># handle the case where castling is illegal by the game rules<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">maybe_castle_kingside<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"n\">piece<\/span><span class=\"p\">},<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"ss\">castling:<\/span> <span class=\"n\">c<\/span><span class=\"p\">},<\/span> <span class=\"n\">_<\/span><span class=\"p\">)<\/span>\n       <span class=\"ow\">when<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">is_map_key<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">,<\/span> <span class=\"n\">piece<\/span><span class=\"p\">),<\/span>\n       <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">[]<\/span>\n\n  <span class=\"c1\"># check that castling is legal for the given position<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">maybe_castle_kingside<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"ss\">:K<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">through_squares<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[{<\/span><span class=\"sx\">?f<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?g<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}]<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"nv\">&amp;1<\/span><span class=\"p\">))<\/span>\n\n    <span class=\"k\">if<\/span> <span class=\"n\">can_castle?<\/span><span class=\"p\">(<\/span><span class=\"n\">through_squares<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">),<\/span>\n      <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?g<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">},<\/span> <span class=\"ss\">castle:<\/span> <span class=\"no\">true<\/span><span class=\"p\">)],<\/span>\n      <span class=\"k\">else<\/span><span class=\"p\">:<\/span> <span class=\"p\">[]<\/span>\n  <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n  \n  <span class=\"c1\"># Going kingside has the same through and attacked squares, but when castling<\/span>\n  <span class=\"c1\"># queenside, the b-file can be attacked, so we have the two function heads.<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">can_castle?<\/span><span class=\"p\">(<\/span><span class=\"n\">through_squares<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">),<\/span>\n    <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"n\">can_castle?<\/span><span class=\"p\">(<\/span><span class=\"n\">through_squares<\/span><span class=\"p\">,<\/span> <span class=\"n\">through_squares<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">)<\/span>\n\n  <span class=\"k\">defp<\/span> <span class=\"n\">can_castle?<\/span><span class=\"p\">(<\/span><span class=\"n\">empty_through<\/span><span class=\"p\">,<\/span> <span class=\"n\">attack_through<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">empty?<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">all?<\/span><span class=\"p\">(<\/span><span class=\"n\">empty_through<\/span><span class=\"p\">,<\/span> <span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">empty?<\/span><span class=\"o\">\/<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">attacked?<\/span> <span class=\"o\">=<\/span>\n      <span class=\"n\">attacks<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">map<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"o\">\/<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">any?<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"p\">(<\/span><span class=\"nv\">&amp;1<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">attack_through<\/span><span class=\"p\">))<\/span>\n\n    <span class=\"n\">empty?<\/span> <span class=\"ow\">and<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">attacked?<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now we can put all this together:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"no\">King<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"c1\"># Note this is the private method shared between the black and white kings.<\/span>\n  <span class=\"c1\"># The difference would be which set of attacks are fed into the function<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">moves<\/span><span class=\"p\">(%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">MapSet<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"c1\"># starting with all the attacked squares<\/span>\n    <span class=\"n\">attacks<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">)<\/span> \n    <span class=\"c1\"># add kingside castling if it's legal<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">concat<\/span><span class=\"p\">(<\/span><span class=\"n\">maybe_castle_kingside<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">))<\/span> \n    <span class=\"c1\"># add queenside castling if it's legal<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">concat<\/span><span class=\"p\">(<\/span><span class=\"n\">maybe_castle_queenside<\/span><span class=\"p\">(<\/span><span class=\"n\">square<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">attacks<\/span><span class=\"p\">))<\/span>\n    <span class=\"c1\"># filter out all moves where the given square is attacked by an enemy piece<\/span>\n    <span class=\"o\">|&gt;<\/span> <span class=\"no\">Enum<\/span><span class=\"o\">.<\/span><span class=\"n\">reject<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"no\">MapSet<\/span><span class=\"o\">.<\/span><span class=\"n\">member?<\/span><span class=\"p\">(<\/span><span class=\"n\">attacks<\/span><span class=\"p\">,<\/span> <span class=\"nv\">&amp;1<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span><span class=\"p\">))<\/span>\n  <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n<span class=\"k\">end<\/span> \n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"3-making-moves\">3. Making moves<\/h1>\n\n<p>Finally, we have a working implementations of all the pieces. We need to be able\nto make moves on the board and have them update our game\u2019s internal state. The\ninternal state has to keep track of a lot of different things. The main ones\ncome from <a href=\"https:\/\/en.wikipedia.org\/wiki\/Forsyth%E2%80%93Edwards_Notation\">FEN<\/a> notation: board state, active color, castling rights, en\npassant, and the halfmove and fullmove clocks. Since Elixir is an immutable\nlanguage, making a move will return a new game struct with all the fields\nupdated.<\/p>\n\n<h2 id=\"validating-moves\">Validating moves<\/h2>\n\n<p>The first part of making a move is ensuring that the proposed move is valid. A\nmove cannot originate from an empty square, and players cannot move out of\norder. Pieces must respect the general rules of the game. We can re-use some of\nour previously implemented code to make all of this happen.\n<code class=\"language-plaintext highlighter-rouge\">Square.legal_locs\/2<\/code> is the same as <code class=\"language-plaintext highlighter-rouge\">Square.legal_moves\/2<\/code> implemented in the\nabove section with kings, just with the move mapped to the <code class=\"language-plaintext highlighter-rouge\">.to<\/code> fields in the\nmove struct.<\/p>\n\n<p>In order to write these validators, I took advantage of Elixir\u2019s beautiful\n<a href=\"https:\/\/hexdocs.pm\/elixir\/Kernel.SpecialForms.html#with\/1\"><code class=\"language-plaintext highlighter-rouge\">with\/1<\/code><\/a> method. Each validator flows down and returns either a\nboolean, or an error atom. This gets fed into a little convenience <code class=\"language-plaintext highlighter-rouge\">or_\/1<\/code>\nmethod which returns either <code class=\"language-plaintext highlighter-rouge\">:ok<\/code>, or <code class=\"language-plaintext highlighter-rouge\">{:error, reason}<\/code>.  This allows for a\nnice little block of readable validations:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Game<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">ensure_valid_move<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">with<\/span> <span class=\"n\">from<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">),<\/span>\n         <span class=\"n\">to<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">get_square<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span><span class=\"p\">),<\/span>\n         <span class=\"ss\">:ok<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"ow\">not<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">empty?<\/span><span class=\"p\">(<\/span><span class=\"n\">from<\/span><span class=\"p\">)<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"ss\">:empty_square<\/span><span class=\"p\">),<\/span>\n         <span class=\"ss\">:ok<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"o\">.<\/span><span class=\"n\">active<\/span> <span class=\"o\">==<\/span> <span class=\"no\">Board<\/span><span class=\"o\">.<\/span><span class=\"n\">color_at<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">from<\/span><span class=\"p\">))<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"ss\">:invalid_from_color<\/span><span class=\"p\">),<\/span>\n         <span class=\"ss\">:ok<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"o\">.<\/span><span class=\"n\">active<\/span> <span class=\"o\">!=<\/span> <span class=\"no\">Board<\/span><span class=\"o\">.<\/span><span class=\"n\">color_at<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">to<\/span><span class=\"p\">))<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"ss\">:invalid_to_color<\/span><span class=\"p\">),<\/span>\n         <span class=\"ss\">:ok<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span> <span class=\"ow\">in<\/span> <span class=\"no\">Square<\/span><span class=\"o\">.<\/span><span class=\"n\">legal_locs<\/span><span class=\"p\">(<\/span><span class=\"n\">from<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">))<\/span> <span class=\"o\">|&gt;<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"ss\">:invalid_move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n      <span class=\"ss\">:ok<\/span>\n    <span class=\"k\">end<\/span>\n  <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"nv\">@spec<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"n\">boolean<\/span><span class=\"p\">(),<\/span> <span class=\"n\">atom<\/span><span class=\"p\">())<\/span> <span class=\"p\">::<\/span> <span class=\"ss\">:ok<\/span> <span class=\"o\">|<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"n\">atom<\/span><span class=\"p\">()}<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"no\">true<\/span><span class=\"p\">,<\/span> <span class=\"n\">_<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"ss\">:ok<\/span>\n  <span class=\"k\">defp<\/span> <span class=\"n\">or_<\/span><span class=\"p\">(<\/span><span class=\"no\">false<\/span><span class=\"p\">,<\/span> <span class=\"n\">reason<\/span><span class=\"p\">),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"n\">reason<\/span><span class=\"p\">}<\/span>\n  <span class=\"c1\"># ...<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>As an aside, this is the kind of thing that really makes Elixir a joy to work\nwith compared to other languages.<\/p>\n\n<h2 id=\"making-moves-in-the-board\">Making moves in the board<\/h2>\n\n<p>Now that we have ensured that our moves our valid, we can update our board\nstate. Making a move has a few possible steps:<\/p>\n\n<ol>\n  <li>The origin piece has to be picked up<\/li>\n  <li>The piece has to be moved to its destination, optionally castling<\/li>\n  <li>If the move is a castle, we have to make sure the king and the rook are\nmoved.<\/li>\n  <li>If the move is a promotion, the pawn needs to be replaced with the promotion\npiece.<\/li>\n<\/ol>\n\n<p>Note that this is a <em>pseudo-legal<\/em> move; we are relying on our move validator in\nstep one above to make sure that the move is legal before making it to this step. <code class=\"language-plaintext highlighter-rouge\">with\/1<\/code> makes this a snap as well:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Board<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"nv\">@spec<\/span> <span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">(),<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">())<\/span> <span class=\"p\">::<\/span>\n          <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">(),<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">t<\/span><span class=\"p\">()}}<\/span> <span class=\"o\">|<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"n\">atom<\/span><span class=\"p\">()}<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">move<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">with<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}}<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"n\">move_from<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">),<\/span>\n         <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">capture<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}}<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"n\">move_to<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">to<\/span><span class=\"p\">,<\/span> <span class=\"n\">piece<\/span><span class=\"p\">),<\/span>\n         <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"n\">castle<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"p\">),<\/span>\n         <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"n\">promote<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n      <span class=\"n\">check<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"no\">Check<\/span><span class=\"o\">.<\/span><span class=\"n\">opponent_in_check?<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">)<\/span>\n\n      <span class=\"n\">move<\/span> <span class=\"o\">=<\/span> <span class=\"p\">%{<\/span>\n        <span class=\"n\">move<\/span>\n        <span class=\"o\">|<\/span> <span class=\"ss\">checking:<\/span> <span class=\"k\">if<\/span><span class=\"p\">(<\/span><span class=\"n\">check<\/span> <span class=\"o\">==<\/span> <span class=\"no\">true<\/span><span class=\"p\">,<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"ss\">:check<\/span><span class=\"p\">,<\/span> <span class=\"k\">else<\/span><span class=\"p\">:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">),<\/span>\n          <span class=\"ss\">capture:<\/span> <span class=\"n\">capture<\/span><span class=\"p\">,<\/span>\n          <span class=\"ss\">piece:<\/span> <span class=\"n\">piece<\/span>\n      <span class=\"p\">}<\/span>\n\n      <span class=\"n\">move<\/span> <span class=\"o\">=<\/span> <span class=\"p\">%{<\/span><span class=\"n\">move<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">san:<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">san<\/span><span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"p\">)}<\/span>\n\n      <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">move<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}}<\/span>\n    <span class=\"k\">end<\/span>\n  <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"moving-pieces-with-move_from2-and-move_to3\">Moving pieces with <code class=\"language-plaintext highlighter-rouge\">move_from\/2<\/code> and <code class=\"language-plaintext highlighter-rouge\">move_to\/3<\/code><\/h3>\n\n<p>When doing the actual moves themselves, we can take advantage of the nice\n<a href=\"https:\/\/hexdocs.pm\/elixir\/Map.html#get_and_update\/3\"><code class=\"language-plaintext highlighter-rouge\">Map.get_and_update\/3<\/code><\/a>. <code class=\"language-plaintext highlighter-rouge\">move_from\/2<\/code> updates the board at the\ngiven square and ensures that a piece is present there, failing with\nthe <code class=\"language-plaintext highlighter-rouge\">:empty_square<\/code> atom if not.<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">def<\/span> <span class=\"n\">move_from<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n  <span class=\"k\">case<\/span> <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">get_and_update<\/span><span class=\"p\">(<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">,<\/span> <span class=\"k\">fn<\/span> <span class=\"n\">current<\/span> <span class=\"o\">-&gt;<\/span>\n          <span class=\"p\">{<\/span><span class=\"n\">current<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"n\">current<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">piece:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">}}<\/span>\n        <span class=\"k\">end<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"p\">{%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">},<\/span> <span class=\"n\">_<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:empty_square<\/span><span class=\"p\">}<\/span>\n    <span class=\"p\">{%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"n\">p<\/span><span class=\"p\">},<\/span> <span class=\"n\">board<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">p<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"n\">game<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">}}}<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">move_to\/3<\/code> does basically the same thing in reverse, just making sure that we\naren\u2019t putting our piece on a square that\u2019s already occupied by a friendly piece.<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">def<\/span> <span class=\"n\">move_to<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">,<\/span> <span class=\"n\">piece<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n  <span class=\"p\">{%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"ss\">piece:<\/span> <span class=\"n\">capture<\/span><span class=\"p\">},<\/span> <span class=\"n\">board<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span>\n    <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">get_and_update<\/span><span class=\"p\">(<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"n\">loc<\/span><span class=\"p\">,<\/span> <span class=\"k\">fn<\/span> <span class=\"n\">current<\/span> <span class=\"o\">-&gt;<\/span>\n      <span class=\"p\">{<\/span><span class=\"n\">current<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"n\">current<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">piece:<\/span> <span class=\"n\">piece<\/span><span class=\"p\">}}<\/span>\n    <span class=\"k\">end<\/span><span class=\"p\">)<\/span>\n\n  <span class=\"k\">cond<\/span> <span class=\"k\">do<\/span>\n    <span class=\"no\">Piece<\/span><span class=\"o\">.<\/span><span class=\"n\">friendly?<\/span><span class=\"p\">(<\/span><span class=\"n\">piece<\/span><span class=\"p\">,<\/span> <span class=\"n\">capture<\/span><span class=\"p\">)<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:error<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:invalid_to_color<\/span><span class=\"p\">}<\/span>\n    <span class=\"no\">true<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">capture<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"n\">game<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">}}}<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"castling\">Castling<\/h3>\n\n<p>For castling, we can enumerate all possible cases (there are only four):<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">def<\/span> <span class=\"n\">castle<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{<\/span><span class=\"ss\">castle:<\/span> <span class=\"no\">false<\/span><span class=\"p\">}),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"n\">castle<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{<\/span><span class=\"ss\">to:<\/span> <span class=\"n\">to<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">move<\/span> <span class=\"o\">=<\/span>\n    <span class=\"k\">case<\/span> <span class=\"n\">to<\/span> <span class=\"k\">do<\/span>\n      <span class=\"p\">{<\/span><span class=\"sx\">?g<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?h<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:R<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?f<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">})<\/span>\n      <span class=\"p\">{<\/span><span class=\"sx\">?g<\/span><span class=\"p\">,<\/span> <span class=\"mi\">8<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?h<\/span><span class=\"p\">,<\/span> <span class=\"mi\">8<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:r<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?f<\/span><span class=\"p\">,<\/span> <span class=\"mi\">8<\/span><span class=\"p\">})<\/span>\n      <span class=\"p\">{<\/span><span class=\"sx\">?c<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?a<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:R<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?d<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">})<\/span>\n      <span class=\"p\">{<\/span><span class=\"sx\">?c<\/span><span class=\"p\">,<\/span> <span class=\"mi\">8<\/span><span class=\"p\">}<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?a<\/span><span class=\"p\">,<\/span> <span class=\"mi\">8<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:r<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?d<\/span><span class=\"p\">,<\/span> <span class=\"mi\">8<\/span><span class=\"p\">})<\/span>\n    <span class=\"k\">end<\/span>\n\n  <span class=\"n\">with<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">_<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}}<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h3 id=\"promotion\">Promotion<\/h3>\n\n<p>Promotion works very similarly to <code class=\"language-plaintext highlighter-rouge\">move_to\/3<\/code>. We use <code class=\"language-plaintext highlighter-rouge\">Map.get_and_update\/3<\/code> to\nupdate the pawn in place:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">def<\/span> <span class=\"n\">promote<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{<\/span><span class=\"ss\">promotion:<\/span> <span class=\"no\">nil<\/span><span class=\"p\">}),<\/span> <span class=\"k\">do<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"n\">promote<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{<\/span><span class=\"ss\">to:<\/span> <span class=\"n\">to<\/span><span class=\"p\">,<\/span> <span class=\"ss\">promotion:<\/span> <span class=\"n\">promotion<\/span><span class=\"p\">})<\/span> <span class=\"k\">do<\/span>\n  <span class=\"n\">with<\/span> <span class=\"p\">{<\/span><span class=\"n\">_<\/span><span class=\"p\">,<\/span> <span class=\"n\">board<\/span><span class=\"p\">}<\/span> <span class=\"o\">&lt;-<\/span>\n          <span class=\"no\">Map<\/span><span class=\"o\">.<\/span><span class=\"n\">get_and_update<\/span><span class=\"p\">(<\/span><span class=\"n\">board<\/span><span class=\"p\">,<\/span> <span class=\"n\">to<\/span><span class=\"p\">,<\/span> <span class=\"k\">fn<\/span> <span class=\"n\">current<\/span> <span class=\"o\">-&gt;<\/span>\n            <span class=\"p\">{<\/span><span class=\"n\">current<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Square<\/span><span class=\"p\">{<\/span><span class=\"n\">current<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">piece:<\/span> <span class=\"n\">promotion<\/span><span class=\"p\">}}<\/span>\n          <span class=\"k\">end<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{<\/span><span class=\"n\">game<\/span> <span class=\"o\">|<\/span> <span class=\"ss\">board:<\/span> <span class=\"n\">board<\/span><span class=\"p\">}}<\/span>\n  <span class=\"k\">end<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h2 id=\"updating-game-state\">Updating game state<\/h2>\n\n<p>Now that we have valid moves and a way to update our board state, we can finally\nupdate the game state:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">defmodule<\/span> <span class=\"no\">Elchesser<\/span><span class=\"o\">.<\/span><span class=\"no\">Game<\/span> <span class=\"k\">do<\/span>\n  <span class=\"c1\"># ...<\/span>\n  <span class=\"k\">def<\/span> <span class=\"n\">move<\/span><span class=\"p\">(%<\/span><span class=\"no\">Game<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"p\">%<\/span><span class=\"no\">Move<\/span><span class=\"p\">{}<\/span> <span class=\"o\">=<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n    <span class=\"n\">with<\/span> <span class=\"ss\">:ok<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"n\">ensure_valid_move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"p\">),<\/span>\n         <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span><span class=\"n\">move<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}}<\/span> <span class=\"o\">&lt;-<\/span> <span class=\"no\">Board<\/span><span class=\"o\">.<\/span><span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"n\">move<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span>\n      <span class=\"n\">game<\/span> <span class=\"o\">=<\/span>\n        <span class=\"n\">game<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">flip_color<\/span><span class=\"p\">()<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">set_in_check<\/span><span class=\"p\">()<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">add_move<\/span><span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"p\">)<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">add_fen<\/span><span class=\"p\">()<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">add_capture<\/span><span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"o\">.<\/span><span class=\"n\">capture<\/span><span class=\"p\">)<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">set_castling_rights<\/span><span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"p\">)<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">set_en_passant<\/span><span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"p\">)<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">set_half_move_count<\/span><span class=\"p\">(<\/span><span class=\"n\">move<\/span><span class=\"p\">)<\/span>\n        <span class=\"o\">|&gt;<\/span> <span class=\"n\">set_full_move_count<\/span><span class=\"p\">()<\/span>\n\n      <span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span>\n    <span class=\"k\">end<\/span>\n  <span class=\"c1\"># ...<\/span>\n<span class=\"k\">end<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>There isn\u2019t a ton of very interesting stuff here, just a lot of housekeeping.\nBut, now with this, we should <em>finally<\/em> have a way of playing a game of chess in\nour program:<\/p>\n\n<div class=\"language-elixir highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">game<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">new<\/span><span class=\"p\">()<\/span>\n\n<span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?e<\/span><span class=\"p\">,<\/span> <span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:P<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?e<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">}))<\/span>\n<span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?e<\/span><span class=\"p\">,<\/span> <span class=\"mi\">7<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:p<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?e<\/span><span class=\"p\">,<\/span> <span class=\"mi\">5<\/span><span class=\"p\">}))<\/span>\n<span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?d<\/span><span class=\"p\">,<\/span> <span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:P<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?d<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">}))<\/span>\n<span class=\"p\">{<\/span><span class=\"ss\">:ok<\/span><span class=\"p\">,<\/span> <span class=\"n\">game<\/span><span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Game<\/span><span class=\"o\">.<\/span><span class=\"n\">move<\/span><span class=\"p\">(<\/span><span class=\"n\">game<\/span><span class=\"p\">,<\/span> <span class=\"no\">Move<\/span><span class=\"o\">.<\/span><span class=\"n\">from<\/span><span class=\"p\">({<\/span><span class=\"sx\">?e<\/span><span class=\"p\">,<\/span> <span class=\"mi\">5<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:p<\/span><span class=\"p\">},<\/span> <span class=\"p\">{<\/span><span class=\"sx\">?d<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">}))<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now that we have this, we can get to the interesting part: hooking it into\na Phoenix LiveView.<\/p>\n\n","pubDate":"Fri, 31 May 2024","link":"https:\/\/bensmithgall.com\/\/blog\/live-view-chess-the-rules","guid":"https:\/\/bensmithgall.com\/\/blog\/live-view-chess-the-rules"},{"title":"Auto-focus after adding a new row in an editable data table","category":{},"description":"<p>I have been messing around a bit with NextJS for a little side project. The main\ninterface in the project have been editable data tables (or, <code class=\"language-plaintext highlighter-rouge\">&lt;td&gt;<\/code> elements\nwith <code class=\"language-plaintext highlighter-rouge\">&lt;input&gt;<\/code> slapped in the middle). For whatever reason, I ended up using the\n<a href=\"https:\/\/tanstack.com\/table\/latest\"><code class=\"language-plaintext highlighter-rouge\">tanstack\/react-table<\/code> library<\/a> as the core\nbuilding block.<\/p>\n\n<p>After using these tables for a few weeks, I wanted to make a small change: after\nadding a new role to a given table, I wanted to focus on the first column of the\nnewly created row.<\/p>\n\n<p>The first two parts of this post go over some Tanstack table basics: setting up\neditable table cells and adding\/removing rows. If you want to skip right on over\nto the focusing bit, you can find that in Step 3 below. The full working example\nis available in\n<a href=\"https:\/\/github.com\/bsmithgall\/tanstack-focus-ref\">this repository<\/a>.<\/p>\n\n<p>Let\u2019s start with something that looks very similar to the\n<a href=\"https:\/\/tanstack.com\/table\/v8\/docs\/framework\/react\/examples\/basic\">basic tanstack table<\/a>:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"o\">*<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">React<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">createColumnHelper<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">flexRender<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">getCoreRowModel<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">useReactTable<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">@tanstack\/react-table<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">helper<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createColumnHelper<\/span><span class=\"p\">();<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">columns<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">one<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column One<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">two<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column Two<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">three<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column Three<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">}),<\/span>\n<span class=\"p\">];<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">defaultData<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n  <span class=\"p\">{<\/span> <span class=\"na\">one<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">ONE<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">two<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">TWO<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">three<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">THREE<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">},<\/span>\n  <span class=\"p\">{<\/span> <span class=\"na\">one<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">one<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">two<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">two<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">three<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">three<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">},<\/span>\n<span class=\"p\">];<\/span>\n\n<span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">data<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setData<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useState<\/span><span class=\"p\">(<\/span><span class=\"nx\">defaultData<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">table<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useReactTable<\/span><span class=\"p\">({<\/span>\n    <span class=\"nx\">data<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">columns<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">getCoreRowModel<\/span><span class=\"p\">:<\/span> <span class=\"nx\">getCoreRowModel<\/span><span class=\"p\">(),<\/span>\n  <span class=\"p\">});<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"border m-2 rounded-md relative\"<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">table<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"w-full overflow-auto\"<\/span><span class=\"p\">&gt;<\/span>\n        <span class=\"p\">&lt;<\/span><span class=\"nt\">thead<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"border-b-2\"<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"si\">{<\/span><span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">getHeaderGroups<\/span><span class=\"p\">().<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">group<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n            <span class=\"p\">&lt;<\/span><span class=\"nt\">tr<\/span> <span class=\"na\">key<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">group<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"si\">}<\/span><span class=\"p\">&gt;<\/span>\n              <span class=\"si\">{<\/span><span class=\"nx\">group<\/span><span class=\"p\">.<\/span><span class=\"nx\">headers<\/span><span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">header<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n                <span class=\"p\">&lt;<\/span><span class=\"nt\">th<\/span> <span class=\"na\">key<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">header<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"si\">}<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"py-2 border-1 first:border-0\"<\/span><span class=\"p\">&gt;<\/span>\n                  <span class=\"si\">{<\/span><span class=\"nx\">flexRender<\/span><span class=\"p\">(<\/span>\n                    <span class=\"nx\">header<\/span><span class=\"p\">.<\/span><span class=\"nx\">column<\/span><span class=\"p\">.<\/span><span class=\"nx\">columnDef<\/span><span class=\"p\">.<\/span><span class=\"nx\">header<\/span><span class=\"p\">,<\/span>\n                    <span class=\"nx\">header<\/span><span class=\"p\">.<\/span><span class=\"nx\">getContext<\/span><span class=\"p\">()<\/span>\n                  <span class=\"p\">)<\/span><span class=\"si\">}<\/span>\n                <span class=\"p\">&lt;\/<\/span><span class=\"nt\">th<\/span><span class=\"p\">&gt;<\/span>\n              <span class=\"p\">))<\/span><span class=\"si\">}<\/span>\n            <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tr<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"p\">))<\/span><span class=\"si\">}<\/span>\n        <span class=\"p\">&lt;\/<\/span><span class=\"nt\">thead<\/span><span class=\"p\">&gt;<\/span>\n        <span class=\"p\">&lt;<\/span><span class=\"nt\">tbody<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"si\">{<\/span><span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">getRowModel<\/span><span class=\"p\">().<\/span><span class=\"nx\">rows<\/span><span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">row<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n            <span class=\"p\">&lt;<\/span><span class=\"nt\">tr<\/span> <span class=\"na\">key<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">row<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"si\">}<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"mt-4\"<\/span><span class=\"p\">&gt;<\/span>\n              <span class=\"si\">{<\/span><span class=\"nx\">row<\/span><span class=\"p\">.<\/span><span class=\"nx\">getVisibleCells<\/span><span class=\"p\">().<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">cell<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n                <span class=\"p\">&lt;<\/span><span class=\"nt\">td<\/span> <span class=\"na\">key<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">cell<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"si\">}<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"p-2 border-1 first:border-0\"<\/span><span class=\"p\">&gt;<\/span>\n                  <span class=\"si\">{<\/span><span class=\"nx\">flexRender<\/span><span class=\"p\">(<\/span><span class=\"nx\">cell<\/span><span class=\"p\">.<\/span><span class=\"nx\">column<\/span><span class=\"p\">.<\/span><span class=\"nx\">columnDef<\/span><span class=\"p\">.<\/span><span class=\"nx\">cell<\/span><span class=\"p\">,<\/span> <span class=\"nx\">cell<\/span><span class=\"p\">.<\/span><span class=\"nx\">getContext<\/span><span class=\"p\">())<\/span><span class=\"si\">}<\/span>\n                <span class=\"p\">&lt;\/<\/span><span class=\"nt\">td<\/span><span class=\"p\">&gt;<\/span>\n              <span class=\"p\">))<\/span><span class=\"si\">}<\/span>\n            <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tr<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"p\">))<\/span><span class=\"si\">}<\/span>\n        <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tbody<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;\/<\/span><span class=\"nt\">table<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">div<\/span><span class=\"p\">&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"nx\">App<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Aside from a small difference in the data model and the inclusion of Tailwind,\nthis is very similar to the example.<\/p>\n\n<h1 id=\"step-1-making-the-inputs-editable\">Step 1: making the inputs editable<\/h1>\n\n<p>Tanstack allows you to pass a <code class=\"language-plaintext highlighter-rouge\">cell<\/code> when defining your columns, which lets us\nhave control over how a given cell is rendered\n(<a href=\"https:\/\/tanstack.com\/table\/v8\/docs\/guide\/column-defs#column-formatting--rendering\">docs<\/a>).\nThis lets us write a custom component for handling cell inputs. This component\ntakes in props defined by Tanstack itself.<\/p>\n\n<p>Here\u2019s something to get started with:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">EditableCellInput<\/span><span class=\"p\">({<\/span> <span class=\"nx\">getValue<\/span><span class=\"p\">,<\/span> <span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"nx\">column<\/span><span class=\"p\">,<\/span> <span class=\"nx\">table<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">value<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setValue<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useState<\/span><span class=\"p\">(<\/span><span class=\"nx\">getValue<\/span><span class=\"p\">());<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">input<\/span>\n      <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"table-cell w-full px-2 py-1\"<\/span>\n      <span class=\"na\">value<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">value<\/span><span class=\"si\">}<\/span>\n      <span class=\"na\">onChange<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">setValue<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">.<\/span><span class=\"nx\">target<\/span><span class=\"p\">.<\/span><span class=\"nx\">value<\/span><span class=\"p\">)<\/span><span class=\"si\">}<\/span>\n      <span class=\"na\">onBlur<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">options<\/span><span class=\"p\">.<\/span><span class=\"nx\">meta<\/span><span class=\"p\">.<\/span><span class=\"nx\">updateRow<\/span><span class=\"p\">(<\/span><span class=\"nx\">row<\/span><span class=\"p\">.<\/span><span class=\"nx\">index<\/span><span class=\"p\">,<\/span> <span class=\"nx\">column<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span> <span class=\"nx\">value<\/span><span class=\"p\">)<\/span><span class=\"si\">}<\/span>\n    <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This is mostly normal React form state management. The only interesting thing to\ncomment on is the <code class=\"language-plaintext highlighter-rouge\">onBlur<\/code> handler, which hooks back into the overall Tanstack\ntable. Tanstack has a notion of a \u201ctable meta\u201d which lets you attach arbitrary\nbits and pieces to your table definition and then access them later. We can add\nthis into our <code class=\"language-plaintext highlighter-rouge\">useReactTable<\/code> definition from the top:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">table<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useReactTable<\/span><span class=\"p\">({<\/span>\n  <span class=\"nx\">data<\/span><span class=\"p\">,<\/span>\n  <span class=\"nx\">columns<\/span><span class=\"p\">,<\/span>\n  <span class=\"na\">getCoreRowModel<\/span><span class=\"p\">:<\/span> <span class=\"nx\">getCoreRowModel<\/span><span class=\"p\">(),<\/span>\n  <span class=\"na\">meta<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">updateRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">,<\/span> <span class=\"nx\">columnId<\/span><span class=\"p\">,<\/span> <span class=\"nx\">value<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">setData<\/span><span class=\"p\">((<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n        <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"nx\">idx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n          <span class=\"nx\">idx<\/span> <span class=\"o\">===<\/span> <span class=\"nx\">rowIdx<\/span> <span class=\"p\">?<\/span> <span class=\"p\">{<\/span> <span class=\"p\">...<\/span><span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span><span class=\"nx\">columnId<\/span><span class=\"p\">]:<\/span> <span class=\"nx\">value<\/span> <span class=\"p\">}<\/span> <span class=\"p\">:<\/span> <span class=\"nx\">row<\/span>\n        <span class=\"p\">)<\/span>\n      <span class=\"p\">);<\/span>\n    <span class=\"p\">},<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>At this point, we need to modify our column definitions to have them render the\nnew custom cell component. Note that this component is actually the result of\nthat\n<a href=\"https:\/\/tanstack.com\/table\/v8\/docs\/api\/core\/cell#getcontext\"><code class=\"language-plaintext highlighter-rouge\">flexRender()<\/code><\/a>\ncall, which means that the result will appear inside of the <code class=\"language-plaintext highlighter-rouge\">&lt;td&gt;<\/code>:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">columns<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">one<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column One<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">EditableCellInput<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">two<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column Two<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">EditableCellInput<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">three<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column Three<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">EditableCellInput<\/span> <span class=\"p\">}),<\/span>\n<span class=\"p\">];<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Here\u2019s what we should end up with:<\/p>\n\n<p><img src=\"..\/..\/images\/table-focus\/editable-cells.png\" alt=\"Getting started\" \/><\/p>\n\n<h1 id=\"step-2-adding-and-removing-rows\">Step 2: adding and removing rows<\/h1>\n\n<h2 id=\"adding-new-row\">Adding new row<\/h2>\n\n<p>Now we need to be able to add and remove rows from our table. To add a new row,\nwe\u2019ll want to add a table footer which includes a button to add a row, along\nwith a new table meta method to push that new row into the bottom of our state.<\/p>\n\n<p>First, let\u2019s add the footer:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">tfoot<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"border-t\"<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">tr<\/span><span class=\"p\">&gt;<\/span>\n        <span class=\"p\">&lt;<\/span><span class=\"nt\">th<\/span> <span class=\"na\">colSpan<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">getCenterLeafColumns<\/span><span class=\"p\">().<\/span><span class=\"nx\">length<\/span><span class=\"si\">}<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"float-right mr-4 my-2\"<\/span><span class=\"p\">&gt;<\/span>\n            <span class=\"p\">&lt;<\/span><span class=\"nt\">button<\/span>\n              <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"text-sm font-light\"<\/span>\n              <span class=\"na\">onClick<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">options<\/span><span class=\"p\">.<\/span><span class=\"nx\">meta<\/span><span class=\"p\">.<\/span><span class=\"nx\">addRow<\/span><span class=\"si\">}<\/span>\n            <span class=\"p\">&gt;<\/span>\n              Add New +\n            <span class=\"p\">&lt;\/<\/span><span class=\"nt\">button<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"p\">&lt;\/<\/span><span class=\"nt\">div<\/span><span class=\"p\">&gt;<\/span>\n        <span class=\"p\">&lt;\/<\/span><span class=\"nt\">th<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tr<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tfoot<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now, let\u2019s add the <code class=\"language-plaintext highlighter-rouge\">addRow<\/code> function to our table definition:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">table<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useReactTable<\/span><span class=\"p\">({<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"na\">meta<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">addRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">setData<\/span><span class=\"p\">((<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">[...<\/span><span class=\"nx\">data<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">one<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">two<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">three<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"\"<\/span> <span class=\"p\">}]);<\/span>\n    <span class=\"p\">},<\/span>\n    <span class=\"na\">updateRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">,<\/span> <span class=\"nx\">columnId<\/span><span class=\"p\">,<\/span> <span class=\"nx\">value<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{...},<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">})<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This function updates the internal table\u2019s <code class=\"language-plaintext highlighter-rouge\">data<\/code> state to include a new row of\nall blanks.<\/p>\n\n<h2 id=\"removing-a-row\">Removing a row<\/h2>\n\n<p>In order to remove a row, we will add a <a href=\"\">display column<\/a> which will serve as a\nbutton that, when clicked, will trigger a new <code class=\"language-plaintext highlighter-rouge\">removeRow<\/code> method from the table\nmeta, similar to <code class=\"language-plaintext highlighter-rouge\">addRow<\/code> above.<\/p>\n\n<p>First, let\u2019s add a component to serve as the remove button:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">RemoveButton<\/span><span class=\"p\">({<\/span> <span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"nx\">table<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">button<\/span>\n      <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"font-mono font-semibold text-red-600 text-xs align-middle\"<\/span>\n      <span class=\"na\">onClick<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">options<\/span><span class=\"p\">.<\/span><span class=\"nx\">meta<\/span><span class=\"p\">.<\/span><span class=\"nx\">removeRow<\/span><span class=\"p\">(<\/span><span class=\"nx\">row<\/span><span class=\"p\">.<\/span><span class=\"nx\">index<\/span><span class=\"p\">)<\/span><span class=\"si\">}<\/span>\n    <span class=\"p\">&gt;<\/span>\n      x\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">button<\/span><span class=\"p\">&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Just like the <code class=\"language-plaintext highlighter-rouge\">EditableCellInput<\/code>, this table will receive props from tanstack\ndirectly, allowing us to reference the table and row object.<\/p>\n\n<p>Next, let\u2019s use that component to add a new display column:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">columns<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">one<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column One<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">EditableCellInput<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">two<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column Two<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">EditableCellInput<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">accessor<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">three<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">header<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Column Three<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">EditableCellInput<\/span> <span class=\"p\">}),<\/span>\n  <span class=\"nx\">helper<\/span><span class=\"p\">.<\/span><span class=\"nx\">display<\/span><span class=\"p\">({<\/span> <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">remove<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">cell<\/span><span class=\"p\">:<\/span> <span class=\"nx\">RemoveButton<\/span> <span class=\"p\">}),<\/span>\n<span class=\"p\">];<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Finally, let\u2019s add in the new method to the table meta:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">table<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useReactTable<\/span><span class=\"p\">({<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"na\">meta<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">addRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{...},<\/span>\n    <span class=\"na\">updateRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">,<\/span> <span class=\"nx\">columnId<\/span><span class=\"p\">,<\/span> <span class=\"nx\">value<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{...},<\/span>\n    <span class=\"na\">removeRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n      <span class=\"nx\">setData<\/span><span class=\"p\">((<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">filter<\/span><span class=\"p\">((<\/span><span class=\"nx\">_<\/span><span class=\"p\">,<\/span> <span class=\"nx\">idx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">idx<\/span> <span class=\"o\">!==<\/span> <span class=\"nx\">rowIdx<\/span><span class=\"p\">));<\/span>\n    <span class=\"p\">},<\/span>\n  <span class=\"p\">},<\/span>\n<span class=\"p\">})<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Here\u2019s a gif of everything so far:<\/p>\n\n<p><img src=\"..\/..\/images\/table-focus\/add-remove.gif\" alt=\"Add and remove\" \/><\/p>\n\n<h1 id=\"step-3-auto-focus-new-rows-when-added\">Step 3: auto-focus new rows when added<\/h1>\n\n<p>Finally, we get to the whole point of this post.<\/p>\n\n<h2 id=\"refs\">Refs<\/h2>\n\n<p>Refs are mutable properties that get attached to components but don\u2019t cause\nre-renders when they are changed. According to the\n<a href=\"https:\/\/react.dev\/learn\/referencing-values-with-refs\">documentation<\/a>:<\/p>\n\n<blockquote>\n  <p>When you want a component to \u201cremember\u201d some information, but you don\u2019t want\nthat information to trigger new renders, you can use a ref.<\/p>\n<\/blockquote>\n\n<p>Managing something like focus is not directly in the purview of React. Instead,\nReact gives us escape hatches which we can use to tie non-React parts of the\nbrowser ecosystem into the React environment. For our purposes, we will use a\n<a href=\"https:\/\/react.dev\/learn\/manipulating-the-dom-with-refs\">ref<\/a>. The\n<a href=\"https:\/\/react.dev\/learn\/manipulating-the-dom-with-refs#example-focusing-a-text-input\">first example in the React documentation<\/a>\nshows almost precisely what we want to do: click a button to focus an input:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">useRef<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">react<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">Form<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">inputRef<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useRef<\/span><span class=\"p\">(<\/span><span class=\"kc\">null<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"kd\">function<\/span> <span class=\"nx\">handleClick<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">inputRef<\/span><span class=\"p\">.<\/span><span class=\"nx\">current<\/span><span class=\"p\">.<\/span><span class=\"nx\">focus<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">input<\/span> <span class=\"na\">ref<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">inputRef<\/span><span class=\"si\">}<\/span> <span class=\"p\">\/&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">button<\/span> <span class=\"na\">onClick<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">handleClick<\/span><span class=\"si\">}<\/span><span class=\"p\">&gt;<\/span>Focus the input<span class=\"p\">&lt;\/<\/span><span class=\"nt\">button<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;\/&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>However, our use case is more complicated: <code class=\"language-plaintext highlighter-rouge\">ref<\/code> is generated via the <code class=\"language-plaintext highlighter-rouge\">useRef<\/code>\nhook, and hooks are only allowed to be called once per component. Our state,\nhowever, is an array of objects. React\u2019s documentation is quite clear here:<\/p>\n\n<blockquote>\n  <p>\u2026 Hooks must only be called at the top-level of your component. You can\u2019t\ncall useRef in a loop, in a condition, or inside a map() call.<\/p>\n<\/blockquote>\n\n<p>React has a way around this limitation.<\/p>\n\n<h2 id=\"ref-callbacks\">Ref callbacks<\/h2>\n\n<p>The <code class=\"language-plaintext highlighter-rouge\">ref<\/code> attribute on a component can take a\n<a href=\"https:\/\/react.dev\/reference\/react-dom\/components\/common#ref-callback\">callback function<\/a>\nwhich takes as an argument the DOM node that is attached to the component at\nhand. The React documentation has a\n<a href=\"https:\/\/react.dev\/learn\/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback\">handy example<\/a>\nof using a ref callback to handle lists by maintaining a separate <code class=\"language-plaintext highlighter-rouge\">Map<\/code>.<\/p>\n\n<p>We can use a similar basic concept, but we have a few additional complications.\nFirst, we\u2019ll need to have our <code class=\"language-plaintext highlighter-rouge\">ref<\/code> be computed when we draw our table. We can\nmanage this in the same way as described in the React documentation: we can use\na <code class=\"language-plaintext highlighter-rouge\">Map&lt;string, HTMLElement&gt;<\/code> as the type of the value inside of our <code class=\"language-plaintext highlighter-rouge\">ref<\/code>, and\nthen update it using a ref callback.<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">ref<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useRef<\/span><span class=\"p\">(<\/span><span class=\"k\">new<\/span> <span class=\"nb\">Map<\/span><span class=\"p\">());<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">tbody<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"si\">{<\/span><span class=\"nx\">table<\/span><span class=\"p\">.<\/span><span class=\"nx\">getRowModel<\/span><span class=\"p\">().<\/span><span class=\"nx\">rows<\/span><span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"nx\">rowIdx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n        <span class=\"p\">&lt;<\/span><span class=\"nt\">tr<\/span> <span class=\"na\">key<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">row<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"si\">}<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"mt-4\"<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"si\">{<\/span><span class=\"nx\">row<\/span><span class=\"p\">.<\/span><span class=\"nx\">getVisibleCells<\/span><span class=\"p\">().<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">cell<\/span><span class=\"p\">,<\/span> <span class=\"nx\">cellIdx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span>\n            <span class=\"p\">&lt;<\/span><span class=\"nt\">td<\/span> <span class=\"na\">key<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">cell<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"si\">}<\/span> <span class=\"na\">className<\/span><span class=\"p\">=<\/span><span class=\"s\">\"p-2 border-l first:border-0\"<\/span><span class=\"p\">&gt;<\/span>\n              <span class=\"si\">{<\/span><span class=\"nx\">flexRender<\/span><span class=\"p\">(<\/span><span class=\"nx\">cell<\/span><span class=\"p\">.<\/span><span class=\"nx\">column<\/span><span class=\"p\">.<\/span><span class=\"nx\">columnDef<\/span><span class=\"p\">.<\/span><span class=\"nx\">cell<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span>\n                <span class=\"p\">...<\/span><span class=\"nx\">cell<\/span><span class=\"p\">.<\/span><span class=\"nx\">getContext<\/span><span class=\"p\">(),<\/span>\n                <span class=\"na\">cellRef<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">node<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n                  <span class=\"kd\">const<\/span> <span class=\"nx\">map<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ref<\/span><span class=\"p\">.<\/span><span class=\"nx\">current<\/span><span class=\"p\">;<\/span>\n                  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">node<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n                    <span class=\"nx\">map<\/span><span class=\"p\">.<\/span><span class=\"kd\">set<\/span><span class=\"p\">(<\/span><span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">}<\/span><span class=\"s2\">-<\/span><span class=\"p\">${<\/span><span class=\"nx\">cellIdx<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">,<\/span> <span class=\"nx\">node<\/span><span class=\"p\">);<\/span>\n                  <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n                    <span class=\"nx\">map<\/span><span class=\"p\">.<\/span><span class=\"k\">delete<\/span><span class=\"p\">(<\/span><span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">}<\/span><span class=\"s2\">-<\/span><span class=\"p\">${<\/span><span class=\"nx\">cellIdx<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">);<\/span>\n                  <span class=\"p\">}<\/span>\n                <span class=\"p\">},<\/span>\n              <span class=\"p\">})<\/span><span class=\"si\">}<\/span>\n            <span class=\"p\">&lt;\/<\/span><span class=\"nt\">td<\/span><span class=\"p\">&gt;<\/span>\n          <span class=\"p\">))<\/span><span class=\"si\">}<\/span>\n        <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tr<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">))<\/span><span class=\"si\">}<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">tbody<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>There are a few changes here worth discussing. First, we establish our new <code class=\"language-plaintext highlighter-rouge\">ref<\/code>\nwith the <code class=\"language-plaintext highlighter-rouge\">useRef<\/code> hook, and initialize it\u2019s <code class=\"language-plaintext highlighter-rouge\">.current<\/code> value to an empty <code class=\"language-plaintext highlighter-rouge\">Map<\/code>.\nOne thing to note is that when calling <code class=\"language-plaintext highlighter-rouge\">.get()<\/code> on a map, keys are compared\nusing <code class=\"language-plaintext highlighter-rouge\">===<\/code>, so we aren\u2019t able to use an array as our key type, because\n<code class=\"language-plaintext highlighter-rouge\">[0, 0] !== [0, 0]<\/code>. This is in contract to a language like Python, where we\ncould use a tuple.<\/p>\n\n<p>In any case, we next change how the inner contents of a cell are rendered.\nRemember in part one we used <code class=\"language-plaintext highlighter-rouge\">flexRender()<\/code>? Here, we do the same but instead of\njust passing in the cell\u2019s context to our component, we also pass a new\n<code class=\"language-plaintext highlighter-rouge\">cellRef<\/code> prop. This prop has the shape of a ref callback function. Now we have\nto change our <code class=\"language-plaintext highlighter-rouge\">EditableCellInput<\/code> to know how to handle it:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">EditableCellInput<\/span><span class=\"p\">({<\/span> <span class=\"nx\">getValue<\/span><span class=\"p\">,<\/span> <span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"nx\">column<\/span><span class=\"p\">,<\/span> <span class=\"nx\">table<\/span><span class=\"p\">,<\/span> <span class=\"nx\">cellRef<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">input<\/span>\n      <span class=\"na\">ref<\/span><span class=\"p\">=<\/span><span class=\"si\">{<\/span><span class=\"nx\">cellRef<\/span><span class=\"si\">}<\/span>\n      <span class=\"c1\">\/\/ ...<\/span>\n    <span class=\"p\">\/&gt;<\/span>\n  <span class=\"p\">);<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Now, whenever we render the table, any <code class=\"language-plaintext highlighter-rouge\">EditableCellInput<\/code> cells that get\nrendered will have their attached <code class=\"language-plaintext highlighter-rouge\">&lt;input \/&gt;<\/code> elements show up as values in our\nref variable, and we can get them at-will via a <code class=\"language-plaintext highlighter-rouge\">Map.get<\/code>. There are two last\nthings we need to do: actually focus the element, and make sure to only focus it\nwhen appropriate.<\/p>\n\n<h2 id=\"focusing-as-a-side-effect\">Focusing as a side-effect<\/h2>\n\n<p>Now that we have our big ref map, we need to focus the appropriate element after\nour table renders. This focus is a <em>possible side effect<\/em> of rendering the\ntable, based on how the underlying data changed. In order to link these sorts of\nside effects with underlying state changes, React offers us the opportunity to\nuse effects in the aptly named\n<a href=\"https:\/\/react.dev\/learn\/synchronizing-with-effects\"><code class=\"language-plaintext highlighter-rouge\">useEffect<\/code> hook<\/a>.<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useEffect<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"kd\">const<\/span> <span class=\"nx\">node<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ref<\/span><span class=\"p\">.<\/span><span class=\"nx\">current<\/span><span class=\"p\">.<\/span><span class=\"kd\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}<\/span><span class=\"s2\">-0`<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">node<\/span><span class=\"p\">)<\/span> <span class=\"nx\">node<\/span><span class=\"p\">.<\/span><span class=\"nx\">focus<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">},<\/span> <span class=\"p\">[<\/span><span class=\"nx\">data<\/span><span class=\"p\">]);<\/span>\n\n  <span class=\"c1\">\/\/ ...<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Unfortunately, this implementation has a really annoying bug. The effect will\nfire <em>whenever any re-render happens<\/em>. This means that when a cell is blurred,\nthe first column of the last row will always be auto-focused, meaning no other\ncells will ever be able to be edited!<\/p>\n\n<p><img src=\"..\/..\/images\/table-focus\/annoying.gif\" alt=\"Very annoying bug\" \/><\/p>\n\n<p>To deal with this, we\u2019ll need a way to have the <code class=\"language-plaintext highlighter-rouge\">.focus()<\/code> called conditionally.\nWe can manage this with a small additional piece of state:<\/p>\n\n<div class=\"language-jsx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">App<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">shouldFocus<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setShouldFocus<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useState<\/span><span class=\"p\">(<\/span><span class=\"kc\">false<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"c1\">\/\/ ...<\/span>\n\n  <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">useEffect<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">shouldFocus<\/span><span class=\"p\">)<\/span> <span class=\"k\">return<\/span><span class=\"p\">;<\/span>\n\n    <span class=\"kd\">const<\/span> <span class=\"nx\">node<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">ref<\/span><span class=\"p\">.<\/span><span class=\"nx\">current<\/span><span class=\"p\">.<\/span><span class=\"kd\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">1<\/span><span class=\"p\">}<\/span><span class=\"s2\">-0`<\/span><span class=\"p\">);<\/span>\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">node<\/span><span class=\"p\">)<\/span> <span class=\"nx\">node<\/span><span class=\"p\">.<\/span><span class=\"nx\">focus<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">},<\/span> <span class=\"p\">[<\/span><span class=\"nx\">data<\/span><span class=\"p\">,<\/span> <span class=\"nx\">shouldFocus<\/span><span class=\"p\">]);<\/span>\n\n  <span class=\"c1\">\/\/ ...<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">table<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useReactTable<\/span><span class=\"p\">({<\/span>\n    <span class=\"nx\">data<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">columns<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">getCoreRowModel<\/span><span class=\"p\">:<\/span> <span class=\"nx\">getCoreRowModel<\/span><span class=\"p\">(),<\/span>\n    <span class=\"na\">meta<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\n      <span class=\"na\">addRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">setShouldFocus<\/span><span class=\"p\">(<\/span><span class=\"kc\">true<\/span><span class=\"p\">)<\/span>\n        <span class=\"nx\">setData<\/span><span class=\"p\">((<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">[...<\/span><span class=\"nx\">data<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">one<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">two<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">,<\/span> <span class=\"na\">three<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"\"<\/span> <span class=\"p\">}]);<\/span>\n      <span class=\"p\">},<\/span>\n      <span class=\"na\">updateRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">,<\/span> <span class=\"nx\">columnId<\/span><span class=\"p\">,<\/span> <span class=\"nx\">value<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">setShouldFocus<\/span><span class=\"p\">(<\/span><span class=\"kc\">false<\/span><span class=\"p\">);<\/span>\n        <span class=\"nx\">setData<\/span><span class=\"p\">((<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n          <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">map<\/span><span class=\"p\">((<\/span><span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"nx\">idx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span>\n            <span class=\"nx\">idx<\/span> <span class=\"o\">===<\/span> <span class=\"nx\">rowIdx<\/span> <span class=\"p\">?<\/span> <span class=\"p\">{<\/span> <span class=\"p\">...<\/span><span class=\"nx\">row<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span><span class=\"nx\">columnId<\/span><span class=\"p\">]:<\/span> <span class=\"nx\">value<\/span> <span class=\"p\">}<\/span> <span class=\"p\">:<\/span> <span class=\"nx\">row<\/span><span class=\"p\">,<\/span>\n          <span class=\"p\">),<\/span>\n        <span class=\"p\">);<\/span>\n      <span class=\"p\">},<\/span>\n      <span class=\"na\">removeRow<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"nx\">rowIdx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">setShouldFocus<\/span><span class=\"p\">(<\/span><span class=\"kc\">false<\/span><span class=\"p\">)<\/span>\n        <span class=\"nx\">setData<\/span><span class=\"p\">((<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">filter<\/span><span class=\"p\">((<\/span><span class=\"nx\">_<\/span><span class=\"p\">,<\/span> <span class=\"nx\">idx<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">idx<\/span> <span class=\"o\">!==<\/span> <span class=\"nx\">rowIdx<\/span><span class=\"p\">));<\/span>\n      <span class=\"p\">},<\/span>\n    <span class=\"p\">},<\/span>\n  <span class=\"p\">})<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"p\">(<\/span>\n    <span class=\"c1\">\/\/ ...<\/span>\n  <span class=\"p\">)<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>That should do it. Now, when we click the \u201cAdd Data\u201d button on our data table,\nwe should automatically focus onto the newly created row:<\/p>\n\n<p><img src=\"..\/..\/images\/table-focus\/success.gif\" alt=\"All done\" \/><\/p>\n\n<p>The full working example is available in\n<a href=\"https:\/\/github.com\/bsmithgall\/tanstack-focus-ref\">this repository<\/a>.<\/p>\n","pubDate":"Thu, 11 Apr 2024","link":"https:\/\/bensmithgall.com\/\/blog\/react-tanstack-new-row-autofocus","guid":"https:\/\/bensmithgall.com\/\/blog\/react-tanstack-new-row-autofocus"},{"title":"Hamburger navigation with Phoenix Framework","category":{},"description":"<p>Long time, no blog! Anyways, I\u2019ve been messing around with Elixir and Phoenix\nfor a <a href=\"https:\/\/github.com\/bsmithgall\/elswisser\">side project around managing some chess\ntournaments<\/a>. Since I plan on\noccasionally managing the application from my phone, I needed a way to handle\ncollapsing navigation at a different viewport. I ended up using the classic\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Hamburger_button\">hamburger menu<\/a> implemented\nusing Phoenix 1.7\u2019s new\n<a href=\"https:\/\/www.phoenixframework.org\/blog\/phoenix-1.7-final-released\">components<\/a>.<\/p>\n\n<p>I have been enjoying working with Elixir and Phoenix, though is quite a\ndifferent way of thinking about problems and how to organize code, and I am\ncertainly no expert, but I liked this approach.<\/p>\n\n<p>Here\u2019s a short gif of the component dropped on top of a freshly generated\nPhoenix application (generated via <code class=\"language-plaintext highlighter-rouge\">mix phx.new --no-ecto hamburger<\/code>):<\/p>\n\n<p><img src=\"..\/..\/images\/2023-09-10-hamburger.gif\" alt=\"Hamburger navigation\" \/><\/p>\n\n<p>And here is the full component\u2019s code:<\/p>\n\n<pre><code class=\"language-plain\">defmodule HamburgerWeb.Topnav do\n  import HamburgerWeb.CoreComponents, only: [icon: 1]\n  use Phoenix.Component\n  alias Phoenix.LiveView.JS\n\n  def nav(assigns) do\n    ~H\"\"\"\n    &lt;header class=\"px-4 z-40 sticky bg-white\"&gt;\n      &lt;div class=\"flex items-center justify-between border-b border-gray-200 text-sm py-3 z-40\"&gt;\n        &lt;p class=\"bg-brand\/5 text-brand rounded-xl px-2 font-medium leading-6\"&gt;\n          Hamburger\n        &lt;\/p&gt;\n        &lt;.hamburger_button \/&gt;\n        &lt;.top_nav_content \/&gt;\n      &lt;\/div&gt;\n      &lt;.open_hamburger \/&gt;\n    &lt;\/header&gt;\n    \"\"\"\n  end\n\n  defp top_nav_content(assigns) do\n    ~H\"\"\"\n    &lt;div class=\"hidden md:flex md:items-center md:gap-4 font-semibold leading-6\"&gt;\n      &lt;a href=\"\/\" class=\"hover:text-zinc-500\"&gt;\n        Home\n      &lt;\/a&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  defp hamburger_button(assigns) do\n    ~H\"\"\"\n    &lt;div class=\"md:hidden\"&gt;\n      &lt;button phx-click={show_hamburger()}&gt;\n        &lt;.icon name=\"hero-ellipsis-vertical-mini\" \/&gt;\n      &lt;\/button&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  defp open_hamburger(assigns) do\n    ~H\"\"\"\n    &lt;div id=\"hamburger-container\" class=\"hidden relative z-50\"&gt;\n      &lt;div id=\"hamburger-backdrop\" class=\"fixed inset-0 bg-zinc-50\/90 transition-opacity\"&gt;&lt;\/div&gt;\n      &lt;nav\n        id=\"hamburger-content\"\n        class=\"fixed top-0 left-0 bottom-0 flex flex-col grow justify-between w-3\/4 max-w-sm py-6 bg-white border-r overflow-y-auto\"\n      &gt;\n        &lt;div&gt;\n          &lt;div class=\"flex items-center mb-4 place-content-between mx-4 border-b-zinc-200\"&gt;\n            &lt;div class=\"flex items-center gap-4\"&gt;\n              &lt;p class=\"bg-brand\/5 text-brand rounded-xl px-2 font-medium leading-6\"&gt;\n                Hamburger\n              &lt;\/p&gt;\n            &lt;\/div&gt;\n            &lt;button class=\"navbar-close\" phx-click={hide_hamburger()}&gt;\n              &lt;.icon name=\"hero-x-mark-mini\" \/&gt;\n            &lt;\/button&gt;\n          &lt;\/div&gt;\n          &lt;div&gt;\n            &lt;ul&gt;&lt;\/ul&gt;\n            &lt;li class=\"block px-6 py-2 text-sm font-semibold hover:bg-gray-200\" phx-click=\"\/\"&gt;\n              &lt;a href=\"\/\"&gt;Home&lt;\/a&gt;\n            &lt;\/li&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/nav&gt;\n    &lt;\/div&gt;\n    \"\"\"\n  end\n\n  defp show_hamburger(js \\\\ %JS{}) do\n    js\n    |&gt; JS.show(\n      to: \"#hamburger-content\",\n      transition:\n        {\"transition-all transform ease-in-out duration-300\", \"-translate-x-3\/4\", \"translate-x-0\"},\n      time: 300,\n      display: \"flex\"\n    )\n    |&gt; JS.show(\n      to: \"#hamburger-backdrop\",\n      transition:\n        {\"transition-all transform ease-in-out duration-300\", \"opacity-0\", \"opacity-100\"}\n    )\n    |&gt; JS.show(\n      to: \"#hamburger-container\",\n      time: 300\n    )\n    |&gt; JS.add_class(\"overflow-hidden\", to: \"body\")\n  end\n\n  defp hide_hamburger(js \\\\ %JS{}) do\n    js\n    |&gt; JS.hide(\n      to: \"#hamburger-backdrop\",\n      transition: {\"transition-all transform ease-in duration-200\", \"opacity-100\", \"opacity-0\"}\n    )\n    |&gt; JS.hide(\n      to: \"#hamburger-content\",\n      transition:\n        {\"transition-all transform ease-in duration-200\", \"translate-x-0\", \"-translate-x-3\/4\"}\n    )\n    |&gt; JS.hide(to: \"#hamburger-container\", transition: {\"block\", \"block\", \"hidden\"})\n    |&gt; JS.remove_class(\"overflow-hidden\", to: \"body\")\n  end\nend\n<\/code><\/pre>\n\n<p>Here\u2019s how it works in a bit more detail: In the top navigation, there are three\nseparate sections:<\/p>\n\n<ol>\n  <li>The \u201cLogo\u201d section, which always appears.<\/li>\n  <li>The top navigation, which will appear in viewports greater than whatever\nthe <code class=\"language-plaintext highlighter-rouge\">md:<\/code> breakpoint is set as (for more, see <a href=\"https:\/\/tailwindcss.com\/docs\/responsive-design\">Tailwind media query\ndocumentation<\/a>). Phoenix\n1.7 ships with Tailwind by default.<\/li>\n  <li>The hamburger button, which is <code class=\"language-plaintext highlighter-rouge\">display: hidden<\/code> at larger viewports and\nvisible at smaller ones.<\/li>\n<\/ol>\n\n<p>The hamburger button has a <code class=\"language-plaintext highlighter-rouge\">phx-click<\/code> listener on it. This is a nice addition\nin Phoenix to allow for server-generated files to have some basic client-side\ncapabilities. When the button is clicked, the <code class=\"language-plaintext highlighter-rouge\">show_hamburger\/1<\/code> method is\ncalled. This method dispatches a handful of <a href=\"https:\/\/hexdocs.pm\/phoenix_live_view\/Phoenix.LiveView.JS.html#show\/1\"><code class=\"language-plaintext highlighter-rouge\">JS.show\/1<\/code>\nevents<\/a> to\nthe different parts of the side navigation. The side navigation portion of the\nhamburger menu is also made up of three components, each of which gets a\ndifferent show transition applied to them:<\/p>\n\n<ol>\n  <li>The actual content of the sidenav, which slides in from the left via an\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/transition-timing-function#try_it\"><code class=\"language-plaintext highlighter-rouge\">ease-in-out<\/code>\ntransition<\/a>.\nThe actual transition slides by translating the element from offscreen by the\ninverse of its width to 0, meaning that it will slide in to the right until\nthe final left edge of the element is at position 0.<\/li>\n  <li>A background which gets dropped over the rest of the page. This transitions\nfrom <code class=\"language-plaintext highlighter-rouge\">opacity-0<\/code> (totally invisible) to <code class=\"language-plaintext highlighter-rouge\">opacity-100<\/code> (totally visible).<\/li>\n  <li>A wrapper element which initially starts out as hidden. This immediately\nbecomes visible to allow the children elements (outlined above) to transition\ninto view.<\/li>\n<\/ol>\n\n<p>The contents also contain a little <code class=\"language-plaintext highlighter-rouge\">x<\/code> icon, which triggers hiding the contents\nby basically replaying the steps in reverse.<\/p>\n\n<p>That\u2019s the main portion of this component. It\u2019s pretty straightforward to extend\nthis by dropping additional pieces into <a href=\"https:\/\/hexdocs.pm\/phoenix_live_view\/Phoenix.Component.html#module-slots\">slots<\/a>.<\/p>\n","pubDate":"Sun, 10 Sep 2023","link":"https:\/\/bensmithgall.com\/\/blog\/phoenix-hamburger-menu","guid":"https:\/\/bensmithgall.com\/\/blog\/phoenix-hamburger-menu"},{"title":"Some mocking tricks with jest","category":{},"description":"<h2 id=\"mock-only-one-method-in-a-module\">Mock only one method in a module<\/h2>\n\n<p>Sometimes I find myself only wanting to mock one method in a built-in module\n(something like <code class=\"language-plaintext highlighter-rouge\">fs<\/code> or <code class=\"language-plaintext highlighter-rouge\">path<\/code>), and want the default behavior for everything\nelse, maybe because a library has some on-import thing it\u2019s doing with those\nbuilt-ins. I can\u2019t use <code class=\"language-plaintext highlighter-rouge\">spyOn<\/code> because I want to have direct control over the\nmock:<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">mockJoin<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">fn<\/span><span class=\"p\">();<\/span>\n<span class=\"kd\">function<\/span> <span class=\"nx\">mockPath<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">original<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">.<\/span><span class=\"nx\">requireActual<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">path<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">{<\/span> <span class=\"p\">...<\/span><span class=\"nx\">original<\/span><span class=\"p\">,<\/span> <span class=\"na\">join<\/span><span class=\"p\">:<\/span> <span class=\"nx\">mockJoin<\/span> <span class=\"p\">};<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">mock<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">path<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">mockPath<\/span><span class=\"p\">());<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>This uses a combination of Jest\u2019s hoisting for things that are named\n<code class=\"language-plaintext highlighter-rouge\">^mock.+$<\/code> and factory import functions (I can\u2019t find the explicit examples\nin the docs, but I get yelled at by the CLI when things are not named in this\nway).<\/p>\n\n<h2 id=\"mocking-axios\">Mocking axios<\/h2>\n\n<p>This one is pretty straightforward but is something that I come back with some\nregularity. There\u2019s <code class=\"language-plaintext highlighter-rouge\">moxios<\/code>, but I have found it to be sort of slow and usually\npretty serious overkill for what I want to do. Using something a bit simpler\nlike this has been successful for me:<\/p>\n\n<div class=\"language-ts highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">import<\/span> <span class=\"nx\">axios<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">axios<\/span><span class=\"dl\">\"<\/span><span class=\"p\">;<\/span>\n<span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">mock<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">axios<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">axiosMock<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">axios<\/span> <span class=\"k\">as<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">Mocked<\/span><span class=\"o\">&lt;<\/span><span class=\"k\">typeof<\/span> <span class=\"nx\">axios<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<span class=\"nx\">axiosMock<\/span><span class=\"p\">.<\/span><span class=\"nx\">request<\/span><span class=\"p\">.<\/span><span class=\"nx\">mockImplementation<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nb\">Promise<\/span><span class=\"p\">.<\/span><span class=\"nx\">resolve<\/span><span class=\"p\">({<\/span>\n    <span class=\"na\">data<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">&lt;html&gt;&lt;\/html&gt;<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n    <span class=\"na\">headers<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">content-type<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">text\/html<\/span><span class=\"dl\">\"<\/span> <span class=\"p\">},<\/span>\n  <span class=\"p\">});<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h2 id=\"mocks-and-streams\">Mocks and streams<\/h2>\n\n<p>When working with streams (most specifically something like\n<code class=\"language-plaintext highlighter-rouge\">fs.createWriteStream<\/code> or <code class=\"language-plaintext highlighter-rouge\">@google-cloud\/storage<\/code>), it\u2019s nice to have a real\nstream to use when testing.<\/p>\n\n<h3 id=\"read-streams\">Read streams<\/h3>\n\n<p>Read streams are relatively straightforward to use. For example, a GCS\n<code class=\"language-plaintext highlighter-rouge\">File<\/code> could be mocked as follows:<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">Readable<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">stream<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">mockReadStream<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">fn<\/span><span class=\"p\">().<\/span><span class=\"nx\">mockImplementation<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">readable<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">Readable<\/span><span class=\"p\">();<\/span>\n  <span class=\"nx\">readable<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">hello<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n  <span class=\"nx\">readable<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">world<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n  <span class=\"nx\">readable<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"kc\">null<\/span><span class=\"p\">);<\/span>\n\n  <span class=\"k\">return<\/span> <span class=\"nx\">readable<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">mockFile<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">fn<\/span><span class=\"p\">().<\/span><span class=\"nx\">mockImplementation<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">createReadStream<\/span><span class=\"p\">:<\/span> <span class=\"nx\">mockReadStream<\/span><span class=\"p\">,<\/span>\n  <span class=\"p\">};<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">mockReadStream<\/span> <span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Then in a test, when the stream is consumed the output should be \u201chello world\u201d.<\/p>\n\n<h3 id=\"write-streams\">Write streams<\/h3>\n\n<p>Write streams are a little bit more complex to create and use.<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">Writable<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">stream<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\n\n<span class=\"kd\">class<\/span> <span class=\"nx\">WriteMemory<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">Writable<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">constructor<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">super<\/span><span class=\"p\">();<\/span>\n    <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">buffer<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"nx\">_write<\/span><span class=\"p\">(<\/span><span class=\"nx\">chunk<\/span><span class=\"p\">,<\/span> <span class=\"nx\">_<\/span><span class=\"p\">,<\/span> <span class=\"nx\">next<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">buffer<\/span> <span class=\"o\">+=<\/span> <span class=\"nx\">chunk<\/span><span class=\"p\">;<\/span>\n    <span class=\"nx\">next<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">}<\/span>\n\n  <span class=\"nx\">reset<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">buffer<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">MockWriteStream<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">WriteMemory<\/span><span class=\"p\">();<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">mockCreateWriteStream<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">fn<\/span><span class=\"p\">().<\/span><span class=\"nx\">mockImplementation<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">MockWriteStream<\/span><span class=\"p\">.<\/span><span class=\"nx\">reset<\/span><span class=\"p\">();<\/span>\n  <span class=\"k\">return<\/span> <span class=\"nx\">MockWriteStream<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"kd\">const<\/span> <span class=\"nx\">mockFile<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">jest<\/span><span class=\"p\">.<\/span><span class=\"nx\">fn<\/span><span class=\"p\">().<\/span><span class=\"nx\">mockImplementation<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\n    <span class=\"na\">createWriteStream<\/span><span class=\"p\">:<\/span> <span class=\"nx\">mockCreateWriteStream<\/span><span class=\"p\">,<\/span>\n  <span class=\"p\">};<\/span>\n<span class=\"p\">});<\/span>\n\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">MockWriteStream<\/span> <span class=\"p\">};<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>Then in the tests, after calling functions that stream into the\n<code class=\"language-plaintext highlighter-rouge\">createWriteStream<\/code>, the contents should be available on\n<code class=\"language-plaintext highlighter-rouge\">MockWriteStream.buffer<\/code> for assertions.<\/p>\n\n<h3 id=\"through-streams\">Through streams<\/h3>\n\n<p>Here\u2019s a test on a class that does line-by-line JSON serialization\n(take an object and transform it into the JSON representation of that\nobject while inserting the correct opening and closing characters):<\/p>\n\n<div class=\"language-js highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">test<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">properly outputs JSON<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"nx\">done<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nx\">expect<\/span><span class=\"p\">.<\/span><span class=\"nx\">assertions<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"nx\">serializer<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">JSONLineSerializer<\/span><span class=\"p\">();<\/span>\n\n  <span class=\"kd\">const<\/span> <span class=\"nx\">items<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[{<\/span> <span class=\"na\">a<\/span><span class=\"p\">:<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"na\">b<\/span><span class=\"p\">:<\/span> <span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"na\">c<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"na\">d<\/span><span class=\"p\">:<\/span> <span class=\"mi\">3<\/span> <span class=\"p\">}<\/span> <span class=\"p\">}];<\/span>\n\n  <span class=\"kd\">let<\/span> <span class=\"nx\">result<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">\"\"<\/span><span class=\"p\">;<\/span>\n  <span class=\"nx\">Readable<\/span><span class=\"p\">.<\/span><span class=\"k\">from<\/span><span class=\"p\">(<\/span><span class=\"nx\">items<\/span><span class=\"p\">).<\/span><span class=\"nx\">pipe<\/span><span class=\"p\">(<\/span><span class=\"nx\">serializer<\/span><span class=\"p\">);<\/span>\n  <span class=\"nx\">serializer<\/span><span class=\"p\">.<\/span><span class=\"nx\">on<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">data<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"nx\">data<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">(<\/span><span class=\"nx\">result<\/span> <span class=\"o\">+=<\/span> <span class=\"nx\">data<\/span><span class=\"p\">));<\/span>\n  <span class=\"nx\">serializer<\/span><span class=\"p\">.<\/span><span class=\"nx\">on<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">end<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">expect<\/span><span class=\"p\">(<\/span><span class=\"nx\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">parse<\/span><span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">)).<\/span><span class=\"nx\">toEqual<\/span><span class=\"p\">(<\/span><span class=\"nx\">items<\/span><span class=\"p\">);<\/span>\n    <span class=\"nx\">done<\/span><span class=\"p\">();<\/span>\n  <span class=\"p\">});<\/span>\n<span class=\"p\">});<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>A few notes about this one: it\u2019s conceptually similar to the read stream\nexample from above, but it uses an explicit callback to understand when the\nstream has been consumed (see\n<a href=\"https:\/\/jestjs.io\/docs\/en\/asynchronous#callbacks\">here<\/a> for information\nabout jest callbacks and\n<a href=\"https:\/\/nodejs.org\/docs\/latest-v12.x\/api\/stream.html#stream_event_end\">here<\/a>)\nfor information about stream events.<\/p>\n","pubDate":"Mon, 06 Jul 2020","link":"https:\/\/bensmithgall.com\/\/blog\/jest-mock-trick","guid":"https:\/\/bensmithgall.com\/\/blog\/jest-mock-trick"},{"title":"2019 and 2020","category":{},"description":"<p>This is yet another look forward and look back end-of-year post. My 2019 goals\ndidn\u2019t really go as planned; I ended up taking a new job about halfway through\nthe year at the New York Times, where I\u2019ve worked on a number of different\nthings, including some <a href=\"https:\/\/www.nytimes.com\/interactive\/2019\/08\/02\/us\/politics\/2020-democratic-fundraising.html\">campaign finance\nstories<\/a>,\nwork on a large <a href=\"https:\/\/www.nytimes.com\/interactive\/2019\/12\/19\/opinion\/location-tracking-cell-phone.html\">leaked location\ndataset<\/a>,\nelections coverage, and tooling to better connect with Times readers. I also\ncontinued writing tiny reviews of all the books I read this past year. My\nfavorites were Kim Stanley Robinson\u2019s <a href=\"\/books\/red-mars\"><em>Mars<\/em><\/a> trilogy,\n<a href=\"\/books\/nixonland\"><em>Nixonland<\/em><\/a>, and Umberto Eco\u2019s tremendous <a href=\"\/books\/name-of-the-rose\"><em>Name of the\nRose<\/em><\/a>.<\/p>\n\n<h3 id=\"2019-goals\">2019 Goals<\/h3>\n\n<h5 id=\"write-and-play-more-music-completed\">Write and play more music: <em>Completed<\/em><\/h5>\n\n<p>I wrote and released a bit more soundtrack music, but also something ambient and\nunrelated to the game that I\u2019m pretty <a href=\"https:\/\/soundcloud.com\/system-of-signs\/floating\">proud\nof<\/a>.<\/p>\n\n<h5 id=\"improved-general-fitness-sort-of-completed\">Improved general fitness: <em>Sort of completed<\/em><\/h5>\n\n<p>In the end, I ended up going back to the 5\/3\/1 split because I couldn\u2019t really\nfigure out anything else to do. I like 5\/3\/1; it\u2019s simple, and I am still making\nprogress towards higher weights. I spent most of the year working back towards\nwhere I was at the end of 2018 since I had taken nearly two months off, but by\nthe end of the year I had set a new PR with a 475lb deadlift.<\/p>\n\n<h5 id=\"work-more-on-my-spanish-not-completed\">Work more on my Spanish: <em>Not completed<\/em><\/h5>\n\n<p>This was a complete failure this year. At the beginning of the year I had bought\ntranslations of a few chapter books (notably, the first two of the <em>Series of\nUnfortunate Events<\/em> books translated into Spanish), but I stopped working\nthrough them about halfway through the year, right around when I started my new\njob. I would say that my Spanish proficiency has basically remained the same\nthis year, but I want to try to work on it more. I\u2019m not sure the best way to\nimprove on where I am now.<\/p>\n\n<h5 id=\"read-and-do-the-exercises-from-the-structure-and-interpretation-of-computer-programs-not-completed\">Read and do the exercises from The Structure and Interpretation of Computer Programs: <em>Not completed<\/em><\/h5>\n\n<p>I didn\u2019t even start this.<\/p>\n\n<h3 id=\"2020-goals\">2020 Goals<\/h3>\n\n<h5 id=\"less-tv\">Less TV<\/h5>\n\n<p>I read <a href=\"https:\/\/thebaffler.com\/salvos\/r-i-p-kill-your-tv-zaitchik\">this<\/a>\ncriticism towards the end of the year and it really resonated with me, so I\u2019m\ngoing to make a concerted effort this year to try to watch less television.\nSpecifically, I want to cut out \u201cfiller\u201d TV.<\/p>\n\n<h5 id=\"deadlift-500\">Deadlift 500<\/h5>\n\n<p>I got pretty close by the end of this past year, but I am going to deload 10% at\nthe start of 2020 and work up towards the big 500. I should hit it towards the\nmiddle of the year. At that point, I may want to recalibrate and move towards\nsomething else.<\/p>\n\n<h5 id=\"survive-the-primary-season\">Survive the primary season<\/h5>\n\n<p>I\u2019m working on covering the election for the Times this year. They say its a\nmarathon, not a sprint!<\/p>\n\n<h5 id=\"play-the-mozart-piano-sonata-no-16-in-c-major-1st-mvmt\">Play the Mozart Piano Sonata No. 16 in C Major (1st mvmt)<\/h5>\n\n<p>This is a <a href=\"https:\/\/www.youtube.com\/watch?v=qjk-YRuQZDE\">very well-known piece<\/a>\nfor early intermediate students, and I think I should be able to manage to get\nthrough it this year.<\/p>\n\n<h5 id=\"keep-reviewing-books\">Keep reviewing books<\/h5>\n\n<p>I\u2019ve really enjoyed doing that, and it has been nice to watch the big list of\nbooks grow over time.<\/p>\n","pubDate":"Thu, 02 Jan 2020","link":"https:\/\/bensmithgall.com\/\/blog\/2019-and-2020","guid":"https:\/\/bensmithgall.com\/\/blog\/2019-and-2020"},{"title":"The Sprawl Trilogy","category":{},"description":"<p>I was inspired to re-read <em>Neuromancer<\/em> after reading this <a href=\"https:\/\/www.newyorker.com\/magazine\/2019\/12\/16\/how-william-gibson-keeps-his-science-fiction-real\">absolutely bonkers\nprofile<\/a>\nof Gibson in the <em>New Yorker<\/em> a few weeks ago. I was reading it while this\n<a href=\"https:\/\/www.nytimes.com\/interactive\/2019\/12\/19\/opinion\/location-tracking-cell-phone.html\">series about\nsurveillance<\/a>\nthat I helped work on came out, which made me think about the incongruities\nbetween the cyberpunk world imagined by Gibson here and by Neil Stephenson in\n<em>Snow Crash<\/em>.<\/p>\n\n<p>Gibson anticipates this complete collapse of privacy: the story in <em>Neuromancer<\/em>\nkicks off when Molly is easily able to find Case. They retreat into a back room\nin the \u201cSprawl\u201d to discuss Armitage in the room with the best privacy that she\ncan afford.<\/p>\n\n<p>There are ways, though, in which the worlds of <em>Neuromancer<\/em> and <em>Snow Crash<\/em>\nare very different from where we find ourselves now. Aside from the obvious\ndystopian elements, I think the biggest differences are in the ways that\ncyberspace manifests itself. For both Gibson and Stephenson, both writing long\nbefore the real rise of the World Wide Web, entering cyberspace is a physical\nand visceral experience. Case \u201cjacks into the matrix\u201d through a \u201ccranial jack.\u201d\nHiro Protagnoist\u2019s digital realm is avatar-centric, in the model that would\neventually be realized in games like Second Life. In both of these cases, the\nact of going online involves a physical separation between the operator and\ntheir physical \u201cmeat space.\u201d In <em>Neuromancer<\/em>, Case physically jacks in and\nattaches \u2018trodes\u2019 to himself and surfs around the ice, which is represented by\ncolorful blocks in a separate world.<\/p>\n\n<p>In the real world, though, this infrastructure is much more banal. Our phones\nare constantly pinging servers, cell towers, Bluetooth beacons, and WiFi\nrouters, but we never see or sense that. We don\u2019t jack into the Matrix or attach\nelectrodes to our faces to go online. With the possible exception of some fully\nimmersive VR, we maintain our normal senses. It\u2019s impossible for us to know that\nthings are being lost or taken from us when we go online. Even the downsides\nthat are outlined in these pieces about private surveillance are physically\nremoved from us: how can we understand ourselves as rows in a database table?\nAll of these systems are completely hidden from us; we can\u2019t see or understand\ntheir effects. In the world of <em>Neuromancer<\/em>, Case experiences brain-death a\nhandful of times: there are real risks associated with being a console cowboy:\nthe AI Wintermute fries his brain to the point of death.<\/p>\n\n<p>This is a major point raised by the piece: why is it legal for companies to\ncollect this data? Why are there no laws regulating them or protecting people?\nI think a large part of it is this gap between these worlds that were imagined\nfrom 20 years ago and how they ended up panning out. Though the internet truly\nhas penetrated into every corner of life (see, for example this <a href=\"https:\/\/www.nytimes.com\/2019\/11\/30\/business\/amazon-baltimore.html\">tremendously\nreported\npiece<\/a> about\nall the ways Amazon is a part of daily life in Baltimore), we don\u2019t really feel\nor understand it in the same way that we might if things were closer to the\nworlds of <em>Neuromancer<\/em> or <em>Snow Crash<\/em>.<\/p>\n\n<p>In any case <em>Neuromancer<\/em> is a fun book, and still applicable to our world\ndespite its differences. I\u2019m looking forward to reading Gibson\u2019s newest series\nafter reading that profile of him, and seeing how he has shaped the world around\nus still.<\/p>\n\n<hr \/>\n\n<p>After finishing <em>Neuromancer<\/em>, I took the time over the break to re-read the two\nother books in the so-called <em>Sprawl Trilogy<\/em>, <em>Count Zero<\/em> and <em>Mona Lisa\nOverdrive<\/em>. There are interesting bits cast throughout these books. One of the\nmost interesting concepts in <em>Count Zero<\/em> is the notion that extreme wealth is\nhas its own sovereignty and even confers its own citizenship to the small army\nof lawyers and managers who look after it. This is another point where Gibson\nseems to anticipate or even bring around reality: Josef Virek attempts to escape\ndeath; his consciousness is uploaded into a giant set of vats in Sweden, and he\nhas many corporeal forms in the \u201csimstim\u201d VR-like entertainment. This doesn\u2019t\nmatch fully but surely rhymes with the way that Google\u2019s executives are looking\nto <a href=\"https:\/\/www.theverge.com\/2013\/9\/19\/4748594\/understanding-calico-larry-page-google-ventures-and-the-quest-for\">\u201ccure\ndeath\u201d<\/a>\nas they age.<\/p>\n\n","pubDate":"Sat, 28 Dec 2019","link":"https:\/\/bensmithgall.com\/\/books\/neuromancer","guid":"https:\/\/bensmithgall.com\/\/books\/neuromancer"}]}}