<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Master Baboon &#187; games</title>
	<atom:link href="http://www.masterbaboon.com/tag/games/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.masterbaboon.com</link>
	<description>The sea of the simulation</description>
	<lastBuildDate>Mon, 14 Nov 2011 08:27:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2</generator>
		<item>
		<title>The road to javascript</title>
		<link>http://www.masterbaboon.com/2011/06/the-road-to-javascript/</link>
		<comments>http://www.masterbaboon.com/2011/06/the-road-to-javascript/#comments</comments>
		<pubDate>Wed, 29 Jun 2011 13:06:53 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[games]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=547</guid>
		<description><![CDATA[I wanted to learn javascript and wrote a small game with an old school look using textareas, that I&#8217;m glad to share: Writing javascript is quite intuitive for a Python developer, even though I&#8217;m sure that there are many idioms and tricks that I did not know about (and the code I wrote is the [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted to learn javascript and wrote a <a href="http://www.masterbaboon.com/brum/" target="_blank">small game</a> with an old school look using textareas, that I&#8217;m glad to share:</p>
<div id="attachment_548" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.masterbaboon.com/brum/"><img class="size-medium wp-image-548" title="brum_screenshot" src="http://www.masterbaboon.com/wp-content/uploads/2011/06/brum_screenshot-300x300.jpg" alt="A screenshot of the game" width="300" height="300" /></a><p class="wp-caption-text">A screenshot of the game</p></div>
<p>Writing javascript is quite intuitive for a Python developer, even though I&#8217;m sure that there are many idioms and tricks that I did not know about (and the code I wrote is the stream-of-thought kind).  I&#8217;m somewhat disappointed as I thought javascript would be more standard across browsers, but there remain important differences, for example in the way Firefox and IE handle ranges in textarea.</p>
<p>I haven&#8217;t tried the game on Safari or Opera, please let me know if it works!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2011/06/the-road-to-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Games with extensible AI</title>
		<link>http://www.masterbaboon.com/2011/06/games-with-extensible-ai/</link>
		<comments>http://www.masterbaboon.com/2011/06/games-with-extensible-ai/#comments</comments>
		<pubDate>Sun, 05 Jun 2011 00:01:06 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[games]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=501</guid>
		<description><![CDATA[Some computer games offer the possibility to extend their Artificial Intelligence with external scripts, or are explicitly designed to be played by bots. Such games are a great resource to develop, test, and teach AI algorithms. I have been looking for a list of this kind of games, but could find very little information, often [...]]]></description>
			<content:encoded><![CDATA[<p>Some computer games offer the possibility to extend their Artificial Intelligence with external scripts, or are explicitly designed to be played by bots. Such games are a great resource to develop, test, and teach AI algorithms.</p>
<p>I have been looking for a list of this kind of games, but could find very little information, often outdated. This is my own, annotated list; let me know if I missed something, and I&#8217;ll make amend. I included only decently active projects:</p>
<p><strong>Games for bots:</strong></p>
<ol>
<li><strong><a href="http://robocode.sourceforge.net/" target="_blank">Robocode</a> (Java, .NET)</strong>: AI bots control tanks equipped with cannon and radar on an square arena. A game with a long tradition, descending from the classic AI games <a href="http://en.wikipedia.org/wiki/RobotWar" target="_blank">RobotWar </a>(1981) and <a href="http://en.wikipedia.org/wiki/Crobots" target="_blank">crobots </a>(1985). This is a very active project, with tournaments organized regularly all over the world. The strategical possibilities are quite limited, though.</li>
<li><strong><a href="http://csclub.uwaterloo.ca/contest/">Tron</a> challenge (any language)</strong>: This is the first AI competition organized by the <a href="http://csclub.uwaterloo.ca/" target="_blank">University of Waterloo Computer Science Club</a> and sponsored by Google. The AIs control the &#8220;snakes&#8221; in a variant of the popular game <a href="http://en.wikipedia.org/wiki/Snake_%28video_game%29">Snake</a> (or <a href="http://en.wikipedia.org/wiki/Nibbles_%28video_game%29">Nibbles</a>, of QBasic fame) on a number of boards with symmetric walls. Although the competition is closed, the code is open source and perfectly usable. A nice game, but there seems to be one dominating strategy: using minimax. Entries in the competition differ in their cleverness in coming up with various approximations to prune down the huge tree of possible  moves, and in handling the endgame.</li>
<li><strong><a href="http://ai-contest.com/" target="_blank">Planet Wars</a> challenge (any language)</strong>: Second AI competition, again organized by the <a href="http://csclub.uwaterloo.ca/" target="_blank">University of Waterloo CS Club</a>. The inspiration is the galactic conquer game <a href="http://www.galcon.com/games/?action=game&amp;name=igalcon" target="_blank">Galcon</a>. Bots controls fleets of spaceships to control planets for resources. Unlike the first competition, the optimal strategy is far from obvious, and the game offers a lot of space for experimenting with different approaches. Not as visually fun as other games, though.</li>
<li><strong><a href="http://inst.eecs.berkeley.edu/~cs188/pacman/projects/contest/contest.html">PacMan capture the flag</a> (Python)</strong>: The CS department at Berkeley offers a very popular <a href="http://inst.eecs.berkeley.edu/~cs188/sp11/information.html" target="_blank">Artificial Intelligence course</a> that requires students to <a href="http://inst.eecs.berkeley.edu/~cs188/pacman/pacman.html" target="_blank">code up classical AI algorithms</a> to control agents in a PacMan-like environment. The course concludes with a <a href="http://inst.eecs.berkeley.edu/~cs188/pacman/projects/contest/contest.html" target="_blank">tournament </a>in which PacMan agents compete to eat each other&#8217;s dots. The game is extremely fun as I wrote about <a href="http://www.masterbaboon.com/2009/09/pacman-capture-the-flag-a-fun-game-for-artificial-intelligence-development-and-education/" target="_self">in</a> <a href="http://www.masterbaboon.com/2010/09/tracking-down-the-enemy/" target="_self">previous</a> <a href="http://www.masterbaboon.com/2011/01/tracking-down-the-enemy-2/">posts</a>. The code is <a href="http://inst.eecs.berkeley.edu/~cs188/pacman/projects/contest/contest.html" target="_blank">available online</a> (although somewhat messy). Unfortunately (but understandably, since the game is used in a course), the authors don&#8217;t want the code of developed agents to leak online.</li>
<li><strong><a href="http://gridsoccer.codeplex.com/" target="_blank">Grid-Soccer</a> (Mono, .NET):</strong> This is a multi-agent soccer-like game played on a grid board. I haven&#8217;t tried this one yet&#8230; According to the authors, it was originally conceived as a testbed for multi-agent reinforcement learning algorithms.</li>
</ol>
<p><strong>Scriptable games</strong> (games that expose an interface for external clients):</p>
<ol>
<li><strong><a href="http://en.wikipedia.org/wiki/StarCraft:_Brood_War" target="_blank">Starcraft Brood War</a> (any language): </strong>This is your one chance at writing AI bots for a commercial game! <img src='http://www.masterbaboon.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  Starcraft <a href="http://code.google.com/p/bwapi/" target="_blank">has been hacked</a> to allow to control any aspect of the game through a user-written client (building construction, units movement, the full monty). Since 2010 there have been a few <a href="http://code.google.com/p/bwapi/wiki/Competitions#AIIDE_2011" target="_blank">AI competitions</a> open to the public (two of them still accepting entries!). Here&#8217;s a video from last year&#8217;s <a href="http://eis.ucsc.edu/StarCraftAICompetition" target="_blank">AIIDE tournament</a>:
<p><iframe width="544" height="327" src="http://www.youtube.com/embed/2rqpz2GU9GU" frameborder="0" allowfullscreen></iframe></li>
<li><strong><a href="http://xblast.sourceforge.net/" target="_blank">XBlast (potentially any):</a> </strong>A free version of <a href="http://en.wikipedia.org/wiki/Bomberman" target="_blank">Bomberman</a>. The game has not been used to develop AI agents&#8230; yet. It is built with a server-client architecture and it&#8217;s begging to get bots running on it.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2011/06/games-with-extensible-ai/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tracking down the enemy (2)</title>
		<link>http://www.masterbaboon.com/2011/01/tracking-down-the-enemy-2/</link>
		<comments>http://www.masterbaboon.com/2011/01/tracking-down-the-enemy-2/#comments</comments>
		<pubDate>Fri, 21 Jan 2011 23:39:42 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[PacMan capture-the-flag]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[PacMan]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=456</guid>
		<description><![CDATA[I never got the chance to show a working agent based on the Bayesian estimator for the enemy position in the PacMan capture-the-flag game. In the previous PacMan post, I wrote about merging a model of agent movements with the noisy measurements returned by the game to track the enemy agents across the maze. Clearly, [...]]]></description>
			<content:encoded><![CDATA[<p>I never got the chance to show a working agent based on the Bayesian estimator for the enemy position in the PacMan capture-the-flag game. In the <a href="http://www.masterbaboon.com/2010/09/tracking-down-the-enemy/" target="_self">previous PacMan post</a>, I wrote about merging a model of agent movements with the noisy measurements returned by the game to track the enemy agents across the maze. Clearly, this information can give you an edge when planning an attack (to avoid ghosts) or when defending (to intercept the PacMen).</p>
<p>For  the traditional faculty-vs-students tournament at the <a href="https://portal.g-node.org/python-autumnschool/" target="_blank">G-Node scientific programming summer school</a> this year, I wrote a PacMan team made by a simple attacker, and a more sophisticated defender that tries to intercept and devour enemy agents.</p>
<p>Both agents plan their movements using a shortest-path algorithm on a weighted graph: before the start of the game, the maze is transformed in a graph, where nodes are the maze tiles, and edges connect adjacent tiles. Weights along the edges are adjusted according to the estimated position of the agents:</p>
<ul>
<li>Weights on edges close to an enemy ghost are increased (starting value is proportional to the probability of the enemy being there, and falls off exponentially with distance)</li>
<li>Weights on edges close to an enemy PacMan are decreased</li>
<li>Weights on edges close to a friendly agent are increased</li>
</ul>
<p>An agent navigating on such a maze will tend to avoid ghosts, chase PacMen, and cover parts of the maze far from other friendly agents. My attacker does little else than updating the weights of the graph at every turn, and move toward the closest food dot.</p>
<p>On the other hand, defending is quite difficult in this game, so I needed a more sophisticated strategy. While the enemy is still a ghost in its own part of the maze, the defender moves toward the closest enemy agent (its estimated position, that is). When the enemy is a PacMan in the friendly half, the chase is on! Since ghosts and PacMen move at the same speed, it would be pointless to just follow it around, one needs to catch them from the front&#8230; Once more, the solution was to modify the weights of the maze graph, making weights behind the enemy (i.e., opposite to its direction of motion) very high, and lowering the edges in front of it.</p>
<p>The combination of estimator and the weighted graph strategy can be quite entertaining:</p>
<p><iframe title="YouTube video player" type="text/html" width="544" height="327" src="http://www.youtube.com/embed/YM-rQJPL16o" frameborder="0" allowFullScreen></iframe></p>
<p>Sometimes the defender only needs to guard the border to scare the opponent shitless:</p>
<p><iframe title="YouTube video player" class="youtube-player" type="text/html" width="544" height="327" src="http://www.youtube.com/embed/1ciCtwW14RY" frameborder="0" allowFullScreen></iframe></p>
<p>Another useful thing to keep in mind for the future: it is better to base strategies on soft constraints (weighted graphs, probabilities). Setting hard, deterministic rules tends to get you stuck in loops. Soft constraints and some randomness give you more flexibility when you need it but are otherwise just as good.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2011/01/tracking-down-the-enemy-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solving the game Set®</title>
		<link>http://www.masterbaboon.com/2010/09/solving-the-game-set/</link>
		<comments>http://www.masterbaboon.com/2010/09/solving-the-game-set/#comments</comments>
		<pubDate>Wed, 15 Sep 2010 14:58:05 +0000</pubDate>
		<dc:creator>pietro</dc:creator>
				<category><![CDATA[games]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=292</guid>
		<description><![CDATA[Recently a friend of mine introduced me to a game called Set. Set is a logic cards game for several players; 3-5 players is probably best, but in there is no limit in principle, and it is also fun to play as a solitary. The game contains a deck of cards with symbols varying across [...]]]></description>
			<content:encoded><![CDATA[<p>Recently a friend of mine introduced me to a game called <em><a href="http://www.setgame.com/set/index.html">Set</a></em>. <em>Set </em>is a logic cards game for several players; 3-5 players is probably best, but in there is no limit in principle, and it is also fun to play as a solitary.</p>
<p>The game contains a deck of cards with symbols varying across 4 dimensions: color, number, shape, and texture. For each dimension, there are 3 possible features (e.g., there are 3 possible textures: full, empty, striped). A valid <em>set</em> is formed by three cards that have on each dimension either the <em>same</em> feature, or <em>three different</em> features. So for example in the image below, the first three cards are a valid set, as they are different on all features across all dimensions; the second three cards also form a valid set, because they share the same features for color and number, and are different in shape and texture; the cards on the bottom are not a set, because two cards have the &#8220;full&#8221; texture, while one is striped.</p>
<p style="text-align: center;">
<div id="attachment_445" class="wp-caption aligncenter" style="width: 410px"><img class="size-large wp-image-445 " title="sets_examples" src="http://www.masterbaboon.com/wp-content/uploads/2010/09/sets_examples1-1024x586.png" alt="Example of valid (top) and invalid (bottom) sets." width="400" height="229" /><p class="wp-caption-text">Example of valid (top) and invalid (bottom) sets.</p></div>
<p>In a Set game, 12 cards are put on the table, and the players have to find a valid set. The quickest among them collects the set and replaces the removed cards with new ones. If the players agree that there is no set on the table, 3 more cards are added. Once the deck is empty, the player who collected the most sets wins! Simple but fun&#8230;</p>
<p>I decided to write a solver for the game that finds all possible sets given a set of cards, with the goal of collecting statistics over thousands of games. The solver should thus be as efficient as I can manage to write it.</p>
<p>I&#8217;m going to represent each cards as a four-dimensional vector (color, shape, texture, number), each element containing either 0, 1, or 2 (representing the 3 possible values for each dimension). A function that checks if three cards form a valid set would thus look like this:</p>
<pre style="font: normal normal normal 12px/18px Consolas, Monaco, 'Courier New', Courier, monospace;">
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">import</span> numpy<br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">itertools</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> same<span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot;Returns True if all elements are the same.&quot;&quot;&quot;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> numpy.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span>x <span style="color: #66cc66;">==</span> x<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> different<span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot;Returns True if all elements are different.&quot;&quot;&quot;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>numpy.<span style="color: black;">unique</span><span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">==</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> is_set<span style="color: black;">&#40;</span>cards<span style="color: #66cc66;">,</span> indices<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot;Checks that the cards indexed by 'indices' form a valid set.&quot;&quot;&quot;</span><br />
&nbsp; &nbsp; ndims <span style="color: #66cc66;">=</span> cards.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; subset <span style="color: #66cc66;">=</span> cards<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> indices<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> dim <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>ndims<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># cards must be all the same or all different for all dimensions</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> same<span style="color: black;">&#40;</span>subset<span style="color: black;">&#91;</span>dim<span style="color: #66cc66;">,</span> :<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #ff7700;font-weight:bold;">not</span> different<span style="color: black;">&#40;</span>subset<span style="color: black;">&#91;</span>dim<span style="color: #66cc66;">,</span> :<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span></div></td></tr></tbody></table></div>
</pre>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># solution checker</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># &#8212;&#8212;&#8212;&#8212;&#8212;-</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def same(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are the same.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return numpy.all(x == x[0])</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def different(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are different.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return len(numpy.unique(x)) == len(x)</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def is_set(cards, indices):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Checks that the cards indexed by &#8216;indices&#8217; form a valid set.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">ndims = cards.shape[0]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">subset = cards[:, indices]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">for dim in range(ndims):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># cards must be all the same or all different for all dimensions</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">if not same(subset[dim, :]) and not different(subset[dim, :]):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return False</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return True</div>
<div>A brute force solver could then try all possible combinations of three cards and check whether they form a valid set:</div>
<pre style="font: normal normal normal 12px/18px Consolas, Monaco, 'Courier New', Courier, monospace;">
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> find_sets<span style="color: black;">&#40;</span>cards<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot;Brute-force Sets solver.&quot;&quot;&quot;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#91;</span>indices<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> indices <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">itertools</span>.<span style="color: black;">combinations</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span>cards.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">3</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> is_set<span style="color: black;">&#40;</span>cards<span style="color: #66cc66;">,</span> indices<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span></div></td></tr></tbody></table></div>
</pre>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># solution checker</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># &#8212;&#8212;&#8212;&#8212;&#8212;-</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def same(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are the same.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return numpy.all(x == x[0])</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def different(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are different.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return len(numpy.unique(x)) == len(x)</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def is_set(cards, indices):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Checks that the cards indexed by &#8216;indices&#8217; form a valid set.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">ndims = cards.shape[0]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">subset = cards[:, indices]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">for dim in range(ndims):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># cards must be all the same or all different for all dimensions</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">if not same(subset[dim, :]) and not different(subset[dim, :]):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return False</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return True</div>
<p>The brute force approach is very inefficient, but it is also very useful to test more efficient solutions (just check that they give the same response as the brute force one).</p>
<p>The second solver improves on the first one with a simple observation. Once you have two cards, there is only one possible card that completes the set: for each dimension, if the two cards have the same feature, the missing one will also have to have the same feature; if the features are different, the missing card will have to have the missing feature.</p>
<p style="text-align: center;">
<div id="attachment_446" class="wp-caption aligncenter" style="width: 410px"><img class="size-full wp-image-446  " title="complete_set" src="http://www.masterbaboon.com/wp-content/uploads/2010/09/complete_set1.png" alt="Given two cards, there is only one possible card that forms a valid set." width="400" height="234" /><p class="wp-caption-text">Given two cards, there is only one possible card that forms a valid set.</p></div>
<p>A possible strategy is thus to consider all possible pairs of cards, derive the one completing the set, and check if it is present on the table. This solution is much more efficient, as there are 220 possible triplets out of 12 cards, but only 66 pairs. It runs about 6 times faster:</p>
<pre style="font: normal normal normal 12px/18px Consolas, Monaco, 'Courier New', Courier, monospace;">
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> find_sets2<span style="color: black;">&#40;</span>cards<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; ndims<span style="color: #66cc66;">,</span> ncards <span style="color: #66cc66;">=</span> cards.<span style="color: black;">shape</span><br />
&nbsp; &nbsp; all_features <span style="color: #66cc66;">=</span> <span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># solutions contain the indices of the cards forming sets</span><br />
&nbsp; &nbsp; solutions <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># iterate over all pairs</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> idx1<span style="color: #66cc66;">,</span> idx2 <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">itertools</span>.<span style="color: black;">combinations</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span>ncards - <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; c1<span style="color: #66cc66;">,</span> c2 <span style="color: #66cc66;">=</span> cards<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> idx1<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> cards<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> idx2<span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># compute card that would complete the set</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; missing <span style="color: #66cc66;">=</span> numpy.<span style="color: black;">empty</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>ndims<span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> dtype<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'i'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> d <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>ndims<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> c1<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span> <span style="color: #66cc66;">==</span> c2<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># same feature on this dimension -&gt;; missing card also has same</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; missing<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> c1<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># different features -&gt; find third missing feature</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; missing<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">list</span><span style="color: black;">&#40;</span>all_features - <span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>c1<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> c2<span style="color: black;">&#91;</span>d<span style="color: black;">&#93;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># look for missing card in the cards array</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; where_idx <span style="color: #66cc66;">=</span> numpy.<span style="color: black;">flatnonzero</span><span style="color: black;">&#40;</span>numpy.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span>cards<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> idx2 + <span style="color: #ff4500;">1</span>:<span style="color: black;">&#93;</span>.<span style="color: black;">T</span> <span style="color: #66cc66;">==</span> missing<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; axis<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># append to solutions if found</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>where_idx<span style="color: black;">&#41;</span> <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; solutions.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>idx1<span style="color: #66cc66;">,</span> idx2<span style="color: #66cc66;">,</span> where_idx<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> + idx2 + <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> solutions</div></td></tr></tbody></table></div>
</pre>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># solution checker</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># &#8212;&#8212;&#8212;&#8212;&#8212;-</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def same(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are the same.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return numpy.all(x == x[0])</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def different(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are different.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return len(numpy.unique(x)) == len(x)</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def is_set(cards, indices):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Checks that the cards indexed by &#8216;indices&#8217; form a valid set.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">ndims = cards.shape[0]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">subset = cards[:, indices]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">for dim in range(ndims):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># cards must be all the same or all different for all dimensions</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">if not same(subset[dim, :]) and not different(subset[dim, :]):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return False</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return True</div>
<p>The trained eye will see at this point that the problem can be re-written as a dynamic programming one. We can start looking at the first dimension, and form groups of cards with the same feature. We know by the reasoning above that valid sets will either contain cards in the same group, or contain one card from each group. First we consider the former case, and recursively apply the same procedure to all the remaining dimensions for cards within each group. Second, we consider all triplets of cards that have one card per group and verify if it&#8217;s a set:</p>
<pre style="font: normal normal normal 12px/18px Consolas, Monaco, 'Courier New', Courier, monospace;">
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> find_sets3<span style="color: black;">&#40;</span>cards<span style="color: #66cc66;">,</span> indices<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; nd<span style="color: #66cc66;">,</span> n <span style="color: #66cc66;">=</span> cards.<span style="color: black;">shape</span><br />
&nbsp; &nbsp; c0 <span style="color: #66cc66;">=</span> cards<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: #66cc66;">,</span> :<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> indices <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; indices <span style="color: #66cc66;">=</span> numpy.<span style="color: black;">arange</span><span style="color: black;">&#40;</span>n<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; groups <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span>c0 <span style="color: #66cc66;">==</span> f<span style="color: black;">&#41;</span>.<span style="color: black;">nonzero</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> f <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>nfeatures<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># equals</span><br />
&nbsp; &nbsp; solequal <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> g <span style="color: #ff7700;font-weight:bold;">in</span> groups:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>g<span style="color: black;">&#41;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">3</span>: <span style="color: #ff7700;font-weight:bold;">continue</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; solequal +<span style="color: #66cc66;">=</span> find_sets3<span style="color: black;">&#40;</span>cards<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>:nd<span style="color: #66cc66;">,</span> g<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> indices<span style="color: black;">&#91;</span>g<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># different</span><br />
&nbsp; &nbsp; soldiff <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#40;</span>indices<span style="color: black;">&#91;</span>i0<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> indices<span style="color: black;">&#91;</span>i1<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> indices<span style="color: black;">&#91;</span>i2<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">for</span> i0 <span style="color: #ff7700;font-weight:bold;">in</span> groups<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> i1 <span style="color: #ff7700;font-weight:bold;">in</span> groups<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> i2 <span style="color: #ff7700;font-weight:bold;">in</span> groups<span style="color: black;">&#91;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">if</span> is_set<span style="color: black;">&#40;</span>cards<span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span>i0<span style="color: #66cc66;">,</span> i1<span style="color: #66cc66;">,</span> i2<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> solequal + soldiff</div></td></tr></tbody></table></div>
</pre>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># solution checker</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># &#8212;&#8212;&#8212;&#8212;&#8212;-</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def same(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are the same.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return numpy.all(x == x[0])</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def different(x):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Returns True if all elements are different.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return len(numpy.unique(x)) == len(x)</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">def is_set(cards, indices):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">&#8220;&#8221;"Checks that the cards indexed by &#8216;indices&#8217; form a valid set.&#8221;"&#8221;</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">ndims = cards.shape[0]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">subset = cards[:, indices]</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">for dim in range(ndims):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;"># cards must be all the same or all different for all dimensions</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">if not same(subset[dim, :]) and not different(subset[dim, :]):</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return False</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow: hidden;">return True</div>
<p>It turns out that, although it is a very efficient strategy to use  while playing with cards, find_sets3 is slower than  find_set2, probably because the overhead of calling the function  recursively outweighs the efficiency for such a small number of cards.</p>
<p>Let&#8217;s have a look at some statistics, then. While playing it can be quite frustrating to stare at the cards without being able to find any set. The instructions that ship with the official game say that such a situation should occur only once every 33 turns, but it certainly doesn&#8217;t feel that way. Is that really so?</p>
<p>First, I drew at random 12 cards from a complete deck of cards, and used the solver to compute the number of valid sets present on the table. I repeated this 10000 times, and ended up with this distribution for the number of set in a random draw:</p>
<div id="attachment_388" class="wp-caption aligncenter" style="width: 410px"><img class="size-full wp-image-388 " title="histogram_random_draws" src="http://www.masterbaboon.com/wp-content/uploads/2010/09/histogram_random_draws.png" alt="histogram_random_draws" width="400" height="248" /><p class="wp-caption-text">Probability of finding a given number of sets in 12 cards drawn at random.</p></div>
<p>As the instructions say, about 3% of the time (1 in 33) there is no set in the cards. However, this is misleading, as during a game the cards are not independently drawn each turn: the players remove one set from the cards on the table, and replace it with new cards. I thus simulated complete games, where at every turn I removed one of the set present on the table at random. The code to simulate a game look like this:</p>
<pre style="font: normal normal normal 12px/18px Consolas, Monaco, 'Courier New', Courier, monospace;">
<div class="codecolorer-container python default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br /></div></td><td><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #ff7700;font-weight:bold;">def</span> random_deck<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># initialize cards deck</span><br />
&nbsp; &nbsp; cards <span style="color: #66cc66;">=</span> numpy.<span style="color: #dc143c;">array</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>card <span style="color: #ff7700;font-weight:bold;">for</span> card <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">itertools</span>.<span style="color: black;">product</span><span style="color: black;">&#40;</span><span style="color: #008000;">range</span><span style="color: black;">&#40;</span>nfeatures<span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; repeat<span style="color: #66cc66;">=</span>ndims<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">T</span><br />
&nbsp; &nbsp; n <span style="color: #66cc66;">=</span> cards.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># shuffle</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> cards<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> numpy.<span style="color: #dc143c;">random</span>.<span style="color: black;">permutation</span><span style="color: black;">&#40;</span>n<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
<br />
ncards <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">12</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> onegame<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; nsolutions <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; deck <span style="color: #66cc66;">=</span> random_deck<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; pos <span style="color: #66cc66;">=</span> ncards<br />
&nbsp; &nbsp; cards <span style="color: #66cc66;">=</span> deck<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> :pos<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># find all sets</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #dc143c;">sets</span> <span style="color: #66cc66;">=</span> find_sets2<span style="color: black;">&#40;</span>cards<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; nsets <span style="color: #66cc66;">=</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sets</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> nsets <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># choose a random set</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; chosen <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">sets</span><span style="color: black;">&#91;</span>numpy.<span style="color: #dc143c;">random</span>.<span style="color: black;">randint</span><span style="color: black;">&#40;</span><span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">sets</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># remove cards from chosen set</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; idx <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>i <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span>cards.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> i <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff7700;font-weight:bold;">in</span> chosen<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cards <span style="color: #66cc66;">=</span> cards<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> idx<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># add new cards</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> cards.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">12</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nadd <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">12</span> - cards.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cards <span style="color: #66cc66;">=</span> numpy.<span style="color: black;">concatenate</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>cards<span style="color: #66cc66;">,</span> deck<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> pos:pos + nadd<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> axis<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pos +<span style="color: #66cc66;">=</span> nadd<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> pos <span style="color: #66cc66;">&gt;=</span> deck.<span style="color: black;">shape</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">break</span> <span style="color: #808080; font-style: italic;"># game is over</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># add additional cards</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cards <span style="color: #66cc66;">=</span> numpy.<span style="color: black;">concatenate</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>cards<span style="color: #66cc66;">,</span> deck<span style="color: black;">&#91;</span>:<span style="color: #66cc66;">,</span> pos:pos + <span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> axis<span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pos +<span style="color: #66cc66;">=</span> <span style="color: #ff4500;">3</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; nsolutions.<span style="color: black;">append</span><span style="color: black;">&#40;</span>nsets<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> nsolutions</div></td></tr></tbody></table></div>
</pre>
<p>The distribution of the number of sets on the table at any point in time looks quite different now (after simulating <del datetime="2010-09-21T14:40:52+00:00">1000</del> 5000 random games):</p>
<div id="attachment_389" class="wp-caption aligncenter" style="width: 410px"><img src="http://www.masterbaboon.com/wp-content/uploads/2010/09/histogram_game_draws1.png" alt="histogram_game_draws" title="histogram_game_draws" width="400" height="248" class="aligncenter size-full wp-image-453" /><p class="wp-caption-text">Probability of there being a given number of sets on the table at any point in a Set game.</p></div>
<p>As you see, the probability of there being no set on the table tripled and became about 1 in 10!  Now that&#8217;s a relief, it is not that weird not to be able to find a set&#8230; or is it? The distribution also tells us that about half of the times there are <em>3 sets or more</em> on the table! Now that one, I didn&#8217;t expect&#8230; (43 percent of the times, to be precise.)</p>
<p>The code and other material is available on the git repository at <a href="http://github.com/pberkes/masterbaboon/tree/master/projects/setgame/">http://github.com/pberkes/masterbaboon/tree/master/projects/setgame/</a> .</p>
<p><strong>Update 09/21/10:</strong> I updated the histogram of number of sets during a game with more games, so that the result is more accurate. The probability of not having a set at any point is even higher than reported before.</p>
<p><em>Cards and game © 1988, 1991 Cannei, LLC.  All rights reserved.  SET® is  registered trademark of Cannei, LLC.  Used with permission from Set  Enterprises, Inc.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2010/09/solving-the-game-set/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Planet Wars &#8211; Google AI Challenge</title>
		<link>http://www.masterbaboon.com/2010/09/planet-wars-google-ai-challenge/</link>
		<comments>http://www.masterbaboon.com/2010/09/planet-wars-google-ai-challenge/#comments</comments>
		<pubDate>Tue, 14 Sep 2010 21:09:43 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Game development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[Planet Wars]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=340</guid>
		<description><![CDATA[The Computer Science of the University of Waterloo is organizing its second Google AI Challenge. The challenge is a competition between computer programs that control the artificial intelligence of the players in a video game. This time, the game is set in space, and features a symmetric configuration of planets, each containing a fleet of [...]]]></description>
			<content:encoded><![CDATA[<p>The Computer Science of the University of Waterloo is organizing its second <a href="http://ai-contest.com/index.php">Google AI Challenge</a>. The challenge is a competition between computer programs that control the artificial intelligence of the players in a video game.</p>
<p>This time, the game is set in space, and features a symmetric configuration of planets, each containing a fleet of defending spaceships. Each turn, new ships are created, with larger planets creating more ships. At the beginning of the game, each player controls one of the planets and a hundred ships; the AIs issue orders to the ships to send them to conquer planets by outnumbering the local defense. The goal of the game is, of course, to eliminate all of the enemy forces.</p>
<p>This video shows an example game between my first AI (green) playing against DualBot (red), one of the bots included in the starter package; the number floating around represent the fleets commanded by the AIs:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/A0ATZoeWV48?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/A0ATZoeWV48?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>The competition is open to everybody, and it is possible to write the AI in basically any programming language. If you plan to use Python, I recommend not using the official starter package, which is un-pythonic and not very sophisticate, but rather <a href="http://github.com/ulope/planetwars-python-kit">this un-official client</a>. The alternative client offers in particular the possibility to log debug information to a local file.</p>
<p>Also, it can be quite frustrating to wait for the official server to match you with some other program, which can take up to an hour. There is an <a href="http://www.benzedrine.cx/planetwars/">alternative server</a> that lets you play with another opponent straight away.</p>
<p>The submission period ends November 27, good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2010/09/planet-wars-google-ai-challenge/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tracking down the enemy</title>
		<link>http://www.masterbaboon.com/2010/09/tracking-down-the-enemy/</link>
		<comments>http://www.masterbaboon.com/2010/09/tracking-down-the-enemy/#comments</comments>
		<pubDate>Sat, 11 Sep 2010 03:13:17 +0000</pubDate>
		<dc:creator>pietro</dc:creator>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Game development]]></category>
		<category><![CDATA[PacMan capture-the-flag]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[PacMan]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=300</guid>
		<description><![CDATA[As another scientific Python course is approaching, I&#8217;ve been brushing up my PacMan skills. I decided to give a try to a strategy I had been thinking on, which relies upon having a good estimate of the enemy&#8217;s position. I should remind the reader that in the PacMan capture-the-flag game, one team does not know [...]]]></description>
			<content:encoded><![CDATA[<p>As another <a href="https://portal.g-node.org/python-autumnschool/">scientific Python course</a> is approaching, I&#8217;ve been brushing up my<a href="http://www.masterbaboon.com/2009/09/pacman-capture-the-flag-a-fun-game-for-artificial-intelligence-development-and-education/"> PacMan skills</a>. I decided to give a try to a strategy I had been thinking on, which relies upon having a good estimate of the enemy&#8217;s position. I should remind the reader that in the <a href="http://www.masterbaboon.com/2009/09/pacman-capture-the-flag-a-fun-game-for-artificial-intelligence-development-and-education/">PacMan capture-the-flag game</a>, one team does not know the exact position of the other agents unless they are within 5 squares of one&#8217;s own agents. The game does, however, return a rough estimate of the opponent&#8217;s distance. Our agents-tracker will thus have to blindly make its best guess, and keep a probability distribution over possible positions.</p>
<p>To estimate the position of the opponent agents we need to apply some probability theory:</p>
<p>P(x(t)) = sum_{x(t-1)}  P( x(t-1) ) P( x(t) | x(t-1) )</p>
<p>or, in other words, the probability that the agent is at position x(t) at time t is equal to the sum of the probability of it being at x(t-1) at time t-1, times the probability of transitioning from x(t-1) to x(t). The first term is given simply by the previous estimate, while the second term is our model of the behavior of our opponent (*).</p>
<p>For example, a very conservative model could assume that the opponent could take any legal move at random:</p>
<p>P( x(t) | x(t-1) ) = 1/N</p>
<p>if x(t-1)-&gt;x(t) is a legal move, where N is the total number of legal moves from x(t-1), and</p>
<p>P( x(t) | x(t-1) ) = 0</p>
<p>otherwise. This video shows how such a model performs when the opponent behaves exactly as assumed; the red agent, in the bottom left corner, is estimating the position of the blue agent in the opposite corner; the area of the red squares is proportional to the probability P(x(t)):</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="544" height="327" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/393puqZt6OM?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="544" height="327" src="http://www.youtube.com/v/393puqZt6OM?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>The tracker is doing a good job in this case, but fails miserably for a more realistic opponent:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="544" height="327" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/1EyIRp7xe-8?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="544" height="327" src="http://www.youtube.com/v/1EyIRp7xe-8?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>We clearly need to improve the opponent&#8217;s model&#8230; luckily another simple model results in a large improvement: we can safely assume that the opponent tends to explore new parts of the maze in search of food. We can formalize this as</p>
<p>P( x(t) | x(t-1) ) = 1/Z exp(-beta * v(x(t))</p>
<p>if x(t-1)-&gt;x(t) is a legal move, and 0 otherwise. v(x) is the number of times the agent visited x in the past, and beta is a constant that controls the how exploratory the opponent is. When beta=0, the model is equivalent to the previous random model. Z is a normalizing constant such that P( x(t) | x(t-1) ) sums to 1.</p>
<p>Let&#8217;s see how this model does in practice (in the video, beta = 10):<br />
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="544" height="327" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/ni827laFLw8?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="544" height="327" src="http://www.youtube.com/v/ni827laFLw8?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>Much better, isn&#8217;t it? We can do even better by using two other sources of information: first, the game gives us a noisy estimation of the distance of the opponent (actual distance +/- 6); second, we know that if the opponent is not visible, it must be at least 5 squares away. We can take this information into account by setting P(x(t)) to 0 for squares that lie outside the noisy distance range, and for those inside the visibility range.</p>
<p>The last video shows the complete tracker at work. The blue lines show the area in which the agent might be according to the noisy distance, and the green line shows the visibility range:</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="544" height="327" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/EsfQ_54MTxk?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="544" height="327" src="http://www.youtube.com/v/EsfQ_54MTxk?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>The biggest area for improvement here is the agent&#8217;s model P(x(t)|x(t-1)). One possibility could be to simulate several common strategies, and to use the transition statistics for the simulated agents to estimate that probability&#8230;</p>
<p>Now, can we use the estimated position to program better AI agents? I&#8217;ll give it a try, and report back soon!</p>
<p><span style="color: #888888;">(*) Strictly speaking, we are doing &#8220;filtering&#8221; here, i.e., we&#8217;re estimating the current position assuming the past inferences are fixed. The alternative is to do &#8220;smoothing&#8221;, where the full joint probability P(x(t), &#8230;, x(1)) is estimated at each step. The information coming from the new observation is propagated back and forth at each to improve the past inferences. For example, knowing that the agent is at a given position at time t might exclude another position at time t-2 because of too large a distance, which in turn could improve the estimate at time t.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2010/09/tracking-down-the-enemy/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PacMan capture-the-flag: a fun game for artificial intelligence development and education</title>
		<link>http://www.masterbaboon.com/2009/09/pacman-capture-the-flag-a-fun-game-for-artificial-intelligence-development-and-education/</link>
		<comments>http://www.masterbaboon.com/2009/09/pacman-capture-the-flag-a-fun-game-for-artificial-intelligence-development-and-education/#comments</comments>
		<pubDate>Sat, 12 Sep 2009 17:55:03 +0000</pubDate>
		<dc:creator>pietro</dc:creator>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[PacMan capture-the-flag]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[PacMan]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=260</guid>
		<description><![CDATA[At the beginning of September I&#8217;ve been invited to teach at a summer school about scientific programming. The whole experience has been really rewarding, but it was the student&#8217;s project that got me going: we had the students write artificial intelligence algorithms for the agents of a PacMan-like game, and organized a tournament for them [...]]]></description>
			<content:encoded><![CDATA[<p>At the beginning of September I&#8217;ve been invited to teach at a<a href="http://www.g-node.org/python-summerschool-2009" target="_blank"> summer school about scientific programming</a>. The whole experience has been really rewarding, but it was the student&#8217;s project that got me going: we had the students write artificial intelligence algorithms for the agents of a PacMan-like game, and organized a tournament for them to compete against each other.</p>
<p>The <strong>PacMan capture-the-flag game</strong> has been written originally by John DeNero, and has been used to teach an artificial intelligence course <a href="http://inst.eecs.berkeley.edu/~cs188/sp09/information.html" target="_blank">by him at Berkley</a> and by Hal Daume III at <a href="http://www.cs.utah.edu/~hal/courses/2009S_AI/" target="_blank">University of Utah</a>. Very often, this kind of games have a single strategy that dominates all others, and once you find it the interest fizzles out. In this case, I was impressed by how rich this game is. The game offers a lot of opportunities to develop and test complex learning and planning algorithms, including cooperation strategies for games with multiple agents.</p>
<p><img class="aligncenter size-large wp-image-264" title="capture_the_flag" src="http://www.masterbaboon.com/wp-content/uploads/2009/09/capture_the_flag-1024x397.png" alt="capture_the_flag" width="516" height="200" /></p>
<p>The rules of the game are quite simple: the board is a PacMan maze, divided in a red and a blue half. The two halves belong to two teams of agents, who are controlled by computer programs to eat the opponent&#8217;s food and protect their own. When in the opponent&#8217;s half, the agents are <strong>PacMan</strong> (PacMen?), while in their own half, the agents are <strong>ghosts</strong> and can kill the opponent&#8217;s PacMan agents, in which case these are returned to their initial position. The players get one point for each food dot they eat; no points are assigned for eating the other team&#8217;s agents. The game ends when one of the two teams eats all of the opponent&#8217;s food, or after 3000 moves; the team with the highest score wins.</p>
<p>To make the game more interesting, one can only observe the position of the other team&#8217;s agents when they are very close to one&#8217;w own agents (5 squares away); otherwise, one can only obtain a noisy estimate of their distance.</p>
<p>The game is written in Python, my programming language of choice, which allows to write rapidly even sophisticated algorithms. I recommend the game to anyone wanting to organize an artificial intelligence course, or simply have fun writing AI agents. I plan to dedicate a couple of posts to the basic strategies to write successful agents in this game.</p>
<p>Here&#8217;s a video of the best students&#8217; agents (red team) playing against the best tutors&#8217; agents (blue team). The tutors won, saving our reputation!</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="344" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/E82IrVhVaVc&amp;hl=en&amp;fs=1&amp;" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/E82IrVhVaVc&amp;hl=en&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><strong>Update:</strong> The authors of the PacMan capture-the-flag game decided to keep the game close-source, and in particular would prefer not to publish the code of agents playing their game, fearing that it might interfere with their course. It&#8217;s a shame because I was planning to write some Genetic Programming agents for the game, but of course I respect their decision. I guess there will be no series of posts re:PacMan&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2009/09/pacman-capture-the-flag-a-fun-game-for-artificial-intelligence-development-and-education/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>My AI reads your mind and kicks your ass (part 2)</title>
		<link>http://www.masterbaboon.com/2009/05/my-ai-reads-your-mind-and-kicks-your-ass-part-2/</link>
		<comments>http://www.masterbaboon.com/2009/05/my-ai-reads-your-mind-and-kicks-your-ass-part-2/#comments</comments>
		<pubDate>Sun, 03 May 2009 23:33:01 +0000</pubDate>
		<dc:creator>pietro</dc:creator>
				<category><![CDATA[ActionScript 3]]></category>
		<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[decision theory]]></category>
		<category><![CDATA[game]]></category>
		<category><![CDATA[games]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=217</guid>
		<description><![CDATA[
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_KarateAI_update_1747616329"
			class="flashmovie"
			width="550"
			height="400">
	<param name="movie" value="/flashes/KarateAI_update.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="/flashes/KarateAI_update.swf"
			name="fm_KarateAI_update_1747616329"
			width="550"
			height="400">
	<!--<![endif]-->
		 
	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object> In the last post I discussed how it is possible to program a game Artificial Intelligence to exploit a player&#8217;s unconscious biases using a simple mathematical model. In the karate game above, the AI uses that model in order to do the largest amount of damage. Give [...]]]></description>
			<content:encoded><![CDATA[
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_KarateAI_update_465699642"
			class="flashmovie"
			width="550"
			height="400">
	<param name="movie" value="/flashes/KarateAI_update.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="/flashes/KarateAI_update.swf"
			name="fm_KarateAI_update_465699642"
			width="550"
			height="400">
	<!--<![endif]-->
		<a href="http://adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>

	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>
<p>In the last post I discussed how it is possible to program a game Artificial Intelligence to exploit a player&#8217;s unconscious biases using a simple mathematical model. In the karate game above, the AI uses that model in order to do the largest amount of damage. Give it a try! You get 10 points if you hit your opponent with a punch or a kick, 0 points if you miss, and 5 points if you block your opponent&#8217;s move. As you play, the AI learns your strategy and adapts to knock you down as often as possible.</p>
<p>How does it work? According to decision theory, we need to maximize the <em>expected score</em>. To compute the expected score for an action &#8216;x&#8217; (e.g., &#8216;punch&#8217;), one needs to consider all possible player&#8217;s moves, &#8216;y&#8217;, and weight the possible outcome with the probability of the player doing that move, i.e.</p>
<pre>E[score for x] = sum_y P(y) * Score(y,x)</pre>
<p>where P(y) is the probability of the player choosing action &#8216;y&#8217; (obtained using last post&#8217;s model), and Score(y,x) gives the score of responding &#8216;x&#8217; to &#8216;y&#8217;.</p>
<p>For example, in the karate game using a low kick has a priori the highest chance of success: you score in 3 out of 4 cases, and only lose 5 points if the opponent decides to block your kick. This is why, at the beginning, the AI tends to choose that move. However, if you know that the AI uses that move often, you will choose the kick-blocking move more often, increasing P(kick-block). This change will make the punch more likely to score points. As you play, the optimal strategy changes and the AI continues to adapt to your style.</p>
<p>With a bit of practice, you&#8217;ll notice that you can compete with the AI and sometimes even gain the upper hand over it. This shows that you are in turn forming an internal model of the computer&#8217;s strategy. I think that the game dynamics that results from this interaction makes the game quite interesting, even though it is extremely simple. Unfortunately, it&#8217;s very rare to see learning AIs in real-life video games&#8230;</p>
<p>As always, you can <a href="http://www.masterbaboon.com/flashes/KarateAI.zip">download the code here</a>.</p>
<p><strong>Update:</strong> Instead of always making the best move, the AI now selects the move with a probability related to its score, which makes it less predictable. More details in the next post&#8230;</p>
<p><script type="text/javascript"><!--
google_ad_client = "pub-5929969675637192";
google_ad_slot = "7572213318";
google_ad_width = 468;
google_ad_height = 15;
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2009/05/my-ai-reads-your-mind-and-kicks-your-ass-part-2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>My AI reads your mind (part 1)</title>
		<link>http://www.masterbaboon.com/2009/04/my-ai-can-read-your-mind-part-1/</link>
		<comments>http://www.masterbaboon.com/2009/04/my-ai-can-read-your-mind-part-1/#comments</comments>
		<pubDate>Thu, 23 Apr 2009 13:58:48 +0000</pubDate>
		<dc:creator>pietro</dc:creator>
				<category><![CDATA[ActionScript 3]]></category>
		<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[decision theory]]></category>
		<category><![CDATA[games]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=204</guid>
		<description><![CDATA[I regularly read about people complaining that AI in games should be improved. I definitely agree with them, but here&#8217;s a argument why pushing it to the limits might not be such a good idea: computers can easily discover and exploit our unconscious biases. Magic? ESP? More like a simple application of decision theory. In [...]]]></description>
			<content:encoded><![CDATA[<p>I regularly read about people complaining that AI in games should be improved. I definitely agree with them, but here&#8217;s a argument why pushing it to the limits might not be such a good idea: computers can easily discover and exploit our unconscious biases.</p>
<p>Magic? ESP? More like a simple application of decision theory. In order to make an unbeatable AI one needs two steps: 1) build a model of a player&#8217;s response in order to predict his next move, and 2) choose actions that maximize the expected score given the prediction of the model.</p>
<p>The basic idea behind 1) is that even if we try to be unpredictable, our actions contain hidden patterns that can be revealed using a pinch of statistics. Formally, the model takes the form of a probability distribution: P(next move | past observations).</p>
<p>Try it out: In the Flash example below, you can type in a sequence numbers 1-4, and the AI will try to predict your next choice. If your choices were completely random, the AI would only be able to guess correctly 25% of the time. In practice, it often guesses correctly 35-40% of the numbers! (It might take a few numbers before the AI starts doing a decent job.)</p>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
			id="fm_predict_sequence_331370569"
			class="flashmovie"
			width="550"
			height="400">
	<param name="movie" value="/flashes/predict_sequence.swf" />
	<!--[if !IE]>-->
	<object	type="application/x-shockwave-flash"
			data="/flashes/predict_sequence.swf"
			name="fm_predict_sequence_331370569"
			width="550"
			height="400">
	<!--<![endif]-->
		<a href="http://adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" /></a></p>

	<!--[if !IE]>-->
	</object>
	<!--<![endif]-->
</object>
<p>In this example I used a 2nd order Markov model, i.e., I assumed that the next number, n(t+1), only depends on the past 2 choices: P(n(t+1) | past observations) = P(n(t+1) | n(t), n(t-1)). The rest is just book-keeping: I used two arrays, one to remember the past 3 numbers, and one to keep track of how many times the player chose number &#8216;k&#8217;, given that his past two moves were &#8216;i&#8217; and &#8216;j&#8217;:</p>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900; font-style: italic;">// last 3 moves</span><br />
<span style="color: #0033ff; font-weight: bold;">public</span> <span style="color: #6699cc; font-weight: bold;">var</span> history<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> = <span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">3</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">2</span><span style="color: #000000;">&#93;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #009900; font-style: italic;">// transition table: transitions[i,j,k] stores the number</span><br />
<span style="color: #009900; font-style: italic;">// of time the player pressed 'i' followed by 'j' followed by 'k'</span><br />
<span style="color: #0033ff; font-weight: bold;">public</span> <span style="color: #6699cc; font-weight: bold;">var</span> transitions<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span><span style="color: #000066; font-weight: bold;">;</span></div></td></tr></tbody></table></div>
<p>When the player makes a new choice, I update the history, and increment the corresponding entry in the transition table:</p>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #3f5fbf;">/*<br />
* Update history and transition tables with player's move.<br />
*/</span><br />
<span style="color: #0033ff; font-weight: bold;">public</span> <span style="color: #339966; font-weight: bold;">function</span> update<span style="color: #000000;">&#40;</span>move<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">int</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #0033ff; font-weight: bold;">void</span> <span style="color: #000000;">&#123;</span><br />
history = <span style="color: #000000;">&#91;</span>history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000000;">&#93;</span><span style="color: #000066; font-weight: bold;">,</span> history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">2</span><span style="color: #000000;">&#93;</span><span style="color: #000066; font-weight: bold;">,</span> move<span style="color: #000000;">&#93;</span><span style="color: #000066; font-weight: bold;">;</span><br />
transitions<span style="color: #000000;">&#91;</span>history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">0</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#91;</span>history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#91;</span>history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">2</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#93;</span> <span style="color: #000066; font-weight: bold;">+</span>= <span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #000000;">&#125;</span></div></td></tr></tbody></table></div>
<p>The probability that the next choice will be n(t+1), is given by the number of times the player pressed n(t+1) after n(t) and n(t-1) before, normalized by the number of time the sequence n(t-1), n(t) occurred in the past:</p>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #3f5fbf;">/*<br />
* Return probability distribution for next move.<br />
*/</span><br />
<span style="color: #0033ff; font-weight: bold;">public</span> <span style="color: #339966; font-weight: bold;">function</span> predict<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> <span style="color: #000000;">&#123;</span><br />
<span style="color: #009900; font-style: italic;">// probability distribution over next move</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> prob<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> = <span style="color: #0033ff; font-weight: bold;">new</span> <span style="color: #004993;">Array</span><span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">4</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<br />
<span style="color: #009900; font-style: italic;">// look up previous transitions from moves at time t-2, t-1</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> tr<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> = transitions<span style="color: #000000;">&#91;</span>history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#91;</span>history<span style="color: #000000;">&#91;</span><span style="color: #000000; font-weight:bold;">2</span><span style="color: #000000;">&#93;</span><span style="color: #000000;">&#93;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<br />
<span style="color: #009900; font-style: italic;">// normalizing constant</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> sum<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span> = <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #0033ff; font-weight: bold;">for</span> <span style="color: #000000;">&#40;</span><span style="color: #6699cc; font-weight: bold;">var</span> k<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">int</span> = <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000066; font-weight: bold;">;</span> k <span style="color: #000066; font-weight: bold;">&amp;</span>lt<span style="color: #000066; font-weight: bold;">;</span> <span style="color: #000000; font-weight:bold;">4</span><span style="color: #000066; font-weight: bold;">;</span> k<span style="color: #000066; font-weight: bold;">++</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span><br />
sum <span style="color: #000066; font-weight: bold;">+</span>= tr<span style="color: #000000;">&#91;</span>k<span style="color: #000000;">&#93;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #000000;">&#125;</span><br />
<br />
<span style="color: #0033ff; font-weight: bold;">for</span> <span style="color: #000000;">&#40;</span>k = <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000066; font-weight: bold;">;</span> k <span style="color: #000066; font-weight: bold;">&amp;</span>lt<span style="color: #000066; font-weight: bold;">;</span> <span style="color: #000000; font-weight:bold;">4</span><span style="color: #000066; font-weight: bold;">;</span> k<span style="color: #000066; font-weight: bold;">++</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span><br />
prob<span style="color: #000000;">&#91;</span>k<span style="color: #000000;">&#93;</span> = tr<span style="color: #000000;">&#91;</span>k<span style="color: #000000;">&#93;</span> <span style="color: #000066; font-weight: bold;">/</span> sum<span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #000000;">&#125;</span><br />
<br />
<span style="color: #0033ff; font-weight: bold;">return</span> prob<span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #000000;">&#125;</span></div></td></tr></tbody></table></div>
<p>The best prediction is given by the choice with maximum probability. <a href="http://www.masterbaboon.com/flashes/PredictSequence.zip">You&#8217;re welcome to have a look at the code!</a></p>
<p>In the next post, I&#8217;ll show how the AI can choose the best actions in order to maximize its expected score in a Virtual Karate game.</p>
<p><script type="text/javascript"><!--
google_ad_client = "pub-5929969675637192";
google_ad_slot = "7572213318";
google_ad_width = 468;
google_ad_height = 15;
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2009/04/my-ai-can-read-your-mind-part-1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Spatial database for collision detection</title>
		<link>http://www.masterbaboon.com/2009/02/spatial-database-for-collision-detection/</link>
		<comments>http://www.masterbaboon.com/2009/02/spatial-database-for-collision-detection/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 20:20:04 +0000</pubDate>
		<dc:creator>pietro</dc:creator>
				<category><![CDATA[ActionScript 3]]></category>
		<category><![CDATA[Game development]]></category>
		<category><![CDATA[collision detection]]></category>
		<category><![CDATA[data structures]]></category>
		<category><![CDATA[games]]></category>

		<guid isPermaLink="false">http://www.masterbaboon.com/?p=37</guid>
		<description><![CDATA[In games and other graphical applications one has to keep track multiple sprites and detect collisions between them. A naif approach would loop over all sprites and check for collision with *every other sprite*. This is, of course, terribly inefficient and can be very slow even for a small number of sprites. One way out [...]]]></description>
			<content:encoded><![CDATA[<p>In games and other graphical applications one has to keep track multiple sprites and detect collisions between them. A naif approach would loop over all sprites and check for collision with *every other sprite*. This is, of course, terribly inefficient and can be very slow even for a small number of sprites.</p>
<p>One way out of this nightmare  is to register the sprites in a <em>spatial database</em> that stores them according to their position, and is able to determine which of them is close to a given point. As a result, one only needs to check for collision between neighboring sprites. There are many implementations of spatial databases, some of which are quite sophisticated. In this post I&#8217;m going to describe an ActionScript 3 implementation of a grid-based spatial database, that can be used for simple flash games.</p>
<h4>Grid: 2D Array with neighborhood</h4>
<p>A <em>Grid </em>is a two-dimensional array with a concept of <em>neighborhood</em>.  In addition to be able to store and retrieve information on the 2D grid, one can request neighboring elements of a given array element.</p>
<p>For example, let&#8217;s create a simple 4&#215;4 grid, and store at each point an increasing number:</p>
<pre>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #6699cc; font-weight: bold;">var</span> grid<span style="color: #000066; font-weight: bold;">:</span>Grid = <span style="color: #0033ff; font-weight: bold;">new</span> Grid<span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">4</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">4</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> k<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">int</span> = <span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #0033ff; font-weight: bold;">for</span> <span style="color: #000000;">&#40;</span><span style="color: #6699cc; font-weight: bold;">var</span> i<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">int</span> = <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000066; font-weight: bold;">;</span> i <span style="color: #000066; font-weight: bold;">&amp;</span>lt<span style="color: #000066; font-weight: bold;">;</span> <span style="color: #000000; font-weight:bold;">4</span><span style="color: #000066; font-weight: bold;">;</span> i<span style="color: #000066; font-weight: bold;">++</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0033ff; font-weight: bold;">for</span> <span style="color: #000000;">&#40;</span><span style="color: #6699cc; font-weight: bold;">var</span> j<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">int</span> = <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000066; font-weight: bold;">;</span> j <span style="color: #000066; font-weight: bold;">&amp;</span>lt<span style="color: #000066; font-weight: bold;">;</span> <span style="color: #000000; font-weight:bold;">4</span><span style="color: #000066; font-weight: bold;">;</span> j<span style="color: #000066; font-weight: bold;">++</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; grid<span style="color: #000066; font-weight: bold;">.</span><span style="color: #0033ff; font-weight: bold;">set</span><span style="color: #000000;">&#40;</span>i<span style="color: #000066; font-weight: bold;">,</span> j<span style="color: #000066; font-weight: bold;">,</span> k<span style="color: #000066; font-weight: bold;">++</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
&nbsp; &nbsp; <span style="color: #000000;">&#125;</span><br />
<span style="color: #000000;">&#125;</span></div></td></tr></tbody></table></div>
</pre>
<p>This image shows the resulting grid, with small numbers indicating grid coordinates and large, bold ones the stored values:</p>
<p><img class="aligncenter size-thumbnail wp-image-114" title="grid11" src="http://www.masterbaboon.com/wp-content/uploads/2009/02/grid11-150x150.png" alt="grid11" width="150" height="150" /></p>
<p>We can now query the grid to get neighbors of a given position: For example, the following code</p>
<pre>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #6699cc; font-weight: bold;">var</span> neighbors<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> = grid<span style="color: #000066; font-weight: bold;">.</span>getNeighbors<span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">1</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #004993;">trace</span><span style="color: #000000;">&#40;</span>neighbors<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
neighbors = grid<span style="color: #000066; font-weight: bold;">.</span>getNeighbors<span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">3</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #004993;">trace</span><span style="color: #000000;">&#40;</span>neighbors<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span></div></td></tr></tbody></table></div>
</pre>
<p>displays <em>1, 2, 3, 5, 7, 9, 10, 11</em> and <em>3, 4, 7, 11, 12</em>, respectively, as shown here:</p>
<p><img class="aligncenter size-medium wp-image-115" title="grids2and3" src="http://www.masterbaboon.com/wp-content/uploads/2009/02/grids2and3-300x147.png" alt="grids2and3" width="300" height="147" /></p>
<p>Note that, by default, the neighbors stop at the border. It is possible to work on a &#8220;toroidal&#8221; grid, meaning that the opposite borders of the grid are connected:</p>
<p><img class="aligncenter size-thumbnail wp-image-116" title="grid4" src="http://www.masterbaboon.com/wp-content/uploads/2009/02/grid4-150x150.png" alt="grid4" width="150" height="150" /></p>
<pre>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">grid<span style="color: #000066; font-weight: bold;">.</span>setToroidal<span style="color: #000000;">&#40;</span><span style="color: #0033ff; font-weight: bold;">true</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
neighbors = grid<span style="color: #000066; font-weight: bold;">.</span>getNeighbors<span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">3</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #004993;">trace</span><span style="color: #000000;">&#40;</span>neighbors<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span></div></td></tr></tbody></table></div>
</pre>
<p>which prints <em>3,4,1,7,5,11,12,9</em> .</p>
<p>I added two functions that simplify working with neighbors. The first, <em>mapOnNeighbors(x, y, fct)</em>, applies a function <em>fct</em> to all neighbors of <em>(x,y)</em>. Let&#8217;s  see which of the neighbors of (1,3), are even numbers, just for fun:</p>
<pre>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339966; font-weight: bold;">function</span> isEven<span style="color: #000000;">&#40;</span><span style="color: #004993;">x</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">int</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Boolean</span> <span style="color: #000000;">&#123;</span> <span style="color: #0033ff; font-weight: bold;">return</span> <span style="color: #000000;">&#40;</span><span style="color: #004993;">x</span> <span style="color: #000066; font-weight: bold;">%</span> <span style="color: #000000; font-weight:bold;">2</span> == <span style="color: #000000; font-weight:bold;">0</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span> <span style="color: #000000;">&#125;</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> neighborsAreEven<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> = grid<span style="color: #000066; font-weight: bold;">.</span>mapOnNeighbors<span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">3</span><span style="color: #000066; font-weight: bold;">,</span> isEven<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #004993;">trace</span><span style="color: #000000;">&#40;</span>neighborsAreEven<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span></div></td></tr></tbody></table></div>
</pre>
<p>This gives <em>false,true,false,false,false,false,true,false</em>. There are several interesting uses of this method: collecting information from elements in a region of the grid, activating neighboring elements, &#8230;</p>
<p>The second function,<em> reduceOnNeighbors(x, y, fct)</em>, is a bit more difficult to explain, but equally useful: it returns a single value constructed by iterating over the neighbors of <em>(x,y)</em> and calling <em>fct(a, b)</em> on the first two items of the sequence, then on the result and the next item, and so on. We can use this function to compute the sum of all neighbors:</p>
<pre>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #339966; font-weight: bold;">function</span> sum<span style="color: #000000;">&#40;</span><span style="color: #004993;">x</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #004993;">y</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span> <span style="color: #000000;">&#123;</span> <span style="color: #0033ff; font-weight: bold;">return</span> <span style="color: #004993;">x</span> <span style="color: #000066; font-weight: bold;">+</span> <span style="color: #004993;">y</span><span style="color: #000066; font-weight: bold;">;</span> <span style="color: #000000;">&#125;</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> sumOfNeighbors<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Number</span> = grid<span style="color: #000066; font-weight: bold;">.</span>reduceOnNeighbors<span style="color: #000000;">&#40;</span><span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> <span style="color: #000000; font-weight:bold;">1</span><span style="color: #000066; font-weight: bold;">,</span> sum<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #004993;">trace</span><span style="color: #000000;">&#40;</span>sumOfNeighbors<span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span></div></td></tr></tbody></table></div>
</pre>
<p>prints <em>48</em> = 1+2+3+5+7+9+10+11 . This function could have been used for example in the <a href="http://www.masterbaboon.com/2009/02/2d-cellular-automata/" target="_self">previous post</a> in the Cellular Automaton code, to compute the number of alive neighbors for every cell of the CA.</p>
<h4>SpatialDatabase</h4>
<p>The <em>SpatialDatabase </em>class registers sprites in a Grid with coarser resolution. For example, a <em>Shape </em>at position <em>(31, 23) </em>on the stage would be stored in element <em>(3,2)</em> if the resolution of the grid is 10 pixels.</p>
<p>That&#8217;s basically all there is to it! For collision detection, we can query the spatial database for sprites registered at neighboring elements in the grid:</p>
<pre>
<div class="codecolorer-container actionscript3 default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="actionscript3 codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #009900; font-style: italic;">// Check collision with shape:Shape</span><br />
<span style="color: #009900; font-style: italic;">// 1. get neighbors</span><br />
<span style="color: #6699cc; font-weight: bold;">var</span> neighbors<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Array</span> = spatialDatabase<span style="color: #000066; font-weight: bold;">.</span>getAllNeighbors<span style="color: #000000;">&#40;</span>shape<span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">x</span><span style="color: #000066; font-weight: bold;">,</span> shape<span style="color: #000066; font-weight: bold;">.</span><span style="color: #004993;">y</span><span style="color: #000000;">&#41;</span><span style="color: #000066; font-weight: bold;">;</span><br />
<span style="color: #009900; font-style: italic;">// 2. loop over all neighbors and check collision</span><br />
<span style="color: #0033ff; font-weight: bold;">for</span> <span style="color: #0033ff; font-weight: bold;">each</span> <span style="color: #000000;">&#40;</span><span style="color: #6699cc; font-weight: bold;">var</span> s<span style="color: #000066; font-weight: bold;">:</span><span style="color: #004993;">Shape</span> <span style="color: #0033ff; font-weight: bold;">in</span> neighbors<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #009900; font-style: italic;">// check collision</span><br />
&nbsp; &nbsp; <span style="color: #0033ff; font-weight: bold;">if</span> <span style="color: #000000;">&#40;</span>checkCollision<span style="color: #000000;">&#40;</span>shape<span style="color: #000066; font-weight: bold;">,</span> s<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900; font-style: italic;">// do something (bounce, explode, ...)</span><br />
&nbsp; &nbsp; <span style="color: #000000;">&#125;</span><br />
<span style="color: #000000;">&#125;</span></div></td></tr></tbody></table></div>
</pre>
<p>A higher resolution gives an optimal performance. Just keep in mind that the grid size should be larger than the size of the largest sprite, otherwise some collisions may go undetected.</p>
<p>There exist much more sophisticated methods to improve the efficiency of collision detection (see for example <a href="http://www.gskinner.com/blog/archives/2008/01/proximitymanage.html" target="_blank">this post</a> and the excellent <a href="http://www.harveycartel.org/metanet/tutorials.html" target="_blank">tutorial at metanet software</a>). However, this simple grid-based approach can be *very* efficient for many applications!</p>
<p>You can grab the code for the <a href="http://www.masterbaboon.com/flashes/grid_demo.zip">Grid and SpatialData`base classes here</a> (including AsUnit tests for the Grid class). I have a nice example of this class at work, but this post is already long enough, have a look at the next one!</p>
<p><script type="text/javascript"><!--
google_ad_client = "pub-5929969675637192";
google_ad_slot = "7572213318";
google_ad_width = 468;
google_ad_height = 15;
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
</p>
]]></content:encoded>
			<wfw:commentRss>http://www.masterbaboon.com/2009/02/spatial-database-for-collision-detection/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

