|
|
让ai生成了一个完整的,可以跑跑看
# -*- coding: utf-8 -*-
"""
中国象棋 - Python + Pygame 实现
点击选中己方棋子,再点击目标位置走子。红方先行。
"""
import pygame
import sys
pygame.init()
# ---------- 常量 ----------
GRID = 60 # 每格像素
MARGIN = 40 # 棋盘外边距
COLS, ROWS = 9, 10 # 9列10行的交叉点
BOARD_W = (COLS - 1) * GRID # 8格宽
BOARD_H = (ROWS - 1) * GRID # 9格高
WIDTH = BOARD_W + MARGIN * 2
HEIGHT = BOARD_H + MARGIN * 2
RADIUS = 26 # 棋子半径
BG = (240, 210, 150) # 棋盘底色
LINE = (60, 40, 20)
RED = (200, 30, 30)
BLACK = (20, 20, 20)
HL = (30, 160, 60) # 选中高亮
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("中国象棋")
# 字体(尝试系统中文字体)
def load_font(size):
for name in ["simhei", "microsoftyahei", "pingfang sc", "wenquanyi micro hei", "arial unicode ms"]:
try:
f = pygame.font.SysFont(name, size)
if f.render("車", True, (0, 0, 0)).get_width() > 0:
return f
except Exception:
continue
return pygame.font.Font(None, size)
FONT = load_font(40)
# ---------- 棋盘状态 ----------
# 每个棋子: (color, name) color: 'r'/'b'
# 用二维数组 board[row][col],None 表示空
def init_board():
b = [[None] * COLS for _ in range(ROWS)]
def put(r, c, color, name):
b[r][c] = (color, name)
# 黑方(上方 row 0-4)
order = ['車', '馬', '象', '士', '將', '士', '象', '馬', '車']
for c, n in enumerate(order):
put(0, c, 'b', n)
put(2, 1, 'b', '砲'); put(2, 7, 'b', '砲')
for c in range(0, 9, 2):
put(3, c, 'b', '卒')
# 红方(下方 row 5-9)
order_r = ['車', '馬', '相', '仕', '帥', '仕', '相', '馬', '車']
for c, n in enumerate(order_r):
put(9, c, 'r', n)
put(7, 1, 'r', '炮'); put(7, 7, 'r', '炮')
for c in range(0, 9, 2):
put(6, c, 'r', '兵')
return b
board = init_board()
turn = 'r'
selected = None # (row, col)
# ---------- 坐标转换 ----------
def pos_to_px(r, c):
return MARGIN + c * GRID, MARGIN + r * GRID
def px_to_pos(x, y):
c = round((x - MARGIN) / GRID)
r = round((y - MARGIN) / GRID)
if 0 <= r < ROWS and 0 <= c < COLS:
return r, c
return None
# ---------- 绘制棋盘 ----------
def draw_board():
screen.fill(BG)
# 横线
for r in range(ROWS):
x1, y = pos_to_px(r, 0)
x2, _ = pos_to_px(r, COLS - 1)
pygame.draw.line(screen, LINE, (x1, y), (x2, y), 2)
# 竖线(河界处上下断开,最外两条贯通)
for c in range(COLS):
x, y1 = pos_to_px(0, c)
_, y2 = pos_to_px(ROWS - 1, c)
if c == 0 or c == COLS - 1:
pygame.draw.line(screen, LINE, (x, y1), (x, y2), 2)
else:
_, ym1 = pos_to_px(4, c)
_, ym2 = pos_to_px(5, c)
pygame.draw.line(screen, LINE, (x, y1), (x, ym1), 2)
pygame.draw.line(screen, LINE, (x, ym2), (x, y2), 2)
# 九宫斜线
for (r0, r1) in [(0, 2), (7, 9)]:
x1, y1 = pos_to_px(r0, 3)
x2, y2 = pos_to_px(r1, 5)
pygame.draw.line(screen, LINE, (x1, y1), (x2, y2), 2)
x1, y1 = pos_to_px(r0, 5)
x2, y2 = pos_to_px(r1, 3)
pygame.draw.line(screen, LINE, (x1, y1), (x2, y2), 2)
# 楚河汉界
mid = load_font(30)
t1 = mid.render("楚河", True, LINE)
t2 = mid.render("漢界", True, LINE)
_, ytop = pos_to_px(4, 0)
screen.blit(t1, (MARGIN + GRID * 1.5, ytop + 15))
screen.blit(t2, (MARGIN + GRID * 5.5, ytop + 15))
# ---------- 绘制棋子 ----------
def draw_pieces():
for r in range(ROWS):
for c in range(COLS):
p = board[r][c]
if p:
x, y = pos_to_px(r, c)
color = RED if p[0] == 'r' else BLACK
pygame.draw.circle(screen, (235, 220, 180), (x, y), RADIUS)
pygame.draw.circle(screen, color, (x, y), RADIUS, 3)
txt = FONT.render(p[1], True, color)
rect = txt.get_rect(center=(x, y))
screen.blit(txt, rect)
# 选中高亮
if selected:
x, y = pos_to_px(*selected)
pygame.draw.circle(screen, HL, (x, y), RADIUS + 3, 3)
# ---------- 走子规则 ----------
def in_palace(r, c, color):
if color == 'r':
return 7 <= r <= 9 and 3 <= c <= 5
return 0 <= r <= 2 and 3 <= c <= 5
def count_between(r1, c1, r2, c2):
cnt = 0
if r1 == r2:
for c in range(min(c1, c2) + 1, max(c1, c2)):
if board[r1][c]:
cnt += 1
else:
for r in range(min(r1, r2) + 1, max(r1, r2)):
if board[r][c1]:
cnt += 1
return cnt
def valid_move(r1, c1, r2, c2):
piece = board[r1][c1]
if not piece:
return False
color, name = piece
target = board[r2][c2]
if target and target[0] == color: # 不能吃己方
return False
dr, dc = r2 - r1, c2 - c1
if name in ('車', '车'):
if r1 != r2 and c1 != c2:
return False
return count_between(r1, c1, r2, c2) == 0
if name in ('馬', '马'):
if (abs(dr), abs(dc)) not in [(2, 1), (1, 2)]:
return False
# 蹩马腿
if abs(dr) == 2:
if board[r1 + dr // 2][c1]:
return False
else:
if board[r1][c1 + dc // 2]:
return False
return True
if name in ('相', '象'):
if (abs(dr), abs(dc)) != (2, 2):
return False
if board[r1 + dr // 2][c1 + dc // 2]: # 塞象眼
return False
# 不能过河
if color == 'r' and r2 < 5:
return False
if color == 'b' and r2 > 4:
return False
return True
if name in ('仕', '士'):
if (abs(dr), abs(dc)) != (1, 1):
return False
return in_palace(r2, c2, color)
if name in ('帥', '將', '帅', '将'):
# 将帅对脸
if c1 == c2 and abs(dr) >= 1 and dc == 0:
pass
if (abs(dr), abs(dc)) not in [(1, 0), (0, 1)]:
# 允许飞将吃对方将
if c1 == c2 and target and target[1] in ('帥', '將', '帅', '将'):
return count_between(r1, c1, r2, c2) == 0
return False
return in_palace(r2, c2, color)
if name in ('炮', '砲'):
if r1 != r2 and c1 != c2:
return False
n = count_between(r1, c1, r2, c2)
if target:
return n == 1 # 吃子需翻山
return n == 0
if name in ('兵', '卒'):
if color == 'r':
if r1 >= 5: # 未过河,只能向前
return dr == -1 and dc == 0
else: # 过河后可左右
return (dr == -1 and dc == 0) or (dr == 0 and abs(dc) == 1)
else:
if r1 <= 4:
return dr == 1 and dc == 0
else:
return (dr == 1 and dc == 0) or (dr == 0 and abs(dc) == 1)
return False
# ---------- 胜负判断 ----------
def check_winner():
kings = [p for row in board for p in row if p and p[1] in ('帥', '將', '帅', '将')]
reds = [k for k in kings if k[0] == 'r']
blacks = [k for k in kings if k[0] == 'b']
if not reds:
return 'b'
if not blacks:
return 'r'
return None
# ---------- 主循环 ----------
def main():
global turn, selected
winner = None
clock = pygame.time.Clock()
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit(); sys.exit()
if e.type == pygame.MOUSEBUTTONDOWN and winner is None:
pos = px_to_pos(*e.pos)
if not pos:
continue
r, c = pos
if selected:
sr, sc = selected
if (r, c) == (sr, sc):
selected = None
elif valid_move(sr, sc, r, c):
board[r][c] = board[sr][sc]
board[sr][sc] = None
selected = None
turn = 'b' if turn == 'r' else 'r'
winner = check_winner()
elif board[r][c] and board[r][c][0] == turn:
selected = (r, c) # 改选
else:
selected = None
else:
if board[r][c] and board[r][c][0] == turn:
selected = (r, c)
draw_board()
draw_pieces()
if winner:
f = load_font(50)
msg = ("红方胜!" if winner == 'r' else "黑方胜!")
t = f.render(msg, True, RED if winner == 'r' else BLACK)
screen.blit(t, t.get_rect(center=(WIDTH // 2, HEIGHT // 2)))
pygame.display.flip()
clock.tick(30)
if __name__ == "__main__":
main() |
|