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()