zylyzghc/script.py

330 lines
12 KiB
Python
Raw Permalink Normal View History

#!/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()