-
Notifications
You must be signed in to change notification settings - Fork 1
/
GTP.pm
134 lines (109 loc) · 3.68 KB
/
GTP.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# DGSLib: A Library to access dragongoserver.net
# Copyright (C) 2006-2010 Yves Rutschle
#
# 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; either
# version 2 of the License, 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 for more
# details.
#
# The full text for the General Public License is here:
# http://www.gnu.org/licenses/gpl.html
package GTP;
use strict;
use IPC::Open2;
use Games::Go::SGF;
use GoQLib;
my $trace_gtp = 0;
# minimal implementation of Go Text Protocol for the DGS robot.
#
# GTP is specified at http://www.lysator.liu.se/~gunnar/gtp/gtp2-spec-draft2/gtp2-spec.html
#
# Create a new GTP Engine. Specify how to invoke the engine and some parameters
# support loading SGF:
# my $engine = new GTP "gnugo --mode gtp", $board;
# The engine string can contain a %d to specify the board size
sub new {
my ($class, $engine, $board, %opts) = @_;
$engine = sprintf($engine, $board->size) if ($engine =~ /%d/);
my ($in, $out);
my $pid = open2($out, $in, $engine);
die "open2: $!\n" unless defined $pid;
my %e;
$e{in} = $in;
$e{out} = $out;
$e{engine} = $engine;
return bless \%e, $class;
}
# Internal function: performs a GTP transaction
sub gtp_transaction {
my ($self, $command) = @_;
my ($out, $in) = ($self->{out}, $self->{in});
warn "GTP<- $command" if $trace_gtp;
print $in $command;
my $resp = <$out>;
warn "GTP-> $resp" if $trace_gtp;
if ($resp !~ /^= (.*)/) {
warn "Illegal GTP response <$resp>\n";
return undef;
}
$resp = $1;
<$out>; # throw out white line
return $resp;
}
use Data::Dumper;
# If loadsgf is not available, simulate it with play commands
sub loadsgf{
my ($self, $filename) = @_;
my ($last_node);
my $sgf = new Games::Go::SGF $filename;
my $size = $sgf->size;
$self->gtp_transaction("boardsize $size\n");
my $node = $sgf;
do {
my ($m, $c);
if (my $handi = $node->AB) {
foreach $m (split /,/, $handi) {
$c = convert_coord_letters_to_std $size, $m;
$self->gtp_transaction("play black $c\n");
}
}
if (my $preset = $node->AW) {
foreach $m (split /,/, $preset) {
$c = convert_coord_letters_to_std $size, $m;
$self->gtp_transaction("play white $c\n");
}
}
if ($m = $node->W) {
$c = convert_coord_letters_to_std $size,$m;
$self->gtp_transaction("play white $c\n");
} elsif ($m = $node->B) {
$c = convert_coord_letters_to_std $size,$m;
$self->gtp_transaction("play black $c\n");
} else {
# Probably first node with no handicap
#print "unknown: ". Dumper $node;
}
$last_node = $node;
} while ($node = $node->next);
# return whose turn it is to play
return 'black' unless defined $last_node->colour;
return $last_node->colour eq 'W' ? 'black' : 'white' ;
}
# Ask the engine the next move for the specified colour
# $move = $engine->next_move('white');
sub next_move {
my ($self, $colour) = @_;
#my $colour = $self->gtp_transaction("loadsgf $sgf\n");
#my $colour = $self->loadsgf($sgf);
die "<$colour> not valid GTP response\n" unless $colour =~ /^(black|white)$/;
my $move = $self->gtp_transaction("genmove $colour\n");
return $move;
}
1;