Python每日一练(跳棋)

import tkinter as tk
from tkinter import messagebox
import sys
import copy


class CheckersGame:
    def __init__(self, master):
        self.master = master
        self.master.title("跳棋游戏")
        self.master.geometry("600x650")
        self.master.resizable(False, False)
        self.master.configure(bg="#f0f0f0")

        # 确保中文显示正常
        self.font_family = ("SimHei", "WenQuanYi Micro Hei", "Heiti TC", "Arial")

        # 游戏参数
        self.board_size = 8
        self.cell_size = 70
        self.selected_piece = None
        self.current_player = 1  # 1 表示玩家1(黑), 2 表示玩家2(红)
        self.game_over = False
        self.jump_in_progress = False

        # 创建棋盘和棋子数据
        self.create_board()

        # 创建UI组件
        self.create_widgets()

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

        # 显示游戏说明
        self.show_instructions()

    def create_board(self):
        # 初始化棋盘数据 (0=空, 1=黑棋, 2=红棋, 11=黑棋国王, 12=红棋国王)
        self.board = [[0 for _ in range(self.board_size)] for _ in range(self.board_size)]

        # 设置初始棋子位置
        for row in range(3):
            for col in range(self.board_size):
                if (row + col) % 2 == 1:
                    self.board[row][col] = 2  # 红棋

        for row in range(5, self.board_size):
            for col in range(self.board_size):
                if (row + col) % 2 == 1:
                    self.board[row][col] = 1  # 黑棋

    def create_widgets(self):
        # 创建顶部信息栏
        self.info_frame = tk.Frame(self.master, bg="#f0f0f0")
        self.info_frame.pack(fill=tk.X, padx=10, pady=10)

        self.player_label = tk.Label(self.info_frame, text="当前玩家: 黑棋",
                                     font=(self.font_family[0], 14), bg="#f0f0f0")
        self.player_label.pack(side=tk.LEFT, padx=10)

        self.reset_button = tk.Button(self.info_frame, text="重新开始",
                                      font=(self.font_family[0], 12),
                                      command=self.reset_game)
        self.reset_button.pack(side=tk.RIGHT, padx=10)

        # 创建棋盘画布
        canvas_width = self.board_size * self.cell_size
        canvas_height = self.board_size * self.cell_size
        self.canvas = tk.Canvas(self.master, width=canvas_width, height=canvas_height,
                                bg="#f0d9b5", highlightthickness=0)
        self.canvas.pack(padx=10, pady=10)

        # 创建底部状态栏
        self.status_frame = tk.Frame(self.master, bg="#f0f0f0")
        self.status_frame.pack(fill=tk.X, padx=10, pady=10)

        self.status_label = tk.Label(self.status_frame, text="请选择要移动的棋子",
                                     font=(self.font_family[0], 12), bg="#f0f0f0")
        self.status_label.pack()

    def draw_board(self):
        # 清空画布
        self.canvas.delete("all")

        # 绘制棋盘格子
        for row in range(self.board_size):
            for col in range(self.board_size):
                x1 = col * self.cell_size
                y1 = row * self.cell_size
                x2 = x1 + self.cell_size
                y2 = y1 + self.cell_size

                # 绘制棋盘方格
                color = "#f0d9b5" if (row + col) % 2 == 0 else "#b58863"
                self.canvas.create_rectangle(x1, y1, x2, y2, fill=color, outline="")

        # 绘制棋子
        for row in range(self.board_size):
            for col in range(self.board_size):
                piece = self.board[row][col]
                if piece != 0:
                    x_center = col * self.cell_size + self.cell_size // 2
                    y_center = row * self.cell_size + self.cell_size // 2
                    radius = self.cell_size // 2 - 5

                    # 根据棋子类型设置颜色和样式
                    if piece == 1:  # 黑棋
                        fill_color = "#000000"
                        outline_color = "#ffffff"
                        is_king = False
                    elif piece == 2:  # 红棋
                        fill_color = "#ff0000"
                        outline_color = "#ffffff"
                        is_king = False
                    elif piece == 11:  # 黑棋国王
                        fill_color = "#000000"
                        outline_color = "#ffffff"
                        is_king = True
                    else:  # 12 红棋国王
                        fill_color = "#ff0000"
                        outline_color = "#ffffff"
                        is_king = True

                    # 绘制棋子
                    self.canvas.create_oval(
                        x_center - radius, y_center - radius,
                        x_center + radius, y_center + radius,
                        fill=fill_color, outline=outline_color, width=2
                    )

                    # 如果是国王,添加皇冠标识
                    if is_king:
                        self.canvas.create_text(
                            x_center, y_center, text="王",
                            fill="white", font=(self.font_family[0], 14, "bold")
                        )

        # 高亮显示选中的棋子和可能的移动位置
        if self.selected_piece is not None:
            row, col = self.selected_piece
            possible_moves = self.get_possible_moves(row, col)

            # 高亮选中的棋子
            x_center = col * self.cell_size + self.cell_size // 2
            y_center = row * self.cell_size + self.cell_size // 2
            radius = self.cell_size // 2 - 2
            self.canvas.create_oval(
                x_center - radius, y_center - radius,
                x_center + radius, y_center + radius,
                outline="#ffff00", width=3
            )

            # 显示可能的移动位置
            for move_row, move_col in possible_moves:
                x_center = move_col * self.cell_size + self.cell_size // 2
                y_center = move_row * self.cell_size + self.cell_size // 2
                radius = self.cell_size // 6
                self.canvas.create_oval(
                    x_center - radius, y_center - radius,
                    x_center + radius, y_center + radius,
                    fill="#ffff00", outline=""
                )

    def get_possible_moves(self, row, col):
        """获取选中棋子的所有可能移动位置"""
        piece = self.board[row][col]
        if piece == 0 or piece // 10 != 0 and piece % 10 != self.current_player:
            return []

        moves = []
        directions = []

        # 确定棋子可以移动的方向
        if piece == 1:  # 黑棋只能向上移动
            directions = [(-1, -1), (-1, 1)]
        elif piece == 2:  # 红棋只能向下移动
            directions = [(1, -1), (1, 1)]
        else:  # 国王可以向任何方向移动
            directions = [(-1, -1), (-1, 1), (1, -1), (1, 1)]

        # 检查普通移动
        for dr, dc in directions:
            new_row = row + dr
            new_col = col + dc

            if 0 <= new_row < self.board_size and 0 <= new_col < self.board_size:
                if self.board[new_row][new_col] == 0:
                    # 如果没有正在进行的跳吃,或者这是连续跳吃的一部分,则允许移动
                    if not self.jump_in_progress or self.is_jump_move(row, col, new_row, new_col):
                        moves.append((new_row, new_col))

        # 检查跳吃移动
        for dr, dc in directions:
            new_row = row + 2 * dr
            new_col = col + 2 * dc

            if 0 <= new_row < self.board_size and 0 <= new_col < self.board_size:
                if self.board[new_row][new_col] == 0:
                    mid_row = row + dr
                    mid_col = col + dc

                    # 检查中间位置是否有对方棋子
                    if self.board[mid_row][mid_col] != 0 and self.board[mid_row][mid_col] % 10 != self.current_player:
                        moves.append((new_row, new_col))

        return moves

    def is_jump_move(self, from_row, from_col, to_row, to_col):
        """判断是否是跳吃移动"""
        return abs(from_row - to_row) == 2 and abs(from_col - to_col) == 2

    def on_click(self, event):
        """处理鼠标点击事件"""
        if self.game_over:
            return

        # 计算点击的格子坐标
        col = event.x // self.cell_size
        row = event.y // self.cell_size

        # 检查坐标是否在棋盘范围内
        if row < 0 or row >= self.board_size or col < 0 or col >= self.board_size:
            return

        # 如果点击的是当前玩家的棋子,则选中它
        if self.board[row][col] != 0 and self.board[row][col] % 10 == self.current_player:
            self.selected_piece = (row, col)
            self.draw_board()
            self.status_label.config(text=f"已选择棋子: ({row}, {col})")
            return

        # 如果已经选中了棋子,并且点击的是合法的移动位置,则移动棋子
        if self.selected_piece is not None:
            from_row, from_col = self.selected_piece
            possible_moves = self.get_possible_moves(from_row, from_col)

            if (row, col) in possible_moves:
                self.move_piece(from_row, from_col, row, col)
            else:
                self.status_label.config(text="无效的移动位置,请重新选择")

    def move_piece(self, from_row, from_col, to_row, to_col):
        """移动棋子并处理跳吃"""
        piece = self.board[from_row][from_col]

        # 移动棋子
        self.board[to_row][to_col] = piece
        self.board[from_row][from_col] = 0

        # 处理跳吃
        if self.is_jump_move(from_row, from_col, to_row, to_col):
            # 计算被吃掉的棋子位置
            mid_row = (from_row + to_row) // 2
            mid_col = (from_col + to_col) // 2

            # 移除被吃掉的棋子
            self.board[mid_row][mid_col] = 0

            # 检查是否可以继续跳吃
            self.jump_in_progress = True
            if len(self.get_possible_jumps(to_row, to_col)) > 0:
                self.selected_piece = (to_row, to_col)
                self.draw_board()
                self.status_label.config(text=f"跳吃成功! 可以继续跳吃: ({to_row}, {to_col})")
                return

        # 重置跳吃状态
        self.jump_in_progress = False

        # 检查是否晋升为国王
        if piece == 1 and to_row == 0:  # 黑棋到达对方底线
            self.board[to_row][to_col] = 11
        elif piece == 2 and to_row == self.board_size - 1:  # 红棋到达对方底线
            self.board[to_row][to_col] = 12

        # 检查游戏是否结束
        if self.check_game_over():
            self.game_over = True
            winner = "黑棋" if self.current_player == 1 else "红棋"
            self.status_label.config(text=f"游戏结束! {winner}获胜!")
            messagebox.showinfo("游戏结束", f"{winner}获胜!")
            return

        # 切换玩家
        self.current_player = 2 if self.current_player == 1 else 1
        self.player_label.config(text=f"当前玩家: {'黑棋' if self.current_player == 1 else '红棋'}")
        self.selected_piece = None

        # 检查下一个玩家是否有可用移动
        if not self.has_any_moves():
            self.game_over = True
            winner = "黑棋" if self.current_player == 2 else "红棋"
            self.status_label.config(text=f"游戏结束! {winner}获胜! (对方无法移动)")
            messagebox.showinfo("游戏结束", f"{winner}获胜! (对方无法移动)")
            return

        self.draw_board()
        self.status_label.config(text=f"轮到 {'黑棋' if self.current_player == 1 else '红棋'} 移动")

    def get_possible_jumps(self, row, col):
        """获取选中棋子的所有可能跳吃位置"""
        piece = self.board[row][col]
        if piece == 0 or piece // 10 != 0 and piece % 10 != self.current_player:
            return []

        jumps = []
        directions = []

        # 确定棋子可以移动的方向
        if piece == 1:  # 黑棋只能向上移动
            directions = [(-1, -1), (-1, 1)]
        elif piece == 2:  # 红棋只能向下移动
            directions = [(1, -1), (1, 1)]
        else:  # 国王可以向任何方向移动
            directions = [(-1, -1), (-1, 1), (1, -1), (1, 1)]

        # 检查跳吃移动
        for dr, dc in directions:
            new_row = row + 2 * dr
            new_col = col + 2 * dc

            if 0 <= new_row < self.board_size and 0 <= new_col < self.board_size:
                if self.board[new_row][new_col] == 0:
                    mid_row = row + dr
                    mid_col = col + dc

                    # 检查中间位置是否有对方棋子
                    if self.board[mid_row][mid_col] != 0 and self.board[mid_row][mid_col] % 10 != self.current_player:
                        jumps.append((new_row, new_col))

        return jumps

    def check_game_over(self):
        """检查游戏是否结束"""
        # 检查是否有一方没有棋子了
        player1_pieces = 0
        player2_pieces = 0

        for row in range(self.board_size):
            for col in range(self.board_size):
                piece = self.board[row][col]
                if piece % 10 == 1:
                    player1_pieces += 1
                elif piece % 10 == 2:
                    player2_pieces += 1

        if player1_pieces == 0:
            return True
        if player2_pieces == 0:
            return True

        return False

    def has_any_moves(self):
        """检查当前玩家是否有任何可用的移动"""
        for row in range(self.board_size):
            for col in range(self.board_size):
                if self.board[row][col] != 0 and self.board[row][col] % 10 == self.current_player:
                    if len(self.get_possible_moves(row, col)) > 0:
                        return True
        return False

    def reset_game(self):
        """重置游戏"""
        self.create_board()
        self.selected_piece = None
        self.current_player = 1
        self.game_over = False
        self.jump_in_progress = False
        self.draw_board()
        self.player_label.config(text="当前玩家: 黑棋")
        self.status_label.config(text="请选择要移动的棋子")

    def show_instructions(self):
        """显示游戏说明"""
        instructions = """
        跳棋游戏规则:
        1. 黑棋先行,双方轮流移动棋子
        2. 普通棋子只能斜向移动一格,国王可以向任何方向移动
        3. 当可以跳吃对方棋子时,必须跳吃
        4. 跳吃后如果还能继续跳吃,必须继续
        5. 棋子到达对方底线时,会晋升为国王
        6. 吃光对方所有棋子或让对方无法移动者获胜

        操作方法:
        - 点击要移动的棋子(己方)
        - 点击高亮的位置进行移动
        """
        messagebox.showinfo("游戏说明", instructions)
        self.draw_board()


if __name__ == "__main__":
    root = tk.Tk()
    game = CheckersGame(root)
    root.mainloop()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值