Python游戏开发之《人机大战象棋》-附完整源码-python教程

今天给大家带来的是人机大战的象棋

中国象棋

首先绘制一下棋盘,看看样子:

在这里插入图片描述

黑白经典款

绘制棋盘:


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,如有侵权,请联系删除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/9724.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

AutoCAD2014

链接: https://pan.baidu.com/s/1Q4fhVmiSYDZ2DbPNi7m4cA 提取码: f3bm

免费送源码:Java+ssm+MySQL 在线购票影城 计算机毕业设计原创定制

摘要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设在线购票影城。 本设计…

MYSQL——事务管理

什么是事务 在数据库使用者角度&#xff0c;事务就是完成一个事件。例如一个员工信息数据库&#xff0c;要完成员工离职的事件&#xff0c;可能需要很多操作&#xff0c;比如删除员工基本信息以及员工在公司的表现&#xff0c;薪资水平等。而这一系列的操作就是为了完成员工离…

书生实战营第四期-基础岛第四关-InternLM + LlamaIndex RAG 实践

一、任务要求1 基于 LlamaIndex 构建自己的 RAG 知识库&#xff0c;寻找一个问题 A 在使用 LlamaIndex 之前 浦语 API 不会回答&#xff0c;借助 LlamaIndex 后 浦语 API 具备回答 A 的能力&#xff0c;截图保存。 1、配置开发机系统 镜像&#xff1a;使用 Cuda12.0-conda 镜…

LC:二分查找——杂记

文章目录 268. 丢失的数字162. 寻找峰值 268. 丢失的数字 LC将此题归类为二分查找&#xff0c;并且为简单题&#xff0c;下面记一下自己对这道题目的思考。 题目链接&#xff1a;268.丢失的数字 第一次看到这个题目&#xff0c;虽然标注的为简单&#xff0c;但肯定不能直接排…

推荐一款国产数据库管理工具Chat2DB

什么是 Chat2DB ? Chat2DB 是一款专为现代数据驱动型企业打造的数据库管理、数据开发及数据分析工具。作为一款AI原生的产品&#xff0c;Chat2DB 将人工智能技术与传统数据库管理功能深度融合&#xff0c;旨在提供更为智能、便捷的工作体验&#xff0c;助力用户高效地管理数据…

前端三件套(HTML + CSS + JS)

前言&#xff1a; 前端三件套&#xff0c;会用就行 毕竟在后面学习JavaWeb&#xff0c;以及在学习vue的时候也有帮助 前端三件套&#xff1a; HTML 定义网页的结构和内容。CSS 负责网页的样式和布局。JavaScript 添加动态交互和功能。 使用到的工具是Visual Studio Code 即…

Flutter错误: uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared

前言 今天要做蓝牙通信的功能&#xff0c;我使用了flutter_reactive_ble这个库&#xff0c;但是在运行的时候发现一下错误 Launching lib/main.dart on AQM AL10 in debug mode... /Users/macbook/Desktop/test/flutter/my_app/android/app/src/debug/AndroidManifest.xml Err…

网络编程示例之网络基础知识

TCP/IP 中有两个具有代表性的传输层协议&#xff0c;分别是 TCP 和 UDP&#xff1a; TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构&#xff0c;当应用程序采用 TCP 发送消息时&#xff0c;虽然可以保证发送的顺序&#xff0c;但还是犹如没有任何间隔的数据流发送…

十七:Spring Boot 依赖(2)-- spring-boot-starter-data-jpa 依赖详解

目录 1. 理解 JPA&#xff08;Java Persistence API&#xff09; 1.1 什么是 JPA&#xff1f; 1.2 JPA 与 Hibernate 的关系 1.3 JPA 的基本注解&#xff1a;Entity, Table, Id, GeneratedValue 1.4 JPA 与数据库表的映射 2. Spring Data JPA 概述 2.1 什么是 Spring Dat…

商品,订单业务流程梳理一

业务架构梳理 业务系统介绍 业务商品流程 业务订单流程 业务售后流程 系统架构 技术栈

HDR视频技术之二:光电转换与 HDR 图像显示

将自然界中的真实场景转换为屏幕上显示出来的图像&#xff0c;往往需要经过两个主要的步骤&#xff1a;第一个是通过摄影设备&#xff0c;将外界的光信息转换为图像信息存储起来&#xff0c;本质上是存储为数字信号&#xff1b;第二个是通过显示设备&#xff0c;将图像信息转换…

Linux完结

学习视频笔记均来自B站UP主" 泷羽sec",如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 【linux基础之病毒编写&#xff08;完结&#xff09;】 https://www.bilibili.com/video…

苹果iOS 18.4将允许欧盟地区的iPhone用户设置默认地图和翻译应用

在一份最新文件中&#xff0c;苹果概述了其为遵守欧盟数字市场法案所采取的措施&#xff0c;并透露将允许欧盟的 iPhone 和 iPad 用户从"2025 年春季"开始设置默认导航和翻译应用程序。 这一时间表表明&#xff0c;这些选项将在 iOS 18.4 和 iPadOS 18.4 中添加&…

鸿蒙进阶篇-type、typeof、类

“在科技的浪潮中&#xff0c;鸿蒙操作系统宛如一颗璀璨的新星&#xff0c;引领着创新的方向。作为鸿蒙开天组&#xff0c;今天我们将一同踏上鸿蒙基础的探索之旅&#xff0c;为您揭开这一神奇系统的神秘面纱。” 各位小伙伴们我们又见面了,我就是鸿蒙开天组,下面让我们进入今…

鸿蒙进阶篇-剩余和展开、简单和复杂类型

“在科技的浪潮中&#xff0c;鸿蒙操作系统宛如一颗璀璨的新星&#xff0c;引领着创新的方向。作为鸿蒙开天组&#xff0c;今天我们将一同踏上鸿蒙基础的探索之旅&#xff0c;为您揭开这一神奇系统的神秘面纱。” 各位小伙伴们我们又见面了,我就是鸿蒙开天组,下面让我们进入今…

爬虫学习4

from threading import Thread#创建任务 def func(name):for i in range(100):print(name,i)if __name__ __main__:#创建线程t1 Thread(targetfunc,args("1"))t2 Thread(targetfunc, args("2"))t1.start()t2.start()print("我是诛仙剑")from …

【LeetCode:3242. 设计相邻元素求和服务 + 模拟 + 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

[产品管理-62]:不同角度看产品的生命周期

目录 一、产品的生命周期与不确定性 创意生成 原型开发 规模化和商业化 - 上市前的准备 产品上市 二、产品的生命周期与组合管理 三、产品生命周期变得越来越短的原因 1. 科技进步 2. 消费需求变化 》 物质需求的单一化到精神需求的易变化 3. 市场竞争加剧 4. 全球化…

WPS单元格重复值提示设置

选中要检查的所有的单元格 设置提示效果 当出现单元格值重复时&#xff0c;重复的单元格就会自动变化 要修改或删除&#xff0c;点击