gnugo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnugo-devel] Yet another twogtp


From: Inge Wallin
Subject: [gnugo-devel] Yet another twogtp
Date: Thu, 8 Aug 2002 20:35:49 +0200 (MEST)

I have been doing some experimenting with other game programming than
Gnu Go, and I have decided to try to use GTP for playing two programs
against each other.  GTP is very flexible and easy to code for and
even if the game in question is not go, it is a good framework.

But I find perl a terrible language to work with, so for those of who
like python more, here is a version of twogtp written in that
language. For me, at least, that will be millions of times easier to
develop further.  And I also think it will be much easier for most
people to read.

So, without further ado, here is twogtp.py.

        -Inge

================================================================
#! /usr/bin/python

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# This program is distributed with GNU GO, a Go program.        #
#                                                               #
# Write address@hidden 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 2.     #
#                                                               #
# 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., 59 Temple Place - Suite 330,       #
# Boston, MA 02111, USA.                                        #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

from getopt import *
import popen2
import sys
import string


debug = 0


def coords_to_sgf(size, board_coords):
    global debug
    
    board_coords = string.lower(board_coords)
    if board_coords == "pass":
        return ""
    if debug:
        print "Coords: <" + board_coords + ">"
    letter = board_coords[0]
    digits = board_coords[1:]
    if letter > "i":
        sgffirst = chr(ord(letter) - 1)
    else:
        sgffirst = letter
    sgfsecond = chr(ord("a") + int(size) - int(digits))
    return sgffirst + sgfsecond



class GTP_connection:

    #
    # Class members:
    #   outfile         File to write to
    #   infile          File to read from

    def __init__(self, command):
        try:
            infile, outfile = popen2.popen2(command)
        except:
            print "popen2 failed"
            sys.exit(1)
        self.infile  = infile
        self.outfile = outfile
        
    def exec_cmd(self, cmd):
        global debug
        
        if debug:
            sys.stderr.write("GTP command: " + cmd + "\n")
        self.outfile.write(cmd + "\n\n")
        self.outfile.flush()
        result = ""
        line = self.infile.readline()
        while line != "\n":
            result = result + line
            line = self.infile.readline()
        if debug:
            sys.stderr.write("Reply: " + line + "\n")

        # Remove trailing newline from the result
        if result[-1] == "\n":
            result = result[:-1]

        if len(result) == 0:
            return "ERROR: len = 0"
        if (result[0] == "?"):
            return "ERROR: GTP Command failed: " + result[2:]
        if (result[0] == "="):
            return result[2:]
        return "ERROR: Unrecognized answer: " + result
        

class GTP_player:

    # Class members:
    #    connection     GTP_connection

    def __init__(self, command):
        self.connection = GTP_connection(command)

    def genmove(self, color):
        if color[0] in ["b", "B"]:
            command = "genmove_black"
        elif color[0] in ["w", "W"]:
            command = "genmove_white"
        return self.connection.exec_cmd(command)

    def black(self, move):
        self.connection.exec_cmd("black " + move)

    def white(self, move):
        self.connection.exec_cmd("white " + move)

    def komi(self, komi):
        self.connection.exec_cmd("komi " + komi)

    def boardsize(self, size):
        self.connection.exec_cmd("boardsize " + size)

    def handicap(self, handi):
        result = self.connection.exec_cmd("fixed_handicap " + handi)
        return string.split(result, " ")

    def quit(self):
        return self.connection.exec_cmd("quit")
    
    def showboard(self):
        return self.connection.exec_cmd("showboard")

    def get_random_seed(self):
        result = self.connection.exec_cmd("get_random_seed")
        if result[:5] == "ERROR":
            return "unknown"
        return result

    def get_program_name(self):
        return self.connection.exec_cmd("name") + " " + \
               self.connection.exec_cmd("version")

    def estimate_score(self):
        return self.connection.exec_cmd("estimate_score")

    def score(self):
        return self.estimate_score(self)


class GTP_game:

    # Class members:
    #    whiteplayer     GTP_player
    #    blackplayer     GTP_player
    #    size            int
    #    komi            float
    #    handicap        int
    #    handicap_stones int
    #    moves           list of string
    #    resultw
    #    resultb

    def __init__(self, whitecommand, blackcommand, size, komi, handicap):
        self.whiteplayer = GTP_player(whitecommand)
        self.blackplayer = GTP_player(blackcommand)
        self.size = size
        self.komi = komi
        self.handicap = handicap

    def writesgf(self, sgffilename):
        "Write the game to an SGF file after a game"

        size = self.size
        outfile = open(sgffilename, "w")
        if not outfile:
            print "Couldn't create " + sgffilename
            return
        black_name = self.blackplayer.get_program_name()
        white_name = self.whiteplayer.get_program_name()
        black_seed = self.blackplayer.get_random_seed()
        white_seed = self.whiteplayer.get_random_seed()
        handicap = self.handicap
        komi     = self.komi
        result   = self.resultw

        outfile.write("(;GM[1]FF[4]RU[Japanese]SZ[%s]HA[%s]KM[%s]RE[%s]\n" %
                      (size, handicap, komi, result))
        outfile.write("PW[%s (random seed %s)]PB[%s (random seed %s)]\n" %
                      (white_name, white_seed, black_name, black_seed))

        if handicap > 1:
            for stone in self.handicap_stones:
                outfile.write("AB[%s]", coords_to_sgf(size, stone))
            outfile.write("\n")

        if int(handicap) < 2:
            to_play = "B"
        else:
            to_play = "W"

        for move in self.moves:
            sgfmove = coords_to_sgf(size, move)
            outfile.write(";%s[%s]\n" % (to_play, sgfmove))
            if to_play == "B":
                to_play = "W"
            else:
                to_play = "B"
        outfile.write(")\n")
        outfile.close

    def play(self, sgffile):
        "Play a game"
        global verbose

        if verbose >= 1:
            print "Setting boardsize and komi for black\n"
        self.blackplayer.boardsize(self.size)
        self.blackplayer.komi(self.komi)

        if verbose >= 1:
            print "Setting boardsize and komi for white\n"
        self.whiteplayer.boardsize(self.size)
        self.whiteplayer.komi(self.komi)

        self.handicap_stones = []
        if int(self.handicap) < 2:
            to_play = "B"
        else:
            self.handicap_stones = self.whiteplayer.handicap(self.handicap)
            for stone in self.handicap_stones:
                self.blackplayer.black(stone)
            to_play = "W"

        self.moves = []
        passes = 0
        while passes < 2:
            if to_play == "B":
                move = self.blackplayer.genmove("black")
                if move[:5] == "ERROR":
                    # FIXME: write_sgf
                    sys.exit(1)

                self.moves.append(move)
                if string.lower(move[:4]) == "pass":
                    passes = passes + 1
                else:
                    passes = 0
                    self.whiteplayer.black(move)
                    if verbose >= 1:
                        print "Black plays " + move

                to_play = "W"
            else:
                move = self.whiteplayer.genmove("white")
                if move[:5] == "ERROR":
                    # FIXME: write_sgf
                    sys.exit(1)

                self.moves.append(move)
                if string.lower(move[:4]) == "pass":
                    passes = passes + 1
                else:
                    passes = 0
                    self.blackplayer.white(move)
                    if verbose >= 1:
                        print "White plays " + move
                to_play = "B"
            if verbose >= 2:
                print self.whiteplayer.showboard()

        self.resultw = self.whiteplayer.estimate_score()
        self.resultb = self.blackplayer.estimate_score()
        if self.resultb == self.resultw:
            print "Result: ", self.resultw
        else:
            print "Result according to W: ", self.resultw
            print "Result according to B: ", self.resultb
        # FIXME:   $self->writesgf($sgffile) if defined $sgffile;
        if sgffile != "":
            self.writesgf(sgffile)

    def result(self):
        return (self.resultw, self.resultb)
    
    def quit(self):
        self.blackplayer.quit()
        self.whiteplayer.quit()


class GTP_match:

    # Class members:
    #    black
    #    white
    #    size
    #    komi
    #    handicap

    def __init__(self, whitecommand, blackcommand, size, komi, handicap):
        self.white = whitecommand
        self.black = blackcommand
        self.size = size
        self.komi = komi
        self.handicap = handicap

    def play(self, games, sgfbase):
        game = GTP_game(self.white, self.black,
                        self.size, self.komi, self.handicap)
        results = []
        for i in range(games):
            sgffilename = "%s%03d.sgf" % (sgfbase, i + 1)
            print "Game ", i + 1
            game.play(sgffilename)
            results.append(game.result())
        game.quit()
        return results


# ================================================================
#                      Main program
#


# Default values
#

white    = ""
black    = ""
komi     = "5.5"
size     = "19"
handicap = "0"

games   = 1
sgffile = "twogtp"

verbose = 0

helpstring = """

Run with:

twogtp --white \'<path to program 1> --mode gtp [program options]\' \\
       --black \'<path to program 2> --mode gtp [program options]\' \\
       [twogtp options]

Possible twogtp options:

  --verbose 1 (to list moves) or --verbose 2 (to draw board)
  --komi <amount>
  --handicap <amount>
  --size <board size>               (default 10)
  --games <number of games to play> (default 1)
  --sgfbase <filename>              (create sgf files with sgfbase as basename)
"""

def usage():
    print helpstring
    sys.exit(1)

(opts, params) = getopt(sys.argv[1:], "",
                        ["black=",
                         "white=",
                         "verbose=",
                         "komi=",
                         "boardsize=", "size=",
                         "handicap=",
                         "games=",
                         "sgffile=",
                         ])
for opt, value in opts:
    if opt == "--black":
        black = value
    elif opt == "--white":
        white = value
    elif opt == "--verbose":
        verbose = int(value)
    elif opt == "--komi":
        komi = value
    elif opt == "--boardsize" or opt == "--size":
        size = value
    elif opt == "--handicap":
        handicap = value
    elif opt == "--games":
        games = int(value)
    elif opt == "--sgffile":
        sgffile = value
        
if params != []:
    usage()
            

if black == "" or white == "":
    usage()

if (int(handicap) > 1):
    komi = "0.5"

match = GTP_match(white, black, size, komi, handicap)
results = match.play(games, sgffile)

i = 0
for resw, resb in results:
    i = i + 1
    if resw == resb:
        print "Game %d: %s" % (i, resw)
    else:
        print "Game %d: %s %s" % (i, resb, resw)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]