# Foreigners make better programmers

We have recently been recruiting a couple of new programmers at Neurobat. This time we submitted the candidates to an online programming test, which consists of a couple of (relatively) simple programming assignments to be completed in a given time.

Out of about 100 applications, 34 were given this test. I have collected the data from these programming tests to see whether any personal factors correlated with the applicant’s performance. The results suggest that the candidate’s country of origin has the biggest influence on the final score.

Out of these 34 candidates, 10 were from France, 5 from Switzerland, and the others from other countries. 6 came from outside of Europe.

Here I show the boxplots for the normalized test scores, where I plotted separately the swiss and the french candidates, and lumped everybody else in a third category. The boxplots are sorted by increasing median.

There is a clear trend suggesting that the further away a candidate comes from, the higher their test scores. However, I must stress that with such low statistics the difference is not statistically significant. An analysis of variance test on the test scores against a simple 2-valued factor (swiss vs non-swiss) gives an F-value for one degree of freedom of 2.076, i.e. a p-value of 0.163. Similarly, a Wilcoxon rank sum test gives a p-value of 0.1698.

Nevertheless, the trend seems to be there, and there is some anecdotal evidence to support it. I can think of at least three hypotheses to explain it:

1. Skilled swiss-born programmers have no difficulty finding jobs in larger, better-paid companies and have little incentive to apply to small startups such as Neurobat.
2. Skilled swiss-born programmers tend to leave the country after graduation, whereas only the best and brightest foreign-born programmers are able to get the necessary work permits and/or scholarships to come to this country.
3. The swiss educational system, especially in the field of computer science, sucks (for lack of a better word).

Posted on May 6, 2013 at 11:00 am by David Lindelöf · Permalink · 6 Comments
In: Uncategorized

# Phileas Fogg on Agile Project Management

I just finished reading Jules Verne’s Around The World In 80 Days, which I had never read before. It’s a great read and I highly recommend it to children and adults alike. But half-way through the book I realized there’s more to this book than a nice tale of adventure.

This book is a complete manual about managing a fixed-time, fixed-price project.

The story starts off with a bet that Phileas Fogg makes with the members of his club, namely, that it is possible to circumnavigate the globe in 80 days or less. Once the bet is placed he takes off immediately, with 20,000 pounds of cash for his travel expenses.

Travelling around the world in 80 days with a limited (albeit sizeable) budget fills, of course, anyone’s definition of a fixed-time, fixed-time project. What lessons can be learned from this adventure for today’s project managers?

Mr Fogg and his valet, Passepartout, waste no time in preparing for their trip. Once the project approved they head immediately to the train station. No feasibility study, no pre-study, no preliminary analysis, no analysis paralysis. Mr Fogg is convinced that the project is feasible and starts immediately. The lesson here is that there is value in speed. The earlier you can start on your project, the quicker you will gain feedback and learn.

## Expect the unexpected

When his friends object that an 80-day schedule makes no allowance for unexpected delays or accidents, Mr Fogg calmly replies that it is “all included.” He knows very well that the 80-day schedule includes some buffer time which, it is hoped, will not be exceeded. Indeed—though I will not give away the surprise ending—the whole trip, in spite of all the delays and adventures, ends up taking less than the stated 80 days.

Whenever Mr Fogg’s party is met with surprises (such as the railway across India being not even finished), he never, ever betrays any surprise or emotion. It is as if he knew from the outset which delays his project should meet. Of course he didn’t, but he knows that his schedule accounts for them.

## Don’t get emotionally involved

Mr Fogg is a very mysterious character in this book. Nothing is told about his origin, his past, or his relatives. He is described as the quintessential englishman: utterly imperturbable yet fiercely resolute in his decisions.

Even assuming that Mr Fogg included some extra buffer time in his estimate, it is hard to believe that he could possibly take into account all the mishaps his party encountered: Aouda’s rescue; Passepartout’s disappearance; Passepartout’s capture by indians and subsequent rescue; Mr Fogg arrest by Mr Fix. Yet at no time does he show the slightest sign of annoyance, hesitation or worry that he might miss his schedule. Is it because he genuinely had factored even such accidents in his estimates? Or is it because he doesn’t allow himself to become too emotionally attached to his project? I’d rather believe the latter.

Mr Fogg keeps a kind of diary throughout his trip where he carefully records exactly how much ahead or behind he is on time. He knows exactly when he is supposed to be where on his trip. In fact, if he had a whiteboard he would probably have plotted what is known as a velocity chart, showing exactly how well he is sticking to his schedule.

He presumably does something similar with his finances, since he starts out for his journey with a fixed amount of cash and seems always to know how much he can afford to spend.

## Open up communication

Mr Fogg’s party narrowly miss the steamer from Hong Kong to Yokohama, which would have spelled disaster for the journey. But Mr Fogg wastes not an instant and scours the docks of Hong Kong, looking for a boat which could take him to Japan.

However, salvation comes not from a boat that can do the Hong Kong-Yokohama trip, but from a tugboat which takes his party to Shanghai. Why Shaghai? Because Mr Fogg, instead of insisting on going to Yokohama, always explained the reasons for his going there: that he needs to catch the transpacific steamer that will take him to the USA. And the crew of the tugboat knew that this steamer does not depart originally from Yokohama, but from Shanghai. Therefore, instead to travelling to Yokohama, Mr Fogg is better served by heading to Shanghai and boarding the steamer there.

Mr Fogg scored points here by clearly explaining his needs and his situation, instead of insisting on a solution of his own making. We too, when faced with customer demands, have the responsibility to understand exactly the context in which such requests are made.

## Accept mistakes and take advantage from them

I’m not going to spoil the ending for you; I’ll simply say that Mr Fogg miscalculates the total time taken on his trip, something which he has a very hard time accepting. But once the reality dawns on him, he wastes not an instant and seizes the opportunity given him, and turns disaster into victory. And Jules Verne gives the best explanation I’ve ever read for the need of an international date line, which was established 7 years after the publication of his book.

Forget now everything I just wrote. Even if you are not involved in project management, Around The World In 80 Days is a great book, a true classic, which I heartily recommend. You won’t regret reading it, but you will regret never having read it.

In: Uncategorized

# ScrumMaster no more

The first thing we did this week was to hold a sprint retrospective. And one of the first things we decided was that I should step down as ScrumMaster.

In recent months I had been travelling more and more, attending more and more meetings, and it had become clear that there was no way I could be sufficiently present with the team to qualify as ScrumMaster.

However, my frequent interactions with the business/marketing/field people qualified me now for arguably the most misunderstood role in the Scrum process: that of Product Owner.

Now that I am free from the responsibilities of a ScrumMaster, I realize how much I had underrated the role of a Product Owner. And in particular, I realize now that my frequent interactions with the rest of the company had made me the de facto Product Owner a long time ago.

I still have a lot to learn before I can become fully effective in this new role, but if I should summarize my understanding of the Product Owner’s role in one sentence it would be this:

The Product Owner’s sacred duty is to keep the product backlog non-empty and prioritized.

Corollary: the team should never, ever find themselves wondering what to do next. Contrary to what I always believed, it’s not the ScrumMaster’s job to make sure the team knows what to do. It’s the Product Owner’s.

In: Uncategorized

# Standup meetings are not diaries

Very often I hear scrum standup meetings go something like this:

Fred: “yesterday I used Git bisect to find out where and when bug #1234 was first introduced. That didn’t work so I created a new unit test to reproduce it and asked Alice what the naming convention for unit tests was and…

Stop. Do you see the problem here?

Fred is not telling me what he’s done yesterday. He’s telling me how he’s done it. He’s using up his precious 5 minutes of public airing to tell his working day in the minutest detail. And chances are, that nobody cares. At least I don’t.

I would much rather hear his tell us something along these lines:

Fred: “yesterday I wanted to fix bug #1234, which we’ve agreed was a blocking one that should take priority. It’s impossible to find out when exactly it was introduced because we have many commits that include several, unrelated changes. I’ve been working on a unit test but I wasn’t sure of the naming convention and was delayed a bit.

See the difference? Full of valuable information for the ScrumMaster and future material for the sprint retrospective. In addition, Fred now gives some context for his teammates, explaining not only what he was doing (instead of how) but also why he was doing it, in case anyone was not clear about it.

If you’re a ScrumMaster, watch out for standup meetings that degenerate into endless lists of I did this then I did that finally I did this other thing. Explain to your team that you and your team want to know what everyone is working on, what was accomplished, and what are blocking issues. Everything else is of accidental, not essential, interest.

In: Uncategorized

# Selenium script to reset a ZyXEL NBG4115 4G Router

The ZyXEL NBG4115 is a small, cheap 3G router that can be used to build a wireless network where there is no access point available. You plug one of those 3G dongles into it, enter the PIN number, and you have a local access point in the middle of nowhere. At Neurobat we use these devices whenever we require internet access to a test site that cannot have internet access otherwise.

[]3

ZyXEL NBG4115 Wireless N-lite 3G Router

One problem we ran into very early on, however, is that the 3G connection provided by Swisscom tends to be, to put it charitably, flaky. Indeed, we have never had a single connection remain alive for more than a week. We have spoken with their tech support, upgraded the firmware on the 3G dongles, all to no avail. As a final solution we decided to write scripts on the local netbooks that would regularly (say, once every three hours) reset the connection through the router’s administrative web interface. Only problem was that none of the “easy” screen-scraping tools out there (wget, Python’s urllib, etc) was Javascript-capable-–|which is something the web interface would detect, and refuse any client that was not Javascript-enabled.
[]4

3G Router on a test site

In the end we decided to install a Selenium server on each netbook, and wrote a Python script that would talk to the Selenium server and reset the router. If this can help anyone, here is the (simple) Python script to reset a ZyXEL 3G router through Selenium, assuming a Selenium server has been started and is listening on port 4444:

#!/usr/bin/env python
from selenium import webdriver
browser = webdriver.Remote("http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNIT)
browser.get("http://192.168.1.1")
pass_field = browser.find_element_by_name("textfield")
pass_field.clear()
pass_field.send_keys("*****")
pass_field.submit()
# Click restart
try:
browser.get("http://192.168.1.1/mtenrestart.html")
except:
pass
button = browser.find_element_by_name('apply')
button.click()


And here is the Bash script that controls it all, and that we call from a cronjob:

#!/bin/bash
TOOLS=dirname $0 # Start Selenium server java -jar$TOOLS/selenium-server-standalone.jar > /dev/null 2>&1 &
SELENIUM=$! sleep 10 # Reset router$TOOLS/restart_zyxel.py
# Kill Selenium
kill $SELENIUM  If you find this to be of use I’d be happy to hear from you in the comments below. Posted on April 26, 2012 at 9:00 am by David Lindelöf · Permalink · 4 Comments In: Uncategorized # Floating-point woes, part 1 You may have several decades of programming experience, certain classes of problems seem to repeatedly cause endless confusion. Floating-point computation is one such class. I’ve been doing a fair amount of numerical analysis these past few months, implementing floating-point calculations on embedded platforms. In the process I’ve stumbled across a few programming gotchas which I’d like to document in a (hopefully short) series of posts. I think that some of them are highly non-trivial, and that no amount of prior lecturing and/or reading (such as Goldberg’s excellent _What Every Computer Scientist Should Know About Floating-Point_ paper), there are still some issues that might surprise any programmer. I begin with a relatively simple and well-known example drawn from the programming class I teach. (All the code examples in this series are available from git@github.com:dlindelof/fpwoes.git) Termination criteria for a square-root calculating routine ——————————————————————– Consider this program for calculating square roots by Newton’s method: “sqrt1.c”: #include #include #define EPS 0.000001 static float fabsf(float x) { return x < 0 ? -x : x; } static int sqrt_good_enough(float x, float guess) { return fabsf(guess*guess - x) < EPS; } static float sqrt_improve(float guess, float x) { return (guess + x/guess)/2; } static float sqrt_iter(float x, float guess) { if (sqrt_good_enough(x, guess)) return guess; else return sqrt_iter(x, sqrt_improve(guess, x)); } float sqrt(float x) { return sqrt_iter(x, 1.0f); } int main(int argc, char** argv) { float x = atof(argv[1]); printf("The square root of %.2f is %.2f\n", x, sqrt(x)); return 0; } This program works well enough with arguments smaller than about 700. Above that value, one gets segmentation faults:$ ./sqrt1 600
The square root of 600.00 is 24.49
$./sqrt1 1000 Segmentation fault Debugging the progam under gdb shows that a stack overflow happened:$ gdb ./sqrt1
(gdb) run 1000
Starting program: /home/dlindelof/Dropbox/Projects/fpwoes/sqrt1 1000

Program received signal SIGSEGV, Segmentation fault.
fabsf (x=Cannot access memory at address 0x7fffff7feff4
) at sqrt1.c:6
6 static float fabsf(float x) {
(gdb) bt
#0 fabsf (x=Cannot access memory at address 0x7fffff7feff4
) at sqrt1.c:6
#1 0x00000000004005aa in sqrt_good_enough (x=1000, guess=31.622776) at sqrt1.c:11
#2 0x0000000000400610 in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:19
#3 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#4 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#5 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#6 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#7 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#8 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#9 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22
#10 0x000000000040063a in sqrt_iter (x=1000, guess=31.622776) at sqrt1.c:22

What's happening should be quite clear here. The best estimate for the square root of 1000 in single precision is 31.622776, whose square is 999.999939, differing from 1000 by about 6.1e-5, i.e. way more than the specified tolerance of 1e-6. But it is the best approximation that can be represented in single precision. In hex, it is 0x41fcfb72. One least-significant bit (LSB) less, or 0x41fcfb71, represents 31.6227741 whose square is 999.999817, off by 18.3e-5. One LSB more is 0x41fcfb73 or 31.6227779, whose square is 1000.00006, off by 6.1e-5, i.e. the same difference as for 31.622776. 31.622776 is therefore the best approximation to the square root of 1000 with single-point precision.

To have the program terminate instead of entering an infinite loop we need to rethink the termination criteria. Obviously we cannot use an absolute difference between the square of our guess and the argument, for as we have just seen, for large values it might not be possible to get an answer whose square is close enough to the argument.

We could imagine using a _relative_ difference instead:

static int sqrt_good_enough(float x, float guess) {
return fabsf(guess*guess - x) / x < EPS;
}

This gives us a second program sqrt2.c, which works much better:

$./sqrt2 1000 The square root of 1000.00 is 31.62$ ./sqrt2 5
The square root of 5.00 is 2.24
$./sqrt2 2 The square root of 2.00 is 1.41$ ./sqrt2 1
The square root of 1.00 is 1.00
$./sqrt2 100000 The square root of 100000.00 is 316.23$ ./sqrt2 10000000
The square root of 10000000.00 is 3162.28
$./sqrt2 1000000000 The square root of 1000000000.00 is 31622.78$ ./sqrt2 0.5
The square root of 0.50 is 0.71
$./sqrt2 0.1 The square root of 0.10 is 0.32$ ./sqrt2 0.01
The square root of 0.01 is 0.10
$./sqrt2 0.05 The square root of 0.05 is 0.22 Interestingly, Kernighan and Plauger (in their clasic _The Elements of Programming Style_) deal with a very similar problem in the first chapter, but introduce a slightly different termination criteria. Translated into C from the original Fortran, this would give: static int sqrt_good_enough(float x, float guess) { return fabsf(x/guess - guess) < EPS * guess; } I asked the good folks on www.stackoverflow.com why one termination criteria might be better than another. I've accepted the answer that explained that the termination criteria should match the update definition. In other words, you want the termination criteria to be equivalent to saying |guess[n+1] - guess[n]| < guess[n] * EPS. Given that guess[n+1] = (guess[n] + x/guess[n])/2, one obtains a termination criteria that reads |x/guess[n] - guess[n]| < guess[n] * EPS --- which is exactly the termination criteria preferred by K&P. Note furthermore that this criteria correctly handles the case when y == 0.0. However, the program will still eventually produce nonsense on an input of 0.0. Quoth the debugger: #1 0x0000000000400636 in sqrt_iter (x=0, guess=0) at sqrt3.c:22 #2 0x0000000000400646 in sqrt_iter (x=0, guess=1.40129846e-45) at sqrt3.c:22 #3 0x0000000000400646 in sqrt_iter (x=0, guess=2.80259693e-45) at sqrt3.c:22 #4 0x0000000000400646 in sqrt_iter (x=0, guess=5.60519386e-45) at sqrt3.c:22 #5 0x0000000000400646 in sqrt_iter (x=0, guess=1.12103877e-44) at sqrt3.c:22 #6 0x0000000000400646 in sqrt_iter (x=0, guess=2.24207754e-44) at sqrt3.c:22 #7 0x0000000000400646 in sqrt_iter (x=0, guess=4.48415509e-44) at sqrt3.c:22 #8 0x0000000000400646 in sqrt_iter (x=0, guess=8.96831017e-44) at sqrt3.c:22 #9 0x0000000000400646 in sqrt_iter (x=0, guess=1.79366203e-43) at sqrt3.c:22 #10 0x0000000000400646 in sqrt_iter (x=0, guess=3.58732407e-43) at sqrt3.c:22 #11 0x0000000000400646 in sqrt_iter (x=0, guess=7.17464814e-43) at sqrt3.c:22 #12 0x0000000000400646 in sqrt_iter (x=0, guess=1.43492963e-42) at sqrt3.c:22 #13 0x0000000000400646 in sqrt_iter (x=0, guess=2.86985925e-42) at sqrt3.c:22 #14 0x0000000000400646 in sqrt_iter (x=0, guess=5.73971851e-42) at sqrt3.c:22 #15 0x0000000000400646 in sqrt_iter (x=0, guess=1.1479437e-41) at sqrt3.c:22 #16 0x0000000000400646 in sqrt_iter (x=0, guess=2.2958874e-41) at sqrt3.c:22 #17 0x0000000000400646 in sqrt_iter (x=0, guess=4.59177481e-41) at sqrt3.c:22 The termination criteria will eventually involve a 0.0/0.0 operation. There is no way out of this other than to explicitly handle that special case. The final program thus reads: sqrt3.c #include #include #define EPS 0.000001 static float fabsf(float x) { return x < 0 ? -x : x; } static int sqrt_good_enough(float x, float guess) { return fabsf(x/guess – guess) < EPS * guess; } static float sqrt_improve(float guess, float x) { return (guess + x/guess)/2; } static float sqrt_iter(float x, float guess) { if (sqrt_good_enough(x, guess)) return guess; else return sqrt_iter(x, sqrt_improve(guess, x)); } float sqrt(float x) { if (x == 0.0) return 0.0; else return sqrt_iter(x, 1.0f); } int main(int argc, char** argv) { float x = atof(argv[1]); printf(“The square root of %.2f is %.2f\n”, x, sqrt(x)); return 0; } What’s the most obscure bug involving floating-point operations you’ve ever been involved in? I’d love to hear from it in the comments below. Posted on March 12, 2012 at 10:01 am by David Lindelöf · Permalink · Leave a comment In: Programming # Automatic data collection without FTP I haven’t written anything in a while, so I thought I might begin by sharing some ideas on how we collect test site data at Neurobat without using FTP. We have a Dropbox Teams account, which gives us about north of 1 TB of storage. The idea is to install Dropbox on each netbook that monitors a test site, and to have it save its monitored data to its Dropbox folder. Then when/if that netbook has internet access, it will automatically upload the data to a shared folder on our Dropbox account, from which it will be re-distributed to all interested parties: X / \ / \ / \ /+—–+\ | | +———+ | |…|netbook-1|…. | | +———+ . +—–+ . site 1 . . . . X . / \ . / \ +—————————————–+ / \ | ____ _ | /+—–+\ | | _ \ _ __ ___ _ __ | |__ _____ __ | | | +———+ | | | | | ‘__/ _ \| ‘_ \| ‘_ \ / _ \ \/ / | | |…|netbook-2|..| | |_| | | | (_) | |_) | |_) | (_) > < |. | | +---------+ | |____/|_| \___/| .__/|_.__/ \___/_/\_\ | +-----+ | |_| | site 2 +-----------------------------------------+ . . . X . / \ . / \ . / \ . /+-----+\ . | | +---------+ . | |...|netbook-3|.... | | +---------+ +-----+ site 3 For simplicity (and for licencing costs) the same user account is linked to each netbook. For historic reasons, that account is nagios@neurobat.net (yes, a Nagios server is going to monitor that data too). Since we don't want data from, say, site 3 to be copied over to the local drive of netbook-1, we enable the "Selective Sync" feature on the netbooks so that each netbook sees only a folder dedicated to that site. The folder layout thus looks like this: Monitoring/ ├── site 1 │ └── sensors ├── site 2 │ └── sensors └── site 3 The Monitoring folder is the globally shared folder, shared by nagios@neurobat.net with all Neurobat users interested in analyzing the data. The only downside we have identified with this approach is that whenever a new site is added, that site's data will by default also be synchronized with all existing sites. Selective sync will only let you exclude folders that already exist; as far as I know, you cannot ask Dropbox to *not* sync new folders in that shared folder. And I am apparently not the only one to find this annoying. An alternative might have been to have a separate shared folder for each site. However, for each new site one would then have to share that folder with all Neurobat users. Furthermore, since all sites use the same Dropbox user, you would still end up with that site’s data being sync’ed all over the place. That being said, we have found the above scheme to work out very well in practice. The automatic synchronization of Dropbox makes it a superior alternative to, say, hand-written FTP-calling scripts we might have used in the past century. And if you have notifications turned on for your Dropbox account (I personally don’t) it will be very reassuring to regularly see your test site data being updated on your local machine without any human intervention. Posted on February 7, 2012 at 6:07 am by David Lindelöf · Permalink · Leave a comment In: Uncategorized # homeR: an R package for building physics For the past few weeks we’ve been very busy here at Neurobat with the analysis of field tests results. In the process of doing that, we had to implement several functions in R that relate to building physics. We thought it might be useful for the community to have access to those functions, so we decided to begin submitting them to CRAN. That’s why we’re pleased to announce the first release of the homeR package, which we will fill over time with such functions. This first release, version 0.1, contains just pmv, a function to calculate the so-called Predicted Mean Vote, i.e. a measure from -3 (cold) to +3 (hot) of thermal comfort as a function of several variables, including air temperature, clothing and metabolism. Here I show how with this function one can derive a contour plot showing, for given clothing and metabolism, the optimal indoor temperature (assuming 50% relative humidity). We’re basically going to solve pmv(clo, met, temp, sat) = 0 equation for temp across a grid of clo and met values with the uniroot function. > clo <- seq(0,2,length=21) > met <- seq(0.6,3.2,length=21) > zero.pmv <- function(clo, met) uniroot(function(temp) pmv(clo,met,temp,50), c(-10,40))$root
> contourplot((outer(clo,met,Vectorize(zero.pmv))),
cuts=20,
aspect=”fill”,
panel=function(…) {
panel.grid(h=-1,v=-1,…)
panel.contourplot(…)
},
row.values=clo, column.values=met,
xlim=extendrange(clo), ylim=extendrange(met),
xlab=”[Clo]“, ylab=”[Met]“)

And here is the resulting plot:

Predicted Mean Vote contour plot

As you can see, this is pretty similar to that sort of plots one finds in standard textbooks on the subject, such as Claude-Alain Roulet’s Santé et qualité de l’environnement intérieur dans les bâtiments:

PMV contour plot from textbook

Please give the homeR package a try, and give us your feedback. There’s only the pmv function in there at the time of writing but we plan to extend the package in the weeks to come.

Posted on May 26, 2011 at 2:54 am by David Lindelöf · Permalink · 2 Comments
In: Programming, Tools

# Saikoro game engine in Lisp

Here at Neurobat we consecrate one day per sprint as a *Lab Day*, i.e. a day when, not unlike what Google does, we are free to work on whatever we want.

Today was Lab Day and I took the opportunity to brush up my Lisp skills by writing a game, inspiring myself heavily from Conrad Burski’s wonderful book Land of Lisp, which will teach you the necessary Lisp skills for the most important programming gig ever, that is, writing games.

I wrote a game engine for the Saikoro boardgame, a game simple enough to be amenable to the kind of AI described in the book. Without doing any major kind of optimization, the computer will play a perfect game on a 4 x 4 board by computing exhaustively a tree of all possible games from a starting position.

Here is what a typical game session looks like:

\$ sbcl --load saikoro.lisp
; snip some noise
Current player: A
| A[ 0] 3[ 1] 3[ 2] 2[ 3] |
| 1[ 4] 4[ 5] 2[ 6] 2[ 7] |
| 3[ 8] 4[ 9] 4[10] 2[11] |
| 1[12] 3[13] 4[14] B[15] |
1. 0 -> 4
2. 0 -> 10
3. 0 -> 1
4. 0 -> 5
2
Current player: B
| 0[ 0] 3[ 1] 3[ 2] 2[ 3] |
| 1[ 4] 4[ 5] 2[ 6] 2[ 7] |
| 3[ 8] 4[ 9] A[10] 2[11] |
| 1[12] 3[13] 4[14] B[15] |
Current player: A
| 0[ 0] 3[ 1] 3[ 2] 2[ 3] |
| 1[ 4] B[ 5] 2[ 6] 2[ 7] |
| 3[ 8] 4[ 9] A[10] 2[11] |
| 1[12] 3[13] 4[14] 0[15] |
1. 10 -> 1
2. 10 -> 7
2
Current player: B
| 0[ 0] 3[ 1] 3[ 2] 2[ 3] |
| 1[ 4] B[ 5] 2[ 6] A[ 7] |
| 3[ 8] 4[ 9] 0[10] 2[11] |
| 1[12] 3[13] 4[14] 0[15] |
Current player: A
| 0[ 0] 3[ 1] 3[ 2] 2[ 3] |
| B[ 4] 0[ 5] 2[ 6] A[ 7] |
| 3[ 8] 4[ 9] 0[10] 2[11] |
| 1[12] 3[13] 4[14] 0[15] |
1. 7 -> 1
1
Current player: B
| 0[ 0] A[ 1] 3[ 2] 2[ 3] |
| B[ 4] 0[ 5] 2[ 6] 0[ 7] |
| 3[ 8] 4[ 9] 0[10] 2[11] |
| 1[12] 3[13] 4[14] 0[15] |
Current player: A
| 0[ 0] A[ 1] 3[ 2] 2[ 3] |
| 0[ 4] 0[ 5] 2[ 6] 0[ 7] |
| 3[ 8] 4[ 9] 0[10] 2[11] |
| 1[12] B[13] 4[14] 0[15] |
1. 1 -> 6
2. 1 -> 3
3. 1 -> 2
3
Current player: B
| 0[ 0] 0[ 1] A[ 2] 2[ 3] |
| 0[ 4] 0[ 5] 2[ 6] 0[ 7] |
| 3[ 8] 4[ 9] 0[10] 2[11] |
| 1[12] B[13] 4[14] 0[15] |
Current player: A
| 0[ 0] 0[ 1] A[ 2] 2[ 3] |
| 0[ 4] 0[ 5] 2[ 6] 0[ 7] |
| 3[ 8] 4[ 9] 0[10] 2[11] |
| B[12] 0[13] 4[14] 0[15] |
B wins!
* (quit)


As you can see the UI is very rudimentary for the moment but I wanted to concentrate on getting the AI right first. Next I plan to optimize the AI, and make it work with a non-exhaustive game tree so it would work on larger boards.

If you’re interested in trying it out, feel free to clone my repository.

Posted on May 20, 2011 at 11:40 am by David Lindelöf · Permalink · 2 Comments
In: Programming · Tagged with: , ,

# Pomodoro + Workrave = Well-being

I’ve been a big fan of the Pomodoro Technique for almost a year now. No, I don’t go as far as actually having a ticking timer in my office in front of my co-workers, and I don’t necessarily plan the day in advance, but I do try to break up my work in 25-min iterations punctuated by 5-min breaks.

I used to use Ubuntu’s Timer applet to alert me to take a break, but a month ago I found what I now believe to be the perfect complement to the Pomodoro Technique: a nice little application called Workrave.

Workrave will let you define work intervals after which it will, shall we say, strongly encourage you to take a break and propose a couple of physical exercises. I’ve installed Workrave under Ubuntu (I believe it runs also on Windows) and configured it for:

* no micro-breaks
* a 5-min rest break after 25 min of work
* a daily limit of 8 hours (never reached)

Try it out! It’s completely free and quite nice. I can also recommend the Pomodoro Technique Illustrated book from the Pragmatic Programmers, but you might also want to begin with the free “official” Pomodoro book.

Posted on April 18, 2011 at 11:30 am by David Lindelöf · Permalink · 2 Comments
In: Uncategorized