Files
reversi/gameGUI.py
2025-07-26 21:23:45 +08:00

242 lines
8.8 KiB
Python

import tkinter as tk
from tkinter import ttk, messagebox
import numpy as np
from game.reversi import Board
class ReversiGUI:
def __init__(self, master):
self.master = master
self.master.title("黑白棋 (Reversi)")
self.master.geometry("1000x600")
self.master.resizable(True, True)
# 创建棋盘实例 (8x8)
self.board = Board(8, 8)
# 棋子颜色和符号
self.colors = {
0: "#228B22", # 空位 - 绿色
1: "#000000", # 黑棋 - 黑色
-1: "#FFFFFF" # 白棋 - 白色
}
self.bg_colors = {
0: "#228B22", # 空位 - 绿色
1: "#000000", # 黑棋 - 黑色
-1: "#FFFFFF" # 白棋 - 白色
}
self.symbols = {
1: "",
-1: "",
0: ""
}
# 创建主框架
self.main_frame = ttk.Frame(self.master)
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 创建左侧主棋盘框架
self.board_frame = ttk.LabelFrame(self.main_frame, text="主棋盘")
self.board_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 创建右侧通道可视化框架
self.channels_frame = ttk.LabelFrame(self.main_frame, text="通道可视化")
self.channels_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 状态标签
self.status_var = tk.StringVar()
self.status_label = ttk.Label(self.master, textvariable=self.status_var, font=('Arial', 12))
self.status_label.pack(pady=5)
# 初始化棋盘按钮
self.board_buttons = []
self.setup_board()
# 初始化通道可视化
self.channel_labels = {
"board_b": [], # 黑棋位置
"board_w": [], # 白棋位置
"board_move": [], # 合法走法
"player": [] # 当前玩家
}
self.setup_channels()
# 更新显示
self.update_display()
# 重置按钮
self.reset_button = ttk.Button(self.master, text="重置游戏", command=self.reset_game)
self.reset_button.pack(pady=10)
def setup_board(self):
"""设置主棋盘界面"""
# 创建行列标签
col_frame = ttk.Frame(self.board_frame)
col_frame.pack(fill=tk.X)
# 空白格用于对齐
ttk.Label(col_frame, text="", width=2).pack(side=tk.LEFT)
# 列标签 (A-H)
for c in range(8):
ttk.Label(col_frame, text=chr(65 + c), width=4).pack(side=tk.LEFT)
# 创建棋盘按钮
for r in range(8):
row_frame = ttk.Frame(self.board_frame)
row_frame.pack(fill=tk.X)
# 行标签 (1-8)
ttk.Label(row_frame, text=str(r+1), width=2).pack(side=tk.LEFT)
row_buttons = []
for c in range(8):
btn = tk.Button(
row_frame,
width=3, height=1,
font=('Arial', 14, 'bold'),
command=lambda r=r, c=c: self.make_move(r, c)
)
btn.pack(side=tk.LEFT, padx=1, pady=1)
row_buttons.append(btn)
self.board_buttons.append(row_buttons)
def setup_channels(self):
"""设置通道可视化界面"""
# 创建通道标签和网格
channels = [
("黑棋位置", "board_b"),
("白棋位置", "board_w"),
("合法走法", "board_move"),
("当前玩家", "player")
]
for idx, (title, key) in enumerate(channels):
# 为每个通道创建框架
channel_frame = ttk.LabelFrame(self.channels_frame, text=title)
channel_frame.grid(row=idx//2, column=idx%2, padx=5, pady=5, sticky="nsew")
# 创建网格
grid_frame = ttk.Frame(channel_frame)
grid_frame.pack(padx=5, pady=5)
# 创建小格子
cell_labels = []
for r in range(8):
row_labels = []
for c in range(8):
lbl = tk.Label(
grid_frame,
width=2, height=1,
relief=tk.RIDGE,
borderwidth=1
)
lbl.grid(row=r, column=c, sticky="nsew")
row_labels.append(lbl)
cell_labels.append(row_labels)
self.channel_labels[key] = cell_labels
# 使行列权重相等
for i in range(2):
self.channels_frame.grid_columnconfigure(i, weight=1)
self.channels_frame.grid_rowconfigure(i, weight=1)
def update_display(self):
"""更新所有显示元素"""
# 更新主棋盘
for r in range(8):
for c in range(8):
value = self.board.board[r, c]
btn = self.board_buttons[r][c]
# 设置按钮颜色和文本
btn.config(
text=self.symbols[value],
fg="#FFFFFF" if value == 1 else "#000000", # 黑棋白字,白棋黑字
bg=self.bg_colors[value], # 使用对应的背景色
state=tk.NORMAL if self.board.board_move[r, c] == 1 else tk.DISABLED
)
# 更新通道可视化
# 黑棋位置通道
self._update_channel_display("board_b", self.board.board_b)
# 白棋位置通道
self._update_channel_display("board_w", self.board.board_w)
# 合法走法通道
self._update_channel_display("board_move", self.board.board_move)
# 当前玩家通道
self._update_channel_display("player", self.board.player_channel)
# 更新状态信息
player_name = "黑棋" if self.board.player == 1 else "白棋"
if self.board.is_game_over():
winner = self.board.get_winner()
if winner == 1:
status = "游戏结束!黑棋获胜!"
elif winner == -1:
status = "游戏结束!白棋获胜!"
else:
status = "游戏结束!平局!"
else:
black_count = np.sum(self.board.board == 1)
white_count = np.sum(self.board.board == -1)
status = f"当前玩家: {player_name} | 黑棋: {black_count} | 白棋: {white_count}"
self.status_var.set(status)
def _update_channel_display(self, channel_key, data):
"""更新特定通道的显示"""
for r in range(8):
for c in range(8):
value = data[r, c]
label = self.channel_labels[channel_key][r][c]
# 设置颜色强度
if channel_key == "board_b":
# 黑棋通道: 黑色
intensity = int(value * 255)
color = f"#{intensity:02x}{intensity:02x}{intensity:02x}"
elif channel_key == "board_w":
# 白棋通道: 白色
intensity = int(255 - value * 255)
color = f"#{intensity:02x}{intensity:02x}{intensity:02x}"
elif channel_key == "board_move":
# 合法走法通道: 蓝色
intensity = int(value * 255)
color = f"#00{intensity:02x}ff"
else:
# 当前玩家通道: 红色(黑棋)/黄色(白棋)
if value > 0:
color = f"#ff0000" # 红色表示黑棋
else:
color = f"#ffff00" # 黄色表示白棋
label.config(bg=color)
def make_move(self, r, c):
"""执行走棋操作"""
try:
self.board.play(r, c)
self.update_display()
# 检查游戏是否结束
if self.board.is_game_over():
winner = self.board.get_winner()
if winner == 1:
messagebox.showinfo("游戏结束", "黑棋获胜!")
elif winner == -1:
messagebox.showinfo("游戏结束", "白棋获胜!")
else:
messagebox.showinfo("游戏结束", "平局!")
except ValueError as e:
messagebox.showerror("错误", str(e))
def reset_game(self):
"""重置游戏"""
self.board.reset()
self.update_display()
if __name__ == "__main__":
root = tk.Tk()
app = ReversiGUI(root)
root.mainloop()