SourceForge.net Logo
Summary
Forums
CVS
Download

Laird Breyer
Download
contents introduction tutorial spam fun man related
previous next

Building the engine

Now let's build the engine. This is where we'll cheat take advantage of the wonderful world of open source.

Remember that what we want to do is take a PGN game that's in progress, and let dbacl complete the next move by picking all possible legal next moves and adding them in turn to the current game, then we let dbacl compute the most probable game under its model. We don't really know if dbacl's model is a good one for chess, and let's not even think about competing with Deep Thought here, but this procedure will at least give us a way to pick the move.

Unfortunately, computing legal moves is straightforward but tedious, so I looked around the internet for something useful, and found the SAN Toolkit (if the link doesn't work, get it here). This is an old freeware toolkit for chess which does everything we want, even though it was last updated in 1993 and probably no longer state of the art. It's written in C and we have to compile it, but most importantly we can use it directly in a Unix shell environment. Download it and place it in the chess directory.

% untar xfz san.tgz
% cd SAN
% cp SAN_DOC/Makefile SAN_SRC
% cd SAN_SRC
% make
% cd ../..

The documentation for this program is a little wanting, but it's not too hard to figure out, and the source code helps. Everything is calculated by the program san, which accepts commands. We'll write a script which accepts a PGN partial game and one of the dbacl categories we learned, and outputs the "best" move, meaning what dbacl thinks is closest to the category that was learned.

Let's try those ideas out before we proceed. We'll need a test PGN file.

% cat > test.pgn
1. e4 c5 2. Nf3 e6 3. d3 Nc6
The cat commands waits until you press CTRL-D to finish. Let's first ask the san program for a list of legal moves.
% echo -ne "svop verb namd nabd napr\nlfer test.pgn\nenum 1\n" \
 | ./SAN/SAN_SRC/san > test.legal 2>&1
% head -10 test.legal
: : : Welcome to the SAN Kit.
Revised: 1993.05.16

Bd2             : 1
Be2             : 1
Be3             : 1
Bf4             : 1
Bg5             : 1
Bh6             : 1
Kd2             : 1

Okay, this looks promising! There are some pieces of text we'll have to remove, but the possible first moves are all there (the listing is cut after 10 lines). Let's build the game line.

% cat test.pgn | sed 's/[0-9]*\.[ ]*//g' > test.gameline
% cat test.gameline
e4 c5 Nf3 e6 d3 Nc6

Next, we complete this gameline with each possible move:

% cat test.legal | grep '^.* : 1$' | cut -f 1 -d ' ' | \
 while read move; do
	echo `cat test.gameline` $move
 done > test.complete
% head -10 test.complete
e4 c5 Nf3 e6 d3 Nc6 Bd2
e4 c5 Nf3 e6 d3 Nc6 Be2
e4 c5 Nf3 e6 d3 Nc6 Be3
e4 c5 Nf3 e6 d3 Nc6 Bf4
e4 c5 Nf3 e6 d3 Nc6 Bg5
e4 c5 Nf3 e6 d3 Nc6 Bh6
e4 c5 Nf3 e6 d3 Nc6 Kd2
e4 c5 Nf3 e6 d3 Nc6 Ke2
e4 c5 Nf3 e6 d3 Nc6 Na3
e4 c5 Nf3 e6 d3 Nc6 Nbd2

And of course, let's check what dbacl thinks of this:

% cat test.complete | ./dbacl/src/dbacl -n -c ./WhiteWinDraw -f 1 > test.scores
% cat test.scores
WhiteWinDraw  53.88 e4 c5 Nf3 e6 d3 Nc6 Bd2
WhiteWinDraw  52.93 e4 c5 Nf3 e6 d3 Nc6 Be2
WhiteWinDraw  52.16 e4 c5 Nf3 e6 d3 Nc6 Be3
WhiteWinDraw  53.11 e4 c5 Nf3 e6 d3 Nc6 Bf4
WhiteWinDraw  52.88 e4 c5 Nf3 e6 d3 Nc6 Bg5
WhiteWinDraw  54.64 e4 c5 Nf3 e6 d3 Nc6 Bh6
WhiteWinDraw  55.32 e4 c5 Nf3 e6 d3 Nc6 Kd2
WhiteWinDraw  54.82 e4 c5 Nf3 e6 d3 Nc6 Ke2
WhiteWinDraw  54.55 e4 c5 Nf3 e6 d3 Nc6 Na3
WhiteWinDraw  57.48 e4 c5 Nf3 e6 d3 Nc6 Nbd2
WhiteWinDraw  52.07 e4 c5 Nf3 e6 d3 Nc6 Nc3
WhiteWinDraw  54.69 e4 c5 Nf3 e6 d3 Nc6 Nd4
WhiteWinDraw  53.83 e4 c5 Nf3 e6 d3 Nc6 Ne5
WhiteWinDraw  60.95 e4 c5 Nf3 e6 d3 Nc6 Nfd2
WhiteWinDraw  56.18 e4 c5 Nf3 e6 d3 Nc6 Ng1
WhiteWinDraw  54.51 e4 c5 Nf3 e6 d3 Nc6 Ng5
WhiteWinDraw  55.05 e4 c5 Nf3 e6 d3 Nc6 Nh4
WhiteWinDraw  52.88 e4 c5 Nf3 e6 d3 Nc6 Qd2
WhiteWinDraw  53.56 e4 c5 Nf3 e6 d3 Nc6 Qe2
WhiteWinDraw  53.83 e4 c5 Nf3 e6 d3 Nc6 Rg1
WhiteWinDraw  48.60 e4 c5 Nf3 e6 d3 Nc6 a3
WhiteWinDraw  49.86 e4 c5 Nf3 e6 d3 Nc6 a4
WhiteWinDraw  49.73 e4 c5 Nf3 e6 d3 Nc6 b3
WhiteWinDraw  49.77 e4 c5 Nf3 e6 d3 Nc6 b4
WhiteWinDraw  48.51 e4 c5 Nf3 e6 d3 Nc6 c3
WhiteWinDraw  49.23 e4 c5 Nf3 e6 d3 Nc6 c4
WhiteWinDraw  47.56 e4 c5 Nf3 e6 d3 Nc6 d4
WhiteWinDraw  49.77 e4 c5 Nf3 e6 d3 Nc6 e5
WhiteWinDraw  48.33 e4 c5 Nf3 e6 d3 Nc6 g3
WhiteWinDraw  50.00 e4 c5 Nf3 e6 d3 Nc6 g4
WhiteWinDraw  49.23 e4 c5 Nf3 e6 d3 Nc6 h3
WhiteWinDraw  49.86 e4 c5 Nf3 e6 d3 Nc6 h4

First, you'll note that each line contains the current game, but ends with one of the legal moves. Just before each game sequence, there is a score for that sequence, and since the sequences are nearly identical, the scores are nearly identical too.

In the world of dbacl, these scores are the negative logarithm (base 2) of the probability of the sequence, based on the model WhiteWinDraw. It's best to think of these scores as a distance away from the category model, so the line with the smallest score is the most likely. If you want to know what probability each sequence has, it's 1/(2^54.73) etc., which is pretty close to zero! But that's normal with these kinds of models.

So what's the best move? Simply sort the lines in increasing order by score and print out the first line:

% cat test.scores | sort -k 2 -n | head -1
WhiteWinDraw  47.56 e4 c5 Nf3 e6 d3 Nc6 d4
Remember, dbacl recommends what it thinks most of the games it has learned would do. We can take a peek at the effect of each half move on the score for the first line of test.complete by using dbacl's debugging switch:
% head -1 test.complete
e4 c5 Nf3 e6 d3 Nc6 Bd2
% head -1 test.complete | ./dbacl/src/dbacl  -nv -c ./WhiteWinDraw -f 1 -d
# categories: WhiteWinDraw 
# format: avg_score * complexity
    20.25 * 0.5         []e4[](1)
     2.91 * 1.0         []e4[]c5[](1)
     8.82 * 1.5         []c5[](1)
     4.55 * 2.0         []c5[]Nf3[](1)
     7.65 * 2.5         []Nf3[](1)
     3.15 * 3.0         []Nf3[]e6[](1)
     5.71 * 3.5         []e6[](1)
     3.21 * 4.0         []e6[]d3[](1)
     5.48 * 4.5         []d3[](1)
     4.34 * 5.0         []d3[]Nc6[](1)
     5.83 * 5.5         []Nc6[](1)
     4.31 * 6.0         []Nc6[]Bd2[](1)
     5.75 * 6.5         []Bd2[](1)
The scores we saw earlier are obtained by multiplying the average by the complexity, but dbacl internally works with nats, not bits, so 5.75 * 6.5 / ln(2) = 53.9. Since here we just want to see the tokens that are being used, these values aren't important. The most important thing to see is that there are contributions from single half moves as well as pairs of half moves, and the score balances them all.
previous next
contents introduction tutorial spam fun man related