<?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>Rosio Pavoris</title>
	<atom:link href="http://cairnarvon.rotahall.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://cairnarvon.rotahall.org</link>
	<description>Unscientific and ultimately destructive.</description>
	<lastBuildDate>Mon, 06 May 2013 14:56:35 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Delivering on other people&#8217;s half-decade-old promises</title>
		<link>http://cairnarvon.rotahall.org/2012/12/26/delivering-on-other-peoples-half-decade-old-promises/</link>
		<comments>http://cairnarvon.rotahall.org/2012/12/26/delivering-on-other-peoples-half-decade-old-promises/#comments</comments>
		<pubDate>Wed, 26 Dec 2012 01:09:52 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Religion]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=2197</guid>
		<description><![CDATA[Click it it is a link.]]></description>
				<content:encoded><![CDATA[<p><center><a href="http://world4search.no-ip.org:8080/"><img src="/pics/promise.png" alt="promise" class="image" /></a></center></p>
<p><a href="http://world4search.no-ip.org:8080/">Click it it is a link.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2012/12/26/delivering-on-other-peoples-half-decade-old-promises/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Number Three</title>
		<link>http://cairnarvon.rotahall.org/2012/06/17/number-three/</link>
		<comments>http://cairnarvon.rotahall.org/2012/06/17/number-three/#comments</comments>
		<pubDate>Sun, 17 Jun 2012 21:04:41 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=2164</guid>
		<description><![CDATA[Because of his pedigree his name had to start with an L, so we called him Linus. I recorded that video using my new ThinkPad® Tablet, which leads me to a question which I will phrase as a demand: recommend some Android &#8220;apps&#8221; I can download to make that thing suck less. They should at [...]]]></description>
				<content:encoded><![CDATA[<p><center><iframe width="500" height="280" src="https://www.youtube-nocookie.com/embed/dRfsEMpTBSw?rel=0" frameborder="0" allowfullscreen></iframe></center></p>
<p>Because of his pedigree his name had to start with an L, so we called him Linus.</p>
<p>I recorded that video using my new <i>ThinkPad® Tablet</i>, which leads me to a question which I will phrase as a demand: recommend some Android &#8220;apps&#8221; I can download to make that thing suck less. They should at least be free as in beer because I don&#8217;t have a credit card, and ideally free as in Stallman too.<br />
I&#8217;m specifically looking for a good terminal emulator (currently using Terminal Emulator, which is balls), an SSH client, something to read PDFs and ideally PostScript files with, and maybe an emulator for one or more of the better gaman consoles, but feel free to recommend whatever you find noteworthy.</p>
<p>(I already know about Angry Birds and the Minecraft demo and don&#8217;t care for either.)</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2012/06/17/number-three/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Adventures on the grey market</title>
		<link>http://cairnarvon.rotahall.org/2012/04/07/adventures-on-the-grey-market/</link>
		<comments>http://cairnarvon.rotahall.org/2012/04/07/adventures-on-the-grey-market/#comments</comments>
		<pubDate>Sat, 07 Apr 2012 00:16:08 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Miscellany]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=2136</guid>
		<description><![CDATA[Look at this picture, and try to guess how many of these cartridges are genuine: Hint: the correct answer is &#8220;not a damn one&#8221;. Yes, I&#8217;ve been playing a lot of Pokémon in the past year or so, so yes, it was inevitable I&#8217;d eventually attempt to acquire some of the GBA games. Belgian toy [...]]]></description>
				<content:encoded><![CDATA[<p>Look at this picture, and try to guess how many of these cartridges are genuine:</p>
<p><center><a href="/pics/pokemon-gba/emeralds_big.jpg"><img src="/pics/pokemon-gba/emeralds.jpg" alt="Emeralds" title="Money well spent" class="image" /></a></center></p>
<p>Hint: the correct answer is &#8220;not a damn one&#8221;.</p>
<p><span id="more-2136"></span></p>
<p>Yes, I&#8217;ve been playing a lot of Pokémon in the past year or so, so yes, it was inevitable I&#8217;d eventually attempt to acquire some of the GBA games. Belgian toy stores don&#8217;t tend to do trade-ins or sell old second-hand games, and I hate interacting directly with people (so eBay or the local equivalent, 2dehands.be, are out), and Amazon doesn&#8217;t deliver here, so it took me a bit to find an actual place to get them, but eventually I did find <a href="http://www.play.com/">this Amazon clone</a>, which has the added bonus of free shipping to most of Europe.<br />
I&#8217;d like to make it clear my misadventures involving Pokémon Emerald are no reflection on the website itself or most sellers using it; several sellers refunded my money when I pointed out the games were bootlegs, and I&#8217;ve had few to no issues with the other things I&#8217;ve ordered there (including three DS games and a GameCube game, controller, and memory card).</p>
<p>For Pokémon Emerald, though, holy fuck. I&#8217;ve made <i>seven</i> attempts to buy this game; the four bootlegs pictured, two orders that were refused because the seller was out of stock but forgot to take down his listing, and my personal favourite, this fucking thing:</p>
<p><center><a href="/pics/pokemon-gba/notemerald_big.jpg"><img src="/pics/pokemon-gba/notemerald.jpg" alt="Not Emerald" title="Quality" class="image" /></a></center></p>
<p>Apart from the fact that it&#8217;s not the right game, look at the effort that went into it. They wrote a (six-page, admittedly) manual. They printed a box with fancy metallic sparkles. They actually folded a cardboard thing to keep the cartridge in place as if it were the real deal. At no point did it occur to them to look at the actual box art to ensure their efforts weren&#8217;t ludicrously misdirected.<br />
The real Pokémon LeafGreen comes with a GBA wireless adaptor, by the way (because link cables are passé). This one obviously didn&#8217;t, but the manual, <i>which they wrote themselves, from scratch</i>, still promises it.</p>
<p>I do actually own a legitimate copy of LeafGreen (also without wireless adaptor, but that&#8217;s to be expected). Here it is with my legitimate copy of FireRed,<sup><a href="#f_adventures-on-the-grey-market_0" id="adventures-on-the-grey-market_0">0</a></sup> and the bootleg copy of FireRed I got on my first attempt (fucker didn&#8217;t even send me a box for that one):</p>
<p><center><a href="http://cairnarvon.rotahall.org/pics/pokemon-gba/frlg_big.jpg"><img src="http://cairnarvon.rotahall.org/pics/pokemon-gba/frlg.jpg" alt="FRLG" title="FRLG" class="image" /></a></center></p>
<p>Obvious things to notice: the bootleg has a battery (that round thing partially visible through the plastic), the real ones don&#8217;t; the Nintendo seal is oval on the bootleg; the label has a metallic effect on the genuine cartridges.<br />
One thing all of my bootlegs have in common is that when they&#8217;re started, they give a pointless message. All of the Emeralds give the following:</p>
<blockquote><p><tt>The save file will be loaded.<br />
The game can be played.</tt></p></blockquote>
<p>Except for the one that used to give that but now gives this one:</p>
<blockquote><p><tt>The save file has been erased due to corruption or damage.<br />
The game can be played.</tt></p></blockquote>
<p>(In case you were wondering why it matters to me that I have a genuine cartridge. And oh: none of the bootlegs I&#8217;ve bothered to test allow migration of Pokémon to the fourth-gen games.)</p>
<p>The bootleg FireRed and LeafGreen are terser:</p>
<blockquote><p><tt>The save file is ok.</tt></p></blockquote>
<p>Real games don&#8217;t give any message when there&#8217;s no problem. I suspect the reason bootlegs do is because the save mechanism is so closely tied to the hardware of the cartridge itself that it has to be reimplemented by the bootleggers (unlike the rest of the game, which is just a ROM ripped from a legitimate cartridge, obviously), and bootleggers don&#8217;t give a shit. Further evidence for this hypothesis is the fact that my bootleg FireRed and LeafGreen have batteries, which they presumably use to save to volatile memory, in the same way old Game Boy and Game Boy Color games did, but the GBA games no longer do (they use flash memory).</p>
<p>Incidentally, the presence of a battery on an <i>Emerald</i> cartridge is not automatic evidence of bootleggery: legitimate Ruby, Sapphire, and Emerald cartridges all have batteries, which they use to keep time, because the GBA doesn&#8217;t. When those batteries run out, you don&#8217;t lose your save (unlike you would for GB and GBC games), but you&#8217;ll lose events that rely on the clock, like berry growing and tides in the Shoal Cave. It can be replaced with relatively little risk to your game.</p>
<p>For science, here&#8217;s the backs of those Emerald cartridges, in the same order as above:</p>
<p><center><a href="http://cairnarvon.rotahall.org/pics/pokemon-gba/emeralds_back_big.jpg"><img src="http://cairnarvon.rotahall.org/pics/pokemon-gba/emeralds_back.jpg" alt="backs" title="Backs" class="image" /></a></center></p>
<p>Note that despite differing labels and cartridge plastic, three of those are actually the same circuit boards. And to give the bootleggers credit, they actually did bother to print &#8220;Nintendo&#8221; on the board just above the connectors, which is a thing that&#8217;s often mentioned in guides about spotting bootlegs; it&#8217;s in the wrong font (which annoys me in its gratuitous indifference), but a pretty casual consumer might not notice.</p>
<p>My point, I suppose, is caveat emptor. And if you&#8217;re going to buy Pokémon GBA games second-hand, ignore the guides and just check if it gives you a startup message; that seems to be the easiest and only sure-fire way to know.<br />
But my secondary point is that you should mail me your copy of Emerald if you aren&#8217;t playing it anymore. After the first two bootlegs I told myself I&#8217;d keep buying them until I either got a genuine one or spent €50, and unless one of the three most recent scammers refunds my money, I&#8217;ve hit that limit.</p>
<p>Maybe I&#8217;ll try for Ruby and Sapphire instead.</p>
<p><b id="edit">Edit (June 11):</b> So I never did get those refunds, but I noticed the same person who sold me my genuine copies of FireRed and LeafGreen had a copy of Emerald. It was €34, but I decided to chance it.</p>
<p><center><a href="http://cairnarvon.rotahall.org/pics/pokemon-gba/real_big.jpg"><img src="http://cairnarvon.rotahall.org/pics/pokemon-gba/real.jpg" alt="REAL!" title="Holy fuck." class="image" /></a> <a href="http://cairnarvon.rotahall.org/pics/pokemon-gba/realback_big.jpg"><img src="http://cairnarvon.rotahall.org/pics/pokemon-gba/realback.jpg" alt="REAL!" title="Backside" class="image" /></a></center></p>
<p><i>Finally.</i><br />
Note how the connectors are visible from the back of the cartridge. The label is a bit worn, but there&#8217;s a holo type effect that isn&#8217;t very visible in pictures. And, importantly, there&#8217;s no message when you start the game.</p>
<p>It took €84 and six months, but I finally own a genuine copy of Pokémon Emerald.</p>
<hr />
<p class="footnote"><sup><a href="#adventures-on-the-grey-market_0" id="f_adventures-on-the-grey-market_0">0</a></sup> Both of those genuine cartridges came from the same seller. <del>Unfortunately, she doesn&#8217;t have Emerald.</del> <ins>See edit!</ins></p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2012/04/07/adventures-on-the-grey-market/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>2011 in books</title>
		<link>http://cairnarvon.rotahall.org/2012/02/15/2011-in-books/</link>
		<comments>http://cairnarvon.rotahall.org/2012/02/15/2011-in-books/#comments</comments>
		<pubDate>Wed, 15 Feb 2012 15:23:20 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Books]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=2124</guid>
		<description><![CDATA[Fine, let&#8217;s do this. Last year&#8217;s entry is here, though both it and that of the year before are still on the front page, too. Apparently I finished 64 books in 2011. Specifically, these: The Selected Works of T.S. Spivet Reif Larsen The Stuff of Thought Steven Pinker Hard-boiled Wonderland and the End of the [...]]]></description>
				<content:encoded><![CDATA[<p>Fine, let&#8217;s do this. Last year&#8217;s entry is <a href="http://cairnarvon.rotahall.org/2010/12/31/2010-in-books/">here</a>, though both it and that of the year before are still on the front page, too.<br />
Apparently I finished 64 books in 2011. Specifically, these:</p>
<p><span id="more-2124"></span></p>
<ul>
<b>The Selected Works of T.S. Spivet</b> Reif Larsen<br />
<b>The Stuff of Thought</b> Steven Pinker<br />
<b>Hard-boiled Wonderland and the End of the World</b> Haruki Murakami<br />
<b>The Humans Who Went Extinct</b> Clive Finlayson<br />
<b>Chronic City</b> Jonathan Lethem<br />
<b>Why Beauty Is Truth</b> Ian Stewart<br />
<b>The Remains of the Day</b> Kazuo Ishiguro<br />
<b>Norwegian Wood</b> Haruki Murakami<br />
<b>Letters to a Young Mathematician</b> Ian Stewart<br />
<b>The Jungle Books</b> Rudyard Kipling<br />
<b>Die Verwandlung</b> Franz Kafka<br />
<b>South of the Border, West of the Sun</b> Haruki Murakami<br />
<b>The Brain that Changes Itself</b> Norman Doidge<br />
<b>Special Topics in Calamity Physics</b> Marisha Pessl<br />
<b>A Wild Sheep Chase</b> Haruki Murakami<br />
<b>Kafka on the Shore</b> Haruki Murakami<br />
<b>Oblivion: Stories</b> David Foster Wallace<br />
<b>Sputnik Sweetheart</b> Haruki Murakami<br />
<b>The Adventures of Huckleberry Finn</b> Mark Twain<br />
<b>1089 and All That: a Journey Into Mathematics</b> David Acheson<br />
<b>Bad Science</b> Ben Goldacre<br />
<b>The Lightness of Being</b> Frank Wilczek<br />
<b>Dance Dance Dance</b> Haruki Murakami<br />
<b>Birthday Stories</b> Haruki Murakami (ed.)<br />
<b>Lights Out in Wonderland</b> Peter &#8220;DBC Pierre&#8221; Finlay<br />
<b>The Great Gatsby</b> F. Scott Fitzgerald<br />
<b>Underground</b> Haruki Murakami<br />
<b>Artificial Intelligence: A Modern Approach</b> Stuart Russell, Peter Norvig<br />
<b>McSweeney&#8217;s Thirty-Two</b> Dave Eggers (ed.)<br />
<b>The Corrections</b> Jonathan Franzen<br />
<b>Content and Consciousness</b> Daniel Dennett<br />
<b>De Eeuwige Terugkeer van het Fascisme</b> Rob Riemen<br />
<b>The Ascent of Man</b> Jacob Bronowski<br />
<b>after the quake</b> Haruki Murakami<br />
<b>The Bottom Billion</b> Paul Collier<br />
<b>Haroun and the Sea of Stories</b> Salman Rushdie<br />
<b>The Crucible</b> Arthur Miller<br />
<b>Remarkable Creatures</b> Sean B. Carroll<br />
<b>Flaubert&#8217;s Parrot</b> Julian Barnes<br />
<b>What I Talk About When I Talk About Running</b> Haruki Murakami<br />
<b>Foundation</b> Isaac Asimov<br />
<b>Blind Willow, Sleeping Woman</b> Haruki Murakami<br />
<b>After Dark</b> Haruki Murakami<br />
<b>Taming the Infinite</b> Ian Stewart<br />
<b>The Grand Design</b> Stephen Hawking, Leonard Mlodinow<br />
<b>Schaum&#8217;s Outlines: Logic</b> John Nolt, Dennis Rohatyn, Achille Varzi<br />
<b>Hitch-22</b> Christopher Hitchens<br />
<b>I Shall Wear Midnight</b> Terry Pratchett<br />
<b>The Discomfort Zone</b> Jonathan Franzen<br />
<b>Alex&#8217;s Adventures in Numberland</b> Alex Bellos<br />
<b>The Magic of Reality</b> Richard Dawkins<br />
<b>1000 Years of Annoying the French</b> Stephen Clarke<br />
<b>Mathematics Without Fear</b> Lawrence Potter<br />
<b>Mathematics of Life</b> Ian Stewart<br />
<b>Ubik</b> Philip K. Dick<br />
<b>Naked Lunch</b> William S. Burroughs<br />
<b>Go the Fuck to Sleep</b> Adam Mansbach<br />
<b>Chaos</b> James Gleick<br />
<b>Fury</b> Salman Rushdie<br />
<b>I Am Number Four</b> James Frey, Jobie Hughes<br />
<b>Warped Passages</b> Lisa Randall<br />
<b>The Emperor of All Maladies</b> Siddhartha Mukherjee<br />
<b>The Beetle</b> Richard Marsh<br />
<b>The Rational Optimist</b> Matt Ridley
</ul>
<p>Would&#8217;ve been more, except I bought a DS in July and Pokémon has interfered with my reading. My goal for the year was 50, as it always is, so I did at least make that.</p>
<p>I also said 2011 was going to be the year in which I read everything Haruki Murakami has ever written (that I hadn&#8217;t read the year before), and while I didn&#8217;t do that, I got a lot closer than I did for Margaret Atwood in 2010; I read everything he&#8217;s ever written that&#8217;s been translated to English and released outside of Japan (<i>Hear the Wind Sing</i> and <i>Pinball, 1973</i> have been translated into English, but those translations were never released outside of Japan), except for <i>1Q84</i>, which I did buy and start in, but didn&#8217;t manage to finish.<br />
I can&#8217;t recommend you try to do this. Any individual book of Murakami&#8217;s is pretty good to great, but reading all of them that quickly will make you dislike either the books or Murakami himself. His non-fiction is merely boring for the most part, but the shared aspects of his fiction show him to be an unpleasant, frustrated little man.</p>
<p>I&#8217;ve written reviews of nearly everything I&#8217;ve read, so if you&#8217;re looking for those I suggest you <a href="http://www.goodreads.com/review/list/3452722-koen-crolla?order=a&#038;read_at=2011&#038;sort=date_read">go to GoodReads</a>.<br />
Since I did this last year, I might as well do it now:</p>
<h3>Better than expected</h3>
<p><i><a href="http://www.goodreads.com/review/show/224367157">The Emperor of All Maladies</a></i>, by Siddhartha Mukherjee, was surprisingly good. It&#8217;s about cancer and the history of cancer research, and it&#8217;s pretty US-centric but otherwise very interesting. Since everyone will either get cancer themselves or be close to someone who will, there&#8217;s really nobody who can afford not to read it.</p>
<p><i><a href="http://www.goodreads.com/review/show/225907450">Warped Passages</a></i> by Lisa Randall is possibly the best pop-sci treatment of modern physics I&#8217;ve seen, despite Lisa Randall being a pretty unpleasant person. As I said in my review, it&#8217;s possibly the first book in its genre that I&#8217;ve read that didn&#8217;t leave me feeling like modern physics is just too complicated and esoteric now to be viable subject matter for pop sci.</p>
<p><i><a href="http://www.goodreads.com/review/show/238246009">The Beetle</a></i> by Richard Marsh is the only fiction entry here; a decade or so ago my expectations of it would have been spot on, but years of Lovecraft and Stoker and (even worse) their imitators have destroyed my expectations of classic horror and left me cynical.</p>
<h3>Worse than expected</h3>
<p>Matt Ridley&#8217;s <i><a href="http://www.goodreads.com/review/show/238246760">The Rational Optimist</a></i>. This is the same Ridley who wrote <i>The Red Queen</i>, which I gave five stars in 2008. Apparently he&#8217;s like Penn &#038; Teller in that he&#8217;s only capable of intelligent discourse on subjects on which his political masters have no opinion; for P&#038;T this is the Cato Institute, for Ridley it&#8217;s the British Tory party.</p>
<p>Oh, and <i><a href="http://www.goodreads.com/review/show/166954308">Hitch-22</a></i>, by Christopher Hitchens, I guess. I never really cared for him and my expectations weren&#8217;t <i>too</i> high, but he still managed to be worse than I imagined. I&#8217;m not even talking about the casual misogyny everyone has come to expect of him by now; I&#8217;m talking about how every position Hitchens has ever held he has only held because his father or his brother have held the opposite. He sure wasted shit-tons of paper on rationalisations trying to convince himself otherwise, though.</p>
<p>Like last year, there are also a lot of books that were shit that I expected to be, and books that were good that I expected to be. GoodReads lets you sort by rating, if you&#8217;re interested.<br />
Either way, discuss.</p>
<p><b>tl;dr:</b> <a href="http://www.goodreads.com/review/list/3452722-koen-crolla?read_at=2011&#038;sort=date_read&#038;order=a">GoodReads</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2012/02/15/2011-in-books/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>So I got a switch for Christmas&#8230;</title>
		<link>http://cairnarvon.rotahall.org/2012/02/09/so-i-got-a-switch-for-christmas/</link>
		<comments>http://cairnarvon.rotahall.org/2012/02/09/so-i-got-a-switch-for-christmas/#comments</comments>
		<pubDate>Thu, 09 Feb 2012 22:33:11 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=2116</guid>
		<description><![CDATA[&#8230; and things got a bit out of hand. Building a LackRack is some sort of rite of passage for sysadmins, I think, and I am, after all a sys- and network admin first and a programmer second. Initially the idea was just to have the switch, and the second table was just to make [...]]]></description>
				<content:encoded><![CDATA[<p>&#8230; and things got a bit out of hand.</p>
<p><center><a href="/pics/lackrack/lackrack_front.jpg"><img src="/pics/lackrack/small/lackrack_front.jpg" alt="LackRack" class="image" /></a></center></p>
<p><span id="more-2116"></span></p>
<p>Building a <a href="http://lackrack.org/">LackRack</a> is some sort of rite of passage for sysadmins, I think, and I am, after all a sys- and network admin first and a programmer second. Initially the idea was just to have the switch, and the second table was just to make it look like a respectable piece of furniture instead of a €5 Ikea table (<a href="http://twitpic.com/839ntk">as per this picture I posted a month ago</a>), but then I figured a power strip would be useful, and in the process of locating a Belgian supplier I found <a href="http://flightcase-brico.be/index.php?categoryID=26">more things I wanted</a>.<br />
I did add the power strip; I added two, in fact. Here&#8217;s the front one:</p>
<p><center><a href="/pics/lackrack/power_front.jpg"><img src="/pics/lackrack/small/power_front.jpg" alt="front power strip" class="image" /></a></center></p>
<p>It&#8217;s sunken not to protect plugs or even to help support the 4U drawer above it (which needs the support; it&#8217;s solid steel and weighs 17 kg), but because the power cable is to the side and there was no other way to mount it. Still, it was the only power strip available, and we make do.<br />
(Notice that it also has Schuko sockets rather than the more sensible CEE 7/5. Fortunately everything I wanted to use it for has either CEE 7/7 plugs or Europlugs.)</p>
<p>Here&#8217;s the view from the back, showing the second power strip:</p>
<p><center><a href="/pics/lackrack/lackrack_back.jpg"><img src="/pics/lackrack/small/lackrack_back.jpg" alt="LackRack back" class="image" /></a></center></p>
<p>Note the connector plates with which I <i>expertly</i> affixed the top table to the bottom. There&#8217;s four such plates because that&#8217;s all I could find in our basement. On the front legs they&#8217;re on the side, for looks.<br />
The dorsal power strip faces inward, because there&#8217;s no reason for it not to. It holds everything that needs to have power 24/7, including the front power strip, the switch, my electric toothbrush, and the laptop on the shelf under the switch.<br />
The frontal power strip is meant to be switched off at night, because I have a lot of things that have too many bright lights to keep them on in my bedroom, including my Wii, the inductive charger for the Wii remote, and the Roomba, for which there&#8217;s just enough space underneath.</p>
<p><center><a href="/pics/lackrack/power_back.jpg"><img src="/pics/lackrack/small/power_back.jpg" alt="back power strip" class="image" /></a></center></p>
<p>That&#8217;s the switch&#8217;s power cable, plugged into the dorsal strip. The duct tape shows it&#8217;s professional.<br />
Here&#8217;s the whole thing from the side:</p>
<p><center><a href="/pics/lackrack/lackrack_side.jpg"><img src="/pics/lackrack/small/lackrack_side.jpg" alt="side view" class="image" /></a></center></p>
<p>Ignore the dust and the fingerprints.<br />
The shelf doesn&#8217;t look too stable, which may be a problem. The table legs are hollow at the point where it&#8217;s attached, and it&#8217;s not particularly light-weight, being entirely steelen as well. Still, it seems to be holding. I&#8217;ll add some sort of reinforcing bracket if I find one.<br />
It slides out, too!</p>
<p><center><a href="/pics/lackrack/shelf.jpg"><img src="/pics/lackrack/small/shelf.jpg" alt="shelf!" class="image" /></a></center></p>
<p>And here&#8217;s what the whole thing looks like when it&#8217;s running:</p>
<p><center><a href="/pics/lackrack/running.jpg"><img src="/pics/lackrack/small/running.jpg" alt="on" class="image" /></a></center></p>
<p>Again, ignore the dust and general grime. My room needs cleaning and will get it tomorrow. Also ignore the quality of the connector attachment on the leftmost cable. I didn&#8217;t do it, though I will redo it, also tomorrow.<br />
The point of the laptop is to have a shell server (as I&#8217;m apparently graduating this year, which means I won&#8217;t have access to the one I&#8217;ve been using for the past half-decade anymore) and, as soon as the new hard drive arrives, a backup server. I was initially going to get a proper rack-mounted server instead, but my old laptop still works, so why not use it?</p>
<p>The total cost of this is €568.81 (€289.90 for the switch, €45.98 for the cable and connectors (admittedly I have 98 meters and 44 connectors left, which is what the drawer is for), €222.95 for the shelf, drawer, and power strips (including automatic discount and shipping; LackRack or not, 19-inch equipment isn&#8217;t cheap), and €9.98 for the tables), plus whatever the old laptop and the handful of screws I found in the basement cost. I&#8217;d say it&#8217;s worth it.</p>
<p>Haskell is less impressed.</p>
<p><center><a href="/pics/lackrack/hasks.jpg"><img src="/pics/lackrack/small/hasks.jpg" alt="gog" class="image" /></a></center></p>
<p>(If you&#8217;re in Belgium and want to do something like this, I recommend both <a href="http://www.tones.be/">Tones</a>, where I got the switch, the cable, and the connectors (not to mention my Thinkpad and my mom&#8217;s inexplicable iPad), and <a href="http://flightcase-brico.be/">Flightcase-brico.be</a>, where I got the other things. The former has free shipping for orders over €200 and the latter is much faster than you&#8217;d expect. Be aware that the latter lists prices without BTW, and BTW is 21% on everything. You don&#8217;t need a credit card for either one.)</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2012/02/09/so-i-got-a-switch-for-christmas/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ClusterDebian 1.0</title>
		<link>http://cairnarvon.rotahall.org/2011/05/28/clusterdebian-1-0/</link>
		<comments>http://cairnarvon.rotahall.org/2011/05/28/clusterdebian-1-0/#comments</comments>
		<pubDate>Sat, 28 May 2011 00:23:53 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Miscellany]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=2036</guid>
		<description><![CDATA[So that thing I hinted at earlier is about as done as I care to get it. Officially the point of this project was for the school to have something with which to replace ClusterKnoppix for their Besturingssystemen II class, but really I just wanted to have something nicer to make use of my ever-growing [...]]]></description>
				<content:encoded><![CDATA[<p>So <a href="http://cairnarvon.rotahall.org/2011/05/01/so/">that thing</a> I hinted at earlier is <a href="https://github.com/Cairnarvon/ClusterDebian">about as done as I care to get it.</a></p>
<p><center><a href="/pics/clusterdebian.png"><img src="/pics/clusterdebian_small.png" class="image" alt="ClusterDebian" /></a></center></p>
<p>Officially the point of this project was for the school to have something with which to replace ClusterKnoppix for their Besturingssystemen II class, but really I just wanted to have something nicer to make use of my ever-growing pile of old computers, which is why I finished it. The README explains. (HPC there stands for &#8220;high-performance computing&#8221; rather than &#8220;Hasty Pudding cipher&#8221;.)<br />
What I need now is people to test it, ideally by building images and then trying them.</p>
<p>If you&#8217;d rather not put that kind of effort in, I&#8217;ve also <a href="/misc/clusterdebian.iso">pre-built an image</a> (291 MB). It doesn&#8217;t come with X to save space, but it does come with (what else?) <a href="https://gist.github.com/994114">this Open MPI tripcode finder</a> I wrote a while ago. It&#8217;s not particularly fast, but it reports its progress if you poke it with SIGUSR1 (as in <code>pkill -USR1 tripfind</code>).<br />
The unprivileged user is called <code>gjs</code>, and the password for both him and root is <code>t</code>. I&#8217;ve also included the <a href="http://www.bindshell.net/tools/johntheripper.html">MPI-patched JtR</a> tarball in the home directory for you to build, if that&#8217;s less pointless.</p>
<p>Feedback appreciated, even if you don&#8217;t find any problems. If you find this project useful at all, or if you have any suggestions, I&#8217;d like to hear about it.</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2011/05/28/clusterdebian-1-0/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Automatic language classification, the slow way</title>
		<link>http://cairnarvon.rotahall.org/2011/05/10/automatic-language-classification/</link>
		<comments>http://cairnarvon.rotahall.org/2011/05/10/automatic-language-classification/#comments</comments>
		<pubDate>Mon, 09 May 2011 22:18:23 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[CompSci]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=1977</guid>
		<description><![CDATA[#!/usr/bin/python2 import sys import bz2 def classify(text, langs=('english', 'german', 'french')): results = {} for lang in langs: with open(lang + '.txt') as f: corpus = f.read() compressed = len(bz2.compress(corpus)) results[lang] = len(bz2.compress(corpus + text)) - compressed return sorted(results, key=results.__getitem__) if __name__ == '__main__': print "Most likely %s." % classify(sys.stdin.read())[0].capitalize() $ wget -qO - http://www.gutenberg.org/ebooks/31469.txt.utf8 &#124; [...]]]></description>
				<content:encoded><![CDATA[<pre style="font-size: inherit"><span style="color: #999988"><i>#!/usr/bin/python2</i></span>

<b>import</b> <span style="color: #555555">sys</span>
<b>import</b> <span style="color: #555555">bz2</span>

<b>def</b> <span style="color: #990000"><b>classify</b></span>(text, langs<b>=</b>(<span style="color: #bb8844">'<a href="/misc/corpora/english.txt">english</a>'</span>, <span style="color: #bb8844">'<a href="/misc/corpora/german.txt">german</a>'</span>, <span style="color: #bb8844">'<a href="/misc/corpora/french.txt">french</a>'</span>)):
    results <b>=</b> {}
    <b>for</b> lang <b>in</b> langs:
        <b>with</b> <span style="color: #999999">open</span>(lang <b>+</b> <span style="color: #bb8844">'.txt'</span>) <b>as</b> f:
            corpus <b>=</b> f<b>.</b>read()

        compressed <b>=</b> <span style="color: #999999">len</span>(bz2<b>.</b>compress(corpus))
        results[lang] <b>=</b> <span style="color: #999999">len</span>(bz2<b>.</b>compress(corpus <b>+</b> text)) <b>-</b> compressed

    <b>return</b> <span style="color: #999999">sorted</span>(results, key<b>=</b>results<b>.</b>__getitem__)

<b>if</b> __name__ <b>==</b> <span style="color: #bb8844">'__main__'</span>:
    <b>print</b> <span style="color: #bb8844">"Most likely </span><span style="color: #bb8844">%s</span><span style="color: #bb8844">."</span> <b>%</b> classify(sys<b>.</b>stdin<b>.</b>read())[<span style="color: #009999">0</span>]<b>.</b>capitalize()</pre>
<hr />
<p><code>$ wget -qO - http://www.gutenberg.org/ebooks/31469.txt.utf8 | ./classific.py<br />
Most likely English.<br />
$ wget -qO - http://www.gutenberg.org/ebooks/22367.txt.utf8 | ./classific.py<br />
Most likely German.<br />
$ wget -qO - http://www.gutenberg.org/ebooks/4968.txt.utf8 | ./classific.py<br />
Most likely French.</code></p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2011/05/10/automatic-language-classification/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>So.</title>
		<link>http://cairnarvon.rotahall.org/2011/05/01/so/</link>
		<comments>http://cairnarvon.rotahall.org/2011/05/01/so/#comments</comments>
		<pubDate>Sun, 01 May 2011 18:53:01 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Miscellany]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=1971</guid>
		<description><![CDATA[If you were a USB/PXE-bootable Linux distro for HPC, what kind of features would you want to have?]]></description>
				<content:encoded><![CDATA[<p>If you were a USB/PXE-bootable Linux distro for HPC, what kind of features would you want to have?</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2011/05/01/so/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>2010 in books</title>
		<link>http://cairnarvon.rotahall.org/2010/12/31/2010-in-books/</link>
		<comments>http://cairnarvon.rotahall.org/2010/12/31/2010-in-books/#comments</comments>
		<pubDate>Fri, 31 Dec 2010 18:34:48 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[Books]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=1883</guid>
		<description><![CDATA[Let&#8217;s do this while I&#8217;m still sort of sober. Looks like I didn&#8217;t blog a lot this year. Last year&#8217;s book post is still on the front page. Anyway, I didn&#8217;t finish quite as many books this year as I did then, but I surpassed my annual target of fifty; I finished fifty-eight: On the [...]]]></description>
				<content:encoded><![CDATA[<p>Let&#8217;s do this while I&#8217;m still sort of sober.<br />
Looks like I didn&#8217;t blog a lot this year. Last year&#8217;s book post is still on the front page. Anyway, I didn&#8217;t finish quite as many books this year as I did <a href="/2009/12/31/overshot-a-bit/">then</a>, but I surpassed my annual target of fifty; I finished fifty-eight:</p>
<p><span id="more-1883"></span></p>
<ul>
<b>On the Road</b> Jack Kerouac<br />
<b>The Robber Bride</b> Margaret Atwood<br />
<b>Does God Play Dice?</b> Ian Stewart<br />
<b>The Pickwick Papers</b> Charles Dickens<br />
<b>Prolog Programming for Articial Intelligence</b> Ivan Bratko<br />
<b>Eating Animals</b> Jonathan Safran Foer<br />
<b>Alias Grace</b> Margaret Atwood<br />
<b>A Strange Manuscript Found in a Copper Cylinder</b> James De Mille<br />
<b>The People&#8217;s Train</b> Thomas Keneally<br />
<b>Le tour du monde en 80 jours</b> Jules Verne<br />
<b>Wuthering Heights</b> Emily Brontë<br />
<b>You Are a Mathematician</b> David Wells<br />
<b>Why Evolution is True</b> Jerry Coyne<br />
<b>The Adventures of Sherlock Holmes</b> Arthur Conan Doyle<br />
<b>Cinq semaines en ballon</b> Jules Verne<br />
<b>The Good Man Jesus and the Scoundrel Christ</b> Philip Pullman<br />
<b>The Blind Assassin</b> Margaret Atwood<br />
<b>Northanger Abbey</b> Jane Austen<br />
<b>A Clockwork Orange</b> Anthony Burgess<br />
<b>And Another Thing&#8230;</b> Eoin Colfer<br />
<b>The Graveyard Book</b> Neil Gaiman<br />
<b>Cloud Atlas</b> David Mitchell<br />
<b>River Out of Eden</b> Richard Dawkins<br />
<b>The City Curious</b> Jean de Bosschère<br />
<b>Discrete Mathematics</b> Seymour Lipschutz, Marc Lipson<br />
<b>Byzantium</b> Judith Herrin<br />
<b>Oryx and Crake</b> Margaret Atwood<br />
<b>Hard Times</b> Charles Dickens<br />
<b>Cradle to Cradle</b> Michael Braungart, William McDonough<br />
<b>The Penguin Dictionary of Curious and Interesting Numbers</b> David Wells<br />
<b>Surfacing</b> Margaret Atwood<br />
<b>Blood Meridian</b> Cormac McCarthy<br />
<b>The Book Thief</b> Markus Zusak<br />
<b>Little Women</b> Louisa M. Alcott<br />
<b>Year of the Flood</b> Margaret Atwood<br />
<b>Unseen Academicals</b> Terry Pratchett<br />
<b>The Adventures of Tom Sawyer</b> Mark Twain<br />
<b>Moral Disorder</b> Margaret Atwood<br />
<b>Bodily Harm</b> Margaret Atwood<br />
<b>Cows in the Maze</b> Ian Stewart<br />
<b>The Lottery and Other Stories</b> Shirley Jackson<br />
<b>Brave New World Revisited</b> Aldous Huxley<br />
<b>Who Moved My Cheese?</b> Spencer Johnson<br />
<b>Reading Lolita in Tehran</b> Azar Nafisi<br />
<b>Writing Scientific Software</b> Suely Oliveira, David Stewart<br />
<b>Science: a Four Thousand Year History</b> Patricia Fara<br />
<b>Professor Stewart&#8217;s Hoard of Mathematical Treasures</b> Ian Stewart<br />
<b>My Man Jeeves</b> P.G. Wodehouse<br />
<b>Linear Algebra</b> Seymour Lipschutz, Marc Lipson<br />
<b>The Bro Code</b> Matt Kuhn<br />
<b>Cat&#8217;s Eye</b> Margaret Atwood<br />
<b>The Black Cloud</b> Fred Hoyle<br />
<b>The Fry Chronicles</b> Stephen Fry<br />
<b>The Wind-Up Bird Chronicle</b> Haruki Murakami<br />
<b>Fight Club</b> Charles Palahniuk<br />
<b>The Language Instinct</b> Steven Pinker<br />
<b>The Elephant Vanishes</b> Haruki Murakami<br />
<b>Two Treatises of Government and a Letter Concerning Toleration</b> John Locke
</ul>
<p>I said that 2010 was going to be the year in which I read everything Margaret Atwood has ever written, but I didn&#8217;t manage that. I read everything she wrote that was available in Leuven&#8217;s bookstores, but she&#8217;s just written too damn much. Maybe I&#8217;ll round off the remainder next year, but I think I&#8217;m going to focus on Haruki Murakami instead; while his books haven&#8217;t changed my mind about reading in translation, the two I&#8217;ve read have been good enough to justify it.<br />
As before, I&#8217;ve written individual reviews of nearly everything I read this year, but here&#8217;s a round-up anyway:</p>
<h3>Better than expected</h3>
<p>As said, Murakami. Despite its fanbase, <a href="http://www.goodreads.com/review/show/126429540"><i>The Wind-Up Bird Chronicle</i></a> is legitimately good, and the short stories in <a href="http://www.goodreads.com/review/show/136530677"><i>The Elephant Varnishes</i></a> were excellent as well.<br />
<a href="http://www.goodreads.com/review/show/95186140"><i>Why Evolution is True</i></a> by Jerry Coyne was also surprisingly good. I didn&#8217;t expect it to be bad, but I also didn&#8217;t expect it to be better than, say, Dawkins&#8217; own <i>The Greatest Show on Earth</i>, which set out to do much the same thing as WEIT; but it is.<br />
Judith Herrin&#8217;s <a href="http://www.goodreads.com/review/show/102245599"><i>Byzantium</i></a> was nice as well, and a good introduction to what is often a blind spot in world history. I picked it up on impulse, because it was there rather than because I&#8217;d heard of it, and that usually doesn&#8217;t turn out well. It did this time.<br />
Also better than expected was Fred Hoyle&#8217;s <a href="http://www.goodreads.com/review/show/131504540"><i>The Black Cloud</i></a>, but my expectations of that were very low because I know what kind of a person Fred Hoyle was, and my opinion of science fiction is justifiably low to begin with.<br />
And finally, Jane Austen&#8217;s <a href="http://www.goodreads.com/review/show/99831228"><i>Northanger Abbey</i></a>. I wouldn&#8217;t necessarily call it excellent in its own right, but it&#8217;s a lot better than Austen usually is.</p>
<h3>Worse than expected</h3>
<p>Probably just Patricia Fara&#8217;s <a href="http://www.goodreads.com/review/show/124868312"><i>Science: a Four Thousand Year History</i></a>. I only expected it to be good because of the pleasing heaviness of the paper and the quality of the print and the illustrations, but it turned out to be Mary Midgley&#8217;s strain of science- and scientist-bashing.<br />
It&#8217;s by no means the only terrible book I read this year, but my expectations are very low in general. Maybe there should be another category.</p>
<h3>Offensively but expectedly terrible</h3>
<p><a href="http://www.goodreads.com/review/show/94562938"><i>On the Road</i></a>, Jack Kerouac; <a href="http://www.goodreads.com/review/show/99149405"><i>And Another Thing&#8230;</i></a>, Eoin Colfer; <a href="http://www.goodreads.com/review/show/94562934"><i>Cloud Atlas</i></a>, David Mitchell; <a href="http://www.goodreads.com/review/show/102245556"><i>Blood Meridian</i></a>, Cormac McCarthy; <a href="http://www.goodreads.com/review/show/125016289"><i>Who Moved My Cheese?</i></a>, Spencer Johnson; <a href="http://www.goodreads.com/review/show/135453278"><i>Fight Club</i></a>, Charles Palahniuk; <a href="http://www.goodreads.com/review/show/136530562"><i>The Language Instinct</i></a>, Steven Pinker.</p>
<p>Everything else was either &#8220;merely&#8221; and expectedly bad (Dickens, Burgess, &#038;c.), expectedly alright (Atwood, Dawkins, &#038;c.), or just pretty unremarkable (Keneally, Verne, &#038;c.). More reviews at <a href="http://www.goodreads.com/Cairnarvon">GoodReads</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2010/12/31/2010-in-books/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Processing constraints is easy</title>
		<link>http://cairnarvon.rotahall.org/2010/06/20/processing-constraints-is-easy/</link>
		<comments>http://cairnarvon.rotahall.org/2010/06/20/processing-constraints-is-easy/#comments</comments>
		<pubDate>Sun, 20 Jun 2010 04:39:44 +0000</pubDate>
		<dc:creator>Cairnarvon</dc:creator>
				<category><![CDATA[CompSci]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://cairnarvon.rotahall.org/?p=1816</guid>
		<description><![CDATA[Alright, we&#8217;ve covered search trees in some detail, and they work great for problems where we have clear states and rules of production to move from one state to the next. Sometimes that&#8217;s not a very convenient way to state a problem, though, and a more natural way to think about things is as a [...]]]></description>
				<content:encoded><![CDATA[<p>Alright, we&#8217;ve covered search trees in some detail, and they work great for problems where we have clear states and rules of production to move from one state to the next. Sometimes that&#8217;s not a very convenient way to state a problem, though, and a more natural way to think about things is as a bunch of variables which can take values in a certain <b>domain</b>, and a number of <b>constraints</b> which describe the relationships of these variables to each other.<br />
The canonical example here is Dijkstra&#8217;s <b>eight queens problem</b>. However, that&#8217;s been done to death, so let&#8217;s instead have two queens and seven knights, and instead of the usual 8×8 chess board, let&#8217;s have a 6×6 one.</p>
<p><center><img src="/pics/notqueens.png" alt="not queens problem" class="image" /></center></p>
<p><span id="more-1816"></span></p>
<p>In case you aren&#8217;t familiar with the problem, the idea is to place all the pieces on the board in such a way that none of them threatens any other.<br />
Clearly we have nine variables (two queens and seven knights), and the domain for each of them is any of the thirty-six positions on the board, represented as a tuple of numbers to indicate the row and column. In Python, we could represent this like this:</p>
<pre>&gt; domains = {&#39;Q1&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;Q2&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K1&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K2&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K3&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K4&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K5&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K6&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;),
&gt;            &#39;K7&#39;: set(&#91;(a, b) for a in range(6) for b in range(6)&#93;)}
&gt; 
&gt; pieces = set(domains.keys())
&gt; queens = set(&#91;&#39;Q1&#39;, &#39;Q2&#39;&#93;)
&gt; knights = pieces - queens</pre>
<p>(Using <code>set</code>s not (primarily) because I&#8217;m worried there would be duplicate entries there, but because Python&#8217;s <code>set</code>s have some useful operations defined on them that <code>list</code>s don&#8217;t, as will become clear.)</p>
<p>Representing the constraints may be a bit trickier. We&#8217;ll only be working with <b>arc constraints</b> (that is, constraints that apply to exactly two variables), because that makes sense, and it turns out that higher-order constraints aren&#8217;t much more useful.<sup><a href="#f_processing-constraints-is-easy_0" id="processing-constraints-is-easy_0">0</a></sup> Should you have any single-variable constraints, those can be applied directly to reduce the domains in advance, obviously.<br />
There are a lot of ways to represent this, but I&#8217;m going to go with a class that stores the two variables the constraint applies to, plus a constraint function, which operates on two values from the domains of the variables and tells you whether the constraint is satisfied or not. The class also has a <code>valid</code> method, which just applies the constraint function.</p>
<pre>&gt; class Constraint():
&gt;     def __init__(self, p1, p2, constr_fun):
&gt;         self.p1, self.p2, self.constr_fun = p1, p2, constr_fun
&gt; 
&gt;     def valid(self, a, b):
&gt;         return self.constr_fun(a, b)</pre>
<p>Now for the constraint functions themselves. Remember, these functions take two values from two domains, and return <code>False</code> if the first piece threatens the second (because we&#8217;re looking for situations where that doesn&#8217;t happen), and <code>True</code> otherwise.<br />
A queen threatens everything on her row, column and diagonals, so her function looks like this:</p>
<pre>&gt; def queen_cf(queen, other):
&gt;     return abs(queen&#91;0&#93; - other&#91;0&#93;) != abs(queen&#91;1&#93; - other&#91;1&#93;) &#92;
&gt;        and queen&#91;0&#93; != other&#91;0&#93; &#92;
&gt;        and queen&#91;1&#93; != other&#91;1&#93;</pre>
<p>The knight threatens everything two moves in the horizontal or vertical direction plus one in the orthogonal, so its function looks like this:</p>
<pre>&gt; def knight_cf(knight, other):
&gt;     if other&#91;0&#93; in (knight&#91;0&#93; - 2, knight&#91;0&#93; + 2):
&gt;         return other&#91;1&#93; not in (knight&#91;1&#93; - 1, knight&#91;1&#93; + 1)
&gt; 
&gt;     if other&#91;1&#93; in (knight&#91;1&#93; - 2, knight&#91;1&#93; + 2):
&gt;         return other&#91;0&#93; not in (knight&#91;0&#93; - 1, knight&#91;0&#93; + 1)
&gt; 
&gt;     return True</pre>
<p>(Does it matter that it ignores the limits of the board? Not really: there will never be a piece outside of the board, but it does no harm to look. Explicitly checking board limits isn&#8217;t going to save any time.)</p>
<p>Finally, an optional constraint. Consider the following two solutions to our problem:</p>
<p><center><img src="/pics/constr1.png" class="image" /> <img src="/pics/constr2.png" class="image" /></center></p>
<p>These are equivalent in every way that matters, of course, but will be returned as different solutions by our algorithm as written. You might think this isn&#8217;t a big deal, but there are 2! × 7! identical variants of every possible solution, and there are (it turns out) twenty-two legitimate solutions; paring down 221,760 solutions to find the twenty-two unique ones would be a challenge in its own right.<br />
So let&#8217;s enforce the constraint that the position of <code>Q1</code> must be strictly less than that of <code>Q2</code>, for some unambiguous ordering of the board&#8217;s squares, and the same for any knights compared to knights with a higher index. This will ensure that each solution found is genuinely unique. Fortunately, Python makes this easy.</p>
<pre>&gt; def index_cf(low, high):
&gt;     return low &#60; high</pre>
<p>(Notice that this also implies that no two pieces can ever occupy the same square; otherwise we&#8217;d have to introduce another constraint for that, or modify our knight function (the queen function already implies it).)</p>
<p>Building our list of constraints is then relatively straightforward.</p>
<pre>&gt; constraints = &#91;Constraint(q, p, queen_cf) for q in queens &#92;
&gt;                                           for p in pieces - set(&#91;q&#93;)&#93; &#92;
&gt;             + &#91;Constraint(k, p, knight_cf) for k in knights &#92;
&gt;                                            for p in pieces - set(&#91;k&#93;)&#93; &#92;
&gt;             + &#91;Constraint(&#34;Q1&#34;, &#34;Q2&#34;, index_cf)&#93; &#92;
&gt;             + &#91;Constraint(a, b, index_cf) for a in knights &#92;
&gt;                                           for b in filter(lambda n: n &#62; a,
&gt;                                                           knights)&#93;</pre>
<p>And hey, that last constraint function means we can pare down our domains already: <code>Q1</code> will never be in the highest square, because if there&#8217;s a queen there, it will be <code>Q2</code>. Similarly, <code>Q2</code> will never be in the lowest, and any given knight will never occupy six squares at the ends of the board. By pruning our domains now, we&#8217;ll reduce our potential search space from 36<sup>9</sup> to 35<sup>2</sup> × 30<sup>7</sup>, which is a savings of over 76 <i>trillion</i>.<sup><a href="#f_processing-constraints-is-easy_1" id="processing-constraints-is-easy_1">1</a></sup><br />
Since each constraint has the potential to prune the domains of the variables it affects, it makes sense to do this as a method of the <code>Constraint</code> class.</p>
<pre>&gt;     def prune_domains(self):
&gt;         p1_domain, p2_domain = domains&#91;self.p1&#93;, domains&#91;self.p2&#93;
&gt;         p1_new_domain, p2_new_domain = set(), set()
&gt;         
&gt;         for v1 in p1_domain:
&gt;             for v2 in p2_domain:
&gt;                 if self.constr_fun(v1, v2):
&gt;                     p1_new_domain |= set(&#91;v1&#93;)
&gt;                     p2_new_domain |= set(&#91;v2&#93;)
&gt; 
&gt;         deleted = len(p1_domain) != len(p1_new_domain) or &#92;
&gt;                   len(p2_domain) != len(p2_new_domain)
&gt; 
&gt;         return {self.p1: p1_new_domain, self.p2: p2_new_domain}, deleted</pre>
<p>Just having each constraint prune once isn&#8217;t enough of course, because one constraint&#8217;s pruning may have repercussions for the pruning of another. The naïve way to deal with this would just be to keep letting all constraints prune until the domains don&#8217;t change anymore. This is a valid way of doing things, and known as the <b>AC-1</b> algorithm (for <i>Arc Consistency</i>).<br />
Smarter might be to have a queue of all constraints, remove one, have it prune, and if it affects the domain, add all constraints involving the variables of the affected domains that aren&#8217;t already in the queue to the queue; repeat until the queue is empty. That one is <b>AC-3</b>. (Both are courtesy of Alan Mackworth, incidentally).<br />
It doesn&#8217;t matter a huge deal which we use; because we don&#8217;t have an easy way of getting the constraints on a given variable, AC-3 is going to be slower than it has to be, to the point where it might even be slower than AC-1. We could deal with this, but it&#8217;s not worth it. Let&#8217;s just do something.</p>
<pre>&gt; def ac3():
&gt;     queue = constraints&#91;:&#93;
&gt; 
&gt;     while len(queue) &#62; 0:
&gt;         c = queue&#91;0&#93;
&gt; 
&gt;         new_domains, d = c.prune_domains()
&gt;         domains.update(new_domains)
&gt; 
&gt;         if d:
&gt;             new_constraints = &#91;&#93;
&gt;         
&gt;             for con in constraints:
&gt;                 if (con.p1 == c.p1 or con.p2 == c.p1 or &#92;
&gt;                     con.p1 == c.p2 or con.p2 == c.p2) and con not in queue:
&gt;                     new_constraints.append(con)
&gt; 
&gt;             queue = queue&#91;1:&#93; + new_constraints
&gt;     
&gt;         else:
&gt;             queue = queue&#91;1:&#93;</pre>
<p>Alright! Now that our affairs are in order, we can start with the algorithm in earnest. What we&#8217;ll be doing basically amounts to a depth-first search of the search space. We&#8217;ll need an unambiguous ordering of our pieces, so let&#8217;s convert our <code>set</code> to a <code>list</code>.<br />
To keep track of our state, we&#8217;ll just use a dictionary that our algorithm will be modifying directly.</p>
<pre>&gt; pieces = list(pieces)
&gt; z = {}</pre>
<p>The search algorithm itself is really straightforward: we&#8217;ll fix a variable to a domain value, make sure it doesn&#8217;t violate any constraints, and then move on to the next variable. If it does violate a constraint, we move on to the next domain value. If we&#8217;re out of domain values, we move back to the previous variable and change that.</p>
<pre>&gt; def backtrack(depth):
&gt;     for a in domains&#91;pieces&#91;depth&#93;&#93;:
&gt;         z&#91;pieces&#91;depth&#93;&#93; = a
&gt; 
&gt;         for c in constraints:
&gt;             if (c.p1 == pieces&#91;depth&#93; and c.p2 in pieces&#91;:depth&#93;) or &#92;
&gt;                (c.p2 == pieces&#91;depth&#93; and c.p1 in pieces&#91;:depth&#93;):
&gt;                 if not c.valid(z&#91;c.p1&#93;, z&#91;c.p2&#93;):
&gt;                     break
&gt; 
&gt;         else:
&gt;             if depth == len(pieces) - 1:
&gt;                 print &#34;Solution:&#34;, z
&gt;             else:
&gt;                 backtrack(depth + 1)</pre>
<p>As written, this will just keep looking for solutions until it&#8217;s exhausted the search space, but it&#8217;s trivial to make it so that it stops after the first result is found, of course.</p>
<p>And that&#8217;s that. Now we just invoke everything and let it run.</p>
<pre>&gt; ac3()
&gt; backtrack(0)</pre>
<p>And we&#8217;re done!<br />
As usual, all of the code can be found <a href="/misc/constraints.py.html">here</a>, in case you want to try it yourself. If you want an exercise to try at home, find a sensible representation for the eight queens problem and solve that. (Hint: there&#8217;s a better one than <code>(row, column)</code>.)</p>
<p>Now, is constraint processing a good alternative to the search tree algorithms previously discussed? Obviously not the algorithm I&#8217;ve just described; that&#8217;s just a naïve DFS without heuristics. It can be improved in a lot of ways: you could sort the list of variables so that the variables with the smallest domains and the most constraints affecting them are first, for example. Or you could go a step further and do <b>dynamic search tree rearrangement</b> using some heuristic, making sure that the variables more likely to cause a constraint violation are filled in first. You could prevent a whole lot of redundant checks using a variety of techniques, like backmarking or backjumping or intelligent backtracking or what have you; there&#8217;s a million things you could do.<sup><a href="#f_processing-constraints-is-easy_2" id="processing-constraints-is-easy_2">2</a></sup> There&#8217;s no shortage of research on constraint processing, and the state of the art is surprisingly advanced.<br />
I&#8217;m not terribly fond of constraint processing algorithms, myself, because I get the impression that they&#8217;re more complex to implement for no significant gain, but I can see why some people would be.</p>
<p>In the end, the most significant factor is probably what the most natural way to express your problem is. Any problem that can be solved using vanilla search trees can be solved as a constraint problem and vice versa, but most problems are just easier to express one way or the other.</p>
<p>(My AI exam is on Monday, so this will probably be the last post in this series. The only course subject I haven&#8217;t covered is version spaces, anyway, and that&#8217;s not hugely interesting. Oh, and automated reasoning, but that&#8217;s more suited to a book than a blog post.)</p>
<hr />
<p class="footnote"><sup><a href="#processing-constraints-is-easy_0" id="f_processing-constraints-is-easy_0">0</a></sup> Most algorithms only bother with two variables, but it&#8217;s entirely possible to formulate this problem as a single constraint on nine variables. Enforcing 9-consistency (see the domain pruning bit later on) would be equivalent to solving it the way we&#8217;re doing now, though.</p>
<p class="footnote"><sup><a href="#processing-constraints-is-easy_1" id="f_processing-constraints-is-easy_1">1</a></sup> In fact, we&#8217;ll only look at a small fraction of that even when we do an exhaustive search, but any advantage is nice to have at this point.</p>
<p class="footnote"><sup><a href="#processing-constraints-is-easy_2" id="f_processing-constraints-is-easy_2">2</a></sup> You could even use a language other than Python and implement it in a way that isn&#8217;t dumb!</p>
]]></content:encoded>
			<wfw:commentRss>http://cairnarvon.rotahall.org/2010/06/20/processing-constraints-is-easy/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.291 seconds -->
<!-- Cached page served by WP-Cache -->
