跳转到主要内容

En Pyssant是一个棋类实现和引擎

项目描述

En Pyssant

En Pyssant是一个棋类实现和引擎。

背景

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

  • 添加了 BitBoardTupleBoard

  • 添加了 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_boundsSquare.goto现在也接受(int, int)元组,而不是Direction对象。这是因为元组的哈希计算更快。

  • Board.all_pieces现在从a1开始,到h8结束。

  • 修改了一些代码,使其更兼容threadingmultiprocessing

  • 修改了Engine的公共接口。具体来说

    • Engine.__init__现在接受一个位置、历史和规则集,而不是一个游戏。

    • Engine.think_for返回True。如果另一个线程正在思考,它返回False并开始思考。

    • Engine.stop_thinking接受一个额外的blocking关键字参数。

0.1.7 (2018-04-02)

更改

  • 重新发布以修复文档。代码库中没有变化。

0.1.5 (2018-03-13)

  • 首次发布。

  • 包含几乎所有功能,除了象棋引擎本身。您可以玩象棋,基本上。只是不能与一个超级智能的计算机对战。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码发行版

en-pyssant-0.2.0.tar.gz (67.0 kB 查看哈希值)

上传时间 源代码

构建发行版

en_pyssant-0.2.0-py3-none-any.whl (18.1 MB 查看哈希值)

上传时间 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面