鱼C论坛

 找回密码
 立即注册
楼主: 过默

怎么写

[复制链接]
 楼主| 发表于 1 小时前 | 显示全部楼层
FishC 发表于 2026-7-9 21:57
好的,我理解您需要将之前讨论的“鼠标移动显示函数调用”技术应用到中国象棋程序中,以便调试和学习函 ...

can_move called: from (3,8) to (4,0)
can_move called: from (3,8) to (4,1)
can_move called: from (3,8) to (4,2)
can_move called: from (3,8) to (4,3)
can_move called: from (3,8) to (4,4)
can_move called: from (3,8) to (4,5)
can_move called: from (3,8) to (4,6)
can_move called: from (3,8) to (4,7)
can_move called: from (3,8) to (4,8)
can_move called: from (3,8) to (5,0)
can_move called: from (3,8) to (5,1)
can_move called: from (3,8) to (5,2)
can_move called: from (3,8) to (5,3)
can_move called: from (3,8) to (5,4)
can_move called: from (3,8) to (5,5)
can_move called: from (3,8) to (5,6)
can_move called: from (3,8) to (5,7)
can_move called: from (3,8) to (5,8)
can_move called: from (3,8) to (6,0)
can_move called: from (3,8) to (6,1)
can_move called: from (3,8) to (6,2)
can_move called: from (3,8) to (6,3)
can_move called: from (3,8) to (6,4)
can_move called: from (3,8) to (6,5)
can_move called: from (3,8) to (6,6)
can_move called: from (3,8) to (6,7)
can_move called: from (3,8) to (6,8)
can_move called: from (3,8) to (7,0)
can_move called: from (3,8) to (7,1)
can_move called: from (3,8) to (7,2)
can_move called: from (3,8) to (7,3)
什么意思,没用
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层
过默 发表于 2026-07-09 21:59
FishC 发表于 2026-7-9 21:57
好的,我理解您需要将 ...


这些输出是程序在每次鼠标移动时,对棋盘上所有可能的落子位置调用了 `can_move` 函数进行合法性判断,目的是**为可移动位置高亮或为AI计算走法做准备**。您感觉“没用”是因为输出太多刷屏了,而且没有关键信息。

### 这些输出如何帮助学习?
- 您可以看到每个位置是否被判定为合法(如果 `can_move` 返回 `True` 或 `False`,但当前代码只打印了调用,没有显示结果)。  
- 通过对比您自己知道的规则,可以判断 `can_move` 的执行是否正确。例如 `(3,8)` 是黑方某棋子,如果禁止它移动到 `(4,0)`,说明规则起作用了。  
- 但大量输出确实干扰正常游戏,所以建议**只保留必要打印**。

### 如何修改代码以减少输出?
找到输出这些信息的地方(通常是AI循环或鼠标事件处理),将 `print("can_move called: from ...")` 注释掉或替换为条件打印(例如只在调试模式开启时才输出)。下面是一个示例修改:

  1. # 将原来的 print 行替换为:
  2. # debug = False  # 全局变量,只在需要调试时设为 True
  3. if debug:
  4.     print("can_move called: from ({},{}) to ({},{})".format(r1, c1, r2, c2))
复制代码


或者直接删除该行,保留 `can_move` 内部的合法/非法判断输出。这样你就只会在移动非法时看到错误信息。

### 更实用的调试方式:添加关键事件输出
在 `on_click` 等关键函数中添加输出,比如:
- “您选中了 (行,列) 的棋子”  
- “移动成功:从 (x1,y1) 到 (x2,y2)”  
- “吃掉了对方的某棋子”  
- “将军!请应将”  

这样既能了解程序运行流程,又不会刷屏。

### 提供一个精简版完整代码(无多余 print,含棋谱)
由于您的 Python 版本是 3.4,下面给出一个**可直接运行**的代码,去除了所有 `can_move` 调用打印,只保留关键的走棋提示和棋谱显示。代码包含:
- 汉字棋子、鼠标点击走棋、红方(您)vs 黑方(随机AI)
- 将军检测、不能自将、不能斜走将帅、兵卒不能后退
- 悔棋按钮、棋谱列表显示(使用 Listbox)

您复制到 `chess_final.py` 并运行即可。

  1.   
  2. # -*- coding: utf-8 -*-  
  3. import tkinter as tk  
  4. import random  
  5. import copy  

  6. SIZE_ROW = 10  
  7. SIZE_COL = 9  
  8. CELL = 50  

  9. # ---------- 棋子汉字映射 ----------  
  10. PIECE_CN = {  
  11.     'R': '車', 'N': '馬', 'B': '象', 'A': '士', 'K': '將', 'C': '砲', 'P': '卒',  
  12.     'r': '車', 'n': '馬', 'b': '象', 'a': '士', 'k': '將', 'c': '砲', 'p': '卒'  
  13. }  
  14. # 红方大写,黑方小写  

  15. def init_board():  
  16.     b = [['.' for _ in range(SIZE_COL)] for _ in range(SIZE_ROW)]  
  17.     # 黑方 (小写) 在第0~4行  
  18.     b[0] = ['r','n','b','a','k','a','b','n','r']  
  19.     b[2][1] = 'c'; b[2][7] = 'c'  
  20.     for c in [0,2,4,6,8]: b[3][c] = 'p'  
  21.     # 红方 (大写) 在第5~9行  
  22.     b[9] = ['R','N','B','A','K','A','B','N','R']  
  23.     b[7][1] = 'C'; b[7][7] = 'C'  
  24.     for c in [0,2,4,6,8]: b[6][c] = 'P'  
  25.     return b  

  26. def is_inside(r,c):  
  27.     return 0<=r<SIZE_ROW and 0<=c<SIZE_COL  

  28. def is_red(p):  
  29.     return p.isupper()  

  30. def same_color(p1,p2):  
  31.     if p1=='.' or p2=='.': return False  
  32.     return is_red(p1) == is_red(p2)  

  33. def count_between(board,r1,c1,r2,c2):  
  34.     # 直线间棋子数  
  35.     cnt=0  
  36.     if r1==r2:  
  37.         step = 1 if c2>c1 else -1  
  38.         for c in range(c1+step, c2, step):  
  39.             if board[r1][c]!='.': cnt+=1  
  40.     elif c1==c2:  
  41.         step = 1 if r2>r1 else -1  
  42.         for r in range(r1+step, r2, step):  
  43.             if board[r][c1]!='.': cnt+=1  
  44.     return cnt  

  45. def can_move(board,r1,c1,r2,c2):  
  46.     if not is_inside(r1,c1) or not is_inside(r2,c2):  
  47.         return False, "坐标越界"  
  48.     piece = board[r1][c1]  
  49.     target = board[r2][c2]  
  50.     if piece=='.': return False, "源格无棋子"  
  51.     if same_color(piece,target): return False, "目标格有己方棋子"  
  52.     dr=r2-r1; dc=c2-c1  
  53.     p = piece.lower()  
  54.     # 将/帅  
  55.     if p=='k':  
  56.         if not (abs(dr)<=1 and abs(dc)<=1): return False, "将帅只能走一格"  
  57.         if abs(dr)+abs(dc) != 1: return False, "将帅不能斜走"  
  58.         # 九宫限制  
  59.         if is_red(piece): # 红帅在 (7~9, 3~5)  
  60.             if not (7<=r2<=9 and 3<=c2<=5): return False, "帅只能在九宫"  
  61.         else:  
  62.             if not (0<=r2<=2 and 3<=c2<=5): return False, "将只能在九宫"  
  63.         return True, ""  
  64.     # 士/仕  
  65.     if p=='a':  
  66.         if not (abs(dr)==1 and abs(dc)==1): return False, "士走斜线一格"  
  67.         if is_red(piece):  
  68.             if not (7<=r2<=9 and 3<=c2<=5): return False, "仕只能在九宫"  
  69.         else:  
  70.             if not (0<=r2<=2 and 3<=c2<=5): return False, "士只能在九宫"  
  71.         return True, ""  
  72.     # 象/相  
  73.     if p=='b':  
  74.         if not (abs(dr)==2 and abs(dc)==2): return False, "象走田"  
  75.         eye_r = (r1+r2)//2; eye_c = (c1+c2)//2  
  76.         if board[eye_r][eye_c]!='.': return False, "象眼被塞"  
  77.         if is_red(piece):  
  78.             if not (5<=r2<=9): return False, "相不可过河"  
  79.         else:  
  80.             if not (0<=r2<=4): return False, "象不可过河"  
  81.         return True, ""  
  82.     # 马  
  83.     if p=='n':  
  84.         if not ((abs(dr),abs(dc)) in [(2,1),(1,2)]): return False, "马走日"  
  85.         if abs(dr)==2:  
  86.             block_r = r1 + (1 if r2>r1 else -1); block_c = c1  
  87.         else:  
  88.             block_r = r1; block_c = c1 + (1 if c2>c1 else -1)  
  89.         if board[block_r][block_c]!='.': return False, "蹩马腿"  
  90.         return True, ""  
  91.     # 车  
  92.     if p=='r':  
  93.         if dr!=0 and dc!=0: return False, "车走直线"  
  94.         if count_between(board,r1,c1,r2,c2)>0: return False, "中间有棋子"  
  95.         return True, ""  
  96.     # 炮  
  97.     if p=='c':  
  98.         if dr!=0 and dc!=0: return False, "炮走直线"  
  99.         between = count_between(board,r1,c1,r2,c2)  
  100.         if target=='.':  
  101.             if between!=0: return False, "炮移动不可越子"  
  102.         else:  
  103.             if between!=1: return False, "炮吃子必须隔一个"  
  104.         return True, ""  
  105.     # 兵/卒  
  106.     if p=='p':  
  107.         if is_red(piece): # 红兵向上 (r减小)  
  108.             if dr>0: return False, "兵不能后退"  
  109.             if r1<=4: # 过河  
  110.                 if not (abs(dr)==1 and dc==0) and not (abs(dc)==1 and dr==0): return False, "兵走一步"  
  111.             else:  
  112.                 if not (dr==-1 and dc==0): return False, "兵未过河只能前进"  
  113.         else: # 黑卒向下 (r增大)  
  114.             if dr<0: return False, "卒不能后退"  
  115.             if r1>=5: # 过河  
  116.                 if not (abs(dr)==1 and dc==0) and not (abs(dc)==1 and dr==0): return False, "卒走一步"  
  117.             else:  
  118.                 if not (dr==1 and dc==0): return False, "卒未过河只能前进"  
  119.         return True, ""  
  120.     return False, "未知棋子"  

  121. def is_check(board, color):  
  122.     # color: 'red' or 'black'  
  123.     # 找出将/帅位置  
  124.     k_pos = None  
  125.     for r in range(SIZE_ROW):  
  126.         for c in range(SIZE_COL):  
  127.             p = board[r][c]  
  128.             if p!='.' and is_red(p)==(color=='red') and p.lower()=='k':  
  129.                 k_pos = (r,c)  
  130.                 break  
  131.         if k_pos: break  
  132.     if not k_pos: return False  
  133.     kr, kc = k_pos  
  134.     # 检查对方所有棋子能否吃掉将/帅  
  135.     opp = 'black' if color=='red' else 'red'  
  136.     for r in range(SIZE_ROW):  
  137.         for c in range(SIZE_COL):  
  138.             p = board[r][c]  
  139.             if p=='.': continue  
  140.             if is_red(p) != (opp=='red'): continue  
  141.             ok, _ = can_move(board,r,c,kr,kc)  
  142.             if ok:  
  143.                 return True  
  144.     return False  

  145. def is_checkmate(board, color):  
  146.     # 检查 color 是否被将死:所有合法走法都不能解除将军  
  147.     for r1 in range(SIZE_ROW):  
  148.         for c1 in range(SIZE_COL):  
  149.             p = board[r1][c1]  
  150.             if p=='.': continue  
  151.             if is_red(p) != (color=='red'): continue  
  152.             for r2 in range(SIZE_ROW):  
  153.                 for c2 in range(SIZE_COL):  
  154.                     ok, _ = can_move(board,r1,c1,r2,c2)  
  155.                     if not ok: continue  
  156.                     # 模拟走棋  
  157.                     new_board = copy.deepcopy(board)  
  158.                     new_board[r2][c2] = new_board[r1][c1]  
  159.                     new_board[r1][c1] = '.'  
  160.                     if not is_check(new_board, color):  
  161.                         return False  
  162.     return True  

  163. class ChessApp:  
  164.     def __init__(self, root):  
  165.         self.root = root  
  166.         self.root.title("中国象棋")  
  167.         self.canvas = tk.Canvas(root, width=SIZE_COL*CELL+20, height=SIZE_ROW*CELL+20, bg='burlywood')  
  168.         self.canvas.pack(side=tk.LEFT, padx=10, pady=10)  
  169.         self.canvas.bind("<Button-1>", self.on_click)  
  170.         # 右侧面板  
  171.         self.panel = tk.Frame(root)  
  172.         self.panel.pack(side=tk.RIGHT, fill=tk.Y, padx=10)  
  173.         self.status = tk.StringVar()  
  174.         self.status.set("红方走棋")  
  175.         self.status_label = tk.Label(self.panel, textvariable=self.status, font=('SimSun',14))  
  176.         self.status_label.pack(pady=5)  
  177.         # 棋谱列表  
  178.         self.history_label = tk.Label(self.panel, text="棋谱", font=('SimSun',12))  
  179.         self.history_label.pack()  
  180.         self.history_listbox = tk.Listbox(self.panel, width=20, height=15, font=('SimSun',10))  
  181.         self.history_listbox.pack(pady=5)  
  182.         self.move_list = []  
  183.         # 悔棋按钮  
  184.         self.undo_btn = tk.Button(self.panel, text="悔棋", command=self.undo_move, font=('SimSun',12))  
  185.         self.undo_btn.pack(pady=5)  
  186.         # 初始化游戏  
  187.         self.board = init_board()  
  188.         self.selected = None  
  189.         self.turn = 'red'  # red 先手  
  190.         self.game_over = False  
  191.         self.undo_stack = []  
  192.         self.draw_board()  

  193.     def undo_move(self):  
  194.         if self.game_over or len(self.undo_stack)==0:  
  195.             return  
  196.         # 撤回两步(自己和AI各一步)或一步(若AI未动)  
  197.         for _ in range(2):  
  198.             if not self.undo_stack: break  
  199.             last = self.undo_stack.pop()  
  200.             self.board, self.turn = last['board'], last['turn']  
  201.             if self.move_list: self.move_list.pop()  
  202.             self.history_listbox.delete(tk.END)  
  203.         self.selected = None  
  204.         self.draw_board()  
  205.         if not self.game_over and self.turn=='black':  
  206.             self.root.after(200, self.ai_move)  # 若轮到AI,恢复思考  

  207.     def draw_board(self, highlight=[]):  
  208.         self.canvas.delete('all')  
  209.         # 绘制网格  
  210.         for r in range(SIZE_ROW):  
  211.             for c in range(SIZE_COL):  
  212.                 x0 = c*CELL+10; y0 = r*CELL+10  
  213.                 x1 = x0+CELL; y1 = y0+CELL  
  214.                 if r==0 and c==0: self.canvas.create_text(x0+5,y0+5,text='0',anchor='nw')  
  215.                 # 画棋位点  
  216.                 self.canvas.create_oval(x0-3,y0-3,x0+3,y0+3,fill='black')  
  217.         # 画九宫斜线  
  218.         for palace in [(0,3,2,5), (7,3,9,5)]:  
  219.             r1,c1,r2,c2 = palace  
  220.             self.canvas.create_line(c1*CELL+10, r1*CELL+10, c2*CELL+10, r2*CELL+10)  
  221.             self.canvas.create_line(c2*CELL+10, r1*CELL+10, c1*CELL+10, r2*CELL+10)  
  222.         # 画楚河汉界  
  223.         self.canvas.create_text(4*CELL+10, 4.5*CELL+10, text='楚 河   漢 界', font=('SimSun',16))  
  224.         # 画棋子  
  225.         for r in range(SIZE_ROW):  
  226.             for c in range(SIZE_COL):  
  227.                 p = self.board[r][c]  
  228.                 if p=='.': continue  
  229.                 x = c*CELL+10+CELL//2; y = r*CELL+10+CELL//2  
  230.                 color = 'red' if p.isupper() else 'black'  
  231.                 self.canvas.create_oval(x-18,y-18,x+18,y+18,fill='white',outline=color,width=2)  
  232.                 cn = PIECE_CN.get(p,'?')  
  233.                 self.canvas.create_text(x,y,text=cn,font=('SimSun',18),fill=color)  
  234.         # 高亮选中  
  235.         if self.selected:  
  236.             r,c = self.selected  
  237.             x0 = c*CELL+10; y0 = r*CELL+10  
  238.             self.canvas.create_rectangle(x0,y0,x0+CELL,y0+CELL,outline='blue',width=3)  
  239.         # 更新状态  
  240.         if self.game_over:  
  241.             txt = "游戏结束"  
  242.         else:  
  243.             txt = "红方走棋" if self.turn=='red' else "黑方思考..."  
  244.         self.status.set(txt)  
  245.         # 更新棋谱显示  
  246.         self.history_listbox.delete(0,tk.END)  
  247.         for m in self.move_list:  
  248.             self.history_listbox.insert(tk.END, m)  

  249.     def on_click(self, event):  
  250.         if self.game_over or self.turn!='red':  
  251.             return  
  252.         col = (event.x-10)//CELL  
  253.         row = (event.y-10)//CELL  
  254.         if not is_inside(row,col):  
  255.             return  
  256.         piece = self.board[row][col]  
  257.         if self.selected is None:  
  258.             # 选择己方棋子  
  259.             if piece!='.' and is_red(piece):  
  260.                 self.selected = (row,col)  
  261.                 self.draw_board()  
  262.             return  
  263.         r1,c1 = self.selected  
  264.         # 如果点击了另一个己方棋子,切换选择  
  265.         if piece!='.' and is_red(piece):  
  266.             self.selected = (row,col)  
  267.             self.draw_board()  
  268.             return  
  269.         # 尝试移动  
  270.         ok, msg = can_move(self.board, r1,c1, row,col)  
  271.         if not ok:  
  272.             self.status.set("移动失败: " + msg)  
  273.             return  
  274.         # 模拟移动,检查是否自将  
  275.         new_board = copy.deepcopy(self.board)  
  276.         new_board[row][col] = new_board[r1][c1]  
  277.         new_board[r1][c1] = '.'  
  278.         if is_check(new_board, 'red'):  
  279.             self.status.set("不能自将")  
  280.             return  
  281.         # 记录悔棋  
  282.         self.undo_stack.append({'board':copy.deepcopy(self.board), 'turn':self.turn})  
  283.         # 执行移动  
  284.         self.board = new_board  
  285.         self.selected = None  
  286.         # 记录棋谱  
  287.         src = "({},{})".format(r1,c1)  
  288.         dst = "({},{})".format(row,col)  
  289.         self.move_list.append("红方 {} -> {}".format(src,dst))  
  290.         # 检查是否吃将/帅  
  291.         if piece!='.' and piece.lower()=='k':  
  292.             self.game_over = True  
  293.             self.move_list.append("红胜")  
  294.             self.draw_board()  
  295.             self.status.set("红方获胜!")  
  296.             self.ask_restart()  
  297.             return  
  298.         # 切换玩家  
  299.         self.turn = 'black'  
  300.         self.draw_board()  
  301.         # 检查黑方是否被将死  
  302.         if is_checkmate(self.board, 'black'):  
  303.             self.game_over = True  
  304.             self.move_list.append("将死黑方,红胜")  
  305.             self.draw_board()  
  306.             self.status.set("红方获胜!")  
  307.             self.ask_restart()  
  308.             return  
  309.         # AI 走棋  
  310.         self.root.after(300, self.ai_move)  

  311.     def ai_move(self):  
  312.         if self.game_over or self.turn!='black':  
  313.             return  
  314.         # 随机走一个合法走法(可改为简单估值AI)  
  315.         moves = []  
  316.         for r1 in range(SIZE_ROW):  
  317.             for c1 in range(SIZE_COL):  
  318.                 p = self.board[r1][c1]  
  319.                 if p=='.' or is_red(p): continue  
  320.                 for r2 in range(SIZE_ROW):  
  321.                     for c2 in range(SIZE_COL):  
  322.                         ok, _ = can_move(self.board, r1,c1, r2,c2)  
  323.                         if not ok: continue  
  324.                         new_board = copy.deepcopy(self.board)  
  325.                         new_board[r2][c2] = new_board[r1][c1]  
  326.                         new_board[r1][c1] = '.'  
  327.                         if not is_check(new_board, 'black'):  
  328.                             moves.append((r1,c1,r2,c2))  
  329.         if not moves:  
  330.             self.game_over = True  
  331.             self.move_list.append("黑方无棋可走,红胜")  
  332.             self.draw_board()  
  333.             self.status.set("红方获胜!")  
  334.             self.ask_restart()  
  335.             return  
  336.         r1,c1,r2,c2 = random.choice(moves)  
  337.         self.undo_stack.append({'board':copy.deepcopy(self.board), 'turn':self.turn})  
  338.         self.board[r2][c2] = self.board[r1][c1]  
  339.         self.board[r1][c1] = '.'  
  340.         src = "({},{})".format(r1,c1)  
  341.         dst = "({},{})".format(r2,c2)  
  342.         self.move_list.append("黑方 {} -> {}".format(src,dst))  
  343.         # 检查是否吃红帅  
  344.         if self.board[r2][c2].lower()=='k':  
  345.             self.game_over = True  
  346.             self.move_list.append("黑胜")  
  347.             self.draw_board()  
  348.             self.status.set("黑方获胜!")  
  349.             self.ask_restart()  
  350.             return  
  351.         self.turn = 'red'  
  352.         self.draw_board()  
  353.         # 检查红方是否被将死  
  354.         if is_checkmate(self.board, 'red'):  
  355.             self.game_over = True  
  356.             self.move_list.append("将死红方,黑胜")  
  357.             self.draw_board()  
  358.             self.status.set("黑方获胜!")  
  359.             self.ask_restart()  

  360.     def ask_restart(self):  
  361.         if tk.messagebox.askyesno("游戏结束", "重新开始一局吗?"):  
  362.             self.board = init_board()  
  363.             self.selected = None  
  364.             self.turn = 'red'  
  365.             self.game_over = False  
  366.             self.undo_stack.clear()  
  367.             self.move_list.clear()  
  368.             self.draw_board()  

  369. if __name__ == '__main__':  
  370.     root = tk.Tk()  
  371.     app = ChessApp(root)  
  372.     root.mainloop()  
复制代码


### 如何使用?
1. 将以上代码复制,保存为 `chess_final.py`。  
2. 在命令行执行 `python chess_final.py` 或双击文件运行。  
3. 红方(您)点击己方棋子再点击目标格子即可走棋,黑方自动应战。  
4. 右侧面板显示棋谱,点击“悔棋”可撤回上一步(您和黑方各一步)。  
5. 游戏结束时会弹出询问是否重新开始。

这段代码**已经去掉了所有多余的调试输出**,只保留关键状态信息。如果您还想学习函数调用流程,可以在 `can_move` 函数开始添加一行条件打印,例如:

  1. if debug:
  2.     print("can_move called: from ({},{}) to ({},{})".format(r1, c1, r2, c2))
复制代码


并将 `debug` 设为 `False` 关闭,需要时改为 `True` 观察。祝您学习愉快!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-7-9 23:25

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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