中国象棋源码(先别复制,BUG有亿点多)
2026-08-08 16:10:21
发布于:四川
- 想看日志去国际象棋源码那里看,所有的象棋类日志都在那里
- [警告|warning]所有象棋部分的代码还在公测阶段,可能存在问题
我是分割线
老规矩不要白嫖,至少留个赞再走吧!
代码
import tkinter as tk
from tkinter import ttk, messagebox
import json
import socket
import threading
import copy
import time
# ==================== 核心游戏逻辑与AI ====================
# 棋子基础分值
PIECE_VALUES = {'车': 900, '马': 400, '炮': 450, '象': 200, '相': 200, '士': 200, '仕': 200, '将': 10000, '帅': 10000, '兵': 100, '卒': 100}
class ChessAI:
@staticmethod
def get_valid_moves(board, x, y):
piece = board[x][y]
if not piece:
return []
moves = []
name, color = piece['name'], piece['color']
# 车、炮
if name in ['车', '炮']:
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for dx, dy in directions:
nx, ny = x + dx, y + dy
jump = False
while 0 <= nx < 9 and 0 <= ny < 10:
if not board[nx][ny]:
if not jump:
moves.append((nx, ny))
else:
if not jump:
if name == '炮':
jump = True
else:
if board[nx][ny]['color'] != color:
moves.append((nx, ny))
break
else:
if board[nx][ny]['color'] != color:
moves.append((nx, ny))
break
nx += dx
ny += dy
# 马
elif name in ['马', '馬']:
offsets = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)]
blocks = [(-1, 0), (-1, 0), (0, -1), (0, 1), (0, -1), (0, 1), (1, 0), (1, 0)]
for (dx, dy), (bx, by) in zip(offsets, blocks):
nx, ny = x + dx, y + dy
bx_, by_ = x + bx, y + by
if 0 <= nx < 9 and 0 <= ny < 10:
if 0 <= bx_ < 9 and 0 <= by_ < 10 and not board[bx_][by_]:
if not board[nx][ny] or board[nx][ny]['color'] != color:
moves.append((nx, ny))
# 象/相
elif name in ['象', '相']:
offsets = [(-2, -2), (-2, 2), (2, -2), (2, 2)]
blocks = [(-1, -1), (-1, 1), (1, -1), (1, 1)]
for (dx, dy), (bx, by) in zip(offsets, blocks):
nx, ny = x + dx, y + dy
bx_, by_ = x + bx, y + by
if 0 <= nx < 9 and 0 <= ny < 10 and 0 <= bx_ <9 and 0 <= by_ <10:
# 象不能过河
if (color == "black" and ny <=4) or (color == "red" and ny >=5):
if not board[bx_][by_]:
if not board[nx][ny] or board[nx][ny]['color'] != color:
moves.append((nx, ny))
# 士/仕
elif name in ['士', '仕']:
offsets = [(-1, -1), (-1, 1), (1, -1), (1, 1)]
for dx, dy in offsets:
nx, ny = x + dx, y + dy
# 九宫范围
if color == "black":
ok = 3 <= nx <=5 and 0 <= ny <=2
else:
ok = 3 <= nx <=5 and 7 <= ny <=9
if ok:
if not board[nx][ny] or board[nx][ny]['color'] != color:
moves.append((nx, ny))
# 将/帅
elif name in ['将', '帅']:
offsets = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for dx, dy in offsets:
nx, ny = x + dx, y + dy
if color == "black":
ok = 3 <= nx <=5 and 0 <= ny <=2
else:
ok = 3 <= nx <=5 and 7 <= ny <=9
if ok:
if not board[nx][ny] or board[nx][ny]['color'] != color:
moves.append((nx, ny))
# 飞将:同列中间无棋子
op_color = 'black' if color == 'red' else 'red'
check_y = y
found_opp_king = False
has_block = False
if color == "red":
for cy in range(y-1, -1, -1):
if board[x][cy]:
if board[x][cy]["name"] == "将":
found_opp_king = True
has_block = True
break
else:
for cy in range(y+1,10):
if board[x][cy]:
if board[x][cy]["name"] == "帅":
found_opp_king = True
has_block = True
break
if found_opp_king and not has_block:
moves.append((x, check_y))
# 兵、卒(修复zip错误)
elif name in ['兵', '卒']:
forward = -1 if color == 'red' else 1
crossed_river = (color == 'red' and y <=4) or (color == 'black' and y >=5)
# 前进
nx0, ny0 = x, y + forward
if 0 <= nx0 <9 and 0 <= ny0 <10:
if not board[nx0][ny0] or board[nx0][ny0]['color'] != color:
moves.append((nx0, ny0))
# 过河后左右走
if crossed_river:
for dx in (-1,1):
nx1, ny1 = x+dx, y
if 0 <= nx1 <9 and 0 <= ny1 <10:
if not board[nx1][ny1] or board[nx1][ny1]['color'] != color:
moves.append((nx1, ny1))
return moves
@staticmethod
def evaluate(board):
score = 0
for x in range(9):
for y in range(10):
p = board[x][y]
if p:
val = PIECE_VALUES.get(p['name'], 0)
if p['name'] in ['兵', '卒']:
crossed = (p['color'] == 'red' and y <=4) or (p['color'] == 'black' and y >=5)
val += 50 if crossed else 0
score += val if p['color'] == 'red' else -val
return score
@staticmethod
def minimax(board, depth, alpha, beta, is_maximizing):
if depth == 0:
return ChessAI.evaluate(board)
all_moves = []
for x in range(9):
for y in range(10):
if board[x][y]:
turn_red = is_maximizing
if (turn_red and board[x][y]['color'] == 'red') or (not turn_red and board[x][y]['color'] == 'black'):
for nx, ny in ChessAI.get_valid_moves(board, x, y):
all_moves.append((x, y, nx, ny))
if not all_moves:
return -99999 if is_maximizing else 99999
if is_maximizing:
max_eval = -99999
for x, y, nx, ny in all_moves:
board_copy = copy.deepcopy(board)
captured = board_copy[nx][ny]
board_copy[nx][ny] = board_copy[x][y]
board_copy[x][y] = None
eval_val = ChessAI.minimax(board_copy, depth - 1, alpha, beta, False)
max_eval = max(max_eval, eval_val)
alpha = max(alpha, eval_val)
if beta <= alpha:
break
return max_eval
else:
min_eval = 99999
for x, y, nx, ny in all_moves:
board_copy = copy.deepcopy(board)
board_copy[nx][ny] = board_copy[x][y]
board_copy[x][y] = None
eval_val = ChessAI.minimax(board_copy, depth - 1, alpha, beta, True)
min_eval = min(min_eval, eval_val)
beta = min(beta, eval_val)
if beta <= alpha:
break
return min_eval
@staticmethod
def get_best_move(board, depth, color):
best_move = None
best_val = -99999 if color == 'red' else 99999
for x in range(9):
for y in range(10):
if board[x][y] and board[x][y]['color'] == color:
for nx, ny in ChessAI.get_valid_moves(board, x, y):
board_copy = copy.deepcopy(board)
board_copy[nx][ny] = board_copy[x][y]
board_copy[x][y] = None
val = ChessAI.minimax(board_copy, depth - 1, -99999, 99999, color == 'black')
if (color == 'red' and val > best_val) or (color == 'black' and val < best_val):
best_val = val
best_move = (x, y, nx, ny)
return best_move
# ==================== 局域网联机网络层 ====================
class NetworkManager:
def __init__(self, game_gui):
self.game_gui = game_gui
self.sock = None
self.server_socket = None
self.is_host = False
self.running = False
def start_host(self, port=9999):
try:
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind(('0.0.0.0', port))
self.server_socket.listen(1)
self.is_host = True
self.running = True
threading.Thread(target=self._accept_loop, daemon=True).start()
return True, f"主机已启动,等待连接 (端口: {port})..."
except Exception as e:
return False, str(e)
def _accept_loop(self):
self.game_gui.update_status("等待对手连接...")
conn, addr = self.server_socket.accept()
self.sock = conn
self.game_gui.update_status(f"对手已连接: {addr[0]}")
self.game_gui.start_online_game('red')
threading.Thread(target=self._receive_loop, daemon=True).start()
def join_host(self, ip, port=9999):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((ip, port))
self.is_host = False
self.running = True
self.game_gui.update_status(f"已连接到 {ip}")
self.game_gui.start_online_game('black')
threading.Thread(target=self._receive_loop, daemon=True).start()
return True, "连接成功!"
except Exception as e:
return False, str(e)
def send_move(self, from_pos, to_pos):
if self.sock and self.running:
data = json.dumps({"type": "move", "from": from_pos, "to": to_pos})
try:
self.sock.sendall(data.encode('utf-8'))
except Exception:
self.game_gui.update_status("连接已断开")
def _receive_loop(self):
while self.running:
try:
data = self.sock.recv(4096).decode('utf-8')
if not data:
break
msg = json.loads(data)
if msg['type'] == 'move':
self.game_gui.root.after(0, lambda: self.game_gui.apply_opponent_move(msg['from'], msg['to']))
except Exception:
self.running = False
self.game_gui.root.after(0, lambda: self.game_gui.update_status("与对手断开连接"))
break
def close(self):
self.running = False
if self.sock:
try:
self.sock.close()
except Exception:
pass
if self.server_socket:
try:
self.server_socket.close()
except Exception:
pass
# ==================== GUI 界面层 ====================
class ChineseChess:
CELL_SIZE = 60
PADDING = 40
BOARD_WIDTH = 8 * CELL_SIZE
BOARD_HEIGHT = 9 * CELL_SIZE
def __init__(self, root):
self.root = root
self.root.title("中国象棋 - 支持单机/局域网联机")
self.root.resizable(False, False)
# 游戏状态
self.board = [[None for _ in range(10)] for _ in range(9)]
self.current_player = 'red'
self.selected_piece = None
self.move_history = []
# 模式状态
self.game_mode = tk.StringVar(value='pvp')
self.ai_depth = tk.IntVar(value=3)
self.lan_role = tk.StringVar(value='host')
self.player_color = tk.StringVar(value='red')
self.is_online = False
self.network = NetworkManager(self)
self._build_ui()
self.init_pieces()
self.draw_board()
def _build_ui(self):
ctrl_frame = tk.Frame(self.root, padx=10, pady=5)
ctrl_frame.pack(fill=tk.X)
tk.Radiobutton(ctrl_frame, text="双人对战", variable=self.game_mode, value='pvp', command=self._toggle_mode).pack(side=tk.LEFT)
tk.Radiobutton(ctrl_frame, text="人机对战", variable=self.game_mode, value='pve', command=self._toggle_mode).pack(side=tk.LEFT)
tk.Radiobutton(ctrl_frame, text="局域网联机", variable=self.game_mode, value='lan', command=self._toggle_mode).pack(side=tk.LEFT)
self.settings_frame = tk.Frame(self.root, padx=10, pady=5)
self.settings_frame.pack(fill=tk.X)
self._toggle_mode()
self.status_var = tk.StringVar(value="请选择模式并点击【重新开始】")
tk.Label(self.root, textvariable=self.status_var, fg="blue", font=("Arial", 10)).pack(pady=2)
canvas_width = self.BOARD_WIDTH + 2 * self.PADDING
canvas_height = self.BOARD_HEIGHT + 2 * self.PADDING
self.canvas = tk.Canvas(self.root, width=canvas_width, height=canvas_height, bg="#E6C88A")
self.canvas.pack(padx=10, pady=10)
self.canvas.bind("<Button-1>", self.on_click)
btn_frame = tk.Frame(self.root, pady=5)
btn_frame.pack()
tk.Button(btn_frame, text="重新开始", command=self.restart_game, width=10).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="悔棋", command=self.undo_move, width=10).pack(side=tk.LEFT, padx=5)
def _toggle_mode(self):
for w in self.settings_frame.winfo_children():
w.destroy()
mode = self.game_mode.get()
if mode == 'pve':
tk.Label(self.settings_frame, text="AI难度:").pack(side=tk.LEFT)
ttk.Combobox(self.settings_frame, textvariable=self.ai_depth, values=[2, 3, 4], state="readonly", width=5).pack(side=tk.LEFT, padx=5)
tk.Label(self.settings_frame, text="玩家执:").pack(side=tk.LEFT, padx=(10, 0))
tk.Radiobutton(self.settings_frame, text="红棋", variable=self.player_color, value='red').pack(side=tk.LEFT)
tk.Radiobutton(self.settings_frame, text="黑棋", variable=self.player_color, value='black').pack(side=tk.LEFT)
elif mode == 'lan':
tk.Label(self.settings_frame, text="角色:").pack(side=tk.LEFT)
tk.Radiobutton(self.settings_frame, text="主机(红)", variable=self.lan_role, value='host').pack(side=tk.LEFT)
tk.Radiobutton(self.settings_frame, text="客机(黑)", variable=self.lan_role, value='client').pack(side=tk.LEFT)
self.lan_ip_entry = tk.Entry(self.settings_frame, width=15)
self.lan_ip_entry.insert(0, "127.0.0.1")
self.lan_ip_entry.pack(side=tk.LEFT, padx=5)
tk.Label(self.settings_frame, text="(客机输入主机IP)").pack(side=tk.LEFT)
def init_pieces(self):
"""严格匹配截图标准象棋初始布局,修正兵/卒、炮点位"""
self.board = [[None for _ in range(10)] for _ in range(9)]
# ===================== 黑方棋子(上侧) =====================
black_list = [
# y=0 底线:车、马、象、士、将、象、马、炮、车
(0, 0, "车", "black"),
(1, 0, "马", "black"),
(2, 0, "象", "black"),
(3, 0, "士", "black"),
(4, 0, "将", "black"),
(5, 0, "士", "black"),
(6, 0, "象", "black"),
(7, 0, "马", "black"),
(8, 0, "车", "black"),
# y=2 卒+炮点位:卒、炮、卒、空、卒、空、卒、炮、卒
(0, 3, "卒", "black"),
(1, 2, "炮", "black"),
(2, 3, "卒", "black"),
(4, 3, "卒", "black"),
(6, 3, "卒", "black"),
(7, 2, "炮", "black"),
(8, 3, "卒", "black"),
]
# ===================== 红方棋子(下侧) =====================
red_list = [
# y=9 底线:车、马、相、仕、帅、相、马、炮、车
(0, 9, "车", "red"),
(1, 9, "马", "red"),
(2, 9, "相", "red"),
(3, 9, "仕", "red"),
(4, 9, "帅", "red"),
(5, 9, "仕", "red"),
(6, 9, "相", "red"),
(7, 9, "马", "red"),
(8, 9, "车", "red"),
# y=7 兵+炮点位:兵、炮、兵、空、兵、空、兵、炮、兵
(0, 6, "兵", "red"),
(1, 7, "炮", "red"),
(2, 6, "兵", "red"),
(4, 6, "兵", "red"),
(6, 6, "兵", "red"),
(7, 7, "炮", "red"),
(8, 6, "兵", "red"),
]
# 批量写入棋盘
for x, y, name, color in black_list:
self.board[x][y] = {"name": name, "color": color}
for x, y, name, color in red_list:
self.board[x][y] = {"name": name, "color": color}
def draw_board(self):
self.canvas.delete("all")
p = self.PADDING
c = self.CELL_SIZE
# 横线
for i in range(10):
self.canvas.create_line(p, p + i * c, p + 8 * c, p + i * c, fill="black")
# 竖线
for i in range(9):
if i == 0 or i == 8:
self.canvas.create_line(p + i * c, p, p + i * c, p + 9 * c, fill="black")
else:
self.canvas.create_line(p + i * c, p, p + i * c, p + 4 * c, fill="black")
self.canvas.create_line(p + i * c, p + 5 * c, p + i * c, p + 9 * c, fill="black")
# 九宫斜线
self.canvas.create_line(p + 3 * c, p, p + 5 * c, p + 2 * c, fill="black")
self.canvas.create_line(p + 5 * c, p, p + 3 * c, p + 2 * c, fill="black")
self.canvas.create_line(p + 3 * c, p + 7 * c, p + 5 * c, p + 9 * c, fill="black")
self.canvas.create_line(p + 5 * c, p + 7 * c, p + 3 * c, p + 9 * c, fill="black")
# 楚河汉界
self.canvas.create_text(p + 4 * c, p + 4.5 * c, text="楚 河 汉 界", font=("SimHei", 20), fill="#8B4513")
# 绘制棋子
for x in range(9):
for y in range(10):
if self.board[x][y]:
self._draw_piece(x, y)
# 选中高亮
if self.selected_piece:
sx, sy = self.selected_piece
cx = p + sx * c
cy = p + sy * c
self.canvas.create_oval(cx - 28, cy - 28, cx + 28, cy + 28, outline="green", width=3)
def _draw_piece(self, x, y):
p = self.PADDING
c = self.CELL_SIZE
cx, cy = p + x * c, p + y * c
piece = self.board[x][y]
color = "#CC0000" if piece['color'] == 'red' else "#000000"
self.canvas.create_oval(cx - 25, cy - 25, cx + 25, cy + 25, fill="#FFF8DC", outline=color, width=2)
self.canvas.create_text(cx, cy, text=piece['name'], font=("SimHei", 20, "bold"), fill=color)
def on_click(self, event):
if self.is_online and self.current_player != self.player_color.get():
return
x = round((event.x - self.PADDING) / self.CELL_SIZE)
y = round((event.y - self.PADDING) / self.CELL_SIZE)
if not (0 <= x < 9 and 0 <= y < 10):
return
if self.selected_piece:
sx, sy = self.selected_piece
if (sx, sy) == (x, y):
self.selected_piece = None
self.draw_board()
return
self._try_move(sx, sy, x, y)
self.selected_piece = None
else:
if self.board[x][y] and self.board[x][y]['color'] == self.current_player:
self.selected_piece = (x, y)
self.draw_board()
def _try_move(self, sx, sy, nx, ny):
valid_moves = ChessAI.get_valid_moves(self.board, sx, sy)
if (nx, ny) in valid_moves:
self.move_history.append(copy.deepcopy(self.board))
self.board[nx][ny] = self.board[sx][sy]
self.board[sx][sy] = None
self.current_player = 'black' if self.current_player == 'red' else 'red'
self.draw_board()
if self.is_online:
self.network.send_move([sx, sy], [nx, ny])
self.update_status("等待对手走棋...")
else:
self.update_status(f"{'红方' if self.current_player == 'red' else '黑方'}走棋")
if self.game_mode.get() == 'pve' and self.current_player != self.player_color.get():
self.update_status("AI思考中...")
self.root.after(500, self._ai_move)
return True
return False
def _ai_move(self):
best = ChessAI.get_best_move(self.board, self.ai_depth.get(), self.current_player)
if best:
self.move_history.append(copy.deepcopy(self.board))
sx, sy, nx, ny = best
self.board[nx][ny] = self.board[sx][sy]
self.board[sx][sy] = None
self.current_player = 'black' if self.current_player == 'red' else 'red'
self.draw_board()
self.update_status("轮到你了")
def apply_opponent_move(self, from_pos, to_pos):
sx, sy = from_pos
nx, ny = to_pos
self.move_history.append(copy.deepcopy(self.board))
self.board[nx][ny] = self.board[sx][sy]
self.board[sx][sy] = None
self.current_player = 'black' if self.current_player == 'red' else 'red'
self.draw_board()
self.update_status("轮到你了")
def start_online_game(self, color):
self.is_online = True
self.player_color.set(color)
self.init_pieces()
self.draw_board()
self.current_player = 'red'
self.selected_piece = None
self.move_history.clear()
if color == 'red':
self.update_status("你是红方,你先走棋")
else:
self.update_status("你是黑方,等待对手走棋...")
def update_status(self, text):
self.status_var.set(text)
def restart_game(self):
self.network.close()
self.is_online = False
self.init_pieces()
self.draw_board()
self.current_player = 'red'
self.selected_piece = None
self.move_history.clear()
mode = self.game_mode.get()
if mode == 'lan':
role = self.lan_role.get()
if role == 'host':
success, msg = self.network.start_host()
self.update_status(msg)
else:
ip = self.lan_ip_entry.get()
success, msg = self.network.join_host(ip)
self.update_status(msg)
elif mode == 'pve':
self.update_status("人机对战开始,你执" + ("红棋" if self.player_color.get() == 'red' else "黑棋"))
if self.player_color.get() == 'black':
self.root.after(500, self._ai_move)
else:
self.update_status("双人对战开始,红方先走")
def undo_move(self):
if not self.move_history:
return
steps = 2 if (self.game_mode.get() == 'pve' and len(self.move_history) >= 2) else 1
for _ in range(steps):
if self.move_history:
self.board = self.move_history.pop()
self.current_player = 'red' if len(self.move_history) % 2 == 0 else 'black'
self.selected_piece = None
self.draw_board()
self.update_status("已悔棋")
if __name__ == "__main__":
root = tk.Tk()
game = ChineseChess(root)
root.mainloop()
这里空空如也




















有帮助,赞一个