今天给大家带来的是人机大战的象棋
中国象棋
首先绘制一下棋盘,看看样子:
黑白经典款
绘制棋盘:
class Board(QLabel):'''棋盘坐标与屏幕坐标类似,左上角为 (0, 0),右下角为 (8, 9)'''BOARD = str(dirpath / u"images/board.png")MARK = str(dirpath / u"images/mark.png")CHECK = str(dirpath / u"images/check.png")FAVICON = str(dirpath / u"images/black_bishop.png")IMAGES = {Chess.R: str(dirpath / 'images/red_rook.png'),Chess.N: str(dirpath / 'images/red_knight.png'),Chess.B: str(dirpath / 'images/red_bishop.png'),Chess.A: str(dirpath / 'images/red_advisor.png'),Chess.K: str(dirpath / 'images/red_king.png'),Chess.C: str(dirpath / 'images/red_cannon.png'),Chess.P: str(dirpath / 'images/red_pawn.png'),Chess.r: str(dirpath / 'images/black_rook.png'),Chess.n: str(dirpath / 'images/black_knight.png'),Chess.b: str(dirpath / 'images/black_bishop.png'),Chess.a: str(dirpath / 'images/black_advisor.png'),Chess.k: str(dirpath / 'images/black_king.png'),Chess.c: str(dirpath / 'images/black_cannon.png'),Chess.p: str(dirpath / 'images/black_pawn.png'),}ANIMATION_DURATION = 280flags = QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHintsignal_class = BoardSignaldef __init__(self, parent=None, callback=None):super().__init__(parent=parent)self.csize = 60self.board_image = QtGui.QPixmap(self.BOARD)if parent is None:self.setWindowIcon(QtGui.QIcon(self.FAVICON))self.setWindowTitle(u"Chinese Chess")# self.setWindowFlags(self.flags)# https://www.mfitzp.com/tutorials/packaging-pyqt5-pyside2-applications-windows-pyinstaller/app = QtWidgets.QApplication.instance()if app:app.setWindowIcon(QtGui.QIcon(self.FAVICON))if os.name == 'nt':logger.info("set model id")ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(f'StevenBaby.Chess.{VERSION}')self.setObjectName(u"Board")self.setScaledContents(True)self.animate = QtCore.QPropertyAnimation(self, b'geometry', self)self.resize(self.csize * Chess.W, self.csize * Chess.H)for chess, path in self.IMAGES.items():self.IMAGES[chess] = QPixmap(path)mark = QPixmap(self.MARK)# 棋盘起始标记self.mark1 = QLabel(self)self.mark1.setPixmap(mark)self.mark1.setScaledContents(True)self.mark1.setVisible(False)# 棋盘落子标记self.mark2 = QLabel(self)self.mark2.setPixmap(mark)self.mark2.setScaledContents(True)self.mark2.setVisible(False)check = QPixmap(self.CHECK)# 将军棋子标记self.mark3 = QLabel(self)self.mark3.setPixmap(check)self.mark3.setScaledContents(True)self.mark3.setVisible(False)self.signal = self.signal_class()self.signal.refresh.connect(self.refresh)self.labels = mat(zeros((Chess.W, Chess.H,)), dtype=QtWidgets.QLabel)self.board = mat(zeros((Chess.W, Chess.H,)), dtype=int)self.fpos = Noneself.tpos = Noneself.check = Noneself.reverse = Falseself.update()self.callback = callbackdef setBoard(self, board, fpos=None, tpos=None):# 设置 棋盘 board,以及该步,的棋子从哪儿 fpos,到哪儿 tpos# 由于 该函数可能在多个线程中调用,所以下面触发 signal.refresh# QT 会自动将刷新棋盘的重任放到主线程去做# 如果直接在非主线程调用 refresh 函数,程序可能莫名其妙的死掉。self.board = boardself.fpos = fposself.tpos = tposself.signal.refresh.emit()
这个主要是绘制棋盘和棋盘中棋子移动产生的回调。
绘制棋子:
# coding=utf-8
import numpy as npfrom logger import loggerclass Chess(object):NONE = 0RMASK = 16 # 第五位表示红方 比如红車 0b01_0_001# 判断棋子是不是黑方只需要 按位与 0b0100000 就可以了BMASK = 32 # 第六位表示黑方 比如黑炮 0b10_0_110# 判断棋子是不是黑方只需要 按位与 0b100000 就可以了RED = RMASKBLACK = BMASKTMASK = 0b110000 # 棋子颜色掩码,可能的取值为 16 和 32CMASK = 0b111 # 棋子掩码# 棋子使用 1-7 来表示,二进制就是 0b001 - 0b111# INDEX = {# '帥': (0, 0), '仕': (1, 0), '相': (2, 0), '傌': (3, 0), '俥': (4, 0), '炮': (5, 0), '兵': (6, 0),# '將': (0, 1), '士': (1, 1), '象': (2, 1), '馬': (3, 1), '車': (4, 1), '砲': (5, 1), '卒': (6, 1),# }KING = 1ADVISOR = 2BISHOP = 3KNIGHT = 4ROOK = 5CANNON = 6PAWN = 7P = PAWN | RMASKR = ROOK | RMASKN = KNIGHT | RMASKB = BISHOP | RMASKA = ADVISOR | RMASKC = CANNON | RMASKK = KING | RMASKp = PAWN | BMASKr = ROOK | BMASKn = KNIGHT | BMASKb = BISHOP | BMASKa = ADVISOR | BMASKc = CANNON | BMASKk = KING | BMASKWIDTH = 9HEIGHT = 10W = WIDTHH = HEIGHTNAMES = {0: ' ',P: 'P',R: 'R',N: 'N',B: 'B',A: 'A',C: 'C',K: 'K',p: 'p',r: 'r',n: 'n',b: 'b',a: 'a',c: 'c',k: 'k',}CHESSES = {P, R, N, B, A, C, K, p, r, n, b, a, c, k, }@staticmethoddef invert(chess):# 转换棋子颜色return (chess & 7) | ((~chess) & 0b110000)@staticmethoddef is_red(chess):return chess & Chess.RED@staticmethoddef is_black(chess):return chess & Chess.BLACK@staticmethoddef chess(c):return c & Chess.CMASK@staticmethoddef color(c):return c & Chess.TMASKORIGIN = np.mat(np.array([[r, 0, 0, p, 0, 0, P, 0, 0, R],[n, 0, c, 0, 0, 0, 0, C, 0, N],[b, 0, 0, p, 0, 0, P, 0, 0, B],[a, 0, 0, 0, 0, 0, 0, 0, 0, A],[k, 0, 0, p, 0, 0, P, 0, 0, K],[a, 0, 0, 0, 0, 0, 0, 0, 0, A],[b, 0, 0, p, 0, 0, P, 0, 0, B],[n, 0, c, 0, 0, 0, 0, C, 0, N],[r, 0, 0, p, 0, 0, P, 0, 0, R]]))MOVE = 1CAPTURE = 2DRAW = 3CHECK = 4INVALID = 5CHECKMATE = 6RESIGN = 7NEWGAME = 8# 以下为引擎专用INFO = 9POPHASH = 10NOBESTMOVE = 11
还有其他文件,这里就不一一列举了,
主程序:
# coding=utf-8import sys
import time
from functools import partial
import threadingimport numpy as npimport keyboardfrom PySide6 import QtWidgets
from PySide6 import QtCore
from PySide6 import QtGuifrom board import BoardFramefrom engine import Engine
from engine import UCCIEngine
from engine import Chess
from engine import dirpath
from engine import logger
from engines import UCCI_ENGINES
from situation import Situationimport audio
import engines
import system
from version import VERSIONfrom dialogs.settings import SettingsDialog
from dialogs.method import MethodDialog
from toast import Toastfrom context import BaseContextMenu
from context import BaseContextMenuMixinfrom arrange import ArrangeBoard
from manual import Manual
import qqchessclass GameSignal(QtCore.QObject):hint = QtCore.Signal(None)undo = QtCore.Signal(None)redo = QtCore.Signal(None)reset = QtCore.Signal(None)debug = QtCore.Signal(None)load = QtCore.Signal(None)save = QtCore.Signal(None)paste = QtCore.Signal(None)capture = QtCore.Signal(None)connecting = QtCore.Signal(None)move = QtCore.Signal(int)draw = QtCore.Signal(None)resign = QtCore.Signal(None)checkmate = QtCore.Signal(None)nobestmove = QtCore.Signal(None)animate = QtCore.Signal(tuple, tuple)settings = QtCore.Signal(None)method = QtCore.Signal(None)arrange = QtCore.Signal(None)thinking = QtCore.Signal(bool)reverse = QtCore.Signal(None)class GameContextMenu(BaseContextMenu):items = [('提示', 'Ctrl+H', lambda self: self.signal.hint.emit(), True),('悔棋', 'Ctrl+Z', lambda self: self.signal.undo.emit(), True),('重走', 'Ctrl+Shift+Z', lambda self: self.signal.redo.emit(), True),'separator',('重置', 'Ctrl+N', lambda self: self.signal.reset.emit(), False),('布局', 'Ctrl+A', lambda self: self.signal.arrange.emit(), True),('着法', 'Ctrl+M', lambda self: self.signal.method.emit(), False),('反转', 'Ctrl+I', lambda self: self.signal.reverse.emit(), False),'separator',('粘贴', 'Ctrl+V', lambda self: self.signal.paste.emit(), True),('载入', 'Ctrl+O', lambda self: self.signal.load.emit(), True),('保存', 'Ctrl+S', lambda self: self.signal.save.emit(), True),('截屏', 'Ctrl+K', lambda self: self.signal.capture.emit(), True),('连线', 'Ctrl+L', lambda self: self.signal.connecting.emit(), True),'separator',('设置', 'Ctrl+,', lambda self: self.signal.settings.emit(), False),]if system.DEBUG:items.extend([('调试', 'Ctrl+D', lambda self: self.signal.debug.emit(), False),'separator',])class Game(BoardFrame, BaseContextMenuMixin):def __init__(self, parent=None):super().__init__(parent, board_class=ArrangeBoard)self.setWindowTitle(f"中国象棋 v{VERSION}")self.setupContextMenu()audio.init()self.image = Noneself.engine = Noneself.engines = {Chess.RED: None,Chess.BLACK: None,}self.engine_side = [Chess.BLACK]self.human_side = [Chess.RED]self.board.csize = 80self.board.callback = self.board_callbackself.resize(self.board.csize * Chess.W, self.board.csize * Chess.H)self.game_signal = GameSignal()self.method = MethodDialog(self)self.method.setWindowIcon(QtGui.QIcon(self.board.FAVICON))self.settings = SettingsDialog(self)self.settings.setWindowIcon(QtGui.QIcon(self.board.FAVICON))self.game_menu = GameContextMenu(self, self.game_signal)self.toast = Toast(self)# 以下初始化信号keyboard.add_hotkey('ctrl+alt+z', lambda: self.game_signal.capture.emit())keyboard.add_hotkey('ctrl+alt+x', lambda: self.game_signal.hint.emit())keyboard.add_hotkey('ctrl+alt+m', lambda: self.game_signal.method.emit())keyboard.add_hotkey('ctrl+alt+l', lambda: self.game_signal.connecting.emit())self.game_signal.reverse.connect(self.reverse)self.game_signal.thinking.connect(self.set_thinking)self.game_signal.settings.connect(self.settings.show)self.game_signal.hint.connect(self.hint)self.game_signal.undo.connect(self.undo)self.game_signal.redo.connect(self.redo)self.game_signal.reset.connect(self.reset)self.game_signal.debug.connect(self.debug)self.game_signal.load.connect(self.load)self.game_signal.save.connect(self.save)self.game_signal.paste.connect(self.paste)self.game_signal.capture.connect(self.capture)self.game_signal.move.connect(self.play)self.game_signal.checkmate.connect(self.checkmateMessage)self.game_signal.checkmate.connect(lambda: self.set_thinking(False))self.game_signal.nobestmove.connect(self.nobestmove)self.game_signal.draw.connect(lambda: self.toast.message('和棋!!!'))self.game_signal.resign.connect(lambda: self.toast.message('认输了!!!'))self.game_signal.animate.connect(self.animate)self.settings.transprancy.valueChanged.connect(lambda e: self.setWindowOpacity((100 - e) / 100))self.settings.reverse.stateChanged.connect(lambda e: self.board.setReverse(self.settings.reverse.isChecked()))self.settings.audio.stateChanged.connect(lambda e: audio.play(Chess.MOVE) if e else None)self.settings.standard_method.stateChanged.connect(lambda e: self.method.set_standard(e))self.settings.ontop.clicked.connect(self.set_on_top)self.settings.ok.clicked.connect(self.accepted)self.settings.loads()self.game_signal.arrange.connect(self.arrange)self.board.signal.finish.connect(self.finish_arrange)self.game_signal.method.connect(lambda: self.method.setVisible(not self.method.isVisible()))self.method.list.currentItemChanged.connect(self.method_changed)self.game_signal.connecting.connect(self.connecting)# self.qqboard = qqchess.Capturer(self)# logger.info("set qqboard %s", self.settings.qqboard)# self.qqboard.setGeometry(*self.settings.qqboard)# self.qqboard.signal.capture.connect(self.capture_image)self.reset()self.accepted()self.check_openfile()def set_on_top(self):logger.info("set on top")# self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)def check_openfile(self):# 直接打开棋谱文件if len(sys.argv) > 1:with open(sys.argv[1], encoding='utf8') as file:content = file.read()self.pasre_content(content)def show_context_menu(self, point):if self.board.arranging:return self.board.arrange_menu.exec_(self.mapToGlobal(point))self.game_menu.exec(self.mapToGlobal(point))def update_action_state(self):if len(self.engine_side) == 2 and not self.engine.checkmate:self.game_menu.setAllMenuEnabled(False)self.game_menu.setAllShortcutEnabled(False)elif self.thinking:self.game_menu.setAllMenuEnabled(False)self.game_menu.setAllShortcutEnabled(False)else:self.game_menu.setAllMenuEnabled(True)self.game_menu.setAllShortcutEnabled(True)def set_thinking(self, thinking):self.thinking = thinkinglogger.debug("set thinking %s", self.thinking)if len(self.engine_side) == 2 and not self.engine.checkmate:QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BusyCursor)elif self.thinking:QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BusyCursor)else:QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.ArrowCursor)self.update_action_state()def arrange(self):self.board.arranging = Trueself.board.setBoard(self.board.board)self.board.setCheck(None)self.game_menu.setAllShortcutEnabled(False)def finish_arrange(self, finished):if not finished:returnlogger.debug('finish arrange')self.engine.close()self.engine = Engine()self.engine.sit.board = self.board.boardself.engine.sit.turn = self.board.first_sideself.engine.sit.fen = self.engine.sit.format_current_fen()self.try_engine_move()self.game_menu.setAllShortcutEnabled(True)def method_changed(self, item: QtWidgets.QListWidgetItem):index = self.method.list.indexFromItem(item).row()self.engine.set_index(index)if self.board.animate.state() == QtCore.QAbstractAnimation.State.Running:returnself.updateBoard()def accepted(self):logger.info("setting accepted....")# 设置棋手或AIself.engine_side = []self.human_side = []if self.settings.redside.currentIndex() == 0:self.human_side.append(Chess.RED)else:self.engine_side.append(Chess.RED)if self.settings.blackside.currentIndex() == 0:self.engine_side.append(Chess.BLACK)else:self.human_side.append(Chess.BLACK)idx = self.settings.red_engine.currentIndex()engine = self.engines[Chess.RED]if not isinstance(engine, UCCI_ENGINES[idx]):self.init_engines(Chess.RED)idx = self.settings.black_engine.currentIndex()engine = self.engines[Chess.BLACK]if not isinstance(engine, UCCI_ENGINES[idx]):self.init_engines(Chess.BLACK)if len(self.engine_side) == 2:self.method.list.setEnabled(False)else:self.method.list.setEnabled(True)self.update_action_state()self.try_engine_move()def try_engine_move(self):if self.engine.sit.turn not in self.engine_side:returnself.go()def go(self):if self.engine.checkmate:self.game_signal.checkmate.emit()logger.debug('engine is checkmated hint ignored...')returnself.game_signal.thinking.emit(True)engine = self.current_engine()engine.position(self.engine.sit.format_fen())params = self.settings.get_params(self.engine.sit.turn)engine.go(**params)def nobestmove(self):if self.engine.sit.turn == Chess.RED:side = '黑方'else:side = '红方'logger.debug('nobestmove')self.toast.message(f"{side}行棋违例!")@QtCore.Slot(int)def play(self, audio_type):if not self.settings.audio.isChecked():returnaudio.play(audio_type)def init_engines(self, turn=None):turns = []if turn == Chess.RED:turns = [Chess.RED]elif turn == Chess.BLACK:turns = [Chess.BLACK]else:turns = [Chess.RED, Chess.BLACK]for turn in turns:old = self.engines[turn]if old:old.close()idx = self.settings.get_engine_box(turn).currentIndex()new = UCCI_ENGINES[idx](callback=self.engine_callback)new.start()self.engines[turn] = newdef current_engine(self) -> Engine:return self.engines[self.engine.sit.turn]def reverse(self):logger.debug("reverse")check = self.settings.reverse.isChecked()self.settings.reverse.setChecked(not check)self.settings.save()def reset(self):self.init_engines()self.engine = Engine()self.connected = Falseself.connect_inited = Falseself.fpos = Noneself.board.arranging = Falseself.thinking = Falseself.game_signal.move.emit(Chess.NEWGAME)self.updateBoard()self.board.setCheck(None)self.try_engine_move()self.method.refresh(self.engine)@QtCore.Slot(None)def undo(self):for _ in range(2):self.engine.undo()if self.engine.sit.turn in self.human_side:breakself.updateBoard()@QtCore.Slot(None)def redo(self):for _ in range(2):self.engine.redo()logger.debug('engine redo result %d', self.engine.sit.result)self.game_signal.move.emit(self.engine.sit.result)if self.engine.checkmate:self.game_signal.checkmate.emit()if self.engine.sit.turn in self.human_side:breakself.updateBoard()@QtCore.Slot(bool)def hint(self):if self.thinking:logger.debug('engine is thinking hint ignored...')returnself.go()@QtCore.Slot(None)def debug(self):logger.debug("debug slot.....")qqchess.show(self.image)# logger.debug(self.engine.sit.format_fen())@QtCore.Slot(None)def save(self):dialog = QtWidgets.QFileDialog(self)dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)filename = dialog.getSaveFileName(self, "保存中国象棋文件 Fen", ".", "文件 (*.fen)")[0]if not filename:returnfen = self.engine.sit.format_fen()with open(filename, 'w', encoding='utf8') as file:file.write('fen ')file.write(fen)logger.info("save file %s - fen %s", filename, fen)def pasre_content(self, content: str):if not content.startswith('fen '):manual = Manual()try:manual.callback = lambda fpos, tpos: self.board.setBoard(manual.sit.board,manual.sit.fpos,manual.sit.tpos)manual.parse(content)except Exception as e:self.toast.message(str(e))returnfen = manual.sit.format_fen()else:fen = content[4:]self.engine.index = 0self.engine.stack = self.engine.stack[:1]self.engine.sit = self.engine.stack[0]self.updateBoard()self.board.setCheck(None)if self.engine.sit.parse_fen(fen, load=True):moves = self.engine.sit.movesself.engine.sit.moves = []for fpos, tpos in moves:result = self.engine.move(fpos, tpos)if result == Chess.CHECKMATE:self.game_signal.checkmate.emit()breakself.updateBoard()else:self.toast.message("加载棋谱失败")@QtCore.Slot(None)def load(self):dialog = QtWidgets.QFileDialog(self)dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)filename = dialog.getOpenFileName(self, "打开中国象棋文件 Fen", ".", "fen 文件 (*.fen);;txt 文件 (*.txt)")[0]if not filename:returnwith open(filename, 'r', encoding='utf8') as file:content = file.read()self.pasre_content(content)def paste(self):content = QtWidgets.QApplication.clipboard().text()logger.debug('Clipboard text %s', content)if content:self.pasre_content(content)returnimage = qqchess.ImageGrab.grabclipboard()if isinstance(image, qqchess.Image.Image):self.paste_image(image)def capture(self):logger.debug("capture...")colors = {0: Chess.RMASK, 1: Chess.BMASK}board = np.zeros((9, 10), dtype=np.int8)while True:pred = qqchess.classifier.get_board()board0 = boardboard = np.zeros((9, 10), dtype=np.int8)for loc, idx in pred.items():board[loc] = (idx[0] + 1) | colors[idx[1]]if np.all(board0 == board):break# 验证数量C = Chesswheres = np.argwhere((board == C.K) | (board == C.k))if len(wheres) != 2:logger.warning("bishop count error %s...", len(wheres))return Nonefor where in wheres:if where[0] < 3 or where[0] > 5:logger.warning("king location error %s...", where)return Noneif 2 < where[1] < 7:logger.warning("king location error %s...", where)return Nonecounts = {C.P: 5,C.R: 2,C.N: 2,C.B: 2,C.A: 2,C.C: 2,C.p: 5,C.r: 2,C.n: 2,C.b: 2,C.a: 2,C.c: 2,}for key, count in counts.items():wheres = np.argwhere(board == key)if len(wheres) > count:logger.warning("chess count error %s > %s...", len(wheres), count)return Nonewheres = np.argwhere((board == C.B) | (board == C.B))for where in wheres:if tuple(where) not in {(2, 0), (6, 0),(0, 2), (4, 2), (8, 2),(2, 4), (6, 4),(2, 5), (6, 5),(0, 7), (4, 7), (8, 7),(2, 9), (6, 9),}:logger.warning("bishop location error %s...", where)return Nonewheres = np.argwhere((board == C.A) | (board == C.a))for where in wheres:if tuple(where) not in {(3, 0), (5, 0),(4, 1),(3, 2), (5, 2),(3, 7), (5, 7),(4, 8),(3, 9), (5, 9),}:logger.warning("advisor location error %s...", where)return Nonewheres = np.argwhere(board == Chess.K)turn = Chess.REDself.settings.reverse.setChecked(False)if wheres[0][1] < 3:logger.debug("reversed")board = board[::-1, ::-1]self.settings.reverse.setChecked(True)turn = Chess.BLACKif turn == Chess.RED and self.settings.redside.currentIndex() != 1:self.settings.redside.setCurrentIndex(1)self.settings.blackside.setCurrentIndex(1)self.accepted()if turn == Chess.BLACK and self.settings.redside.currentIndex() != 0:self.settings.redside.setCurrentIndex(0)self.settings.blackside.setCurrentIndex(0)self.accepted()wheres = np.argwhere((board - self.engine.sit.board) != 0)# logger.debug("wheres %s", wheres)if len(wheres) == 0:returnif len(wheres) == 2:pos1 = tuple(wheres[0])pos2 = tuple(wheres[1])if board[pos1] != 0 and board[pos2] != 0:returnassert (board[pos1] == 0 or board[pos2] == 0)assert (board[pos1] != 0 or board[pos2] != 0)if board[pos1] == 0:fpos = pos1tpos = pos2else:fpos = pos2tpos = pos1if Chess.color(board[tpos]) != turn:self.move(fpos, tpos)elif not self.connect_inited:logger.info("reset engine situation")# self.engine.close()# self.engine = Engine()self.engine.index = 0self.engine.sit = Situation(board, turn=turn)self.engine.stack = [self.engine.sit]self.fpos = Noneself.updateBoard()self.try_engine_move()self.connect_inited = True@QtCore.Slot(tuple, tuple)def animate(self, fpos, tpos):self.board.move(self.engine.sit.board, fpos, tpos,self.updateBoard,self.settings.ui.animate.isChecked(),)def move(self, fpos, tpos):self.game_signal.thinking.emit(False)if self.engine.checkmate:self.game_signal.checkmate.emit()returnresult = self.engine.move(fpos, tpos)logger.debug('move result %s', result)if not result:returnif result != Chess.INVALID:self.game_signal.animate.emit(fpos, tpos)else:returnif self.engine.sit.check:self.board.setCheck(self.engine.sit.check)logger.debug('check ... %s', self.engine.sit.check)else:self.board.setCheck(None)self.game_signal.move.emit(result)if result == Chess.CHECKMATE:logger.debug("emit checkmate")self.game_signal.checkmate.emit()returnself.try_engine_move()@QtCore.Slot(int)def change_difficulty(self, diff):self.depth_computer = diffdef updateBoard(self):if hasattr(self, 'method'):self.method.signal.refresh.emit()self.board.setBoard(self.engine.sit.board,self.engine.sit.fpos,self.engine.sit.tpos)@QtCore.Slot(None)def checkmateMessage(self):if not self.engine.checkmate:returnself.connected = Falseself.connect_inited = Falseif self.engine.sit.turn == Chess.RED:self.toast.message("黑方胜!!!")# QtWidgets.QMessageBox(self).warning(self, '信息', '')else:self.toast.message("红方胜!!!")# QtWidgets.QMessageBox(self).information(self, '信息', '红方胜!!!')def engine_callback(self, type, data):if type == Chess.MOVE:time.sleep(self.settings.delay.value() / 1000)self.move(data[0], data[1])elif type == Chess.INFO:logger.debug(data)elif type == Chess.POPHASH:logger.debug(data)elif type == Chess.DRAW:self.game_signal.draw.emit()elif type == Chess.RESIGN:self.game_signal.resign.emit()elif type == Chess.CHECKMATE:pass# self.game_signal.checkmate.emit()elif type == Chess.NOBESTMOVE:self.game_signal.nobestmove.emit()def board_callback(self, pos):if self.engine.sit.where_turn(pos) == self.engine.sit.turn:self.fpos = posself.board.setBoard(self.engine.sit.board, self.fpos)returnif not self.fpos:returnself.move(self.fpos, pos)def closeEvent(self, event):self.engine.close()return super().closeEvent(event)def connecting(self):self.connected = not self.connectedif not self.connected:if hasattr(self, 'connect_task'):self.connect_task.join()returndef task():while self.connected:self.game_signal.capture.emit()logger.debug('connecting... task')time.sleep(2)self.connect_task = threading.Thread(target=task, daemon=True)self.connect_task.start()def main():app = QtWidgets.QApplication(sys.argv)# import qt_material as material# extra = {# 'font_family': "dengxian SumHei"# }# material.apply_stylesheet(app, theme='light_blue.xml', invert_secondary=True, extra=extra)window = Game()window.show()sys.exit(app.exec())if __name__ == '__main__':main()
运行后:
需要游戏素材,和完整代码,wx扫描下方二维码获取,长期有效或者点击公众号获取。
本文源码转自 https://mp.weixin.qq.com/s/8TY8w6Pd13EI9fPdF8ncAw,如有侵权,请联系删除。