1 Star 1 Fork 0

wzpan / chinese-chess-bot

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
tools.py 5.80 KB
一键复制 编辑 原始数据 按行查看 历史
wzpan 提交于 2022-04-18 16:14 . 增加悔棋功能
import itertools
import re
import time
import sys
import elephantfish
################################################################################
# This module contains functions used by test.py and xboard.py.
# Nothing from here is imported into sunfish.py which is entirely self-sufficient
################################################################################
# Sunfish doesn't have to know about colors, but for more advanced things, such
# as xboard support, we have to.
WHITE, BLACK = range(2)
FEN_INITIAL = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1"
def search(searcher, pos, secs, history=()):
"""This used to be in the Searcher class"""
start = time.time()
for depth, move, score in searcher.search(pos, history):
if time.time() - start > secs:
break
return move, score, depth
################################################################################
# Parse and Render moves
################################################################################
def gen_legal_moves(pos):
"""pos.gen_moves(), but without those that leaves us in check.
Also the position after moving is included."""
for move in pos.gen_moves():
pos1 = pos.move(move)
if not can_kill_king(pos1):
yield move, pos1
def can_kill_king(pos):
# If we just checked for opponent moves capturing the king, we would miss
# captures in case of illegal castling.
return any(pos.value(m) >= elephantfish.MATE_LOWER for m in pos.gen_moves())
def mrender(pos, m):
# Sunfish always assumes promotion to queen
p = (
"q"
if elephantfish.A9 <= m[1] <= elephantfish.I9 and pos.board[m[0]] == "P"
else ""
)
m = m if get_color(pos) == WHITE else (254 - m[0], 254 - m[1])
return elephantfish.render(m[0]) + elephantfish.render(m[1]) + p
def mparse(color, move):
m = (elephantfish.parse(move[0:2]), elephantfish.parse(move[2:4]))
return m if color == WHITE else (254 - m[0], 254 - m[1])
################################################################################
# Parse and Render positions
################################################################################
def get_color(pos):
"""A slightly hacky way to to get the color from a elephantfish position"""
return BLACK if pos.board.startswith("\n") else WHITE
def parseFEN(fen):
"""Parses a string in Forsyth-Edwards Notation into a Position"""
board, color, _, __, ___, ____ = fen.split()
board = re.sub(r"\d", (lambda m: "." * int(m.group(0))), board)
board = list(
(16 * 3 + 3) * " " + " ".join(board.split("/")) + (16 * 3 + 4) * " "
)
board[15::16] = ["\n"] * 16
# if color == 'w': board[::10] = ['\n']*12
# if color == 'b': board[9::10] = ['\n']*12
board = "".join(board)
score = sum(elephantfish.pst[p][i] for i, p in enumerate(board) if p.isupper())
score -= sum(
elephantfish.pst[p.upper()][254 - i] for i, p in enumerate(board) if p.islower()
)
pos = elephantfish.Position(board, score)
return pos if color == "w" else pos.rotate()
def renderFEN(pos, half_move_clock=0, full_move_clock=1):
color = "wb"[get_color(pos)]
if get_color(pos) == BLACK:
pos = pos.rotate()
board = "/".join(pos.board.split())
board = re.sub(r"\.+", (lambda m: str(len(m.group(0)))), board)
castling = "-"
ep = "-"
clock = "{} {}".format(half_move_clock, full_move_clock)
return " ".join((board, color, castling, ep, clock))
################################################################################
# Pretty print
################################################################################
def pv(searcher, pos, include_scores=True, include_loop=False):
res = []
seen_pos = set()
color = get_color(pos)
origc = color
if include_scores:
res.append(str(pos.score))
while True:
move = searcher.tp_move.get(pos)
# The tp may have illegal moves, given lower depths don't detect king killing
if move is None or can_kill_king(pos.move(move)):
break
res.append(mrender(pos, move))
pos, color = pos.move(move), 1 - color
if pos in seen_pos:
if include_loop:
res.append("loop")
break
seen_pos.add(pos)
if include_scores:
res.append(str(pos.score if color == origc else -pos.score))
return " ".join(res)
################################################################################
# Bulk move generation
################################################################################
def expand_position(pos):
"""Yields a tree of generators [p, [p, [...], ...], ...] rooted at pos"""
yield pos
for _, pos1 in gen_legal_moves(pos):
yield expand_position(pos1)
def collect_tree_depth(tree, depth):
"""Yields positions exactly at depth"""
root = next(tree)
if depth == 0:
yield root
else:
for subtree in tree:
for pos in collect_tree_depth(subtree, depth - 1):
yield pos
def flatten_tree(tree, depth):
"""Yields positions exactly at less than depth"""
if depth == 0:
return
yield next(tree)
for subtree in tree:
for pos in flatten_tree(subtree, depth - 1):
yield pos
################################################################################
# Non chess related tools
################################################################################
# Disable buffering
class Unbuffered(object):
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
sys.stderr.write(data)
sys.stderr.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
Python
1
https://gitee.com/wzpan/chinese-chess-bot.git
git@gitee.com:wzpan/chinese-chess-bot.git
wzpan
chinese-chess-bot
chinese-chess-bot
master

搜索帮助