#!/usr/bin/perl use strict; use Data::Dumper; my( $matches, $games, $forced_moves, $total_moves, $analysed_pos, $diff_best ); my( $file1, $file2 ); my( $line ); my( $dir1, $dir2 ); my ($gno, $mno); # array of sum of discrepencies # win, wing, winbg, loseg, loseb, equity my @diffs; my @squares; # as above, but only for moves where best move decision differs my @best_diffs; my @best_squares; open( BEST, "> /tmp/bestdata" ) or die "Can't open /tmp/best: $!"; # called with one line from a file and a hash reference (either empty # or the returned value from a call of this routine from another file # parameter update simply toggles so that only one file is used to count # moves and forced moves. # returns "" if there is no move in the input line (double/take/drop # otherwise the input hash will have a set of keys for each move analysed # (the key is one to four letter pairs indicating the source and destination # of one chequer moved # the value for each key will be a hash with keys rank - the rank, 1=best # of the move corresponding to the key and key 'ana' which is an array # reference - the array has the elements win, wing, winbg, loseg, losebg, # equity sub doline { my( $l, $moves, $update ) = @_; my $key; # exit early if not a move return "" if( $l !~ /;[BW]\[([^\]]*)\]/); # extrace dice roll my $m = $1; # not a dice roll, exit early return "" if( $m =~ /^\d+$/ ); $total_moves++ if( $update ); # look for the analysis of the move - it will be form # A[n][rank of move made 0 = best][move encoded as letter string # src/dst (there will be between 1 and 4 such pairs of source dest # letters, then the evaluation # force moves aren't analysed, so we simply record it as forced if( $l !~ /^.*A\[\d+\](.*)/ ) { ++$forced_moves if( $update ); return ""; } $l = $1; # discard the leading '[' $l =~ s/\[//g; then break the move evaluations into separate strings my @evals = split( /\]/, $l ); my $rank = 0; # the list of evaluations are in order best to worst while( my $e = shift @evals ) { # rank of move 1 = best ++$rank; # skip if we can't make sense of it next if( not $e =~ /^([a-z]+) E (.*) 2C .*/ ); # key is encoding of move, $e is the series of values # win, wing, winbg, loseg, losebg, equitey ($key, $e) = ($1, $2); $$moves{ $key }->{ 'rank' } = $rank; $$moves{ $key }->{ 'ana' } = [ split( /\s+/, $e ) ]; } return $m; } # called with one move from each of the two files to be compared sub domove { my( $l1, $l2 ) = @_; my( $m1, $m2, %move1, %move2 ); # we count all lines as moves unless they are takes (don't know # why I did this ++$mno if( $l1 !~ /^;[BW]\[take\]/ ); # get a hash from each file's line $m1 = doline( $l1, \%move1, 0 ); $m2 = doline( $l2, \%move2, 1 ); # comparision is shallow, actually only checks that they have the # the same first level keys, meaning each file analysed the same set # of moves if( $m1 ne $m2 ) { die "mismatched moves at line $line of $file1\n"; } # exit early if it's not a move or it's a forced move return if( $m1 eq "" ); # now compare the ranks and analyses of the moves foreach my $k1 (sort ( keys %move1 ) ) { my ($r1, $r2); $r1 = $move1{ $k1 }->{ 'rank' }; $r2 = $move2{ $k1 }->{ 'rank' }; # processing if best mvoe from file1 isn't the best mvoe in file2 if( ( $r1 == 1 ) and ( $r1 != $r2 ) ) { if ( $move2{ $k1 }->{ 'rank ' } != 1 ) { # print "Move1:\n"; # print Dumper($move1{ $k1 }); # print "Move2:\n"; # print Dumper($move2{ $k1 }); ++$diff_best; # find the best move in file 2 foreach my $k2 ( sort ( keys %move2 ) ) { next if( $move2{ $k2 }->{ 'rank' } != 1 ); # see what rank it had in file 1 $r1 = $move1{ $k2 }->{ 'rank' }; my $d; # if the best move in file 2 was analysed in file 1... if( defined( $move1{ $k2 } ) ) { # update stats on difference between file1's analysis # of file1's best move with file1's analysis of the # best move according to file2 my $max = 0; for( my $i = 0; $i < 6; ++$i ) { $d = $move1{ $k1 }->{ 'ana' }[ $i ] - $move1{ $k2 }->{ 'ana' }[ $i ]; $best_diffs[ $i ] += abs $d ; $best_squares[ $i ] += $d * $d; $max = abs $d if(((abs $d) > $max) and ($i != 5)); # printf BEST "%g ", abs $d; } # report difference in equities between best move # in file1 vs. file1's equitie for best mvoe according # to file2 printf BEST "%5.5f %s %d:%d\n", $d, $file1, $gno,$mno; } # report files disagree on best move print STDERR "Wrong rank $k1 ($r2, $r1) at game $gno, ". "move $mno - $file1:$line\n$l1\n$file2:$line\n$l2\n"; } } } next if( !defined( $move2{ $k1 }->{ 'ana' } ) ); ++$analysed_pos; # accumulate differences in the various figures for every move # which was analysed in both matches for( my $i = 0; $i < 6; ++$i ) { my $d = $move1{ $k1 }->{ 'ana' }[ $i ] - $move2{ $k1 }->{ 'ana' }[ $i ]; $diffs[ $i ] += abs $d ; $squares[ $i ] += $d * $d; } } } # called after the first line of a game has been read, $fh1, $fh2 are # the input handles for the files being compared, the next read will # get a move line, the first line of the next game, or eof returns # TRUE if there are more games to read sub dogame { my( $fh1, $fh2 ) = @_; my( $l1, $l2 ); ++$games; ++$gno; $mno = 0; while( $l1 = <$fh1> ) { ++$line; $l1 =~ s/\s+$//; $l2 = <$fh2> or die "Mismatched games - eof at $line of $file2"; if( $l1 =~ /^\(/ ) { return 1 if( $l2 =~ /^\(/ ); die "End of game at $line of $file1 but not in $file2"; } die "End of game at $line of $file2 but not in $file1" if ( $l2 =~ /^\(/ ); domove( $l1, $l2 ); } while ( $l2 = <$fh2> ) { die "Eof at $line of $file1, not in $file2"; } return 0; } # process an entire match out of an sgf file sub domatch { my( $file ) = @_; ++$matches; $gno = -1; $file1 = "$dir1/$file"; $file2 = "$dir2/$file"; $line = 0; open( FILE1, "< $file1" ) or die "Can't open $file1: $!"; open( FILE2, "< $file2" ) or die "Can't open $file2: $!"; while( dogame( \*FILE1, \*FILE2 ) ) { # no body } close( FILE1 ); close( FILE2 ); } sub usage { print < ) { s/\s+$//; next if( /^$/ ); domatch( $_ ); } print "$matches matches, $games games, $total_moves moves\n"; print "$forced_moves forced moves, $analysed_pos positions\n"; print "The best move was different in $diff_best cases\n"; print "Average Diff/Std Err\n"; for( my $i = 0; $i < 6; ++$i ) { printf "%1.5f ", $diffs[ $i ] / $analysed_pos; } print "\n"; for( my $i = 0; $i < 6; ++$i ) { printf "%1.5f ", sqrt( $squares[ $i ] / $analysed_pos ); } print "\n"; if( $diff_best > 0 ) { print "Where best moves differed, $dir1 match evaluations of the choices\n"; print "Average Diff/Std Err\n"; for( my $i = 0; $i < 6; ++$i ) { printf "%1.5f ", $best_diffs[ $i ] / $diff_best; } print "\n"; for( my $i = 0; $i < 6; ++$i ) { printf "%1.5f ", sqrt( $best_squares[ $i ] / $diff_best ); } print "\n"; } } main;