chChess.py
这是一个你能下赢,同时支持中国象棋和中国揭棋的 Python 脚本。
与大部分其它同类软件相比,它有如下的优点:
1)程序同时支持中国象棋和中国揭棋,启动时自动检查环境,可兼有控制台色彩和语音播报功能。
2)理论上,程序可以在任意有控制台并正确部署了 Python 环境的操作系统上运行。目前已在 Windows、CentOS、Ubuntu 等操作系统上进行了测试,在 Android 手机的 termux 上进行了测试,均可正常使用。
3)程序同时支持中文纵线格式(炮二平五、前车平四、中兵一进一、二兵进一)、WXF 格式(C8.5、R+.4、Pc+1)、ICCS 坐标格式(H2-E2、H2E2)、二维数组格式(7774)。
4)程序能显示下棋记录,支持保存局面过程;运行过程支持多分支记录,支持悔棋。
5)程序能识别开局名称,能够判断走子是否违规,是否未正确应将或送将,自动判断长将作负、双方不变着作和、自然作和、120 回合上限作和。
6)程序能自动识别输入的残局是否非法,并判断残局是否为终局以避免吃掉一方将军后被吃将军方将死对方导致错判的荒谬局面。
7)程序采用看上去较为友好的代码块进行编写,有大量注释,并使用 tab 表示缩进。
8)程序采用单文件编写,便于逆向破解,完全免费且开源。
与大部分其它同类软件相比,它有如下的局限性:
1)它的 AI 十分薄弱,局面分评估尚不完善;目前 4 及以上等级的 AI 尚不完善,揭棋中的 3 会出错(生成的虚拟局面不合法)。
2)它没有可视化的界面,无法通过鼠标进行棋子的移动。
import os
import sys
from re import findall
from secrets import randbelow
from threading import Lock, Semaphore, Thread
os.chdir(os.path.abspath(os.path.dirname(__file__)))#解析进入程序所在目录
PLATFORM = __import__("platform").system().upper()
if PLATFORM == "WINDOWS":
from msvcrt import getch
else:
os.system("setfont /usr/share/consolefonts/Lat2-Terminus16.psf.gz")
import tty
import termios
def getch():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return bytes(ch, encoding = "utf-8")
EXIT_SUCCESS = 0
EXIT_FAILURE = 1
if "idlelib" in sys.modules or not sys.stdin.isatty(): # 未检测到终端
print("未检测到终端,部分功能无法正常使用,请勿尝试在 IDE 或 IDLE 中启动本程序。")
input("请按下回车键退出。")
sys.exit(EXIT_FAILURE)
try:
from colorama import Fore, Back, Style, init as initColor
if PLATFORM == "WINDOWS" and sys.stdin.isatty(): # 在 Windows 终端
initColor(wrap = True) # Windows 终端
defaultFore = Fore.BLACK
foreRed = Fore.RED # 红方
foreBlack = Fore.GREEN # 黑方
foreRound = Fore.BLUE # 环绕颜色
print(defaultFore + Back.WHITE, end = Style.DIM)
except:
print("未能调用 colorama 库,请尝试在合适的环境中运行“pip install colorama”进行安装。")
print("程序将继续运行,但这可能会影响您的视觉效果,请按任意键继续。")
defaultFore = foreRed = foreBlack = foreRound = "" # 关闭颜色
getch()
try:
import pyttsx3
engine = pyttsx3.init()
del engine
lock = Lock() # 锁定 toSpeakList
lock.acquire()
toSpeakList = []
sem = Semaphore(0) # 信号量
lock.release()
def speak():
engine = pyttsx3.init()
while True:
sem.acquire() # 申请信号量(减少死循环)
lock.acquire()
if toSpeakList:
toSpeak = toSpeakList.pop(0) # 弹出第一个
lock.release()
if toSpeak:
engine.say(toSpeak)
engine.runAndWait()
else:
engine.say("欢迎再次使用!")
engine.runAndWait()
break
t = Thread(target = speak)
t.start()
def doSpeak(text):
lock.acquire()
global toSpeakList # 修改全局变量
toSpeakList.append(text)
lock.release()
sem.release() # 释放信号量
except:
def doSpeak(text): # 初始化失败,设置函数:什么也不干
pass
print("未能调用 pyttsx3 库,请尝试在合适的环境中运行“pip install pyttsx3”进行安装。")
print("程序将继续运行,但这可能会影响您的听觉效果,请按任意键继续。")
getch()
class Board:
mode_statement = { \
0b0000:"中国象棋不用电脑", \
0b0001:"中国象棋电脑执黑", \
0b0010:"中国象棋电脑执红", \
0b0011:"中国象棋电脑执双", \
0b0100:"中国象棋残局不用电脑", \
0b0101:"中国象棋残局电脑执黑", \
0b0110:"中国象棋残局电脑执红", \
0b0111:"中国象棋残局电脑执双", \
0b1000:"中国揭棋随机生成不用电脑", \
0b1001:"中国揭棋随机生成电脑执黑", \
0b1010:"中国揭棋随机生成电脑执红", \
0b1011:"中国揭棋随机生成电脑执双", \
0b1100:"中国揭棋人工指派不用电脑", \
0b1101:"中国揭棋人工指派电脑执黑", \
0b1110:"中国揭棋人工指派电脑执红", \
0b1111:"中国揭棋人工指派电脑执双" \
}
FEN_statement = { \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR b":"中炮局(B00)", \
"rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w":"中炮对进左马(B05)", \
"r1bakab1r/9/1cn3nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/9/RNBAKAB1R w":"中炮对屏风马(C00)", \
"r1bakabr1/9/1cn3nc1/p1p1p1p1p/9/9/P1P1P1P1P/NC2C1N2/9/R1BAKABR1 b":"中炮左边马对屏风马(C05)", \
"r1bakabr1/9/1cn3nc1/p1p1p1p1p/9/9/P1P1P1P1P/1CN1C1N2/9/R1BAKABR1 b":"中炮七路马对屏风马(C01)", \
"r1bakabr1/9/1cn3nc1/p1p1p1p1p/9/9/P1P1P1P1P/3CC1N2/9/RNBAKABR1 b":"五六炮对屏风马(C50)", \
"r1bakabr1/9/1cn3nc1/p1p1p1p1p/9/7R1/P1P1P1P1P/1C2C1N2/9/RNBAKAB2 b":"中炮巡河车对屏风马——红不进左马(C15)", \
"r1bakab1r/9/1cn3nc1/p1p1p3p/6p2/2P6/P3P1P1P/1CN1C1N2/9/R1BAKAB1R b":"中炮七路马对屏风马(C01)", \
"r1bakab1r/9/1cn3nc1/p3p1p1p/2p6/6P2/P1P1P3P/1CN1C1N2/9/R1BAKAB1R b":"中炮七路马对屏风马(C01)", \
"r1bakab1r/9/1cn3nc1/p1p1p1p1p/9/9/P1P1P1P1P/1CN1C1N2/9/R1BAKAB1R b":"中炮七路马对屏风马(C01)", \
"rnbakab1r/9/1c4n1c/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/9/RNBAKAB1R w":"中炮右横车对左三步虎(B22)", \
"rnbakab1r/9/1c4n1c/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/8R/RNBAKAB2 b":"中炮对左三步虎(B20)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w":"中炮对进右马(B01)", \
"r1bakabnr/9/c1n4c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/9/RNBAKAB1R w":"中炮对右三步虎(B04)", \
"rnbakabnr/9/1c2c4/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w":"顺炮缓开车局(D00)", \
"rnbakab1r/9/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/9/RNBAKABR1 b":"顺炮直车对缓开车(D10)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/9/RNBAKABR1 w":"顺炮直车对横车(D20)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/6P2/P1P1P3P/1C2C1N2/9/RNBAKABR1 b":"顺炮直车对横车——红进三兵(D26)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/3CC1N2/9/RNBAKABR1 b":"顺炮直车对横车——红仕角炮(D25)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/7R1/P1P1P1P1P/1C2C1N2/9/RNBAKAB2 b":"顺炮直车对横车——红巡河车(D23)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/NC2C1N2/9/R1BAKABR1 b":"顺炮直车对横车——红左边马(D22)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/2P6/P3P1P1P/1C2C1N2/9/RNBAKABR1 b":"顺炮直车对横车——红进七兵(D27)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/4A4/RNBAK1BR1 b":"顺炮直车对横车——红先上仕(D21)", \
"rnbakab1r/9/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/8R/RNBAKAB2 b":"顺炮横车对缓开车(D03)", \
"rnbakabr1/9/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/8R/RNBAKAB2 w":"顺炮横车对直车(D04)", \
"rnbakab2/9/1c2c1n2/p1p1p1p1p/7r1/9/P1P1P1P1P/1C2C1N2/3R5/RNBAKAB2 w":"顺炮横车对直车巡河(D05)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/6P2/P1P1P3P/1C2C1N2/9/RNBAKAB1R w":"顺炮缓开车对横车(D01)", \
"rnbakabr1/9/1c2c1n2/p1p1p1p1p/9/6P2/P1P1P3P/1C2C1N2/9/RNBAKAB1R w":"顺炮缓开车对直车(D02)", \
"rnbakabr1/9/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1CN1C1N2/9/R1BAKAB1R w":"顺炮缓开车对直车(D02)", \
"rnbakab2/8r/1c2c1n2/p1p1p1p1p/9/9/P1P1P1P1P/1CN1C1N2/9/R1BAKAB1R w":"顺炮缓开车对横车(D01)", \
"rnbakabn1/8r/1c2c4/p1p1p1p1p/9/9/P1P1P1P1P/1C2C1N2/9/RNBAKAB1R w":"顺炮缓开车对横车(D01)", \
"rnbakabnr/9/1c2c4/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/8R/RNBAKABN1 b":"顺炮横车对缓开车(D03)", \
"rnbakabnr/9/4c2c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2C4/9/RNBAKABNR w":"中炮对列炮(D50)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/6P2/P1P1P3P/1C5C1/9/RNBAKABNR b":"仙人指路(E00)", \
"rnbakabnr/9/1c4c2/p1p1p1p1p/9/6P2/P1P1P3P/1C5C1/9/RNBAKABNR w":"仙人指路对卒底炮(E10)", \
"rnbakabnr/9/1c4c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2C1/9/RNBAKABNR b":"仙人指路转左中炮对卒底炮(E13)", \
"rnbaka1nr/9/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2C1/9/RNBAKABNR w":"仙人指路转左中炮对卒底炮飞左象(E20)", \
"rnbaka1nr/9/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/2N1C2C1/9/R1BAKABNR b":"仙人指路转左中炮对卒底炮飞左象——红进左马(E22)", \
"rnbaka1nr/9/1c2b1c2/p1p1p3p/6p2/6P2/P1P1P3P/2N1C2C1/9/R1BAKABNR w":"仙人指路转左中炮对卒底炮飞左象——黑进7卒(E30)", \
"rnbaka1nr/9/1c2b1c2/p1p1p3p/9/6p2/P1P1P3P/2N1C2C1/9/1RBAKABNR w":"仙人指路转左中炮对卒底炮飞左象——黑连进7卒(E31)", \
"rnbaka1nr/9/1c2b1c2/p1p1p3p/9/6p2/P1P1P3P/2N1C2CN/9/R1BAKAB1R w":"仙人指路转左中炮对卒底炮飞左象——黑连进7卒(E31)", \
"1nbaka1nr/r8/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/2N1C2C1/9/R1BAKABNR w":"仙人指路转左中炮对卒底炮飞左象——红进左马对黑右横车(E23)", \
"rnbaka1nr/9/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2CN/9/RNBAKAB1R b":"仙人指路转左中炮对卒底炮飞左象——红右边马(E37)", \
"rnbaka1nr/9/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2C1/4A4/RNBAK1BNR b":"仙人指路转左中炮对卒底炮飞左象——红先上仕(E21)", \
"rnbaka1nr/9/1c2b1c2/p1p1C1p1p/9/6P2/P1P1P3P/7C1/9/RNBAKABNR b":"仙人指路转左中炮对卒底炮飞左象——红炮打中卒(E38)", \
"rn1akabnr/9/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2C1/9/RNBAKABNR w":"仙人指路转左中炮对卒底炮飞右象(E14)", \
"rn1akabnr/9/1c2b1c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2CN/9/RNBAKAB1R b":"仙人指路转左中炮对卒底炮飞右象——红右边马(E15)", \
"rn1akab1r/9/1c2b1c1n/p1p1p1p1p/9/6P2/P1P1P3P/4C2CN/9/RNBAKAB1R w":"仙人指路转左中炮对卒底炮飞右象——互进边马(E16)", \
"rnbakabnr/9/4c1c2/p1p1p1p1p/9/6P2/P1P1P3P/4C2C1/9/RNBAKABNR w":"仙人指路转左中炮对卒底炮转顺炮(E17)", \
"rnbakabnr/9/1c4c2/p1p1p1p1p/9/6P2/P1P1P3P/1C2C4/9/RNBAKABNR b":"仙人指路转右中炮对卒底炮(E12)", \
"rnbakabnr/9/1c4c2/p1p1p1p1p/9/6P2/P1P1P3P/1C2B2C1/9/RN1AKABNR b":"仙人指路飞相对卒底炮(E11)", \
"rnbakabnr/9/1c4c2/p1p1p1p1p/9/6P2/P1P1P3P/1C2B2C1/9/RNBAKA1NR b":"仙人指路飞相对卒底炮(E11)", \
"rnbakabnr/9/1c5c1/p3p1p1p/2p6/6P2/P1P1P3P/1C5C1/9/RNBAKABNR w":"对兵局(E40)", \
"rnbakabnr/9/1c5c1/p3p1p1p/2p6/6P2/P1P1P3P/2C4C1/9/RNBAKABNR b":"对兵转兵底炮(E46)", \
"rnbakabnr/9/4c2c1/p3p1p1p/2p6/6P2/P1P1P3P/2C4C1/9/RNBAKABNR w":"对兵转兵底炮对右中炮(E47)", \
"rnbakabnr/9/1c2c4/p3p1p1p/2p6/6P2/P1P1P3P/2C4C1/9/RNBAKABNR w":"对兵转兵底炮对左中炮(E48)", \
"rnbakabnr/9/1c5c1/p3p1p1p/2p6/6P2/P1P1P3P/1C4NC1/9/RNBAKAB1R b":"对兵进右马局(E41)", \
"r1bakabnr/9/1cn4c1/p3p1p1p/2p6/6P2/P1P1P3P/1C4NC1/9/RNBAKAB1R w":"对兵互进右马局(E42)", \
"r1bakabnr/9/1cn4c1/p3p1p1p/2p6/6P2/P1P1P3P/1C4NC1/8R/RNBAKAB2 b":"对兵互进右马局——红横车(E44)", \
"r1bakabnr/9/1cn4c1/p3p1p1p/2p6/6P2/P1P1P3P/1C4N1C/9/RNBAKAB1R b":"对兵互进右马局——红边炮(E45)", \
"r1bakabnr/9/1cn4c1/p3p1p1p/2p6/6P2/P1P1P3P/1C2B1NC1/9/RN1AKAB1R b":"对兵互进右马局——红飞相(E43)", \
"r1bakabnr/9/1cn4c1/p3p1p1p/2p6/6P2/P1P1P3P/1C2B1NC1/9/RNBAKA2R b":"对兵互进右马局——红飞相(E43)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/6P2/P1P1P3P/1C5C1/9/RNBAKABNR w":"仙人指路对进右马(E06)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/6P2/P1P1P3P/1C4NC1/9/RNBAKAB1R b":"仙人指路互进右马局(E07)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/2P3P2/P3P3P/1C5C1/9/RNBAKABNR b":"两头蛇对进右马(E08)", \
"r1bakabnr/9/1cn3c2/p1p1p1p1p/9/2P3P2/P3P3P/1C5C1/9/RNBAKABNR w":"两头蛇对进右马转卒底炮(E09)", \
"rnbaka1nr/9/1c2b2c1/p1p1p1p1p/9/6P2/P1P1P3P/1C5C1/9/RNBAKABNR w":"仙人指路对飞象(E01)", \
"rnbaka1nr/9/1c2b2c1/p1p1p1p1p/9/6P2/P1P1P3P/1C4NC1/9/RNBAKAB1R b":"仙人指路进右马对飞象(E02)", \
"rnbakabnr/9/4c2c1/p1p1p1p1p/9/6P2/P1P1P3P/1C5C1/9/RNBAKABNR w":"仙人指路对中炮(E03)", \
"rnbakabnr/9/6cc1/p1p1p1p1p/9/6P2/P1P1P3P/1C5C1/9/RNBAKABNR w":"仙人指路对金钩炮(E05)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR b":"飞相局(A10)", \
"rnbakabnr/9/1c1c5/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对左过宫炮(A30)", \
"rnbakabnr/9/1c1c5/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR b":"飞相进右马对左过宫炮(A31)", \
"rnbakabnr/9/3c3c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对右士角炮(A21)", \
"rnbakabnr/9/3c3c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/R8/1NBAKA1NR b":"飞相横车对右士角炮(A24)", \
"rnbakabnr/9/3c3c1/p1p1p1p1p/9/9/P1P1P1P1P/NC2B2C1/9/R1BAKA1NR b":"飞相左边马对右士角炮(A23)", \
"rnbakabnr/9/3c3c1/p1p1p1p1p/9/9/P1P1P1P1P/1CN1B2C1/9/R1BAKA1NR b":"飞相进左马对右士角炮(A22)", \
"rnbakabnr/9/3c3c1/p1p1p1p1p/9/2P6/P3P1P1P/1C2B2C1/9/RNBAKA1NR b":"飞相进七兵对右士角炮(A26)", \
"rnbakabnr/9/3c3c1/p1p1p1p1p/9/6P2/P1P1P3P/1C2B2C1/9/RNBAKA1NR b":"飞相进三兵对右士角炮(A25)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对进7卒(A36)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/2P6/P3P1P1P/1C2B2C1/9/RNBAKA1NR b":"飞相互进七兵局(A38)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/9/P1P1P1P1P/1CN1B2C1/9/R1BAKA1NR b":"飞相进左马对进7卒(A37)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对进右马(A14)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/6P2/P1P1P3P/1C2B2C1/9/RNBAKA1NR b":"飞相进三兵对进右马(A15)", \
"r1bakabnr/9/1cn4c1/p1p1p1p1p/9/2P6/P3P1P1P/1C2B2C1/9/RNBAKA1NR b":"飞相进七兵对进右马(A16)", \
"rnbakabnr/9/1c2c4/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对左中炮(A27)", \
"rnbakabnr/9/5c1c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对右过宫炮(A35)", \
"rnbakabnr/9/1c3c3/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对左士角炮(A20)", \
"rnbaka1nr/9/1c2b2c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"顺相局(A11)", \
"rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对进左马(A13)", \
"rnbakabnr/9/1c5c1/p3p1p1p/2p6/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对进3卒(A39)", \
"rn1akabnr/9/1c2b2c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"列相局(A12)", \
"rnbakabnr/9/4c2c1/p1p1p1p1p/9/9/P1P1P1P1P/1C2B2C1/9/RNBAKA1NR w":"飞相对右中炮(A29)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C4NC1/9/RNBAKAB1R b":"起马局(A40)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/9/P1P1P1P1P/1C4NC1/9/RNBAKAB1R":"起马对进7卒(A41)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/2P6/P3P1P1P/1C4NC1/9/RNBAKAB1R b":"起马互进七兵局(A45)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/9/P1P1P1P1P/3C2NC1/9/RNBAKAB1R b":"起马转仕角炮对进7卒(A43)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/9/P1P1P1P1P/1C4N1C/9/RNBAKAB1R b":"起马转边炮对进7卒(A42)", \
"rnbakabnr/9/1c5c1/p1p1p3p/6p2/9/P1P1P1P1P/4C1NC1/9/RNBAKAB1R b":"起马转中炮对进7卒(A44)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C1C5/9/RNBAKABNR b":"过宫炮局(A60)", \
"rnbakab1r/9/1c4nc1/p1p1p1p1p/9/9/P1P1P1P1P/1C1C5/9/RNBAKABNR w":"过宫炮对进左马(A61)", \
"rnbakabnr/9/1c2c4/p1p1p1p1p/9/9/P1P1P1P1P/1C1C5/9/RNBAKABNR w":"过宫炮对左中炮(A63)", \
"rnbakabn1/8r/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C1C5/9/RNBAKABNR w":"过宫炮对横车(A62)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/3C3C1/9/RNBAKABNR b":"仕角炮局(A50)", \
"rnbakabnr/9/1c2c4/p1p1p1p1p/9/9/P1P1P1P1P/3C3C1/9/RNBAKABNR w":"仕角炮对右中炮(A52)", \
"rnbakabnr/9/1c5c1/p3p1p1p/2p6/9/P1P1P1P1P/3C3C1/9/RNBAKABNR w":"仕角炮对进7卒(A54)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/NC5C1/9/R1BAKABNR b":"边马局(A02)", \
"rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1CC6/9/RNBAKABNR b":"金钩炮局(A07)" \
}
Initial_Board_Empty = "9/9/9/9/9/9/9/9/9/9 w - - 0 1"
Initial_Board_Light = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1"
Initial_Board_Dark = "xxxxkxxxx/9/1x5x1/x1x1x1x1x/9/9/X1X1X1X1X/1X5X1/9/XXXXKXXXX w - - 0 1"
nodes = "RNHBEAKCPXrnhbeakcpx"
uncovered = list("rnbaabnrccpppppPPPPPCCRNBAABNR") # 初始的未揭的棋
natural_end = 120 # 无吃子步数作和
longGeneral_end = 3 # 长将作负
sameFEN_end = 4 # 不变着判和局面数
def __init__(self, mode = 0b0000, FEN = Initial_Board_Light, prev = None, prevStep = None, next = [], nextStep = [], eaten = [], direction = True) -> tuple:
self.mode = mode # 模式(棋类 | 机器辅助)
self.FEN = FEN # 使用 FEN 记录局面
self.FEN2lists() # 转 lists
assert(self.checkFEN()) # 检查局面
self.prev = prev # 上一个局面(只有一个局面)
self.prevStep = prevStep # 上一步着法(只有一个局面)
self.next = next[::] # 下一个局面(可能有多个局面)
self.nextStep = nextStep[::] # 下一步着法(可能有多个局面)
assert(len(self.next) == len(self.nextStep))
if mode >> 3: # 揭棋
if eaten is None: # 传递为 None 表示上一局面会处理
self.eaten = None
else:
self.eaten = eaten[::] # 被吃子
self.uncovered = Board.uncovered[::] # 初始化未揭子
for line in self.lists[0]: # 去掉局面中已揭开的棋
for piece in line:
if piece == "H": # 马有两种表示
self.uncovered.remove("N")
elif piece == "h": # 马有两种表示
self.uncovered.remove("n")
elif piece == "E": # 相有两种表示
self.uncovered.remove("B")
elif piece == "e": # 象有两种表示
self.uncovered.remove("b")
elif piece not in "XxKk.": # 是其它非将帅明子
self.uncovered.remove(piece)
for piece in self.eaten: # 去掉被吃子
if piece[-1] == "H": # 马有两种表示
self.uncovered.remove("N")
elif piece[-1] == "h": # 马有两种表示
self.uncovered.remove("n")
elif piece[-1] == "E": # 相有两种表示
self.uncovered.remove("B")
elif piece[-1] == "e": # 象有两种表示
self.uncovered.remove("b")
else:
self.uncovered.remove(piece[-1]) # 第一个字符存明暗
boardFEN = self.FEN.split(" ")[0]
assert( \
len(findall("[A-Z]", boardFEN)) + len([piece for piece in self.eaten if "A" <= piece[-1] <= "Z"]) == 16 \
and len(findall("[a-z]", boardFEN)) + len([piece for piece in self.eaten if "a" <= piece[-1] <= "z"]) == 16 \
and boardFEN.count("X") == len([item for item in self.uncovered if "A" <= item <= "Z"]) \
and boardFEN.count("x") == len([item for item in self.uncovered if "a" <= item <= "z"]) \
) # 一方棋盘剩余子与被吃子和 == 16 and 暗子 == 未揭子
else: # 象棋
if eaten is None: # 传递为 None 表示上一局面会处理
self.eaten = None
else:
self.eaten = Board.uncovered[::] # 初始化
for line in self.lists[0]: # 逐行
for piece in line: # 逐子
if piece == "H": # 马有两种表示
self.eaten.remove("N")
elif piece == "h": # 马有两种表示
self.eaten.remove("n")
elif piece == "E": # 相有两种表示
self.eaten.remove("B")
elif piece == "e": # 象有两种表示
self.eaten.remove("b")
elif piece not in "Kk.": # 排除将帅空(遇到 Xx 报错)
self.eaten.remove(piece)
self.uncovered = [] # 未被揭开(象棋无暗子)
self.direction = direction # 视角(红方为 True | 黑方为 False)
tmpJudgement = self.judge() # 是否已终(防止残局无法走子)
self.preMsg = tmpJudgement[2] if tmpJudgement[1] else None # 用于传递(构造函数无法传递返回值)
self.startMsg = None
def checkFEN(self) -> bool: # 局面检查
boardFEN = self.FEN.split(" ")[0]
return ( \
(len(self.lists) == 6 and len(self.lists[0]) == 10 and not any([len(self.lists[0][i]) != 9 for i in range(10)])) \
and (boardFEN.count("R") <= 2 and boardFEN.count("r") <= 2) \
and (boardFEN.count("N") + boardFEN.count("H") <= 2 and boardFEN.count("n") + boardFEN.count("h") <= 2) \
and (boardFEN.count("B") + boardFEN.count("E") <= 2 and boardFEN.count("b") + boardFEN.count("e") <= 2) \
and (boardFEN.count("A") <= 2 and boardFEN.count("a") <= 2) \
and (boardFEN.count("K") == 1 and boardFEN.count("k") == 1) \
and (boardFEN.count("C") <= 2 and boardFEN.count("c") <= 2) \
and (boardFEN.count("P") <= 5 and boardFEN.count("p") <= 5) \
and (