{"id":35,"date":"2024-06-21T02:45:03","date_gmt":"2024-06-21T02:45:03","guid":{"rendered":"https:\/\/think-twice.me\/?p=35"},"modified":"2024-06-21T02:45:03","modified_gmt":"2024-06-21T02:45:03","slug":"solving-rush-hour","status":"publish","type":"post","link":"https:\/\/think-twice.me\/?p=35","title":{"rendered":"Solving rush-hour"},"content":{"rendered":"\n<p>For those who\u2019ve been inspired by last <a href=\"https:\/\/web.archive.org\/web\/20230131024126\/http:\/\/alexrohde.com\/zennish\/index.html\">programming challenge<\/a>, I thought I\u2019d give annotations on a programming exercise I\u2019ve created.<\/p>\n\n\n\n<p>The challenge: code an AI to solve Rush Hour, you can\u00a0<a href=\"https:\/\/web.archive.org\/web\/20230131024126\/http:\/\/www.thinkfun.com\/play-online\/rush-hour\/\">play Rush Hour\u00a0online<\/a>\u00a0if you\u00a0aren\u2019t familiar with how the game works. If you want to follow along, you can find\u00a0<a href=\"https:\/\/github.com\/its-your-favorite\/RushHourSolver\">my solution<\/a> on github.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1003\" height=\"1024\" src=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-3-1003x1024.png\" alt=\"\" class=\"wp-image-36\" srcset=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-3-1003x1024.png 1003w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-3-294x300.png 294w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-3-768x784.png 768w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-3.png 1050w\" sizes=\"auto, (max-width: 1003px) 100vw, 1003px\" \/><\/figure>\n\n\n\n<p>Let me start with the high-level thought process.<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">How to solve it?<\/h6>\n\n\n\n<p>The most naive approach is a brute-force solution, trying every move, filtering out backtracking.&nbsp;Some simple math will let us rule this possibility in or out.<\/p>\n\n\n\n<p>The only state in Rush Hour&nbsp;the positions of the cars. Thus the number of possible game states is equal to the number of possible arrangements of cars. We can approximate an upper bound of the latter by multiplying the number of possible spaces each car could move to (this ignores cars being on top of each other, hence upper-bound). In the above board we see 8 vehicles, and each vehicle can&nbsp;only ever occupy 4 or 5 squares [they can\u2019t turn ever] depending if their size). Some boards have more vehicles, so if we estimate 10&nbsp;vehicles at 5 positions that\u2019s 5^10 &lt; 10 million. &nbsp;<em><strong>So yes, brute force is in.<\/strong><\/em><\/p>\n\n\n\n<h6 class=\"wp-block-heading\">The general solution:<\/h6>\n\n\n\n<p>Because \u201cbrute force\u201d is such a general mechanism, there really is a lot of reusable code in the solution. In my&nbsp;solution I\u2019ve pulled out the reusable logic&nbsp;into<em> genericSearch.js<\/em>. The idea is that a whole host of puzzles follow the same form: Given an Initial state try to get to End State.<\/p>\n\n\n\n<p>The docblock below is the interface to genericSearch that is used by the solver, but this same search function could be used by any number of puzzles (triangle peg game, sudoku, etc).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"201\" src=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-4-1024x201.png\" alt=\"\" class=\"wp-image-37\" srcset=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-4-1024x201.png 1024w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-4-300x59.png 300w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-4-768x151.png 768w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-4-1536x301.png 1536w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-4-2048x402.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Thus, to write the program (after having written genericSearch.js) all I must do is to make&nbsp;those 5 parameters [and any helpers to go along].<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">Initial state \u2013 Brain Dead Simple<\/h6>\n\n\n\n<p>I know a lot of engineers who would be tempted to represent the game of rush hour with at least a half dozen classes (\u201cso we have vehicle base class, which has car or truck subclasses, and we\u2019d need a class for the board and legal moves\u201d).<\/p>\n\n\n\n<p>And that is a valid way to go about it. I went with a minimalist solution\u2013 a\u00a0nested array 36 characters. Brain dead simple.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"988\" height=\"358\" src=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-5.png\" alt=\"\" class=\"wp-image-38\" srcset=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-5.png 988w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-5-300x109.png 300w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-5-768x278.png 768w\" sizes=\"auto, (max-width: 988px) 100vw, 988px\" \/><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>It seems that perfection is attained, not when there is nothing more to add, but when there is nothing more to take away.<\/p>\n<cite>Antoine de Saint Exupery<\/cite><\/blockquote>\n\n\n\n<p>Because every car is at least 2 in size, this string notation suffices to convey both the position and bearing&nbsp;of vehicles, everything we need. Since the exit is always in the same spot, that needn\u2019t be represented in state. Adjacent characters of the same value represent the same car.<\/p>\n\n\n\n<p>We have our first parameter, initial state.&nbsp;However, we have skimped in our state, by not defining what a vehicle is or orientation in the state object we\u2019ll have to define that logic later.<\/p>\n\n\n\n<p>Some of the advantages of representing the board state as an array of characters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It\u2019s trivial to serialize. Serialization is key to making sure we don\u2019t backtrack when searching for solutions.<\/li>\n\n\n\n<li>It\u2019s very easy to input.<\/li>\n\n\n\n<li>It\u2019s very easy to output and debug. At the end to see the solution found I can simply \u201cplay\u201d the states and watch the characters move.<\/li>\n\n\n\n<li>At any given moment it\u2019s trivial to determine if a space is vacant of any vehicles.<\/li>\n\n\n\n<li>Certain impossible board-states are prevented automatically (e.g. a misconfigured initial state could not result in overlapping vehicles).<\/li>\n<\/ul>\n\n\n\n<p>Some of the disadvantages:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We must determine every car on the board and which direction it\u2019s facing. This is\u00a0non-trivial (let\u2019s call \u201ctrivial\u201d something that I code correctly on my first try).<\/li>\n\n\n\n<li>Moving a vehicle is kind of \u201cdirty.\u201d By\u00a0moving vehicles by overwriting the board state it\u2019d be easy make bugs that create weird board states (vehicle with size one).<\/li>\n\n\n\n<li>Certain impossible board-states are prevented automatically (e.g. a misconfigured board state could not result in a vehicle with no bearing)<\/li>\n<\/ul>\n\n\n\n<h6 class=\"wp-block-heading\">Parameter 2 \u2013 Possible moves<\/h6>\n\n\n\n<p>Each vehicle may move forward, or backward (assuming its path is unobstructed). To keep things simple, we can say a vehicle can only move 1 square at a time and represent longer travels as multiple moves.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"360\" src=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-6-1024x360.png\" alt=\"\" class=\"wp-image-39\" srcset=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-6-1024x360.png 1024w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-6-300x106.png 300w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-6-768x270.png 768w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-6.png 1364w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This definition introduces a few more functions (getVehicles, canGoForward, canGoBackward), which I won\u2019t put into the post. See the <a href=\"https:\/\/github.com\/its-your-favorite\/RushHourSolver\">full code<\/a> for that. The reason I exclude them is because I don\u2019t have any particularly elegant solution to those tasks.<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">Parameter 3 \u2013 Apply move<\/h6>\n\n\n\n<p>Again I have no magic to work on this one so I won\u2019t show&nbsp;the 15 lines.&nbsp;In fact, it feels like the least dry piece of the code to me, and it makes me want to refactor the most. The number of occurrences of -1 are too numerous (6) and speak to casual coding.<\/p>\n\n\n\n<p>So don\u2019t get me wrong, I don\u2019t want to say anybody should hold this code up as an amazing example of good code. What I hoped to illustrate is how a problem that seems daunting can actually be broken down into 19 functions, the longest being 23 lines.<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">The Gimmes<\/h6>\n\n\n\n<p>I have two functions left. One is how to uniquely represent the state as a string. The other is win condition. The former is easy: JSON.stringify. The latter is easy as well.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"164\" src=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-7-1024x164.png\" alt=\"\" class=\"wp-image-40\" srcset=\"https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-7-1024x164.png 1024w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-7-300x48.png 300w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-7-768x123.png 768w, https:\/\/think-twice.me\/wp-content\/uploads\/2024\/06\/image-7.png 1134w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>And we\u2019re done! Now I\u2019ve left our the genericSearch.js, which honestly was perhaps the most fun, but this post is long enough. If your thirst isn\u2019t quenched try <a href=\"https:\/\/web.archive.org\/web\/20230131024126\/https:\/\/github.com\/anfurny\/RushHourSolver\">playing around with it yourself<\/a>.<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">Takeaways<\/h6>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Keep the pure &amp;\u00a0reusable code in a separate file (or separate function) from your 1-time, implementation-dependent code<\/li>\n\n\n\n<li>Maybe 2 classes would have come in handy. Despite being small, the code doesn\u2019t seem as friendly as I\u2019d want. If I had kept state as an object of objects I would never have to define string -> state mapping.<\/li>\n\n\n\n<li>Unit tests\u00a0would have been a good way to\u00a0allow such a refactoring to be less painful.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>For those who\u2019ve been inspired by last programming challenge, I thought I\u2019d give annotations on a programming exercise I\u2019ve created. The challenge: code an AI to solve Rush Hour, you can\u00a0play Rush Hour\u00a0online\u00a0if you\u00a0aren\u2019t familiar with how the game works. If you want to follow along, you can find\u00a0my solution on github. Let me start&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-35","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/think-twice.me\/index.php?rest_route=\/wp\/v2\/posts\/35","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/think-twice.me\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/think-twice.me\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/think-twice.me\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/think-twice.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=35"}],"version-history":[{"count":1,"href":"https:\/\/think-twice.me\/index.php?rest_route=\/wp\/v2\/posts\/35\/revisions"}],"predecessor-version":[{"id":41,"href":"https:\/\/think-twice.me\/index.php?rest_route=\/wp\/v2\/posts\/35\/revisions\/41"}],"wp:attachment":[{"href":"https:\/\/think-twice.me\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=35"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/think-twice.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=35"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/think-twice.me\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=35"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}