#! /usr/bin/perl -w # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # This program is distributed with GNU Go, a Go program. # # # # Write gnugo@gnu.org or see http://www.gnu.org/software/gnugo/ # # for more information. # # # # Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 # # 2008 and 2009 by the Free Software Foundation. # # # # This program is free software; you can redistribute it and/or # # modify it under the terms of the GNU General Public License # # as published by the Free Software Foundation - version 3, # # or (at your option) any later version. # # # # This program is distributed in the hope that it will be # # useful, but WITHOUT ANY WARRANTY; without even the implied # # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # # PURPOSE. See the GNU General Public License in file COPYING # # for more details. # # # # You should have received a copy of the GNU General Public # # License along with this program; if not, write to the Free # # Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # # Boston, MA 02111, USA. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Here is a small perlscript twogtp. Its purpose is to run # two programs against each other. Both must support the Go # Text Protocol. For example GNU Go 2.7.241 or higher works. # # It is easier to implement this program in gtp than gmp. # The script is almost trivial. It also works with cygwin on # windows. # # Run with: # # twogtp --white ' --mode gtp ' \ # --black ' --mode gtp ' \ # [twogtp options] # # Possible twogtp options: # # --verbose 1 (to list moves) or --verbose 2 (to draw board) # --komi # --handicap # --size (default 19) # --games (-1 to play forever) # --sgffile # # package TWOGTP_A; use IPC::Open2; use Getopt::Long; use FileHandle; use strict; use warnings; use Carp; STDOUT->autoflush(1); #following added globally to allow "use strict" : my $vertex; my $first; my $sgfmove; my $sgffilename; my $pidw; my $pidb; my $sgffile; my $handicap_stones; my $resultw; my $resultb; my @vertices; my $second; my %game_list; #end of "use strict" repairs my $white; my $black; my $size = 19; my $verbose = 0; my $komi; my $handicap = 0; my $games = 1; my $wanthelp; my $helpstring = " Run with: twogtp --white \' --mode gtp [program options]\' \\ --black \' --mode gtp [program options]\' \\ [twogtp options] Possible twogtp options: --verbose 1 (to list moves) or --verbose 2 (to draw board) --komi --handicap --size (default 19) --games (-1 to play forever) --sgffile --help (show this) "; GetOptions( "white|w=s" => \$white, "black|b=s" => \$black, "verbose|v=i" => \$verbose, "komi|k=f" => \$komi, "handicap|h=i" => \$handicap, "size|boardsize|s=i" => \$size, "sgffile|o=s" => \$sgffilename, "games=i" => \$games, "help" => \$wanthelp, ); if ($wanthelp) { print $helpstring; exit; } if (!$white) { $white = '../gnugo.exe --mode gtp --quiet'; warn "Defaulting white to: $white"; } if (!$black) { $black = '../gnugo.exe --mode gtp --quiet'; warn "Defaulting black to: $black"; } die $helpstring unless defined $white and defined $black; # create FileHandles #my $black_in; my $black_in = new FileHandle; # stdin of black player my $black_out = new FileHandle; # stdout of black player my $white_in = new FileHandle; # stdin of white player my $white_out = new FileHandle; # stdout of white player my $b_gtp_ver; # gtp version of black player my $w_gtp_ver; # gtp version of white player while ($games > 0) { $pidb = open2($black_out, $black_in, $black); print "black pid: $pidb\n" if $verbose; $pidw = open2($white_out, $white_in, $white); print "white pid: $pidw\n" if $verbose; $sgffile = rename_sgffile($games, $sgffilename) if defined $sgffilename; if ((defined $sgffilename) && !open(SGFFILEHANDLE, ">$sgffile")) { printf("can't open $sgffile\n"); undef($sgffilename); } if (!defined $komi) { if ($handicap eq 0) { $komi = 5.5; } else { $komi = 0.5; } } print $black_in "protocol_version\n"; $b_gtp_ver = eat_gtp_ver($black_out); print $black_in "boardsize $size\n"; eat_no_response($black_out); print $black_in "clear_board\n"; eat_no_response($black_out); print $black_in "komi $komi\n"; eat_no_response($black_out); print $white_in "protocol_version\n"; $w_gtp_ver = eat_gtp_ver($white_out); print $white_in "boardsize $size\n"; eat_no_response($white_out); print $white_in "clear_board\n"; eat_no_response($white_out); print $white_in "komi $komi\n"; eat_no_response($white_out); print SGFFILEHANDLE "(;GM[1]FF[4]RU[Japanese]SZ[$size]HA[$handicap]KM[$komi]" if defined $sgffilename; my $pass = 0; my ($move, $toplay); if ($handicap < 2) { $toplay = 'B'; } else { $toplay = 'W'; print $black_in "fixed_handicap $handicap\n"; $handicap_stones = eat_handicap($black_out); if (defined $sgffilename) { print SGFFILEHANDLE $handicap_stones; } print $white_in "fixed_handicap $handicap\n"; $handicap_stones = eat_handicap($white_out); } while ($pass < 2) { if ($toplay eq 'B') { if ($b_gtp_ver eq 1) { print $black_in "genmove_black\n"; } else { print $black_in "genmove black\n"; } $move = eat_move($black_out); $sgfmove = standard_to_sgf($move); print SGFFILEHANDLE ";B[$sgfmove]\n" if defined $sgffilename; print "Black plays $move\n" if $verbose; if ($move =~ /PASS/i) { $pass++; } else { $pass = 0; } if ($w_gtp_ver eq 1) { print $white_in "black $move\n"; } else { print $white_in "play black $move\n"; } eat_no_response($white_out); if ($verbose > 1) { print $white_in "showboard\n"; if ($w_gtp_ver eq 2) { eat_showboard($white_out); } else { eat_no_response($white_out); } } $toplay = 'W'; } else { if ($w_gtp_ver eq 1) { print $white_in "genmove_white\n"; } else { print $white_in "genmove white\n"; } $move = eat_move($white_out); $sgfmove = standard_to_sgf($move); print SGFFILEHANDLE ";W[$sgfmove]\n" if defined $sgffilename; print "White plays $move\n" if $verbose; if ($move =~ /PASS/i) { $pass++; } else { $pass = 0; } if ($b_gtp_ver eq 1) { print $black_in "white $move\n"; } else { print $black_in "play white $move\n"; } eat_no_response($black_out); if ($verbose > 1) { print $black_in "showboard\n"; if ($b_gtp_ver eq 2) { eat_showboard($black_out); } else { eat_no_response($black_out); } } $toplay = 'B'; } } print $white_in "final_score\n"; $resultw = eat_score($white_out); print "Result according to W: $resultw\n"; print $black_in "final_score\n"; $resultb = eat_score($black_out); print "Result according to B: $resultb\n"; print $white_in "quit\n"; print $black_in "quit\n"; if (defined $sgffilename) { print "sgf file: $sgffile\n"; print SGFFILEHANDLE ")"; close SGFFILEHANDLE; $game_list{$sgffile} = $resultw . "|" . $resultb } $games-- if $games > 0; close $black_in; close $black_out; close $white_in; close $white_out; waitpid $pidb, 0; waitpid $pidw, 0; print "games remaining: $games\n"; } if (defined $sgffilename) { my $index_out = new FileHandle; open ($index_out, "> " . index_name($sgffilename)); print $index_out "game results

Game Results

White: ".html_encode($white)."

Black: ".html_encode($black)."

"; foreach (sort by_result keys(%game_list)) { print $index_out "" . "\n"; } print $index_out "
SGF file Result
$_".html_encode(game_result($_))."
\n"; } sub game_result { $_ = shift; $_ = $game_list{$_}; #i.e.: B+13.5 (upper bound: -13.5, lower: -13.5)|B+13.5 (upper bound: -13.5, lower: -13.5) #Make sure that all 4 values are the same. I've not seen them different yet. #If they are ever different, need to improve the HTML output (now just -999) - # an explanation of the score mismatch problem would be appropriate. $_ =~ /^.*upper bound..([0-9+.\-]*)..lower..\1.\|.*upper bound..\1..lower..\1./; if (defined($1)) { return $1; } else { return -999; } } sub by_result { game_result($a) <=> game_result($b) || $a cmp $b; } sub html_encode { #print shift; my $r = shift; $r =~ s/&/&/g; $r =~ s//>/g; return $r; } sub eat_no_response { my $h = shift; # ignore empty lines my $line = ""; while ($line eq "") { chop($line = <$h>) or die "No response!"; $line =~ s/(\s|\n)*$//smg; } } sub eat_move { my $h = shift; # ignore empty lines my $line = ""; while ($line eq "") { if (!defined($line = <$h>)) { print SGFFILEHANDLE ")"; close SGFFILEHANDLE; die "Engine crashed!\n"; } $line =~ s/(\s|\n)*$//smg; } my ($equals, $move) = split(' ', $line, 2); $line = <$h>; defined($move) or confess "no move found: line was: '$line'"; return $move; } sub eat_handicap { my $h = shift; my $sgf_handicap = "AB"; # ignore empty lines, die if process is gone my $line = ""; while ($line eq "") { chop($line = <$h>) or die "No response!"; } @vertices = split(" ", $line); foreach $vertex (@vertices) { if (!($vertex eq "=")) { $vertex = standard_to_sgf($vertex); $sgf_handicap = "$sgf_handicap\[$vertex\]"; } } return "$sgf_handicap;"; } sub eat_score { my $h = shift; # ignore empty lines, die if process is gone my $line = ""; while ($line eq "") { chop($line = <$h>) or die "No response!"; $line =~ s/^\s*//msg; $line =~ s/\s*$//msg; } $line =~ s/\s*$//; my ($equals, $result) = split(' ', $line, 2); $line = <$h>; return $result; } sub eat_gtp_ver { my $h = shift; my $line = ""; while ($line eq "") { chop($line = <$h>) or die "No response!"; $line =~ s/^\s*//msg; $line =~ s/\s*$//msg; } $line =~ s/\s*$//; my ($equals, $result) = split(' ', $line, 2); $line = <$h>; return $result; } sub eat_showboard { my $h = shift; my $line = ""; while ($line eq "") { chop($line = <$h>) or die "No response!"; $line =~ s/^\s*//msg; $line =~ s/\s*$//msg; } $line =~ s/\s*$//; my ($equals, $result) = split(' ', $line, 2); while (!($line =~ /^\s*$/)) { $result .= $line; $line = <$h>; } print STDERR $result; } sub standard_to_sgf { for (@_) { confess "Yikes!" if !defined($_); } for (@_) { tr/A-Z/a-z/ }; $_ = shift(@_); /([a-z])([0-9]+)/; return "tt" if $_ eq "pass"; $first = ord $1; if ($first > 104) { $first = $first - 1; } $first = chr($first); $second = chr($size+1-$2+96); return "$first$second"; } sub rename_sgffile { my $nogames = int shift(@_); $_ = shift(@_); s/\.sgf$//; # Annoying to loose _001 on game #1 in multi-game set. # Could record as an additional parameter. # return "$_.sgf" if ($nogames == 1); return sprintf("$_" . "_%03d.sgf", $nogames); } sub index_name { $_ = shift; s/\.sgf$//; return $_ . "_index.html"; }