翻译Deep Learning and the Game of Go(5)第3章:实现你第一个围棋AI(下)

3.4 创建你第一个AI:可以想见的最弱AI

在实现了围棋棋盘和游戏状态类之后,您可以构建您的第一个围棋AI。这个机器人将是一个很弱的玩家,但它将为你的后续所有改进奠定基础。首先,您需要定义所有机器人都要调用的接口,并把定义放入agent文件下中的base.py中。

class Agent:
    def __init__(self):
        pass

    # 根据棋盘盘面状态去选择相应落子
    def select_move(self,game_state):
        raise NotImplementedError()  # 预留一个借口先不实现,让子类去实现

就是这样一种方法,机器人所做的一切都是在当前游戏状态下选择一个落子。当然,在内部,这可能需要十分复杂的操作,如评估目前的棋盘盘面,但玩游戏,这就是我们的机器人永远需要的。

我们的第一个实现将尽可能的弱智:它将随机选择任何有效的移动,并且不去填它自己的真眼。你可以把这个随机AI放在agent下面。回想起第二章,在围棋中,学生的排名通常从30级到1级。按这个比例,你的随机机器人在30级水平,是绝对的初学者。

import random
from dlgo.agent.base import Agent
from dlgo.agent.helpers import is_point_true_eye
from dlgo.goboard_slow import Move
from dlgo.gotypes import Point


# 随机落子AI
class RandomBot(Agent):
    def select_move(self, game_state):
        # 合法落子集合
        valid_moves = []
        for r in range(1, game_state.board.num_rows+1):
            for c in range(1, game_state.board.num_cols+1):
                point = Point(row=r,col=c)
                # 保证合法且不是自填眼位
                if game_state.is_valid(Move.play(point)) and not is_point_true_eye(point,game_state.current_player):
                    valid_moves.append(point)
        if not valid_moves:
            print("over")
        return Move.play(random.choice(valid_moves))

现在保证你的agent模块是如下结构(空的_init_py以初始化模块):

dlgo
    agent
        __init__.py 
        helpers.py
        base.py 
        naive.py

最后,您可以设置一个驱动程序,在随机落子AI的两个实例之间进行完整的游戏。首先,您要定义方便的功能函数,例如在控制台中打印整个棋盘或个人落子。

围棋棋盘的坐标可以用多种方式来指定,但在欧洲,用字母表的字母来标记列是最常见的,从A开始,行用递增的数字表示,从1开始。在一个标准的19×19棋盘上,左下角是A1,右上角是T19。请注意,根据惯例,省略字母I从而避免与1混淆。

先定义一个字符串变量COLS=‘ABCDEFGHJ KLMNOPQRST’,其字符代表围棋棋盘的列。若要在命令行上显示棋盘,棋盘上的空点就用点表示,x表示黑棋,o表示白棋。把下面的代码放入一个dlgo文件夹下的新文件utils.py,print_move该函数打印用户操作到命令行,print_board函数用来打印当前的棋盘。您将此代码放入dlgo模块之外的一个名为bot_v_bot.py的文件中。

from dlgo import gotypes

COLS = 'ABCDEFGHJKLMNOPQRST'
STONE_TO_CHAR = {
    None: ' . ',
    gotypes.Player.black: ' x ',
    gotypes.Player.white: ' o ',
}


def print_move(player, move):
    if move.if_pass:
        move_str = 'passes'
    elif move.if_resign:
        move_str = 'resigns'
    else:
        move_str = '%s%d' % (COLS[move.point.col - 1], move.point.row)
    print('%s %s' % (player, move_str))


def print_board(board):
    for row in range(board.num_rows, 0, -1):
        bump = " " if row <= 9 else ""
        line = []
        for col in range(1, board.num_cols + 1):
            stone = board.get(gotypes.Point(row=row, col=col))
            line.append(STONE_TO_CHAR[stone])
        print('%s%d %s' % (bump, row, ''.join(line)))
        print('    ' + '  '.join(COLS[:board.num_cols]))

您可以设置一个脚本,启动两个随机落子AI,在一个9×9棋盘上相互对弈,直到他们游戏结束。

from dlgo.agent import naive
from dlgo import goboard
from dlgo import gotypes
from dlgo.utils import print_board,print_move
import time


def main():
    board_size = 9
    game = goboard.GameState.new_game(board_size)
    if game is None:
        print("hh")
    # 两个随机AI
    bots = {
        gotypes.Player.black:naive.RandomBot(),
        gotypes.Player.white:naive.RandomBot()
    }
    print("游戏开始")
    while not game.is_over():
        time.sleep(0.3)  # 你将计时器设置为0.3秒,这样机器人的操作就不会打印得太快,无法观察到。
        print(chr(27)+"[2J")  # 清屏
        print_board(game.board)
        bot_move = bots[game.current_player].select_move(game)
        print_move(game.current_player, bot_move)
        game = game.apply_move(bot_move)
    print("游戏结束")


if __name__ == '__main__':
    main()

最后结果(我按它的代码得到的结果,完成了两个随机AI对弈,由结果可知,两个AI成功防止了填真眼。

                                                                  

这个机器人不仅弱,而且是一个令人沮丧的对手:它会顽固地落子,直到没有可下的地方,而且,不管你让这些AI互相对弈多少时间,也从不涉及学习,因此这个随机AI将永远保持在当前的水平。

在整本书的其余部分,你会慢慢地在这两方面都有所改进,去建立一个更有趣和强大的围棋引擎。

3.5 使用佐布里斯特散列加快你AI的落子速度

3.6 和你的AI对弈

3.7 总结

发布了9 篇原创文章 · 获赞 5 · 访问量 1903

猜你喜欢

转载自blog.csdn.net/qq_41957257/article/details/103951003
今日推荐