翻译Deep Learning and the Game of Go(10)第八章:部署你的AI

本章包括:

  • 构建一个端到端的应用程序来训练和运行一个围棋机器人
  • 在前端运行来对抗你的机器人
  • 让你的机器人在本地与其他机器人对抗。
  • 部署到在线围棋服务器

到目前为止,你已经知道如何为围棋落子预测去构建和训练一个强大的深度学习模型,但是你如何将它集成到一个与对手玩游戏的应用程序中?训练神经网络工作只是构建端到端应用程序的一部分,不管你是自我对弈,还是让你的机器人与其他机器人竞争,这个模型必须集成成一个可以使用的引擎。

在本章中,您将构建一个简单的围棋模型服务器和两个前端。首先,我们为您提供一个HTTP前端,您可以用来对抗您的机器人。然后,我们介绍围棋文本协议(GTP),这是一种广泛使用的协议,围棋AI可以互相进行对抗,比如你可以挑战GNUGo或Pachi,这是两个免费提供的基于GTP的围棋程序。最后,我们将向您展示如何在AmazonWebServices(AWS)上部署围棋机器人并将其与在线围棋服务器(OGS)连接。这样做可以让你的机器人在真正的游戏中与世界各地的其他机器人和人类玩家竞争,获得相应的排名。为了做到这一切,你需要完成以下任务:

  • 构建一个围棋落子预测-你在第6章和第7章中训练的神经网络需要集成到一个框架中,允许你在游戏中使用它们。在第8.1节中,我们将按照第3章中得到的随机落子AI的概念创建深度学习机器人
  • 提供一个图形界面---作为人类,我们需要这个界面可以方便地对抗围棋机器人。在8.2节中,我们将为您配备一个有趣的届满让你可以与AI进行对抗。
  • 把AI部署到云上----如果你的计算机中没有强大的GPU,你就不会得到训练强大的围棋机器人。幸运的是,大多数的云都提供GPU实例,但即使你有足够强大的GPU进行训练,你仍然可能希望把你以前训练过的模型托管在服务器上。在第8.3节中,我们将向您展示如何托管,要了解更多的细节可以去看附录D。
  • 与其他AI对弈----人类喜欢使用图形和其他界面,但对于机器人来说,习惯上通过标准化的协议进行通信。在第8.4节中,我们将向您介绍通用围棋文本协议(GTP)。下面两点是重要的组成部分:

                      1.与其他机器人对弈-----后你将为你的机器人建立一个GTP前端,让它与8.5节中的其他程序进行对抗。我们将教你                         如何让你的机器人在本地与另外两个围棋程序进行比赛,去看看你的AI有多好。

                      2.在联机围棋服务器上部署机器人-----在第8.6节中,我们将向您展示如何在联机围棋平台上部署机器人,以便让其                        他机器人可以和你的机器人竞争。这样,您的机器人甚至可以得到排名,所有这些我们将在最后一节展示给您。因                       为大部分材料都是技术性的,你可以附录E中找到大量细节。 

8.1 创建一个深度学习的落子预测AI 

现在,您已经有了所有的构建块来为围棋数据构建一个强大的神经网络,让我们将这些网络集成到一个为它们服务的代理中。回顾第三章的概念,我们将其定义实现select_move方法为当前游戏状态选择下一个落子点的类。让我们使用Keras模型和围棋盘Encoder去编写DeepLearningAgent(将此代码放入dlgo中的agent模块中的predict.py中)

from dlgo.agent.base import Agent
from dlgo.agent.helpers import is_point_true_eye
from dlgo import goboard
from dlgo import Encoder


class DeepLearningAgent(Agent):

    def __init__(self, model, encoder):
        super().__init__()
        self.model = model
        self.encoder = encoder

接着,您将使用编码器将棋盘撞他转换为特征,然后使用该模型去预测下一步落子。实际上,您将使用该模型去计算所有可能的概率分布。

# 返回整个棋盘预测概率分布
    def predict(self, game_state):
        encoded_state = self.encoder.encode(game_state)
        input_tensor = np.array([encoded_state])
        return self.model.predict(input_tensor)[0]

    def select_move(self,game_state):
        num_moves = self.encoder.board_width*self.encoder*board_height
        # 获得预测概率分布
        move_probs = predict(game_state)

接下来,您可以稍微改变存储在move_probs中的概率分布。首先,计算所有值的三次方,以大幅增加可能和不可能落子点之间的距离。你希望最好的落子点能尽可能挑选频繁,然后使用一种叫做裁剪的技巧,它可以防止落子概率太接近0或1。这是通过定义一个极小的正值,ε=0.000001,设置值小于ε到ε,值大于1-ε到1-ε。然后,对得到的值进行规范化,再次得到概率分布

 

  # 大幅拉开可能点与非可能点之间的距离
        move_probs = move_probs ** 3
        small_data = 1e-6
        # 防止落子概率卡在0或1
        move_probs = np.clip(move_probs,small_data,1-small_data)
        # 重新得到另一个概率分布
        move_probs = move_probs/sum(small_data)

您进行此转换是因为您希望根据它们的概率从这个分布采样落子点。(另一个可行的策略不是抽样调查,而是始终采取分布的最大值)。你不采取第二种做法的好处是有时会选择其他可能有用的落子

     # 把概率转成一个排序列表
        candidates = np.arange(num_moves)
        # 按照落子概率进行采样,不允许取相同的落子
        ranked_moves = np.random.choice(
            candidates, num_moves, replace=False, p=move_probs)
        # 从最高层开始,找到一个有效的落子,不会减少视野空间
        for point_index in ranked_moves:
            point = self.encoder.decode_point_index(point_index)
            if game_state.is_valid(goboard.Move.play(point)) and \
                    not is_point_an_eye(game_state.board, point, game_state.current_player):
                return goboard.Move.play(point)
        return goboard.Move.pass_turn()

你训练了一个深度学习模型,并创建了一个代理,然后你坚持下去。稍后,这个代理将被反序列化成了服务,这样人类玩家或其他机器人就可以对抗它。要执行序列化步骤,就需要序列化Keras的格式。当您持久化Keras模型时,它将以HDF5进行存储,这是一种有效的序列化格式。HDF5文件包含灵活的组,用于存储元信息和数据。对于任何一个Keras模型,您可以调用model.save(“model_path.h5”)将完整模型(即神经网络体系结构和所有权重)持久化为本地文件model_path.h5。你唯一需要的就是在保持像这样的Keras模型之前,先安装Python库h5py

要存储完整的代理,您可以添加一个额外的组以获取有关你的围棋棋盘编码器。

 # 序列化模型
    def serialize(self, h5file):
        # 先创建一个编码器组
        h5file.create_group('encoder')
        h5file['encoder'].attrs['name'] = self.encoder.name()
        h5file['encoder'].attrs['board_width'] = self.encoder.board_width
        h5file['encoder'].attrs['board_height'] = self.encoder.board_height
        # 再创建一个模型组
        h5file.create_group('model')
        kerasutil.save_model_to_hdf5_group(self.model, h5file['model'])

最后,在序列化模型之后,您还需要知道如何从HDF5文件去加载它。


    # 从序列化的文件中加载模型
    def load_prediction_agent(h5file):
        # 加载模型
        model = kerasutil.load_model_from_hdf5_group(h5file['model'])
        # 加载编码器
        encoder_name = h5file['encoder'].attrs['name']
        if not isinstance(encoder_name, str):
            encoder_name = encoder_name.decode('ascii')
        board_width = h5file['encoder'].attrs['board_width']
        board_height = h5file['encoder'].attrs['board_height']
        encoder = encoders.get_encoder_by_name(
            encoder_name, (board_width, board_height))
        return DeepLearningAgent(model, encoder)
这就完成了我们深度学习代理的定义。作为下一步,您必须确保此代理与环境能与人进行交互。通过将DeepLearningAgent嵌入到人类玩家可以在浏览器中使用的Web应用程序中
 

8.2.将你的机器人放到一个网络前端

在第6章和第7章中,你设计并训练了一个神经网络,它预测一个人在围棋游戏中会下哪里。在第8.1节中,你将落子预测模型转换为DeepLearningAgent,下一步是与你的机器人对弈!回到第三章,你建立了一个光秃秃的界面,让你可以在控制台输入落子座标,而你的RandomBot则将其回应打印到控制台。现在你已经建立了一个更复杂的机器人,它应该有一个更好的前端来与人类玩家交流对弈。

在本节中,您将把DeepLearningAgent加入到PythonWeb应用程序,这样您就可以在Web浏览器中与它对局了。您将使用Flask通过HTTP为这样的代理服务。在浏览器方面,您将使用JavaScript中一个名叫jgoboard的库来渲染人类可以使用的围棋棋盘。.8.1提供了将在本章中构建的应用程序的概述。

8.3.在云上的训练和部署你的AI

直到这一点,所有的开发都是在本地机器上进行的。如果你的电脑上有一个现代化的GPU,那就训练我们开发的深层神经网络第如果你没有强大的GPU,或者不能在它上留出任何计算时间,那么去租用云使用GPU计算通常是一个很好的选择。在第8.2节中,通过Web应用程序运行机器人由本地主机托管。如果你想和朋友分享你的机器人,或者公开它,这并不完全理想。你既不想确保你的电脑日夜运转,也不想让公众知道去你的机器。通过在云中托管您的机器人,让你可以将开发与部署分开,并且可以简单地与任何对与你的AI对弈感兴趣的人共享URL。

因为这个话题很重要我们把它完全放在了附录D。在附录D中,您将了解如何开始使用一个特定的云提供商AmazonWeb服务(AWS)。您将在附录中学习以下技能:

  • 使用AWS创建帐户
  • 灵活设置、运行和终止虚拟服务器实例创建一个AWS实例
  • 在费用合理的情况下创建适合在云GPU上进行深度学习的模型训练
  • 在一个(几乎)免费的服务器上放置你的AI

通过HTTP除了学习这些有用的技能外,附录D也展示了部署一个完整的Go机器人的先决条件,该机器人可以连接到联机Go服务器

8.4 与其他AI对弈:使用GTP

在8.2节中,您看到了如何将AI框架集成到Web前端。为此,您使用HTTP(运行Web的核心协议之一)处理了机器人和人类棋手之间的通信。为了避免分心,我们有目的地忽略了所有细节,但有一个标准化的协议是必要的,以实现这一点。人类和机器人不能使用一种通用语言来交换围棋落子,但是协议可以充当桥梁。

GTP(围棋文本协议)是世界各地围棋服务器用来连接人类棋手和他们平台上的机器人。许多离线Go程序也是基于GTP的。本节通过示例向您介绍GTP;您将在Python中实现协议的一部分并使用此实现来让你的机器人与其他AI进行对弈。

在附录C中,我们解释了如何下载GNUGO和Pachi,这两个通用的围棋程序几乎适用于所有的操作系统。我们建议两者都安装,所以请确保在您的系统上有两个程序。你不需要任何前端,只需要简单的命令行工具。如果安装了GNU Go,您可以GTP模式启动它:

gnugo ­­--mode gtp

使用此模式,您现在可以探索GTP是如何工作的。GTP是一种基于文本的协议,因此您可以在终端中键入命令并按回车键。例如,要设置9×9的棋盘,可以键入boardsize 9。这将触发GNU Go返回响应并确认命令已正确执行。每一个成功的GTP命令都会触发一个以=开头的响应,而失败的命令会导致一个?要检查当前的棋盘状态,您可以发出showboard命令,它将按预期打印出一个空的9×9棋盘。

在实际的游戏中,两个命令是最重要的:genmove和play..第一个命令genmove用于请求GTP bot去生成下一个落子。GTP机器人通常也会应用这个落子到它内部的游戏状态。所有这些命令都需要参数是玩家的棋子颜色,无论是黑色还是白色。例如,要生成一个白色落子并将其放置在GNU Go的棋盘上,请键入genmove white。这将导致响应,如=C4,这意味着GNUGo接受此命令(=),并将白石放置在C4。如您所见,GTP接受第2章和第2章所介绍的标准座标

另一个与游戏相关的动作是play。此命令用于让GTP机器人知道它必须在棋盘上落子。例如,你通过键入play black D4来让它将黑棋下在D4上,它将返回一个=来确认此命令。当两个机器人互相对抗时,他们会轮流要求对方落出下一步,然后将选择的落子放在棋盘上。这一切都很直截了当----但我们遗漏了许多细节。一个完整的GTP客户端有更多的命令要处理,从处理让子棋到处理时间设置和计数规则。如果您对GTP的细节感兴趣,请参阅http://mng.bz/MWNQ.。尽管如此,在一个基本的层次上,genmove和play将足以让我们的深度学习机器人对抗GNUGo和Pachi。

要处理GTP幷包装您的Agent概念,以便它可以通过使用此协议交换围棋落子,您可以创建一个名为GTP的新dlgo模块,我们建议直接跟踪我们在http://mng.bz/a4Wj的GitHub上的实现。

首先,让我们形式化GTP命令。要做到这一点,我们必须注意,在许多围棋服务器上,命令需要获得序列号,以确保我们能够匹配命令和响应。这些序号数字是可选的,可以是None的。对我们来说,GTP命令由序列号、命令和该命令的潜在多个参数组成,这个命令在gtp模块下的command.py里找到。 ,

class Command:
    # GTP命令由序列号、命令和该命令的潜在多个参数组成
    def __init__(self, sequence, name, args):
        self.sequence = sequence
        self.name = name
        self.args = tuple(args)

    # 判断命令是否相同
    def __eq__(self, other):
        return self.sequence == other.sequence and \
            self.name == other.name and \
            self.args == other.args

    def __repr__(self):
        return 'Command(%r, %r, %r)' % (self.sequence, self.name, self.args)

    def __str__(self):
        return repr(self)

接下来,要将从命令行输入的文本解析为Command。例如,解析“999playwhiteD4”应该导致命令(999,‘play’,(‘white’,‘D4’)。parse就是实现了这种功能。

  # 根据命令字符串解析命令
    def parse(command_string):
        pieces = command_string.split()
        try:
            sequence = int(pieces[0])  # 序号
            pieces = pieces[1:]
        except ValueError:  # 如果第一个不是数字,就没有序列号
            sequence = None
        name, args = pieces[0], pieces[1:]
        return Command(sequence, name, args)

我们刚才认为GTP座标是标准表示法,所以将GTP座标解析成棋盘位置,反之也是简单的。定义两个辅助函数使用gtp在座标和位置之间转变

from dlgo.gotypes import Point
from dlgo.agent.FastRandomAgent.goboard_fast  import Move

COLS = 'ABCDEFGHJKLMNOPQRST'

# 将落子转成gtp棋盘位置
def coords_to_gtp_position(move):
    point = move.point
    return COLS[point.col - 1] + str(point.row)

# 将gtp棋局位置转成座标
def gtp_position_to_coords(gtp_position):
    col_str, row_str = gtp_position[0], gtp_position[1:]
    point = Point(int(row_str), COLS.find(col_str.upper()) + 1)
    return Move(point)

 8.5 和本地其他机器人进行对弈

 现在您了解了GTP的基本知识,让我们深入到一个应用程序中,并构建一个程序来加载您的一个机器人,并让它与GNUGo或Pachi竞争。在我们开始程序之前,我们只有一个技术问题要解决---什么时候让我们的机器人投降或pass

8.5.1 当一个机器人应该投降或pass

到目前为止,你的深度学习机器人没有办法知道什么时候停止下棋。你至今设计它们的方式是,你的机器人总是会选择最好的落子来下。这个直到对局结束才停止是有害的,当情况看起来有点太糟糕的时候,选择pass甚至投降可能会更好。出于这个原因,您将实施终止策略:您将告诉机器人什么时候停下来。在第13章和第14章中,您将学习强大的技术,这将使这完全无用(您的机器人将学会判断当前的棋局,从而判断是否投降)。但目前,这个概念是有用的,并将帮助您试验这种方式部署一个机器人对抗其他对手。

调用dlgo下的agent下的termination.py的TerminationStrategy 。它所做的只是决定你什么时候应该pass投降----默认情况下,你永远不会pass或投降。 

from dlgo import goboard
from dlgo.agent.base import Agent
from dlgo.ComputeWinner import compute_game_result


class TerminationStrategy:
     def __init__(self):
         pass
     
     def should_pass(self,game_state):
         return False
     
     def should_resign(self,game_state):
         return False

 停止游戏的一个简单的启发是当你的对手pass时pass。你必须依靠这样一个事实,即你的对手知道什么时候要pass,但这是一个开始,而且它在对抗GNU Go和Pachi。

class PassWhenOpponentPasses(TerminationStrategy):

    def should_pass(self, game_state):
        if game_state.last_move is not None:
            return True if game_state.last_move.is_pass else False

def get(termination):
    if termination == 'opponent_passes':
        return PassWhenOpponentPasses()
    else:
        raise ValueError("Unsupported termination strategy: {}"
                         .format(termination))

 在termination.py中,你还发现了另一种叫做ResignLargeMargin的策略,它每当游戏的估计分数对手过于有利时就会投降。你还能做许多别的策略,但要记住,最终你可以通过机器学习摆脱这种东西。

# 过于有利于对手就resign
class ResignLargeMargin(TerminationStrategy):

    def __init__(self, own_color, cut_off_move, margin):
        TerminationStrategy.__init__(self)
        self.own_color = own_color
        self.cut_off_move = cut_off_move
        self.margin = margin

        self.moves_played = 0

    def should_pass(self, game_state):
        return False

    def should_resign(self, game_state):
        self.moves_played += 1
        if self.moves_played:
            game_result = scoring.compute_game_result(self)
            if game_result.winner != self.own_color and game_result.winning_margin >= self.margin:
                return True
        return False

最后一件事,为了让机器人相互对抗,你最不需要的就是给代理配备一个终止策略,以便在适当的时候pass或投降。

class TerminationAgent(Agent):

    def __init__(self, agent, strategy=None):
        Agent.__init__(self)
        self.agent = agent
        self.strategy = strategy if strategy is not None \
            else TerminationStrategy()

    def select_move(self, game_state):
        if self.strategy.should_pass(game_state):
            return goboard.Move.pass_turn()
        elif self.strategy.should_resign(game_state):
            return goboard.Move.resign()
        else:
            return self.agent.select_move(game_state)

8.5.2 让你的围棋AI与其他围棋程序对抗

在讨论了终止策略之后,您现在可以让围棋机器人与其他程序对局。在gtp模块中的play_local.py下,找到一个在你的某个机器人与无论是GNUGo还是Pachi之间设置游戏的脚本。从必要的导入开始,一步一步地完成这个脚本 

from __future__ import print_function
import subprocess
import re
import h5py

from dlgo.agent.DeepLearningAgent.DeepLearningAgent import load_prediction_agent
from dlgo.agent.DeepLearningAgent.termination import PassWhenOpponentPasses, TerminationAgent
from dlgo.agent.FastRandomAgent.goboard_fast import GameState, Move
from dlgo.gotypes import Player
from dlgo.gtp.board import gtp_position_to_coords, coords_to_gtp_position
from dlgo.utils import print_board
from dlgo.ComputeWinner import compute_game_result

要初始化LocalGtpBot,您需要提供深度学习代理和可选的终止策略。此外,您还可以指定应该让几个子,以及要对弈的对手。对于后者,你可以在gnugo和pachi之间进行选择..LocalGtpBot将这些程序中的任何一个初始化为子进程,然后您的AI和它的对手将通过GTP进行通信。

# tag::play_local_init[]
class LocalGtpBot:

    def __init__(self, go_bot, termination=None, handicap=0,
                 opponent='gnugo',our_color='b'):
        self.bot = TerminationAgent(go_bot, termination)  # 用代理和终止策略初始化机器人
        self.handicap = handicap
        self._stopped = False
        self.game_state = GameState.new_game(19)

        self.our_color = Player.black if our_color == 'b' else Player.white
        self.their_color = self.our_color.other

        cmd = self.opponent_cmd(opponent)  #你的对手将是GNU Go或Pachi
        # 开辟子进程
        pipe = subprocess.PIPE
        self.gtp_stream = subprocess.Popen(
            cmd, stdin=pipe, stdout=pipe  #从命令行读写GTP命令
        )

    @staticmethod
    def opponent_cmd(opponent):
        if opponent == 'gnugo':
            return ["gnugo", "--mode", "gtp"]
        elif opponent == 'pachi':
            return ["pachi"]
        else:
            raise ValueError("Unknown bot name {}".format(opponent))

 我们在这里演示的工具是command_and_response,它可以发出GTP命令并读取该命令的响应

 # 发送命令
    def send_command(self, cmd):
        self.gtp_stream.stdin.write(cmd.encode('utf-8'))

    # 得到回应
    def get_response(self):
        succeeded = False
        result = ''
        while not succeeded:
            line = self.gtp_stream.stdout.readline()
            if line[0] == '=':
                succeeded = True
                line = line.strip()
                result = re.sub('^= ?', '', line)
        return result

    def command_and_response(self, cmd):
        self.send_command(cmd)
        return self.get_response()

下棋的效果如下:

  1. 用GTP棋盘大小命令去设置棋盘。你在这里只允许19×19围棋棋盘,因为你的深度学习机器人是为此量身定做的。
  2. 在set_handicap方法中正确设置让子。
  3. 对弈本身,你将在play方法中涵盖它。
 # 运行
    def run(self):
        self.command_and_response("boardsize 19\n")
        self.set_handicap()
        self.play()

    # 设置让子
    def set_handicap(self):
        if self.handicap == 0:
            self.command_and_response("komi 7.5\n")
        else:
            stones = self.command_and_response("fixed_handicap {}\n".format(self.handicap))
            for pos in stones.split(" "):
                move = gtp_position_to_coords(pos)
                self.game_state = self.game_state.apply_move(move)

你的机器人对弈的游戏逻辑很简单:没有一个对手停下来,轮流并持续落子。机器人在名为play_our_move和play_their_move的方法中做到了这一点。您还清除屏幕,并打印出当前的棋局和对结果的粗略估计。

    def play(self):
        while not self._stopped:
            if self.game_state.current_player == self.our_color:
                self.play_our_move()
            else:
                self.play_their_move()
            print(chr(27) + "[2J")
            print_board(self.game_state.board)
            print("Estimated result: ")
            print(compute_game_result(self.game_state))
    # 我方落子
    def play_our_move(self):
        move = self.bot.select_move(self.game_state)
        self.game_state = self.game_state.apply_move(move)

        our_name = self.our_color.name
        
        if move.is_pass:
            self.command_and_response("play {} pass\n".format(our_name))
        elif move.is_resign:
            self.command_and_response("play {} resign\n".format(our_name))
        else:
            pos = coords_to_gtp_position(move)
            self.command_and_response("play {} {}\n".format(our_name, pos))
       

    # 对方落子
    def play_their_move(self):
        their_name = self.their_color.name
        pos = self.command_and_response("genmove {}\n".format(their_name))
        if pos.lower() == 'resign':
            self.game_state = self.game_state.apply_move(Move.resign())
            self._stopped = True
        elif pos.lower() == 'pass':
            self.game_state = self.game_state.apply_move(Move.pass_turn())
                
            if self.game_state.last_move.is_pass:
                self._stopped = True
        else:
            move = gtp_position_to_coords(pos)
            self.game_state = self.game_state.apply_move(move)

然后就可以使用你之前训练的AI进行对弈了

from dlgo.gtp.play_gtp_local import LocalGtpBot
from dlgo.agent.DeepLearningAgent.DeepLearningAgent import load_prediction_agent
from dlgo.agent.DeepLearningAgent.termination import PassWhenOpponentPasses

my_bot = load_prediction_agent("deep_go.h5")
gnu_go = LocalGtpBot(go_bot=my_bot, termination=PassWhenOpponentPasses(),
                         handicap=0, opponent='pachi', )
gnu_go.run()

在图的顶部,你看到你打印的棋盘,然后是你目前的估计。在下半变,你可以在左边看到Pacho的比赛状态(与你的状态相同),在右边Pachi给你一个关于游戏的评估,它用于判断棋盘上的点属于哪一方。

这是一个希望令人信服和激动人心的演示.,但这还不是故事的结尾。

GNU Go

GNU Go是在1989年开发的,是至今仍在使用的最古老的围棋引擎之一。最近一次发布是在2009年。尽管最近没有什么进展,但GNU Go仍然是一个受欢迎的AI 对手,在许多围棋o服务器上为初学者服务。此外,它是基于人工制作规则的最强的围棋引擎之一;这与MCTS和深度学习机器人形成了很好的对比。你可以从www.gnu.org/software/gnugo/download.html下载安装GNUGo。此页面包括安装GNUGo作为命令行接口(CLI)工具的说明和各种图形界面。为了要安装CLI工具,您需要从http://ftp.gnu.org/gnu/gnugo/下载最近版本的AI二进制文件,解压缩各自的tarball,并遵循下载中包含的INSTALL和README文件中的平台说明。对于图形界面,我们建议从www.rene­grothmann.de/jago/下载Windows和Linux版本的JagoClient和从http://sente.ch/software/goban/freegoban.html.下载MacOS版本的FreeGoban

Pachi

你可以在http://pachi.or.cz/.上找到一个比GNU Go整体要强得多的程序Pachi,此外,Pachi的源代码和详细的安装说明可以在https://github.com/pasky/pachi中找到.要测试Pachi,请在命令行上运行Pachi,并键入genmove Black,让它在9×9棋盘上为您生成一个黑色落子。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章