zylyzghc/script.py

330 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
英语学习程序主脚本Python版本
实现对话流程、API集成和用户交互功能
"""
import json
import time
import requests
import os
from typing import Dict, List, Optional
# 导入数据
from data import english_data
class GameState:
"""游戏状态管理类"""
def __init__(self):
self.current_scene_index = 0
self.current_dialog_index = 0
self.score = 0
self.total_questions = 0
self.correct_answers = 0
def reset(self):
"""重置游戏状态"""
self.current_scene_index = 0
self.current_dialog_index = 0
self.score = 0
self.total_questions = 0
self.correct_answers = 0
class EnglishLearningApp:
"""英语学习应用主类"""
def __init__(self):
self.game_state = GameState()
self.current_scene = None
self.api_key = os.getenv("DEEPSEEK_API_KEY", "your-api-key-here")
self.api_url = "https://api.deepseek.com/v1/chat/completions"
def load_scene(self, scene_index: int) -> bool:
"""加载指定场景"""
if scene_index < 0 or scene_index >= len(english_data["scenes"]):
print("所有场景已完成!")
return False
self.current_scene = english_data["scenes"][scene_index]
self.game_state.current_scene_index = scene_index
self.game_state.current_dialog_index = 0
print("\n" + "=" * 50)
print(f"场景:{self.current_scene['title']}")
print(f"描述:{self.current_scene['description']}")
print("=" * 50)
# 加载场景图片(在命令行环境下仅显示图片路径)
self._load_scene_image()
# 开始对话
self.show_next_dialog()
return True
def _load_scene_image(self):
"""加载场景图片"""
scene = self.current_scene
image_path = scene.get("image", "")
if not image_path:
# 自动查找图片
possible_extensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"]
base_name_id = str(scene["id"])
# 简单的拼音转换(仅保留字母数字)
base_name_title = ''.join(c for c in scene["title"] if c.isalnum()).lower()
all_possible_paths = [
# 优先使用ID命名
*[f"images/{base_name_id}{ext}" for ext in possible_extensions],
# 支持标题命名
*[f"images/{base_name_title}{ext}" for ext in possible_extensions]
]
for path in all_possible_paths:
if os.path.exists(path):
image_path = path
break
if image_path and os.path.exists(image_path):
print(f"图片:{image_path}")
else:
print("图片:无")
def show_next_dialog(self):
"""显示下一条对话"""
if not self.current_scene:
return
conversation = self.current_scene["conversation"]
dialog_index = self.game_state.current_dialog_index
if dialog_index >= len(conversation):
# 当前场景对话结束
self._scene_complete()
return
dialog = conversation[dialog_index]
if dialog["type"] == "user_question":
# 区分选择题和自由回答题
if "options" in dialog:
self._show_question(dialog)
else:
self._show_dynamic_question(dialog)
else:
# 显示普通对话
self._add_message(dialog)
# 自动显示下一个对话
self.game_state.current_dialog_index += 1
time.sleep(1)
self.show_next_dialog()
def _add_message(self, dialog: Dict):
"""添加对话消息"""
speaker = dialog["speaker"]
text = dialog["text"]
print(f"\n{speaker}: {text}")
def _show_question(self, dialog: Dict):
"""显示选择题"""
print(f"\n{dialog['question']}")
for i, option in enumerate(dialog["options"]):
print(f"{i + 1}. {option}")
# 获取用户输入
try:
user_choice = int(input("请输入选项编号:")) - 1
if 0 <= user_choice < len(dialog["options"]):
self._check_answer(dialog, user_choice)
else:
print("无效的选项,请重新选择!")
self._show_question(dialog)
except ValueError:
print("请输入有效的数字!")
self._show_question(dialog)
def _show_dynamic_question(self, dialog: Dict):
"""显示动态问题使用API调用"""
print(f"\n{dialog['question']}")
user_input = input("请输入您的回答:")
# 保存用户输入到对话历史
self._save_user_input(dialog, user_input)
# 获取AI响应
ai_response = self._get_ai_response(user_input)
# 显示AI响应
print(f"\nAI响应: {ai_response}")
# 继续对话
self.game_state.current_dialog_index += 2 # 跳过用户问题和AI响应
self.show_next_dialog()
def _save_user_input(self, dialog: Dict, user_input: str):
"""保存用户输入到对话历史"""
if not self.current_scene:
return
conversation = self.current_scene["conversation"]
dialog_index = self.game_state.current_dialog_index
# 插入用户输入
user_message = {
"speaker": "",
"text": user_input,
"type": "user"
}
conversation.insert(dialog_index + 1, user_message)
# 插入AI响应占位符
ai_message = {
"speaker": "AI",
"text": "",
"type": "other"
}
conversation.insert(dialog_index + 2, ai_message)
def _get_ai_response(self, user_input: str) -> str:
"""获取AI响应"""
if not self.api_key:
return "抱歉API密钥未配置无法获取AI响应。"
try:
# 构建对话历史
messages = [
{"role": "system", "content": "你是一位英语学习助手,帮助用户进行日常对话练习。请用英语回复,保持对话自然流畅。"}
]
# 添加当前对话历史
if self.current_scene:
for dialog in self.current_scene["conversation"][:self.game_state.current_dialog_index + 1]:
role = "assistant" if dialog["speaker"] != "" else "user"
content = dialog.get("text", dialog.get("question", ""))
messages.append({"role": role, "content": content})
# 添加用户最新输入
messages.append({"role": "user", "content": user_input})
# 调用API
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": "deepseek-chat",
"messages": messages,
"stream": True
}
response = requests.post(self.api_url, headers=headers, json=payload, stream=True)
response.raise_for_status()
# 处理流式响应
ai_response = ""
print("\nAI正在思考...", end="", flush=True)
for chunk in response.iter_content(chunk_size=None):
if chunk:
decoded_chunk = chunk.decode("utf-8")
# 解析SSE格式
lines = decoded_chunk.split("\n")
for line in lines:
if line.startswith("data: "):
data = line[6:].strip()
if data != "[DONE]":
try:
json_data = json.loads(data)
if "choices" in json_data and json_data["choices"]:
delta = json_data["choices"][0].get("delta", {})
if "content" in delta:
content = delta["content"]
ai_response += content
print(content, end="", flush=True)
except json.JSONDecodeError:
continue
print() # 换行
return ai_response if ai_response else "抱歉,无法生成响应。"
except requests.exceptions.RequestException as e:
print(f"\nAPI调用错误: {e}")
return "抱歉API调用失败。"
def _check_answer(self, dialog: Dict, user_choice: int):
"""检查答案"""
is_correct = user_choice == dialog["correctAnswer"]
if is_correct:
self.game_state.correct_answers += 1
print("\n✅ 回答正确!")
else:
correct_answer = dialog["options"][dialog["correctAnswer"]]
print(f"\n❌ 回答错误!正确答案是:{correct_answer}")
self.game_state.total_questions += 1
self.game_state.score += 1 if is_correct else 0
# 更新进度
self.update_progress()
# 继续对话
self.game_state.current_dialog_index += 1
time.sleep(1)
self.show_next_dialog()
def _scene_complete(self):
"""场景完成"""
print("\n" + "=" * 50)
print("当前场景对话已完成!")
print("=" * 50)
if self.game_state.current_scene_index < len(english_data["scenes"]) - 1:
# 询问是否进入下一个场景
next_scene = input("是否进入下一个场景?(y/n): ")
if next_scene.lower() == "y":
next_index = self.game_state.current_scene_index + 1
self.load_scene(next_index)
else:
print("\n🎉 所有场景已完成!")
self._show_final_score()
def _show_final_score(self):
"""显示最终得分"""
if self.game_state.total_questions > 0:
accuracy = (self.game_state.correct_answers / self.game_state.total_questions) * 100
print(f"\n最终得分: {self.game_state.score}")
print(f"正确率: {accuracy:.1f}%")
print(f"总题目数: {self.game_state.total_questions}")
print(f"正确答案: {self.game_state.correct_answers}")
else:
print("\n未完成任何题目")
def update_progress(self):
"""更新进度信息"""
total_scenes = len(english_data["scenes"])
current_scene = self.game_state.current_scene_index + 1
print(f"\n进度:场景 {current_scene}/{total_scenes} | 得分:{self.game_state.score}")
def start(self):
"""启动应用"""
print("欢迎使用英语学习程序!")
print("=" * 50)
# 加载第一个场景
self.load_scene(0)
if __name__ == "__main__":
# 创建应用实例
app = EnglishLearningApp()
# 启动应用
app.start()