Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Perl, Chess, Lies, Damn Lies, and Statistics

by hsmyers (Canon)
on Dec 13, 2002 at 19:11 UTC ( #219705=CUFP: print w/ replies, xml ) Need Help??

Lately, I've been playing around with computer generated analysis of chess games. I have two programs that I use for this purpose, one a commercial chess program called 'Fritz' and the other, an open source effort by Bob Hyatt called 'Crafty'1. In particular, I've been looking at the output of Crafty and how I could convert the raw data generated into graphs for a more useful look at the information created.

Without going too deeply into the details, I generate that source file for this exploration this way2:

  • First I break out the game I want to analyze. For instance I've collected all of the games from the 1997 Cutthroat Classic into a file called ctcl97.pgn. So I extract the first game into ctcl97.1.pgn as a temporary file.
  • Next I create a command file called cmd.txt. It contains the line
    annotate ctcl99.1.pgn bw 1-999 -1.000 30
  • Then I feed this to Crafty with a command line like:
    C:\crafty < cmd.txt
When the dust settles (the '30' in cmd.txt is the number of seconds to devote to analysis, so this process can take a while) the result is a file called ctcl97.1.pgn.can, which looks something like:
[Event "Cutthroat Classic 97"] [Site "Sun Valley (ID)"] [Date "1997.05.17"] [Round "01"] [White "Richardson Tom (ID)"] [WhiteElo ""] [Black "Myers Hugh S (ID)"] [BlackElo ""] [Result "0-1"] [Annotator "Crafty v18.5"] {annotating both black and white moves.} {using a scoring margin of -1.00 pawns.} {search time limit is 30.00} 1. e4 d5 2. exd5 Nf6 3. Nc3 Nxd5 4. d3 ({12:-0.35} 4. d3 Nxc3 5. bxc3 e6 6. Nf3 Bd6 7. Be2 Q +f6 8. Bd2 O-O 9. O-O Nc6 $15) ({0:+0.00} 4. Bc4 Nb6 $10) 4. ... e5 ({12:-0.11} 4. ... e5 5. Qh5 Nc6 6. Bg5 Qd6 7. Nge2 N +xc3 8. Nxc3 Qb4 9. Rb1 Bg4 10. Qh4 $10) ({12:-0.19} 4. ... Nxc3 5. bxc3 e5 6. Be2 Bc5 7. Nf3 +Nc6 8. Bg5 f6 9. Be3 $10) . . .
As a rough guide to what you are looking at:
  • The stuff in brackets either comes from the source file or was generated by Crafty. In any event, it is all .pgn header information.
  • The first 3 moves have no analysis---this is because they are all still in 'book', i.e. they conform to a particular well known opening, in this case the "Center Counter" also known as the "Scandinavian" or the "Scandinavian Gambit".
  • Move 4. for white begins the analysis.
  • Next is the first line of analytical output. Roughly speaking, this says that 'd3' looses the equivalent of 35 hundredths of a pawn, i.e. the move is bad! The '12' before the colon says that this position was examined to a depth of 12 plys (where ply is a half move) or 6 moves ahead.
  • The second line, says that Crafty thinks that a better response would have been the book move, 'Bc4'. The 0 for look ahead, represents a table lookup, i.e. Crafty read this move out of his opening book. Here the +0.00 given as the positional score indicates both sides even, no advantage to either.
The rest of the lines are all variations on this theme.

So where is the code you ask? Well, that's easy, it's here:

#!/perl/bin/perl # # craftysvg.pl -- script to generate .svg file from Crafty .pgn analy +sis. use strict; use warnings; use diagnostics; my @scores; my @allscores; my @bestblack; my @bestwhite; my $black; my $level; my $score; my $value; my $previous_level = 0; while (<>) { if (/^\s/) { if (/^\s+(\d+)\./) { $level = 1; $black = ( /\.\.\./ ? '1' : '0' ); s/^\s+//; my @temp = split (/\s+/); unless ( scalar(@temp) == 2 ) { unless ($black) { push ( @allscores, '0,1,0:+0.00' ); push ( @allscores, '0,2,0:+0.00' ); push ( @allscores, '1,1,0:+0.00' ); push ( @allscores, '1,2,0:+0.00' ); } } } elsif (/^\s+\(\{/) { /{(.*?)}/; push ( @allscores, "$black,$level,$1" ); $level++; } } } foreach (@allscores) { ( $black, $level, $score ) = split /,/; $score =~ /:([-+]\d+\.\d+)/; $value = $1; if ( $level == 1 ) { if ( $previous_level == 1 ) { push ( @bestblack, undef ); push ( @bestwhite, undef ); } if ($value) { push ( @scores, $value ); } else { push ( @scores, undef ); } } else { if ($black) { push ( @bestblack, $value ); push ( @bestwhite, undef ); } else { push ( @bestblack, undef ); push ( @bestwhite, $value ); } } $previous_level = $level; }
In short, given a Crafty .can file as input, parse it into three arrays.
  1. @scores Craftie's positional analysis for each side's move.
  2. @bestwhite Craftie's best short at improving white's move.
  3. @bestblack Craftie's best short at improving black's move.

So at this point we have the data, what about the pretty pictures? Well the first approach I used was to add use GD::Graph::mixed; to the top of my script and then this at the bottom:

my @data = ( [0..scalar(@scores) - 1], [@scores[0..scalar(@scores) - 1]], [@bestwhite[0..scalar(@scores) - 1]], [@bestblack[0..scalar(@scores) - 1]], ); my $graph = GD::Graph::mixed->new(800, 700); $graph->set( x_label => 'Performance By Half-move', y_label => 'Scoring By Centipawn', title => 'Game Performance Graph', y_max_value => 15, y_min_value => -15, y_tick_number => 30, y_label_skip => 2, zero_axis => 1, dclrs => [qw(lgray red green)], types => [qw(bars linespoints linespoints)], ); my $gd = $graph->plot(\@data); open(IMAGE,'>image.png') or die "Couldn't open image.png:$!\n"; binmode IMAGE; print IMAGE $gd->png(); close IMAGE;
Of course this isn't immediately useful, but a trival web page with a single image reference quickly gets you something you can hot-key to!

The second approach uses the same code to generate the arrays, but instead of GD as the graphics engine, I thought I'd investigate SVG instead. The necessary difference looks like this:

openSVG( 450, 450 ); openG( transform => 'translate(10,85) scale(1,-1)' ); openG( transform => 'scale(5)' ); foreach ( -15 .. 15 ) { hline( 0, 80, $_ ); } foreach ( 0 .. 80 ) { vline( -15, 15, $_ ); } path( d => 'M -1 0 H 81', style => 'stroke: red; stroke-opacity: .25; stroke-width: .1' ); graphline( 'black', 0.1, @scores ); graphline( 'green', 0.1, @bestblack ); graphline( 'red', 0.1, @bestwhite ); closeG(); closeG(); closeSVG(); sub path { openTAG( 'path', @_ ); closeTAG(); } sub openTAG { my $tag = shift; my %attributes = @_; print "<$tag"; foreach ( keys %attributes ) { print " $_=\"$attributes{$_}\""; } } sub closeTAG { my $s = shift; if ($s) { print "</$s>\n"; } else { print "/>\n"; } } sub openG { openTAG( 'g', @_ ); print ">\n"; } sub closeG { closeTAG('g'); } sub openSVG { my $height = shift; my $width = shift; print "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" +?>\n"; print "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"; print " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dt +d\">\n"; print "<svg height=\"$height\" width=\"$width\" viewbox=\"0,0,$width,$height +\">\n"; } sub closeSVG { closeTAG('svg'); } sub hline { my ( $x1, $x2, $y1, $color ) = @_; if ($color) { path( d => "M $x1,$y1 H $x2", style => "stroke: $color;" ); } else { path( d => "M $x1,$y1 H $x2" ); } } sub vline { my ( $y1, $y2, $x1, $color ) = @_; if ($color) { path( d => "M $x1,$y1 V $y2", style => "stroke: $color;" ); } else { path( d => "M $x1,$y1 V $y2" ); } } sub graphline { my $color = shift; my $width = shift; my @values = @_; my @list; my $y = 0; foreach (@values) { if ($_) { push ( @list, $y ); push ( @list, $_ ); } $y++; } openTAG( 'polyline', points => join ( ',', @list ), style => "stroke: $color; stroke-width: $width; fill: none;", ); closeTAG(); }
Yes, it is quite a bit longer, but it is also more interesting---at least to me!

1 For more information try Robert Hyatt's hompage.
2 Actually, I don't do it this way, I use a perl script that does all of the work for me in good 'lazy' programmer fashion!

--hsm

"Never try to teach a pig to sing...it wastes your time and it annoys the pig."

Comment on Perl, Chess, Lies, Damn Lies, and Statistics
Select or Download Code

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://219705]
Approved by Tanalis
Front-paged by Tanalis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (8)
As of 2014-11-22 12:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My preferred Perl binaries come from:














    Results (122 votes), past polls