En Pyssant是一个棋类实现和引擎
项目描述
En Pyssant
En Pyssant是一个棋类实现和引擎。
自由软件:GNU通用公共许可证第3版或更高版本
Python: 3.4+
背景
En Pyssant是一个个人项目,旨在用Python实现一个完整的棋类实现和引擎,具有简单直观的API。因此,公共API被这样文档化和实现,以便可以相对容易地用不同的实现来替换单个组件。
重点是保持API简洁灵活。这可能会以性能为代价,但如果性能是主要目标,可能一开始就不应该使用Python。
目标是通过对单元和集成测试进行全面测试来保持项目。后者比前者更多。
安装
安装En Pyssant应该是执行以下命令的简单过程
pip3 install --user en-pyssant
用法
通过规则走得很远,通过例子则既短又有效——通过规则走得很远,但通过例子则既短又有效。
首先,导入所有内容
>>> import threading >>> import time >>> from en_pyssant import * >>> from en_pyssant.engine import * >>> from en_pyssant.rules import * >>> # Technically you should never star-import, but it makes >>> # the examples easier.
En Pyssant 有几种核心数据类型
>>> white_pawn = Piece(Type.PAWN, Side.WHITE) >>> white_pawn Piece(type=<Type.PAWN: 'p'>, side=<Side.WHITE: 1>) >>> a1 = Square('a1') >>> a1.up().up() 'a3'
您可以轻松创建起始棋盘,或从部分Forsyth-Edwards 符号(FEN)导入任何其他棋盘布局
>>> board = DictBoard() >>> board rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR >>> DictBoard.from_fen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR') rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR >>> board[a1] Piece(type=<Type.ROOK: 'r'>, side=<Side.WHITE: 1>) >>> print(board['a3']) None >>> board.put('a3', white_pawn) rnbqkbnr/pppppppp/8/8/8/P7/PPPPPPPP/RNBQKBNR
您也可以以相同的方式轻松创建棋局位置,这是一个完整的棋局状态(即棋盘和一些额外信息)。下面是创建起始位置的不同方法
>>> position = Position() >>> position rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 >>> Position( ... board=DictBoard(), ... side_to_play=Side.WHITE, ... castling=Castling( ... CastlingSide(True, True), ... CastlingSide(True, True)), ... en_passant_target=None, ... half_move_clock=0, ... move_count=1) ... rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 >>> Position.from_fen('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1') rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 >>> position.move_count 1
如果 Forsyth-Edwards 符号过于简略,您可以轻松地获得一些相当漂亮的输出
>>> print(board.pretty()) A B C D E F G H 8 r n b q k b n r 7 p p p p p p p p 6 . . . . . . . . 5 . . . . . . . . 4 . . . . . . . . 3 . . . . . . . . 2 P P P P P P P P 1 R N B Q K B N R >>> print(position.pretty()) A B C D E F G H 8 r n b q k b n r 7 p p p p p p p p 6 . . . . . . . . 5 . . . . . . . . 4 . . . . . . . . 3 . . . . . . . . 2 P P P P P P P P 1 R N B Q K B N R <BLANKLINE> FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
棋盘和位置是不可变的数据容器。当您通常更改位置的状态时,您只需创建一个新的并丢弃旧的即可。尽管通常您通过在棋盘上执行走法让 En Pyssant 为您创建新位置
>>> move = Move('a2', 'a3') >>> new_position = do_move(position, move) >>> new_position rnbqkbnr/pppppppp/8/8/8/P7/1PPPPPPP/RNBQKBNR b KQkq - 0 1 >>> print(new_position.board.pretty()) A B C D E F G H 8 r n b q k b n r 7 p p p p p p p p 6 . . . . . . . . 5 . . . . . . . . 4 . . . . . . . . 3 P . . . . . . . 2 . P P P P P P P 1 R N B Q K B N R
您还可以使用标准代数符号进行走法。您在创建您的 SAN 字符串时可以稍微发挥一点创意。解析器相当宽容和宽容
>>> san = 'a3' # or 'Pa3', or 'a2a3', or 'Pa2-a3' >>> assert new_position == do_move(position, san)
您可以轻松获取所有走法的列表或对位置执行其他游戏逻辑。任何象棋游戏开始时都有 20 种合法走法
>>> assert len(list(moves(position))) == 20 >>> is_check(position) False >>> is_checkmate(position) False
您还提供了一个简单的包装器,用于跟踪当前位置和游戏的历史记录。以下是一个简单的愚者的棋局
>>> game = Game() >>> game.position rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 >>> game.do_move('f3') rnbqkbnr/pppppppp/8/8/8/5P2/PPPPP1PP/RNBQKBNR b KQkq - 0 1 >>> game.do_move('e5') rnbqkbnr/pppp1ppp/8/4p3/8/5P2/PPPPP1PP/RNBQKBNR w KQkq e6 0 2 >>> game.do_move('g4') rnbqkbnr/pppp1ppp/8/4p3/6P1/5P2/PPPPP2P/RNBQKBNR b KQkq g3 0 2 >>> game.do_move('Qh4#') rnb1kbnr/pppp1ppp/8/4p3/6Pq/5P2/PPPPP2P/RNBQKBNR w KQkq - 1 3 >>> print(game.position.board.pretty()) A B C D E F G H 8 r n b . k b n r 7 p p p p . p p p 6 . . . . . . . . 5 . . . . p . . . 4 . . . . . . P q 3 . . . . . P . . 2 P P P P P . . P 1 R N B Q K B N R >>> game.is_gameover() <Gameover.CHECKMATE: 1> >>> game.winner() <Side.BLACK: 0> >>> assert len(game.history) == 4
您还可以将游戏导出(和导入)为便携式游戏符号
>>> pgn = game.pgn() >>> print(pgn) [Result "0-1"] <BLANKLINE> 1. f3 e5 2. g4 Qh4# 0-1 >>> new_game = Game.from_pgn(pgn) >>> new_game.winner() <Side.BLACK: 0>
玩一局完整象棋游戏的最简单方法
>>> game = Game() >>> while not game.is_gameover(): ... new_position = game.do_move(next(game.moves())) ... >>> assert game.is_gameover()
然而,最有趣的事情是让计算机为您下棋。下面是一个简单的利用引擎的例子
>>> engine = MCTSEngine() >>> # Let the engine do its thinking magic for a few seconds. >>> engine.think_for(3) True >>> engine.is_thinking() # Thinking has just finished. False >>> best_move = engine.best_move() >>> position = engine.do_move(best_move) >>> assert position == engine.position >>> >>> # You can also let the engine think in a subthread. >>> thread = threading.Thread(target=engine.think_for, args=(0,)) >>> thread.start() >>> time.sleep(0.2) >>> # The engine is now thinking infinitely in another thread. >>> engine.is_thinking() True >>> # You can query the object while the engine is calculating. >>> new_best_move = engine.best_move() >>> assert best_move != new_best_move >>> _ = engine.do_move(new_best_move) >>> engine.is_thinking() True >>> # Cannot think again while thinking. >>> engine.think_for(0) False >>> engine.stop_thinking() >>> thread.join()
维护者
Carmen Bianca Bakker <carmen@carmenbianca.eu>.
贡献
欢迎在 https://gitlab.com/carmenbianca/en-pyssant 或通过电子邮件向维护者提出合并请求或建议。
开始本地开发非常简单。只需执行以下命令
git clone git@gitlab.com:carmenbianca/en-pyssant.git cd en-pyssant/ python3 -mvenv venv source venv/bin/activate make develop
您至少需要运行一次 make develop 来设置虚拟环境。
接下来,运行 make help 以查看可用的交互。
提交合并请求时,请确保所有测试都通过。如果可能的话,还提供伴随更改功能的额外测试。始终添加更改日志条目,并确保将自己添加到 AUTHORS.rst。
您需要将版权声明添加到您更改的文件中。假定您在合并请求中根据这些文件标题中的许可证许可更改。如果不是,请具体说明。有关许可的更多信息,请参阅https://reuse.software/。
许可证
GNU 通用公共许可证版本 3 或更高版本。
更改日志
此更改日志遵循Keep a Changelog规范。每个版本都包含以下部分
新增 用于新功能。
更改 用于现有功能的更改。
弃用 用于即将删除的功能。
删除 用于现在已删除的功能。
修复 用于任何错误修复。
安全 在存在漏洞的情况下。
版本遵循 语义版本控制。
0.2.0 (2018-07-04)
新增
moves_single 现在作为生成单个起点方格所有合法走法的函数补充了 moves。
添加了 BitBoard 和 TupleBoard。
添加了 Piece.from_str。
添加了 do_move_with_history,它返回一个 (Position, HistoryRecord) 元组。
添加了 ParallelEngine(基类)和 MCTSEngine。现在有一个完全功能性的引擎,它不是 RandomEngine。
为 Engine 添加了更多方法。
更改
已将CastlingAvailability替换为CastlingSide。现在位置不再包含一个CastlingAvailability对象的字典,而是包含一个Castling对象。例如
castling = {Side.WHITE: CastlingAvailability(True, True), Side.BLACK: CastlingAvailability(True, True)
现在变为
castling = Castling(CastlingSide(True, True), CastlingSide(True, True))
实际上,这使得Position对象可哈希且完全不可变。
新的Castling类仍然允许键查找。因此castling[Side.WHITE].kingside仍然是有效的。
Square.in_bounds和Square.goto现在也接受(int, int)元组,而不是Direction对象。这是因为元组的哈希计算更快。
Board.all_pieces现在从a1开始,到h8结束。
修改了一些代码,使其更兼容threading和multiprocessing。
修改了Engine的公共接口。具体来说
Engine.__init__现在接受一个位置、历史和规则集,而不是一个游戏。
Engine.think_for返回True。如果另一个线程正在思考,它返回False并开始思考。
Engine.stop_thinking接受一个额外的blocking关键字参数。
0.1.7 (2018-04-02)
更改
重新发布以修复文档。代码库中没有变化。
0.1.5 (2018-03-13)
首次发布。
包含几乎所有功能,除了象棋引擎本身。您可以玩象棋,基本上。只是不能与一个超级智能的计算机对战。
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。