the lyf so short, the craft so long to lerne.

Post navigation

Othello Strategies

This is the second part of my re-write of Norvig’s Othello, originally written in Common Lisp, in Clojure. In this part I show how I’ve implemented the various Othello playing strategies in Clojure.

One important thing to note is that, with the exception of the random and human player strategies, the strategies are static. By that I mean given the same board position they will always generate the same move. This means playing two of these strategies against each other will always end with the same result (switching which moves first may produce a different result)! Norvig notes this in Section 18.8 and shows how we can use random starting positions to evaluate strategies against each other.

As with Part One of this series I’ve left my test and experimental code in place but commented out (usually just using ; but occasionally using the #_ reader macro which, as you recall, causes the next form to be ignored). The same caveats apply: I may well have modified the code or changed names since writing the commented out elements so they may no longer work.

This takes us up to section 18.8 of PAIP (I’ve decided not to implement the tournament clock until after I have a working GUI).

Next

I’m currently working on using a Clojure wrapper for Swing, SeeSaw, to build a GUI for Othello. This uses an atom to hold the current board state with a watcher to update the display of the board when the board changes. My currently problem is handling the mismatch between repaint!, paint and reference watchers! Hopefully that won’t prove insoluble without having to resort to a non-functional solution (such as dynamic scope vars to pass values around). Readers familiar with PAIP will note that Norvig doesn’t implement a GUI (his focus is AI of course) but one of the big reasons I chose Clojure over Common Lisp was precisely the ability to build a standard, native-looking GUI application; something that’s not possible on Common Lisp because the ANSI standard doesn’t cover GUIs.

The Core Game and Command Line UI

One of my favourite computer science / programming books is Peter Norvig’s “Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp” (PAIP). And the extended Othello example had always fascinated me so when I was looking for something to write to help me learn Clojure it was an obvious candidate: re-write Norvig’s Othello in Clojure.

This is the result, it takes you up to the end of Section 18.2 of the book, which includes the core game and one simple strategy that makes random moves. In translating it into Clojure I tried to keep it as identical to the original except where Clojure (a Lisp-1) makes it easier to write in a different way from Common Lisp (a Lisp-2).

I used idiomatic Clojure and made use of the most appropriate Clojure data structures: arrays, sets and maps rather than lists. I did make three changes from the original:

The original used a list for the board. I use an array of arrays.

I split one of the original functions, get-move, because the original actually both got the move from the strategy and also made the move. So I split it into get-and-make-move and get-move, which just gets the move from the strategy.

The third change was not to use a function to generate the starting board but instead defined it by hand as I think this makes it easier to understand.

Finally, Common Lisp uses mutable data structures whereas Clojure’s are immutable. So this changed the copy-board strategy used by the PAIP version: in Clojure you don’t need to make explicit copies, that’s the default behaviour.

I’ve generally written ‘idiomatic Clojure’ but because my code is still evolving (there’s a lot more Othello still!) and because I generally prefer to avoid syntactic sugar (even if the result is greater conciseness) in favour of simplicity and obviousness I’ve generally, but not always, avoided the compound macros like if-let.

I’ve left in the forms I used whilst developing and testing the code (commented out, rather than using the #_ reader macro). Note that some of the variables’ names changed during development so some of the earlier testing code might still use the old names.