Python游戏开发之《人机大战象棋》-附完备源码-python教程
本日给各人带来的是人机大战的象棋中国象棋
首先绘制一下棋盘,看看样子:
https://i-blog.csdnimg.cn/direct/e4d3232046684da7ae899a92337465ec.png
黑白经典款
绘制棋盘:
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 = 280
flags = QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint
signal_class = BoardSignal
def __init__(self, parent=None, callback=None):
super().__init__(parent=parent)
self.csize = 60
self.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 = 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 = None
self.tpos = None
self.check = None
self.reverse = False
self.update()
self.callback = callback
def setBoard(self, board, fpos=None, tpos=None):
# 设置 棋盘 board,以及该步,的棋子从哪儿 fpos,到哪儿 tpos
# 由于 该函数可能在多个线程中调用,所以下面触发 signal.refresh
# QT 会自动将刷新棋盘的重任放到主线程去做
# 如果直接在非主线程调用 refresh 函数,程序可能莫名其妙的死掉。
self.board = board
self.fpos = fpos
self.tpos = tpos
self.signal.refresh.emit()
这个主要是绘制棋盘和棋盘中棋子移动产生的回调。
绘制棋子:
# coding=utf-8
import numpy as np
from logger import logger
class Chess(object):
NONE = 0
RMASK = 16# 第五位表示红方 比如红車 0b01_0_001
# 判断棋子是不是黑方只需要 按位与 0b0100000 就可以了
BMASK = 32# 第六位表示黑方 比如黑炮 0b10_0_110
# 判断棋子是不是黑方只需要 按位与 0b100000 就可以了
RED = RMASK
BLACK = BMASK
TMASK = 0b110000# 棋子颜色掩码,可能的取值为 16 和 32
CMASK = 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 = 1
ADVISOR = 2
BISHOP = 3
KNIGHT = 4
ROOK = 5
CANNON = 6
PAWN = 7
P = PAWN | RMASK
R = ROOK | RMASK
N = KNIGHT | RMASK
B = BISHOP | RMASK
A = ADVISOR | RMASK
C = CANNON | RMASK
K = KING | RMASK
p = PAWN | BMASK
r = ROOK | BMASK
n = KNIGHT | BMASK
b = BISHOP | BMASK
a = ADVISOR | BMASK
c = CANNON | BMASK
k = KING | BMASK
WIDTH = 9
HEIGHT = 10
W = WIDTH
H = HEIGHT
NAMES = {
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, }
@staticmethod
def invert(chess):
# 转换棋子颜色
return (chess & 7) | ((~chess) & 0b110000)
@staticmethod
def is_red(chess):
return chess & Chess.RED
@staticmethod
def is_black(chess):
return chess & Chess.BLACK
@staticmethod
def chess(c):
return c & Chess.CMASK
@staticmethod
def color(c):
return c & Chess.TMASK
ORIGIN = np.mat(
np.array(
[
,
,
,
,
,
,
,
,
]
)
)
MOVE = 1
CAPTURE = 2
DRAW = 3
CHECK = 4
INVALID = 5
CHECKMATE = 6
RESIGN = 7
NEWGAME = 8
# 以下为引擎专用
INFO = 9
POPHASH = 10
NOBESTMOVE = 11
还有其他文件,这里就不一一列举了,
https://i-blog.csdnimg.cn/direct/d843fdc0bf03476194989fc4adcef8f1.png
主步调:
# coding=utf-8
import sys
import time
from functools import partial
import threading
import numpy as np
import keyboard
from PySide6 import QtWidgets
from PySide6 import QtCore
from PySide6 import QtGui
from board import BoardFrame
from 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 Situation
import audio
import engines
import system
from version import VERSION
from dialogs.settings import SettingsDialog
from dialogs.method import MethodDialog
from toast import Toast
from context import BaseContextMenu
from context import BaseContextMenuMixin
from arrange import ArrangeBoard
from manual import Manual
import qqchess
class 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 = None
self.engine = None
self.engines = {
Chess.RED: None,
Chess.BLACK: None,
}
self.engine_side =
self.human_side =
self.board.csize = 80
self.board.callback = self.board_callback
self.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, 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 = thinking
logger.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 = True
self.board.setBoard(self.board.board)
self.board.setCheck(None)
self.game_menu.setAllShortcutEnabled(False)
def finish_arrange(self, finished):
if not finished:
return
logger.debug('finish arrange')
self.engine.close()
self.engine = Engine()
self.engine.sit.board = self.board.board
self.engine.sit.turn = self.board.first_side
self.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:
return
self.updateBoard()
def accepted(self):
logger.info("setting accepted....")
# 设置棋手或AI
self.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
if not isinstance(engine, UCCI_ENGINES):
self.init_engines(Chess.RED)
idx = self.settings.black_engine.currentIndex()
engine = self.engines
if not isinstance(engine, UCCI_ENGINES):
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:
return
self.go()
def go(self):
if self.engine.checkmate:
self.game_signal.checkmate.emit()
logger.debug('engine is checkmated hint ignored...')
return
self.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():
return
audio.play(audio_type)
def init_engines(self, turn=None):
turns = []
if turn == Chess.RED:
turns =
elif turn == Chess.BLACK:
turns =
else:
turns =
for turn in turns:
old = self.engines
if old:
old.close()
idx = self.settings.get_engine_box(turn).currentIndex()
new = UCCI_ENGINES(callback=self.engine_callback)
new.start()
self.engines = new
def current_engine(self) -> Engine:
return self.engines
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 = False
self.connect_inited = False
self.fpos = None
self.board.arranging = False
self.thinking = False
self.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:
break
self.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:
break
self.updateBoard()
@QtCore.Slot(bool)
def hint(self):
if self.thinking:
logger.debug('engine is thinking hint ignored...')
return
self.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)")
if not filename:
return
fen = 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))
return
fen = manual.sit.format_fen()
else:
fen = content
self.engine.index = 0
self.engine.stack = self.engine.stack[:1]
self.engine.sit = self.engine.stack
self.updateBoard()
self.board.setCheck(None)
if self.engine.sit.parse_fen(fen, load=True):
moves = self.engine.sit.moves
self.engine.sit.moves = []
for fpos, tpos in moves:
result = self.engine.move(fpos, tpos)
if result == Chess.CHECKMATE:
self.game_signal.checkmate.emit()
break
self.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)")
if not filename:
return
with 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)
return
image = 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 = board
board = np.zeros((9, 10), dtype=np.int8)
for loc, idx in pred.items():
board = (idx + 1) | colors]
if np.all(board0 == board):
break
# 验证数量
C = Chess
wheres = np.argwhere((board == C.K) | (board == C.k))
if len(wheres) != 2:
logger.warning("bishop count error %s...", len(wheres))
return None
for where in wheres:
if where < 3 or where > 5:
logger.warning("king location error %s...", where)
return None
if 2 < where < 7:
logger.warning("king location error %s...", where)
return None
counts = {
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 None
wheres = 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 None
wheres = 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 None
wheres = np.argwhere(board == Chess.K)
turn = Chess.RED
self.settings.reverse.setChecked(False)
if wheres < 3:
logger.debug("reversed")
board = board[::-1, ::-1]
self.settings.reverse.setChecked(True)
turn = Chess.BLACK
if 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:
return
if len(wheres) == 2:
pos1 = tuple(wheres)
pos2 = tuple(wheres)
if board != 0 and board != 0:
return
assert (board == 0 or board == 0)
assert (board != 0 or board != 0)
if board == 0:
fpos = pos1
tpos = pos2
else:
fpos = pos2
tpos = pos1
if Chess.color(board) != turn:
self.move(fpos, tpos)
elif not self.connect_inited:
logger.info("reset engine situation")
# self.engine.close()
# self.engine = Engine()
self.engine.index = 0
self.engine.sit = Situation(board, turn=turn)
self.engine.stack =
self.fpos = None
self.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()
return
result = self.engine.move(fpos, tpos)
logger.debug('move result %s', result)
if not result:
return
if result != Chess.INVALID:
self.game_signal.animate.emit(fpos, tpos)
else:
return
if 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()
return
self.try_engine_move()
@QtCore.Slot(int)
def change_difficulty(self, diff):
self.depth_computer = diff
def 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:
return
self.connected = False
self.connect_inited = False
if 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, data)
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 = pos
self.board.setBoard(self.engine.sit.board, self.fpos)
return
if not self.fpos:
return
self.move(self.fpos, pos)
def closeEvent(self, event):
self.engine.close()
return super().closeEvent(event)
def connecting(self):
self.connected = not self.connected
if not self.connected:
if hasattr(self, 'connect_task'):
self.connect_task.join()
return
def 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()
运行后:
https://i-blog.csdnimg.cn/direct/f8193746a342447285d5677140b8258d.png
必要游戏素材,和完备代码,wx扫描下方二维码获取,长期有效大概点击公众号获取。
https://i-blog.csdnimg.cn/direct/43f90830182848f39e1a91bcea53ea6f.png#pic_center
本文源码转自 https://mp.weixin.qq.com/s/8TY8w6Pd13EI9fPdF8ncAw,如有侵权,请接洽删除。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]