{"id":499,"date":"2008-01-04T12:45:59","date_gmt":"2008-01-04T02:45:59","guid":{"rendered":"http:\/\/www.somethinkodd.com\/oddthinking\/2008\/01\/04\/message-passing-in-my-puzzle-solving-architecture\/"},"modified":"2008-01-04T12:47:20","modified_gmt":"2008-01-04T02:47:20","slug":"message-passing-in-my-puzzle-solving-architecture","status":"publish","type":"post","link":"https:\/\/www.somethinkodd.com\/oddthinking\/2008\/01\/04\/message-passing-in-my-puzzle-solving-architecture\/","title":{"rendered":"Message-Passing in my Puzzle-Solving Architecture"},"content":{"rendered":"<h3>Introduction to a Puzzle-Solving Architecture<\/h3>\n<p>In the past, I have blogged about a several projects to solve some puzzles with a common theme: <a href=\"http:\/\/www.somethinkodd.com\/oddthinking\/2005\/06\/11\/solving-nonograms\/\">Nonograms<\/a>, <a href=\"http:\/\/www.somethinkodd.com\/oddthinking\/2006\/02\/12\/a-kakuro-solver\/\">Kakuro<\/a>, <a href=\"http:\/\/www.somethinkodd.com\/oddthinking\/2005\/05\/21\/21\/\">Sudoku<\/a>, <a href=\"http:\/\/www.somethinkodd.com\/oddthinking\/2006\/09\/28\/solitaire-battleships\/\">Solitaire Battleships<\/a> and Slitherlinks [in press].<\/p>\n<p>My solutions shared a similar architecture &#8211; one that has been evolving over time.<\/p>\n<p>The architecture is based on a common Artificial Intelligence approach of having a large number of different objects representing part of the puzzle (e.g. a coloured line or a square in the grid). Each object is trying to independently resolve some internal constraints. As they make progress and discover some information, they share the current information with their neighbours. Those neighbours use that to improve their situation, recursively, until, hopefully, a solution is found. The whole system is decentralised, with no master object driving the algorithm.<\/p>\n<p>The area of the architecture that has changed the most is the message-passing mechanism.<\/p>\n<h3>Scope<\/h3>\n<p>This article focuses on that message-passing system and the variants I have tried. <\/p>\n<p>The question I am trying to answer is: Each object knows about its direct neighbours. When it comes time to tell those neighbours some new information, how should it go about that?<\/p>\n<div class=\"aside\">I am sorry if this isn&#8217;t of general interest, but writing about it will probably clear it up in my head, and help me in future projects.<\/div>\n<h3>Recursion<\/h3>\n<p>The obvious solution is for the object simply to call each of the neighbours in turn, and have them check their constraints immediately, while the original object waits. If the constraints lead to new information, the neighbours recursively call their neighbours, until there is no new information to be shared, the stack unwinds and the original object completes.<\/p>\n<h4>Advantages<\/h4>\n<p>This system is conceptually fairly straightforward, and it is easy to get the basic implementation working.<\/p>\n<p>It is helpful in debugging, as the stack trace shows a complete history of how the information was passed around. (If you are debugging why a piece of wrong information was passed to an object, you can see who the culprit was.)<\/p>\n<h4>Disadvantages<\/h4>\n<p>It is inefficient. For example, if the cell_1 object passes a message to line_1 and cell_2 objects, and then the line_1 object updates some data and passes another message to cell_2, then cell_2 is going to get two calls to its constraint checking. It will check its constraint twice rather than once.<\/p>\n<p>Care must be taken to ensure that functions support re-entrance. An object&#8217;s methods may be called when it is half-way through a function. Its invariants must all be met before passing the message to its neighbours.<\/p>\n<p>Animation hooks are a bit trickier. Many of these projects have an associated game board that is displayed to show progress. Rendering of these graphics are CPU-intensive. Typically, I would refresh the screen after every contraint check during early debugging but only after every 50-or-so constraint checks during the final runs. Having a recursive solution means putting calls to the animation system throughout the code for the object. It couples the algorithm to the rendering.<\/p>\n<p>If the puzzle is solved before all the constraints are checked, it is difficult to finish early. Similar to the animation hooks, checks for puzzle-completeness are required throughout the code.<\/p>\n<h3>Command Queue<\/h3>\n<p>Another implementation of message passing is to use a &#8220;command queue&#8221;.<\/p>\n<p>Rather than using the run-time stack to keep track of the neighbours, a separate queue is created.<\/p>\n<p>Rather than calling the neighbours directly, a request is pushed on the command queue for each of the neighbours. Each constraint check is now a short (probably constant time) operation that is guaranteed to return quickly.<\/p>\n<p>The calling software needs to pop the first command off the queue, execute it (which calls the appropriate object) and repeat until the queue is empty or the puzzle is solved.<\/p>\n<div class=\"aside\">A minor improvement to reduce coupling is to move the command queue push operation into the neighbour&#8217;s code. So if a Line object needs to pass a message to a Cell object, it simply calls a method on the Cell object, which pushes the correct command type onto the queue, and returns immediately. This decoupling allows you to switch between the different message-passing techniques, without your client objects necessarily needing to know.<\/div>\n<h4>Advantages<\/h4>\n<p>Animation and puzzle-completeness hooks can be moved into the calling code. They can perform the updates\/checks after executing every command or every <code>n<\/code> commands, as desired.<\/p>\n<p>Coding can afford to be a bit sloppier, if you know you are using this technique. You can afford to trigger the call to the neighbour object even before you finish updating your local data to maintain the invariants. Code is not re-entrant.<\/p>\n<p>While I haven&#8217;t tried it, I think this technique supports multi-threading better. The items in the queue can be distributed amongst different threads &#8211; perhaps based on some partition of the affected objects to minimise the amount of locking required. I&#8217;ve been meaning to try this.<\/p>\n<p>Again, I haven&#8217;t tried this, but if you have computer-assisted solving of the puzzles and a slow algorithm (e.g. brute force), it allows an opportunity for the user to make an update before the entire algorithm has completed. So the human can play in real-time, with the computer just slowly adding information as it discovers it.<\/p>\n<h4>Disadvantages<\/h4>\n<p>The effect of an update is divorced from its cause. During debugging, I have found incorrect information lined up in the command queue, and it has been difficult to back trace it to the code that incorrectly created it.<\/p>\n<p>The system remains inefficient. The example above of cell_2 updating twice continues to apply.<\/p>\n<p>The pureness of the bottom-up, decentralised nature of the implementation is tainted. There is now a centralised master object (i.e. the command queue) that every object needs to know about &#8211; either it is a global or it needs to passed around to almost every object in the system.<\/p>\n<h3>Prioritised Command Queue<\/h3>\n<p>Some messages are more useful than others.<\/p>\n<p>Consider two messages to a Sudoku cell. One says &#8220;Your value is not an 8. Please eliminate that from your list of possibilities.&#8221; The other says &#8220;Your value is a 3.&#8221; The latter message has more information content.<\/p>\n<p>By giving each type of message a relative priority, and keeping the command queue sorted by that priority, you can ensure the more useful messages are processed first.<\/p>\n<p>The less useful messages will either be abandoned (if the puzzle is solved before they are processed), ignored (once an object has resolved all of its own constraints using the more useful messages, it can discard further information quickly) or eventually processed (if the object is still looking for more information.)<\/p>\n<p>I also used this technique where some objects had multi-stage initialisation (e.g. before and after their neighbours were associated) but I eventually moved this responsibility to the object that created them, because it was simpler.<\/p>\n<p>Prioritised Command Queues are an optimisation that is only application in some situations. There is a small cost in keeping the queue sorted.<\/p>\n<h3>Command Set<\/h3>\n<p>Looking at the command queue data reveals that there are generally many duplicate commands, like the cell_1 example, above.<\/p>\n<p>Converting the command queue into a set can greatly reduce the number of redundant calls. Duplicates are simply discarded as they are added to the queue.<\/p>\n<p>Note: Compare these two different styles of message-passing:<\/p>\n<ol>\n<li>&#8220;Some data has changed. Please fetch the latest data from your neighbours and update your constraints.&#8221;<\/li>\n<li>&#8220;This is line_1. I can no longer be blue. Please use this information to update your constraints.&#8221;<\/li>\n<\/ol>\n<p>If fetching data from neighbours is expensive, the latter might be more efficient. However, if the evaluation of the data is the expensive part, the the former style, plus the command set design, can save a large number of constraint checks.<\/p>\n<p>A Prioritised Command Set is also possible, combining the duplication-removal features of the set with the best-data first features of the prioritised queue.<\/p>\n<h3>Iteration<\/h3>\n<p>During the Kakuro project, I made a gross simplification.<\/p>\n<p>I dropped the message-passing between neighbours entirely. Instead, I had the calling program simply iterate repeatedly over every cell object, and tell it to check its constraints, whether its neighbours had changed or not.<\/p>\n<p>This contained an inherent inefficiency: cells were called on to update even when their environment hadn&#8217;t changed in the meantime.<\/p>\n<p>However, the code was much, much simpler.<\/p>\n<p>If the neighbourhood connectivity is high compared to the number of objects, if it takes a large number of messages to an object before it can solve itself, and if there is only one message type, this is a worthwhile consideration.<\/p>\n<h3>Conclusion<\/h3>\n<p>I have experimented with a number of different designs. The Prioritised Command Set is the default that I reach for now. Now I can use this article to justify that decision to myself!<\/p>\n<p>If I was ever hired to take these disposable prototypes and replace them with massively over-engineered production code, I could certainly see the opportunity for a puzzle-solving framework (including a Prioritised Command Set) to do most of the infrastructure work.<\/p>\n<p>By the way, if you like this sort of stuff, you&#8217;d probably like Douglas Hofstadter&#8217;s <a href=\"http:\/\/en.wikipedia.org\/wiki\/Fluid_Concepts_and_Creative_Analogies\" title=\"Wikipedia definition of Fluid_Concepts_and_Creative_Analogies\" class=\"wikipedia\">Fluid Concepts and Creative Analogies<\/a> book. It&#8217;s a lot more &#8220;cognitive&#8221; and serious than my attempts, but it&#8217;s interesting seeing a lot of different puzzle implementations by different people that also have a common theme.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article focuses on that message-passing system and the variants I have tried in the puzzle-solving architecture that has evolved over a number of pet projects.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_s2mail":"","footnotes":""},"categories":[28,33,34],"tags":[217,55,69],"class_list":["post-499","post","type-post","status-publish","format-standard","hentry","category-doubleplus-geek","category-puzzle-solving","category-software-development","tag-architecture","tag-puzzle","tag-software"],"_links":{"self":[{"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/posts\/499","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/comments?post=499"}],"version-history":[{"count":0,"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/posts\/499\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/media?parent=499"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/categories?post=499"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.somethinkodd.com\/oddthinking\/wp-json\/wp\/v2\/tags?post=499"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}