鱼C论坛

 找回密码
 立即注册
查看: 300|回复: 1

[作品展示] python中国象棋游戏

[复制链接]
发表于 2025-2-21 18:09:58 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. import tkinter as tk
  2. from tkinter import messagebox
  3. import random

  4. class ChineseChess:
  5.     def __init__(self, master):
  6.         self.master = master
  7.         self.master.title("中国象棋")
  8.         self.canvas = tk.Canvas(master, width=670, height=750, bg="#EDAA56")
  9.         self.canvas.pack()

  10.         # 游戏状态
  11.         self.board = {}
  12.         self.current_player = "red"
  13.         self.selected_piece = None

  14.         # 初始化棋盘
  15.         self.init_board()
  16.         self.draw_board()

  17.         # 绑定事件
  18.         self.canvas.bind("<Button-1>", self.on_click)

  19.     def init_board(self):
  20.         # 初始化棋子位置(红方在下方)
  21.         initial_setup = [
  22.             # 红方棋子
  23.             ((0, 9), "车", "red"), ((1, 9), "马", "red"), ((2, 9), "相", "red"),
  24.             ((3, 9), "仕", "red"), ((4, 9), "帅", "red"), ((5, 9), "仕", "red"),
  25.             ((6, 9), "相", "red"), ((7, 9), "马", "red"), ((8, 9), "车", "red"),
  26.             ((1, 7), "炮", "red"), ((7, 7), "炮", "red"),
  27.             *[((i * 2, 6), "兵", "red") for i in range(0, 5)],

  28.             # 黑方棋子
  29.             ((0, 0), "车", "black"), ((1, 0), "马", "black"), ((2, 0), "象", "black"),
  30.             ((3, 0), "士", "black"), ((4, 0), "将", "black"), ((5, 0), "士", "black"),
  31.             ((6, 0), "象", "black"), ((7, 0), "马", "black"), ((8, 0), "车", "black"),
  32.             ((1, 2), "炮", "black"), ((7, 2), "炮", "black"),
  33.             *[((i * 2, 3), "卒", "black") for i in range(0, 5)]
  34.         ]

  35.         for pos, piece, color in initial_setup:
  36.             self.board[pos] = {"type": piece, "color": color}
  37.         print('self.board=', self.board)

  38.     def draw_board(self):
  39.         self.canvas.delete("all")
  40.         # 绘制棋盘线条
  41.         for i in range(10):
  42.             y = 50 + i * 70
  43.             self.canvas.create_line(50, y, 50 + 8 * 70, y, width=2, fill='green')

  44.         for i in range(9):
  45.             x = 50 + i * 70
  46.             self.canvas.create_line(x, 50, x, 50 + 4 * 70, width=2, fill='green')
  47.             self.canvas.create_line(x, 50 + 5 * 70, x, 50 + 9 * 70, width=2, fill='green')

  48.         self.canvas.create_line(50, 50 + 4 * 70, 50, 50 + 5 * 70, width=2, fill='green')
  49.         self.canvas.create_line(50 + 8 * 70, 50 + 4 * 70, 50 + 8 * 70, 50 + 5 * 70, width=2, fill='green')
  50.         # 绘制九宫斜线
  51.         self.canvas.create_line(50 + 3 * 70, 50, 50 + 5 * 70, 50 + 2 * 70, width=2, fill='green')
  52.         self.canvas.create_line(50 + 3 * 70, 50 + 2 * 70, 50 + 5 * 70, 50, width=2, fill='green')
  53.         self.canvas.create_line(50 + 3 * 70, 50 + 7 * 70, 50 + 5 * 70, 50 + 9 * 70, width=2, fill='green')
  54.         self.canvas.create_line(50 + 3 * 70, 50 + 9 * 70, 50 + 5 * 70, 50 + 7 * 70, width=2, fill='green')

  55.         # 绘制棋子
  56.         for (x, y), piece in self.board.items():
  57.             self.draw_piece(x, y, piece["type"], piece["color"])

  58.         # 绘制当前玩家提示
  59.         self.canvas.create_text(335, 730, text=f"当前玩家:{self.current_player}", font=("simhei", 14), fill="blue")

  60.     def draw_piece(self, x, y, piece_type, color):
  61.         x_pixel = 50 + x * 70
  62.         y_pixel = 50 + y * 70
  63.         self.canvas.create_oval(x_pixel - 32, y_pixel - 32, x_pixel + 32, y_pixel + 32,
  64.                                 fill=color, outline=color)
  65.         text_color = "white"
  66.         self.canvas.create_text(x_pixel, y_pixel, text=piece_type,
  67.                                 font=("楷体", 28), fill=text_color)

  68.     def on_click(self, event):
  69.         x = round((event.x - 50) / 70)
  70.         y = round((event.y - 50) / 70)
  71.         pos = (x, y)

  72.         if not (0 <= x <= 8 and 0 <= y <= 9):
  73.             return

  74.         if not self.selected_piece:
  75.             if pos in self.board and self.board[pos]["color"] == self.current_player:
  76.                 self.selected_piece = pos
  77.                 self.highlight_position(pos)
  78.         else:
  79.             start_pos = self.selected_piece
  80.             if self.is_valid_move(start_pos, pos):
  81.                 self.move_piece(start_pos, pos)
  82.                 self.check_game_over()
  83.                 # 切换玩家并触发AI移动
  84.                 self.current_player = "black"
  85.                 self.draw_board()
  86.                 if self.current_player == "black":
  87.                     self.master.after(100, self.ai_move)
  88.             self.selected_piece = None
  89.             self.draw_board()

  90.     def highlight_position(self, pos):
  91.         x, y = pos
  92.         x_pixel = 50 + x * 70
  93.         y_pixel = 50 + y * 70
  94.         self.canvas.create_rectangle(x_pixel - 35, y_pixel - 35, x_pixel + 35, y_pixel + 35,
  95.                                      outline="yellow", width=3)

  96.     def is_valid_move(self, start, end, board=None):
  97.         if board is None:
  98.             board = self.board
  99.         if start not in board:
  100.             return False
  101.         piece = board[start]
  102.         target_piece = board.get(end)

  103.         if target_piece and target_piece["color"] == piece["color"]:
  104.             return False

  105.         move_type = piece["type"]
  106.         if move_type == "车":
  107.             return self.validate_rook(start, end, board)
  108.         elif move_type in ("马", "馬"):
  109.             return self.validate_knight(start, end, board)
  110.         elif move_type in ("象", "相"):
  111.             return self.validate_elephant(start, end, piece["color"], board)
  112.         elif move_type in ("士", "仕"):
  113.             return self.validate_advisor(start, end, piece["color"], board)
  114.         elif move_type in ("将", "帅"):
  115.             return self.validate_king(start, end, piece["color"], board)
  116.         elif move_type == "炮":
  117.             return self.validate_cannon(start, end, board)
  118.         elif move_type in ("兵", "卒"):
  119.             return self.validate_pawn(start, end, piece["color"], board)
  120.         return False

  121.     def validate_rook(self, start, end, board):
  122.         x1, y1 = start
  123.         x2, y2 = end
  124.         if x1 != x2 and y1 != y2:
  125.             return False

  126.         step = 1 if x2 > x1 or y2 > y1 else -1
  127.         if x1 == x2:
  128.             for y in range(y1 + step, y2, step):
  129.                 if (x1, y) in self.board:
  130.                     return False
  131.         else:
  132.             for x in range(x1 + step, x2, step):
  133.                 if (x, y1) in self.board:
  134.                     return False
  135.         return True

  136.     def validate_knight(self, start, end, board):
  137.         x1, y1 = start
  138.         x2, y2 = end
  139.         dx = abs(x2 - x1)
  140.         dy = abs(y2 - y1)

  141.         if not ((dx == 2 and dy == 1) or (dx == 1 and dy == 2)):
  142.             return False

  143.         # 检查蹩脚
  144.         if dx == 2:
  145.             mx = (x1 + x2) // 2
  146.             if (mx, y1) in self.board:
  147.                 return False
  148.         else:
  149.             my = (y1 + y2) // 2
  150.             if (x1, my) in self.board:
  151.                 return False
  152.         return True

  153.     def validate_elephant(self, start, end, color, board):
  154.         x1, y1 = start
  155.         x2, y2 = end
  156.         dx = abs(x2 - x1)
  157.         dy = abs(y2 - y1)

  158.         if dx != 2 or dy != 2:
  159.             return False

  160.         # 检查田心
  161.         mx, my = (x1 + x2) // 2, (y1 + y2) // 2
  162.         if (mx, my) in self.board:
  163.             return False

  164.         # 不能过河
  165.         if (color == "black" and y2 > 4) or (color == "red" and y2 < 5):
  166.             return False
  167.         return True

  168.     def validate_advisor(self, start, end, color, board):
  169.         x1, y1 = start
  170.         x2, y2 = end
  171.         dx = abs(x2 - x1)
  172.         dy = abs(y2 - y1)

  173.         if dx != 1 or dy != 1:
  174.             return False

  175.         # 九宫范围
  176.         if color == "black":
  177.             return 3 <= x2 <= 5 and 0 <= y2 <= 2
  178.         else:
  179.             return 3 <= x2 <= 5 and 7 <= y2 <= 9

  180.     def validate_king(self, start, end, color, board):
  181.         x1, y1 = start
  182.         x2, y2 = end
  183.         dx = abs(x2 - x1)
  184.         dy = abs(y2 - y1)

  185.         if dx + dy != 1:
  186.             return False

  187.         # 九宫范围
  188.         if color == "black":
  189.             return 3 <= x2 <= 5 and 0 <= y2 <= 2
  190.         else:
  191.             return 3 <= x2 <= 5 and 7 <= y2 <= 9

  192.     def validate_cannon(self, start, end, board):
  193.         x1, y1 = start
  194.         x2, y2 = end
  195.         if x1 != x2 and y1 != y2:
  196.             return False

  197.         count = 0
  198.         if x1 == x2:
  199.             step = 1 if y2 > y1 else -1
  200.             for y in range(y1 + step, y2, step):
  201.                 if (x1, y) in self.board:
  202.                     count += 1
  203.         else:
  204.             step = 1 if x2 > x1 else -1
  205.             for x in range(x1 + step, x2, step):
  206.                 if (x, y1) in self.board:
  207.                     count += 1

  208.         target_piece = self.board.get(end)
  209.         if target_piece:
  210.             return count == 1  # 吃子需要隔一个棋子
  211.         else:
  212.             return count == 0  # 移动不能有棋子阻挡

  213.     def validate_pawn(self, start, end, color, board):
  214.         x1, y1 = start
  215.         x2, y2 = end
  216.         dx = abs(x2 - x1)
  217.         dy = y2 - y1 if color == "black" else y1 - y2

  218.         # 只能移动一格
  219.         if dx + abs(y2 - y1) != 1:
  220.             return False

  221.         # 前进方向判断
  222.         if color == "black":
  223.             if y1 <= 4:  # 未过河
  224.                 return y2 == y1 + 1 and x2 == x1
  225.             else:  # 过河后
  226.                 return dy == 1 or (dy == 0 and dx == 1)
  227.         else:
  228.             if y1 >= 5:  # 未过河
  229.                 return y2 == y1 - 1 and x2 == x1
  230.             else:  # 过河后
  231.                 return dy == 1 or (dy == 0 and dx == 1)

  232.     def move_piece(self, start, end):
  233.         self.board[end] = self.board[start]
  234.         del self.board[start]

  235.     def check_game_over(self):
  236.         # 检查是否存在将帅
  237.         has_red_king = any(p["type"] == "帅" for p in self.board.values())
  238.         has_black_king = any(p["type"] == "将" for p in self.board.values())

  239.         if not has_red_king:
  240.             self.show_winner("black")
  241.         elif not has_black_king:
  242.             self.show_winner("red")

  243.     def show_winner(self, winner):
  244.         messagebox.showinfo("游戏结束", f"{winner}方获胜!")
  245.         self.master.destroy()

  246.     # 新增AI相关方法
  247.     def ai_move(self):
  248.         best_score = -float('inf')
  249.         best_moves = []
  250.         for start in list(self.board.keys()):
  251.             piece = self.board[start]
  252.             if piece["color"] != self.current_player:
  253.                 continue
  254.             for x in range(9):
  255.                 for y in range(10):
  256.                     end = (x, y)
  257.                     if self.is_valid_move(start, end):
  258.                         score = self.evaluate_move(start, end)
  259.                         if score > best_score:
  260.                             best_score = score
  261.                             best_moves = [(start, end)]
  262.                         elif score == best_score:
  263.                             best_moves.append((start, end))
  264.         if best_moves:
  265.             best_move = random.choice(best_moves)
  266.             self.move_piece(best_move[0], best_move[1])
  267.             self.check_game_over()
  268.             self.current_player = "red"
  269.             self.draw_board()

  270.     def evaluate_move(self, start, end):
  271.         temp_board = {pos: dict(data) for pos, data in self.board.items()}
  272.         current_color = temp_board[start]["color"]
  273.         score = 0

  274.         # 吃子得分
  275.         target_piece = temp_board.get(end)
  276.         if target_piece and target_piece["color"] != current_color:
  277.             score += self.get_piece_value(target_piece)

  278.         # 模拟移动
  279.         temp_board[end] = temp_board[start]
  280.         del temp_board[start]

  281.         # 检查是否被对方吃
  282.         opponent_color = "red" if current_color == "black" else "black"
  283.         for pos in list(temp_board.keys()):
  284.             piece = temp_board[pos]
  285.             if piece["color"] == opponent_color:
  286.                 if self.is_valid_move(pos, end, temp_board):
  287.                     score -= self.get_piece_value(temp_board[end]) * 0.5  # 被吃风险折扣

  288.         return score

  289.     def get_piece_value(self, piece):
  290.         values = {
  291.             "将": 1000, "帅": 1000,
  292.             "车": 90, "炮": 45, "马": 40,
  293.             "士": 20, "仕": 20, "象": 20, "相": 20,
  294.             "卒": 10, "兵": 10
  295.         }
  296.         return values.get(piece["type"], 0)

  297. if __name__ == "__main__":
  298.     root = tk.Tk()
  299.     game = ChineseChess(root)
  300.     root.mainloop()
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-2-21 22:57:30 | 显示全部楼层
不错,感谢分享~
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-5 23:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表