1234567890(2)

你好

{
  "riddles": [
    {
      "question": "四个'口'围小狗",
      "answer": "器",
      "hint": "注意'犬'字的位置",
      "difficulty": 3
    },
    {
      "question": "山上还有山",
      "answer": "出",
      "hint": "两个'山'字叠加",
      "difficulty": 2
    },
    {
      "question": "一加一不是二",
      "answer": "王",
      "hint": "注意字形",
      "difficulty": 3
    },
    {
      "question": "九十九",
      "answer": "白",
      "hint": "一百减一",
      "difficulty": 2
    },
    {
      "question": "夫人回娘家",
      "answer": "圭",
      "hint": "注意'女'字的位置",
      "difficulty": 3
    }
  ],
  "xiehouyu": [
    {
      "first_part": "张飞审西瓜",
      "second_part": "粗中有细",
      "explanation": "猛将也有细心时"
    },
    {
      "first_part": "孔夫子搬家",
      "second_part": "净是书(输)",
      "explanation": "谐音双关"
    },
    {
      "first_part": "外甥打灯笼",
      "second_part": "照舅(旧)",
      "explanation": "谐音双关"
    },
    {
      "first_part": "和尚打伞",
      "second_part": "无法无天",
      "explanation": "双关语"
    },
    {
      "first_part": "泥菩萨过河",
      "second_part": "自身难保",
      "explanation": "比喻自身难保"
    },
    {
      "first_part": "狗拿耗子",
      "second_part": "多管闲事",
      "explanation": "比喻多管闲事"
    },
    {
      "first_part": "猪八戒照镜子",
      "second_part": "里外不是人",
      "explanation": "比喻两面不讨好"
    },
    {
      "first_part": "老虎屁股",
      "second_part": "摸不得",
      "explanation": "比喻不能招惹"
    },
    {
      "first_part": "老鼠过街",
      "second_part": "人人喊打",
      "explanation": "比喻人人痛恨"
    },
    {
      "first_part": "竹篮打水",
      "second_part": "一场空",
      "explanation": "比喻白费力气"
    },
    {
      "first_part": "瞎子点灯",
      "second_part": "白费蜡",
      "explanation": "比喻白费力气"
    },
    {
      "first_part": "黄鼠狼给鸡拜年",
      "second_part": "没安好心",
      "explanation": "比喻不怀好意"
    },
    {
      "first_part": "哑巴吃黄连",
      "second_part": "有苦说不出",
      "explanation": "比喻有苦难言"
    },
    {
      "first_part": "骑驴看唱本",
      "second_part": "走着瞧",
      "explanation": "比喻看事情发展"
    },
    {
      "first_part": "铁公鸡",
      "second_part": "一毛不拔",
      "explanation": "比喻非常吝啬"
    },
    {
      "first_part": "鸡蛋碰石头",
      "second_part": "不自量力",
      "explanation": "比喻自不量力"
    },
    {
      "first_part": "猫哭老鼠",
      "second_part": "假慈悲",
      "explanation": "比喻假慈悲"
    },
    {
      "first_part": "猴子捞月亮",
      "second_part": "白忙一场",
      "explanation": "比喻白费力气"
    },
    {
      "first_part": "画蛇添足",
      "second_part": "多此一举",
      "explanation": "比喻多余的行为"
    },
    {
      "first_part": "对牛弹琴",
      "second_part": "白费劲",
      "explanation": "比喻对不懂的人说教"
    }
  ],
  "version": "1.0.0"
}
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import json
import random
from PIL import Image, ImageTk
import time

class HanziAdventure:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("汉字秘境闯关")
        self.window.geometry("800x600")
        self.score_default = False  # 初始化 score_default

        # 添加设置按钮,放在底部正中间
        self.settings_button = ttk.Button(self.window, text="⚙️ 设置", command=self.show_settings_window)
        self.settings_button.place(relx=0.5, rely=1.0, anchor='s', y=-10)

        # 加载题库
        with open('questions.json', 'r', encoding='utf-8') as f:
            self.questions = json.load(f)

        self.score = 0
        # 初始化默认时间为 10 分钟
        self.DEFAULT_TIME_SECOND = 600 # 10 * 60
        self.time_left = self.DEFAULT_TIME_SECOND
        self.used_questions = {
            "xiehouyu": set(),
            "riddles": set()
        }  # 记录已使用的题目索引
        self.rule_window = None  # 规则窗口
        self.version_window = None  # 版本更新窗口  # 新增
        self.current_question_type = None  # 当前题目类型
        self.current_question = None  # 当前题目

        # 创建界面
        self.create_welcome_page()

    def clear_window(self):
        """清除窗口中的所有内容"""
        if hasattr(self, 'timer_id'):  # 如果计时器存在
            self.window.after_cancel(self.timer_id)  # 停止计时器
        for widget in self.window.winfo_children():
            if widget != self.settings_button:  # 保留设置按钮
                widget.destroy()
        # 确保设置按钮始终在底部正中间
        self.settings_button.lift()  # 将按钮置于最上层
        self.settings_button.place(relx=0.5, rely=1.0, anchor='s', y=-10)

    def create_welcome_page(self):
        """创建欢迎页面"""
        self.clear_window()
        # 重置已使用的题目索引
        for key in self.used_questions:
            self.used_questions[key] = set()
        tk.Label(self.window, text="汉字秘境探险", font=("楷体", 36)).pack(pady=50)
        ttk.Button(self.window, text="开始挑战", command=self.main_game_loop).pack(pady=10)
        ttk.Button(self.window, text="挑战规则", command=self.show_rules).pack(pady=10)
        ttk.Button(self.window, text="更新谜语版本", command=self.show_version_window).pack(pady=10)  # 删除这行代码:self.settings_button.place(relx=1.0, rely=0.0, anchor='ne', x=-10, y=10)

    def load_questions(self):
        """加载题库"""
        try:
            with open(self.questions_file, 'r', encoding='utf-8') as f:
                self.questions = json.load(f)
        except Exception as e:
            messagebox.showerror("错误", f"加载题库失败: {str(e)}")
            self.questions = {"xiehouyu": [], "riddles": []}

    def show_version_window(self):
        """显示版本更新窗口"""
        if self.version_window is not None and self.version_window.winfo_exists():
            return  # 如果窗口已经存在,则不再创建

        self.version_window = tk.Toplevel(self.window)
        self.version_window.title("更新谜语版本")
        self.version_window.geometry("400x300")

        # 显示当前版本信息
        version = self.get_current_version()
        tk.Label(self.version_window, text=f"当前版本: {version}", font=("楷体", 14)).pack(pady=20)

        # 选择新题库文件
        ttk.Button(self.version_window, text="选择新题库文件", command=self.select_new_questions_file).pack(pady=10)

        # 关闭窗口时重置变量
        self.version_window.protocol("WM_DELETE_WINDOW", self.close_version_window)

    def get_current_version(self):
        """获取当前版本"""
        try:
            with open(self.questions_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
                return data.get("version", "未知")
        except:
            # 如果当前打开了加载题库默认的 questions.json 文件,返回 questions.json 文件的版本号
            try:
                with open('questions.json', 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    return data.get("version", "未知")
            except:
                return "未知"

    def select_new_questions_file(self):
        """选择新的题库文件"""
        file_path = tk.filedialog.askopenfilename(
            title="选择题库文件",
            filetypes=[("JSON 文件", "*.json")],
            initialdir="."
        )
        if file_path:
            if self.validate_questions_file(file_path):
                self.questions_file = file_path
                print(f"新的题库文件已选择: {self.questions_file}")
                self.load_questions()
                messagebox.showinfo("成功", "题库文件已选择!")
                self.version_window.destroy()
            else:
                messagebox.showerror("错误", "选择的题库文件格式不正确")

    def validate_questions_file(self, file_path):
        """验证题库文件格式"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
                if "xiehouyu" in data and "riddles" in data:
                    return True
                return False
        except:
            return False

    def close_version_window(self):
        """关闭版本更新窗口"""
        self.version_window.destroy()
        self.version_window = None

    def create_game_frame(self):
        """创建游戏主框架"""
        self.top_frame = tk.Frame(self.window)
        self.top_frame.pack(fill=tk.X)

        self.score_label = tk.Label(self.top_frame, text=f"积分: {self.score}")
        self.score_label.pack(side=tk.LEFT)

        self.timer_label = tk.Label(self.top_frame, text="剩余时间: 20:00")
        self.timer_label.pack(side=tk.RIGHT)

        self.main_frame = tk.Frame(self.window)
        self.main_frame.pack(expand=True, fill=tk.BOTH)

        # 底部按钮
        self.bottom_frame = tk.Frame(self.window)
        self.bottom_frame.pack(fill=tk.X, side=tk.BOTTOM)

        ttk.Button(self.bottom_frame, text="结束挑战", command=self.show_result).pack(side=tk.LEFT, padx=10)
        ttk.Button(self.bottom_frame, text="挑战说明", command=self.show_rules).pack(side=tk.RIGHT, padx=10)

    def update_timer(self):
        """更新计时器"""
        if not hasattr(self, 'timer_label') or not self.timer_label.winfo_exists():
            return  # 如果计时器标签不存在,直接返回

        mins, secs = divmod(self.time_left, 60)
        self.timer_label.config(text=f"剩余时间: {mins:02d}:{secs:02d}")
        if self.time_left > 0:
            self.time_left -= 1
            self.timer_id = self.window.after(1000, self.update_timer)  # 保存计时器 ID
        else:
            self.show_result()

    def show_xiehouyu(self):
        """显示歇后语题目"""
        if len(self.used_questions["xiehouyu"]) >= len(self.questions["xiehouyu"]):
            # 如果没有更多歇后语题目,检查是否还有字谜题目
            if len(self.used_questions["riddles"]) < len(self.questions["riddles"]):
                self.show_riddles()
            else:
                self.show_result()  # 所有题目都用完,直接结束游戏
            return
    
        # 确保 main_frame 存在
        if not hasattr(self, 'main_frame') or not self.main_frame.winfo_exists():
            self.create_game_frame()  # 如果 main_frame 不存在,重新创建
    
        # 随机选择一个未使用过的题目
        available_questions = [
            i for i in range(len(self.questions["xiehouyu"]))
            if i not in self.used_questions["xiehouyu"]
        ]
        question_index = random.choice(available_questions)
        self.current_question = self.questions["xiehouyu"][question_index]
        self.current_question_type = "xiehouyu"
    
        # 检查题目格式是否正确
        if 'first_part' not in self.current_question or 'second_part' not in self.current_question:
            # 记录错误日志
            with open('error_log.txt', 'a', encoding='utf-8') as f:
                f.write(f"歇后语题目格式错误,时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"题目内容: {str(self.current_question)}\n\n")
            
            # 跳过该题目
            self.used_questions["xiehouyu"].add(question_index)
            self.show_xiehouyu()
            return

        self.used_questions["xiehouyu"].add(question_index)

        question = f"{self.current_question['first_part']} —— ?"

        # 清除主框架内容
        for widget in self.main_frame.winfo_children():
            widget.destroy()

        # 显示题目
        self.question_label = tk.Label(self.main_frame, text=question, font=("楷体", 24))
        self.question_label.pack(pady=20)

        # 输入框
        self.answer_entry = ttk.Entry(self.main_frame, font=("楷体", 18))
        self.answer_entry.pack(pady=10)

        # 提交按钮
        ttk.Button(self.main_frame, text="提交答案", command=self.check_answer).pack()

    def show_riddles(self):
        """显示字谜题目"""
        if len(self.used_questions["riddles"]) >= len(self.questions["riddles"]):
            # 如果没有更多字谜题目,检查是否还有歇后语题目
            if len(self.used_questions["xiehouyu"]) < len(self.questions["xiehouyu"]):
                self.show_xiehouyu()
            else:
                self.show_result()  # 所有题目都用完,直接结束游戏
            return

        # 确保 main_frame 存在
        if not hasattr(self, 'main_frame') or not self.main_frame.winfo_exists():
            self.create_game_frame()  # 如果 main_frame 不存在,重新创建

        # 随机选择一个未使用过的题目
        available_questions = [
            i for i in range(len(self.questions["riddles"]))
            if i not in self.used_questions["riddles"]
        ]
        question_index = random.choice(available_questions)
        self.current_question = self.questions["riddles"][question_index]
        self.current_question_type = "riddles"

        # 检查题目格式是否正确
        if 'question' not in self.current_question or 'answer' not in self.current_question:
            # 记录错误日志
            with open('error_log.txt', 'a', encoding='utf-8') as f:
                f.write(f"字谜题目格式错误,时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"题目内容: {str(self.current_question)}\n\n")
            
            # 跳过该题目
            self.used_questions["riddles"].add(question_index)
            self.show_riddles()
            return

        self.used_questions["riddles"].add(question_index)

        # 动态计算答案字数
        answer_length = len(self.current_question["answer"])
        question = f"{self.current_question['question']}(答案字数: {answer_length})"

        # 清除主框架内容
        for widget in self.main_frame.winfo_children():
            widget.destroy()

        # 显示题目
        self.question_label = tk.Label(self.main_frame, text=question, font=("楷体", 24))
        self.question_label.pack(pady=20)

        # 输入框
        self.answer_entry = ttk.Entry(self.main_frame, font=("楷体", 18))
        self.answer_entry.pack(pady=10)

        # 提交按钮
        ttk.Button(self.main_frame, text="提交答案", command=self.check_answer).pack()

    def check_answer(self):
        """检查答案"""
        user_answer = self.answer_entry.get().strip()

        try:
            if self.current_question_type == "xiehouyu":
                correct_answer = self.current_question["second_part"]
            elif self.current_question_type == "riddles":
                correct_answer = self.current_question["answer"]
        except KeyError:
            with open('error_log.txt', 'a', encoding='utf-8') as f:
                f.write(f"题目格式错误,时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"题目类型: {self.current_question_type}\n")
                f.write(f"题目内容: {str(self.current_question)}\n\n")

                    
                # 更友好的提示并自动跳转到下一题
                messagebox.showwarning("提示", "当前题目数据异常,将自动跳转到下一题")
                self.main_game_loop()
                return

        if user_answer == correct_answer:
            self.score += 10
            self.score_label.config(text=f"积分: {self.score}")
            messagebox.showinfo("正确", "回答正确!")
            self.main_game_loop()  # 答对后直接更新题目
        else:
            self.show_error_buttons()  # 答错后显示按钮

    def show_error_buttons(self):
        """显示答错后的按钮"""
        # 隐藏输入框和提交按钮
        self.answer_entry.pack_forget()
        for widget in self.main_frame.winfo_children():
            if isinstance(widget, ttk.Button) and widget["text"] == "提交答案":
                widget.pack_forget()

        # 显示按钮
        ttk.Button(self.main_frame, text="重新答题", command=self.reset_question).pack(pady=5)
        ttk.Button(self.main_frame, text="展示答案", command=self.show_answer).pack(pady=5)
        ttk.Button(self.main_frame, text="下一题", command=self.main_game_loop).pack(pady=5)

    def reset_question(self):
        """重新答题"""
        # 清空输入框并重新显示输入框和提交按钮
        self.answer_entry.delete(0, tk.END)
        self.answer_entry.pack(pady=10)
        ttk.Button(self.main_frame, text="提交答案", command=self.check_answer).pack()

        # 隐藏错误按钮
        for widget in self.main_frame.winfo_children():
            if isinstance(widget, ttk.Button) and widget["text"] in ["重新答题", "展示答案", "下一题"]:
                widget.pack_forget()

    def show_answer(self):
        """展示答案"""
        # 创建自定义对话框
        answer_window = tk.Toplevel(self.window)
        answer_window.title("正确答案")
        
        # 设置答案和寓意文本
        if self.current_question_type == "xiehouyu":
            answer_text = self.current_question["second_part"]
            explanation_text = self.current_question["explanation"]
        elif self.current_question_type == "riddles":
            answer_text = self.current_question["answer"]
            explanation_text = "无寓意"  # 字谜没有寓意字段
        
        # 答案部分
        tk.Label(answer_window, text="答案:", font=("楷体", 14)).pack(pady=(10, 0))
        answer_box = tk.Text(answer_window, wrap=tk.WORD, height=1, width=20, font=("楷体", 14), bg=self.window.cget("bg"))
        answer_box.insert(tk.END, answer_text)
        answer_box.config(state=tk.DISABLED)  # 设置为只读
        answer_box.pack(padx=20, pady=5)
        
        # 含义部分
        tk.Label(answer_window, text="含义:", font=("楷体", 14)).pack(pady=(10, 0))
        explanation_box = tk.Text(answer_window, wrap=tk.WORD, height=3, width=30, font=("楷体", 14), bg=self.window.cget("bg"))
        explanation_box.insert(tk.END, explanation_text)
        explanation_box.config(state=tk.DISABLED)  # 设置为只读
        explanation_box.pack(padx=20, pady=5)
        
        # 复制答案按钮
        def copy_answer():
            self.window.clipboard_clear()
            self.window.clipboard_append(answer_text)
            messagebox.showinfo("成功", "答案已复制到剪贴板")
        
        ttk.Button(answer_window, text="复制答案", command=copy_answer).pack(pady=10)
        
        # 关闭按钮
        ttk.Button(answer_window, text="关闭", command=answer_window.destroy).pack(pady=10)

    def show_rules(self):
        """显示挑战说明"""
        if self.rule_window is not None and self.rule_window.winfo_exists():
            return  # 如果规则窗口已经存在,则不再创建

        self.rule_window = tk.Toplevel(self.window)
        self.rule_window.title("挑战说明")
        self.rule_window.geometry("500x300")

        rules_text = """
        游戏规则:
        1. 每次回答一个题目,题目类型包括歇后语、字谜。
        2. 答对一题得10分。
        3. 答错后可以选择重新答题、查看答案或跳过。
        4. 时间结束后或主动结束挑战时显示最终得分。
        """
        #去除空格
        rules_text = rules_text.replace(" ", "")
        tk.Label(self.rule_window, text=rules_text, font=("楷体", 14), justify=tk.LEFT).pack(pady=20)

        # 关闭规则窗口时重置变量
        self.rule_window.protocol("WM_DELETE_WINDOW", self.close_rule_window)

    def close_rule_window(self):
        """关闭规则窗口"""
        self.rule_window.destroy()
        self.rule_window = None

    def show_result(self):
        """显示最终结果"""
        self.clear_window()  # 清除所有窗口内容
        # 重置分数和时间
        if self.score_default:
            self.score = 0
        self.time_left = self.DEFAULT_TIME_SECOND
    
        tk.Label(self.window, text="挑战结束!", font=("楷体", 36)).pack(pady=50)
        tk.Label(self.window, text=f"你的最终得分是: {self.score}", font=("楷体", 24)).pack()
        ttk.Button(self.window, text="重新开始", command=self.create_welcome_page).pack()

    def main_game_loop(self):
        """主游戏逻辑"""
        self.clear_window()  # 清除当前窗口内容,包括结束页
        self.create_game_frame()
        self.update_timer()
        # 确保设置按钮始终可见
        self.settings_button.lift()
        self.settings_button.place(relx=0.5, rely=1.0, anchor='s', y=-10)

        # 重置时间
        self.time_left = self.DEFAULT_TIME_SECOND
    
        # 随机选择一种题目类型
        question_types = ["xiehouyu", "riddles"]
        self.current_question_type = random.choice(question_types)
        if self.current_question_type == "xiehouyu":
            self.show_xiehouyu()
        elif self.current_question_type == "riddles":
            self.show_riddles()

    def run(self):
        """运行程序"""
        self.window.mainloop()

    def show_settings_window(self):
        """显示设置窗口"""
        # 如果设置窗口已经存在且未关闭,则直接返回
        if hasattr(self, 'settings_window') and self.settings_window.winfo_exists():
            return

        self.settings_window = tk.Toplevel(self.window)
        self.settings_window.title("设置")
        self.settings_window.geometry("300x220")

        # 添加分数重置选项
        tk.Label(self.settings_window, text="分数设置", font=("楷体", 14)).pack(pady=5)
        self.score_default_var = tk.BooleanVar(value=self.score_default)
        ttk.Checkbutton(self.settings_window, text="每次重新开始重置分数", 
                       variable=self.score_default_var).pack()

        # 添加当前分数设置
        tk.Label(self.settings_window, text="当前分数", font=("楷体", 14)).pack(pady=5)
        self.score_entry = ttk.Entry(self.settings_window)
        self.score_entry.insert(0, str(self.score))
        self.score_entry.pack()

        # 添加时间设置
        tk.Label(self.settings_window, text="默认时间(秒)", font=("楷体", 14)).pack(pady=5)
        self.time_entry = ttk.Entry(self.settings_window)
        self.time_entry.insert(0, str(self.DEFAULT_TIME_SECOND))
        self.time_entry.pack()

        # 保存按钮
        ttk.Button(self.settings_window, text="保存", command=self.save_settings).pack(pady=10)

        # 关闭窗口时重置变量
        self.settings_window.protocol("WM_DELETE_WINDOW", self.close_settings_window)

    def close_settings_window(self):
        """关闭设置窗口"""
        self.settings_window.destroy()
        del self.settings_window

    def save_settings(self):
        """保存设置"""
        try:
            # 更新分数重置选项
            self.score_default = self.score_default_var.get()

            # 更新当前分数
            new_score = int(self.score_entry.get())
            self.score = new_score
            if hasattr(self, 'score_label'):
                self.score_label.config(text=f"积分: {self.score}")

            # 更新默认时间
            new_time = int(self.time_entry.get())
            self.DEFAULT_TIME_SECOND = new_time
            self.time_left = new_time
            if hasattr(self, 'timer_label'):
                mins, secs = divmod(self.time_left, 60)
                self.timer_label.config(text=f"剩余时间: {mins:02d}:{secs:02d}")

            messagebox.showinfo("成功", "设置已保存!")
            self.settings_window.destroy()
        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字")

if __name__ == "__main__":
    app = HanziAdventure()
    app.run()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值