Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

comment on

( #3333=superdoc: 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."

In reply to Perl, Chess, Lies, Damn Lies, and Statistics by hsmyers

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others about the Monastery: (4)
    As of 2021-02-25 20:00 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found

      Notices?