#!/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 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. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # use Tk; use ttgo; use FileHandle; use IPC::Open2; # use strict; $| = 1; my $boardsize = 11; my $autoplay = 1; my @program = (); my @cur_id = (); my $Aprg_in = new FileHandle; my $Aprg_out = new FileHandle; $program[0] = 'gnugo --mode gtp --quiet'; $cur_id[0] = 1; # starting id my $Bprg_in = new FileHandle; my $Bprg_out = new FileHandle; $program[1] = 'gnugo --mode gtp --score aftermath --capture-all-dead --chinese-rules --quiet'; $cur_id[1] = 1; # starting id my $state = 'start'; # first initialization state open2($Aprg_out, $Aprg_in, $program[0]); $flags = fcntl( $Aprg_out, F_GETFL, 0) or die "Error with fcntl\n"; $flags = fcntl( $Aprg_out, F_SETFL, $flags | O_NOBLOCK) or die "Error with fcntl\n"; open2($Bprg_out, $Bprg_in, $program[1]); $flags = fcntl( $Bprg_out, F_GETFL, 0) or die "Error with fcntl\n"; $flags = fcntl( $Bprg_out, F_SETFL, $flags | O_NOBLOCK) or die "Error with fcntl\n"; my $flags = 0; my $consecutive_passes = 0; my $ctm = 'B'; # who's turn to move? my $cc = 'W'; # computers color my $msgstr = ''; # This handles up to 25 size boards # ================================= my @letter = qw ( A B C D E F G H J K L M N O P Q R S T U V W X Y Z ); # color definitions # ================= my %cstr = ( 'b' => '#604040', 'B' => '#604040', 'w' => '#ffffff', 'W' => '#ffffff' ); my $bkc = '#eeeeee'; # get command line arguments start with defaults # ============================================== my $sqwh = 26; my $sqwh2 = 12; # 1/2 of sqwh my %toix = (); foreach my $ix (0 .. $#letter) { $toix{ $letter[$ix] } = $ix; } # initialize graphics and such # ---------------------------- my $top = MainWindow->new; $top->title("ptkgo.pl"); $top->resizable(0,0); my $geox = ($boardsize-1) * $sqwh + 80; my $geoy = ($boardsize-1) * $sqwh + 140; $top->geometry( $geox . 'x' . $geoy ); $top->configure( background => $bkc ); # build the background go board my $backing = $top->Canvas( -width => $sqwh * $boardsize + 80, -height => $sqwh * $boardsize + 80, -background => $bkc )->place( -x => 0, -y => 0, ); foreach my $x ( 0 .. $boardsize-1 ) { $backing->createText( 40 + $x * $sqwh, 25, -text => $letter[$x], -fill => 'black', -justify => 'center', -font => '-b&h-*-bold-r-*-*-11-*-*-*-*-*-*-*' ); $backing->createText( 40 + $x * $sqwh, ($boardsize-1)*$sqwh + 55, -text => $letter[$x], -fill => 'black', -justify => 'center', -font => '-b&h-*-bold-r-*-*-11-*-*-*-*-*-*-*' ); $backing->createLine( $x*$sqwh + 40, 40, $x*$sqwh+40, ($boardsize-1)*$sqwh + 40, -fill => 'black', -width => 1 ); } foreach my $y ( 0 .. $boardsize-1 ) { $backing->createText( 25, $y * $sqwh + 40, -text => $boardsize - $y, -fill => 'black', -justify => 'center', -font => '-b&h-*-bold-r-*-*-11-*-*-*-*-*-*-*' ); $backing->createText( ($boardsize-1) * $sqwh + 55, $y * $sqwh + 40, -text => $boardsize - $y, -fill => 'black', -justify => 'center', -font => '-b&h-*-bold-r-*-*-11-*-*-*-*-*-*-*' ); $backing->createLine( 40, $y*$sqwh+40, ($boardsize-1)*$sqwh+40, $y*$sqwh + 40, -fill => 'black', -width => 1 ); } ttNewGame($boardsize); ttShowBoard(); # pass button # ----------- my $pass = $top->Button( -text => 'Pass', -command => sub { }, -width => 2, -height => 1, -font => '5x7', -borderwidth => 1, -highlightcolor => 'black', -highlightthickness => 1, -highlightbackground => 'black', -relief => 'flat' )->place( -x => 40 + 0 * 40, -y => ($boardsize + 2) * $sqwh, ); # undo button # ----------- my $undo = $top->Button( -text => 'Undo', -command => sub { }, -width => 2, -height => 1, -font => '5x7', -borderwidth => 1, -highlightcolor => 'black', -highlightthickness => 1, -highlightbackground => 'black', -relief => 'flat' )->place( -x => 40 + 1 * 40, -y => ($boardsize + 2) * $sqwh, ); $top->bind( "", [ \&drop_stone, Ev('x'), Ev('y') ] ); $top->fileevent( $Aprg_out, 'readable', [ \&getmessage, 0] ); $top->fileevent( $Bprg_out, 'readable', [ \&getmessage, 1] ); $state = 'start'; # first initialization state control(); MainLoop(); my $tmpstr; sub getmessage { my ($pi) = @_; if ($pi == 0) { $tmpstr = <$Aprg_out>; } else { $tmpstr = <$Bprg_out>; } if (defined $tmpstr) { chomp($tmpstr); if ($tmpstr eq '') { # eat the line, update id $cur_id[$pi] ++; control( $msgstr ); } else { $msgstr = $tmpstr; print "Came up with $msgstr\n"; } } } sub xputstone { my ($color, $x, $y) = @_; my $xx = $x * $sqwh + 40; my $yy = $y * $sqwh + 40; $backing->createOval( $xx-$sqwh2, $yy-$sqwh2, $xx+$sqwh2, $yy+$sqwh2, -tags => $x . '_' . $y, -outline => 'black', -fill => $cstr{$color} ); } # This routine clears all empty squares, it does # not actually draw board sub xfixboard { my @vis = ttGetBoard(); my $st; foreach my $y (0 .. $boardsize -1) { foreach my $x (0 .. $boardsize -1) { $st = shift @vis; if ($st eq '+') { $backing->delete( $x . '_' . $y ); } } } } sub pass { } sub drop_stone { my ( $w, $x, $y) = @_; $x = -1 + int(($x-3) / 26); $y = -1 + int(($y-3) / 26); if ($x < 0) { return 1; } if ($y < 0) { return 1; } if ($x >= $boardsize) { return 1; } if ($y >= $boardsize) { return 1; } my $gn = $letter[$x] . ($boardsize - $y); if ( !ttPlaceStone( $ctm, $gn ) ) { xputstone( $ctm, $x, $y ); xfixboard(); ttShowBoard(); } else { return 1; } if ($ctm eq 'W') { $state = 'white'; } else { $state = 'black'; } swap_ctm(); } # This routine is called after each message is recieved # ----------------------------------------------------- # How the control loop works: # # the '$state' variable determines where to jump in. # control is called when a program responds to a message sub control { my ($msg) = @_; # send boardsize 0 (prgA) # send boardsize 1 (prgB) # xxx # send genmove_black (prgA); # send black (prgB); # send genmove_white (prgB); # white (prgA) # goto xxx if (defined $msg) { print STDERR "state/msg = $state $msg\n"; } else { print STDERR "state/msg = $state NULL\n"; } if ($state eq 'start') { snd( 0, "$cur_id[0] boardsize $boardsize" ); $state = 'startb'; return; } if ($state eq 'startb') { snd( 1, "$cur_id[1] boardsize $boardsize" ); $state = 'genmove_black'; return; } if ( $state eq 'genmove_black' ) { snd( 0, "$cur_id[0] genmove_black" ); $state = 'black'; return; } if ( $state eq 'black' ) { my $y; my $x; my $gn; print "msg ---> $msg\n"; $msg =~ /^=\d+\s+(.)(.*)/; # parse out move components if ( $msg =~ /PASS/ ) { $consecutive_passes++; $gn = 'PASS'; } else { $consecutive_passes = 0; $y = $boardsize - $2; $x = $toix{$1}; $gn = $letter[$x] . ($boardsize - $y); } # show blacks move to the interface # --------------------------------- if ( !ttPlaceStone( $ctm, $gn ) ) { xputstone( $ctm, $x, $y ) if $gn ne 'PASS'; xfixboard(); ttShowBoard(); swap_ctm(); } else { return 1; } # send the move along to WHITE # ---------------------------- snd( 1, "$cur_id[1] black $gn" ); $state = 'genmove_white'; if ($consecutive_passes == 2) { $state = 'gameover'; } return; } if ( $state eq 'genmove_white' ) { snd( 1, "$cur_id[1] genmove_white" ); $state = 'white'; return; } if ( $state eq 'white' ) { my $y; my $x; my $gn; print "msg ---> $msg\n"; $msg =~ /^=\d+\s+(.)(.*)/; # parse out move components if ( $msg =~ /PASS/ ) { $consecutive_passes++; $gn = 'PASS'; } else { $consecutive_passes = 0; $y = $boardsize - $2; $x = $toix{$1}; $gn = $letter[$x] . ($boardsize - $y); } # show blacks move to the interface # --------------------------------- if ( !ttPlaceStone( $ctm, $gn ) ) { xputstone( $ctm, $x, $y ) if $gn ne 'PASS'; xfixboard(); ttShowBoard(); swap_ctm(); } else { return 1; } # send the move along to BLACK # ---------------------------- snd( 0, "$cur_id[0] white $gn" ); $state = 'genmove_black'; if ($consecutive_passes == 2) { $state = 'gameover'; } return; } if ( $state eq 'gameover' ) { print "Game Over\n"; ttScore(); } } sub snd { my ($who, $str) = @_; if ($who == 0) { print $Aprg_in "$str\n"; } else { print $Bprg_in "$str\n"; } print STDERR "----> $str\n"; } sub swap_ctm { if ( $ctm eq 'B' ) { $ctm = 'W'; } else { $ctm = 'B'; } }