You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Curriculum_Design/src/typing_logic.py

353 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

class TypingLogic:
def __init__(self, learning_content: str):
"""
初始化打字逻辑状态。。。。
- 存储学习材料
- 初始化当前索引为0
- 初始化错误计数为0
"""
self.learning_content = learning_content
self.current_index = 0
self.error_count = 0
self.total_chars = len(learning_content)
self.typed_chars = 0
self.image_positions = [] # 存储图片位置信息
self.image_data = {} # 存储图片数据 {图片名称: 二进制数据}
self.image_display_queue = [] # 待显示的图片队列
def check_input(self, user_text: str) -> dict:
"""
检查用户输入与学习材料的匹配情况
- 逐字符比较逻辑
- 支持中文整词匹配
- 进度跟踪
- 准确率计算
- 返回字典包含:
* correct: 布尔值,当前输入是否正确
* expected: 字符串,当前期望的字符
* position: 整数,当前位置
* completed: 布尔值,是否完成
* accuracy: 浮点数,准确率
"""
# 保存当前索引用于返回
current_position = len(user_text)
# 临时保存原始的typed_chars值用于准确率计算
original_typed_chars = self.typed_chars
# 更新已输入字符数
self.typed_chars = len(user_text)
# 如果用户输入的字符数超过了学习材料的长度,截取到相同长度
if len(user_text) > self.total_chars:
user_text = user_text[:self.total_chars]
current_position = len(user_text)
# 检查当前输入是否正确(支持中文整词匹配)
correct = True
expected_char = ''
if self.current_index < self.total_chars:
expected_char = self.learning_content[self.current_index]
# 中文整词匹配优化
# 如果当前位置是中文文本的开始,尝试进行整词匹配
if self._is_chinese_text_start(self.current_index):
# 获取期望的中文词组
expected_word = self._get_chinese_word_at(self.current_index)
# 获取用户输入的对应部分
user_word = user_text[self.current_index:min(self.current_index + len(expected_word), len(user_text))]
# 如果用户输入的词组与期望词组匹配,则认为是正确的
if user_word == expected_word:
correct = True
else:
# 如果整词不匹配,回退到逐字符匹配
if len(user_text) > self.current_index and user_text[self.current_index] != expected_char:
correct = False
else:
# 非中文词组开始位置,使用逐字符匹配
if len(user_text) > self.current_index and user_text[self.current_index] != expected_char:
correct = False
else:
# 已经完成所有输入
# 恢复原始的typed_chars值用于准确率计算
accuracy = self._calculate_accuracy()
self.typed_chars = original_typed_chars
return {
"correct": True,
"expected": "",
"position": self.current_index,
"completed": True,
"accuracy": accuracy
}
# 检查是否完成
completed = current_position >= self.total_chars
# 计算准确率
accuracy = self._calculate_accuracy()
# 恢复原始的typed_chars值
self.typed_chars = original_typed_chars
return {
"correct": correct,
"expected": expected_char,
"position": current_position,
"completed": completed,
"accuracy": accuracy
}
def update_position(self, user_text: str) -> dict:
"""
更新当前位置并计算错误数
- 支持中文整词匹配
- 逐字符比较逻辑(回退机制)
- 错误计数
- 位置更新
- 返回字典包含:
* new_position: 整数,新的位置
* error_count: 整数,错误数
* completed: 布尔值,是否完成
"""
# 更新当前索引位置
self.current_index = len(user_text)
# 重置错误计数
self.error_count = 0
# 使用中文整词匹配优化错误计算
self._calculate_errors_with_chinese_support(user_text)
# 检查是否完成
completed = self.current_index >= self.total_chars
return {
"new_position": self.current_index,
"error_count": self.error_count,
"completed": completed
}
def get_expected_text(self, length: int = 10) -> str:
"""
获取用户接下来应该输入的内容
- 返回从当前位置开始的一定长度文本如10个字符
- 处理文本结束情况
"""
start_pos = self.current_index
end_pos = min(start_pos + length, self.total_chars)
return self.learning_content[start_pos:end_pos]
def get_progress(self) -> dict:
"""
获取当前学习进度统计
- current: 整数,当前位置
- total: 整数,总长度
- percentage: 浮点数,完成百分比
- remaining: 整数,剩余字符数
"""
current = self.current_index
total = self.total_chars
percentage = (current / total * 100) if total > 0 else 0
remaining = max(0, total - current)
return {
"current": current,
"total": total,
"percentage": percentage,
"remaining": remaining
}
def reset(self, new_content: str = None):
"""
重置打字状态
- 重置当前索引为0
- 重置错误计数
- 如果提供了新内容,更新学习材料
"""
if new_content is not None:
self.learning_content = new_content
self.total_chars = len(new_content)
self.current_index = 0
self.error_count = 0
self.typed_chars = 0
self.image_positions = [] # 重置图片位置信息
self.image_data = {} # 重置图片数据
self.image_display_queue = [] # 重置图片显示队列
def get_statistics(self) -> dict:
"""
获取打字统计信息
- total_chars: 整数,总字符数
- typed_chars: 整数,已输入字符数
- error_count: 整数,错误次数
- accuracy_rate: 浮点数,准确率
"""
return {
"total_chars": self.total_chars,
"typed_chars": self.typed_chars,
"error_count": self.error_count,
"accuracy_rate": self._calculate_accuracy()
}
def set_image_positions(self, image_positions: list):
"""
设置图片位置信息
- image_positions: 列表,包含图片位置信息
"""
self.image_positions = image_positions
def get_current_image_info(self, position: int) -> dict:
"""
获取当前位置的图片信息
- position: 整数,当前输入位置
- 返回字典包含图片信息如果没有图片返回None
"""
for img_info in self.image_positions:
if img_info['start_pos'] <= position <= img_info['end_pos']:
return img_info
return None
def set_image_data(self, image_data: dict):
"""
设置图片数据
- image_data: 字典,{图片名称: 二进制数据}
"""
self.image_data = image_data
def get_images_to_display(self, current_position: int) -> list:
"""
获取在当前位置需要显示的图片
- current_position: 整数,当前输入位置
- 返回图片信息列表
"""
images_to_display = []
for img_info in self.image_positions:
if img_info['start_pos'] <= current_position <= img_info['end_pos']:
# 尝试获取图片名称(支持多种键名)
image_name = img_info.get('image_name', '') or img_info.get('filename', '')
if image_name in self.image_data:
img_info_copy = img_info.copy()
img_info_copy['image_data'] = self.image_data[image_name]
images_to_display.append(img_info_copy)
return images_to_display
def should_show_image(self, current_position: int) -> bool:
"""
检查在当前位置是否应该显示图片
- current_position: 整数,当前输入位置
- 返回布尔值
"""
return len(self.get_images_to_display(current_position)) > 0
def check_image_at_position(self, position: int) -> bool:
"""
检查指定位置是否有图片
- position: 整数,位置索引
- 返回布尔值,该位置是否有图片
"""
return self.get_current_image_info(position) is not None
def _calculate_accuracy(self) -> float:
"""
计算准确率
"""
# 防止递归的保护措施
if hasattr(self, '_calculating_accuracy') and self._calculating_accuracy:
return 0.0
if self.typed_chars == 0:
return 0.0
# 设置递归保护标志
self._calculating_accuracy = True
try:
# 准确率 = (已输入字符数 - 错误次数) / 已输入字符数
accuracy = (self.typed_chars - self.error_count) / self.typed_chars
return max(0.0, min(1.0, accuracy)) # 确保准确率在0.0到1.0之间
except (ZeroDivisionError, RecursionError):
return 0.0
finally:
# 清除递归保护标志
self._calculating_accuracy = False
def _is_chinese_text_start(self, index: int) -> bool:
"""
判断指定位置是否是中文文本的开始
- 检查当前字符是否为中文
- 检查前一个字符是否为非中文或空格
"""
if index < 0 or index >= len(self.learning_content):
return False
current_char = self.learning_content[index]
# 检查当前字符是否为中文
if not self._is_chinese_char(current_char):
return False
# 如果是第一个字符,或者是紧跟在非中文字符后的中文,则认为是中文文本的开始
if index == 0:
return True
prev_char = self.learning_content[index - 1]
return not self._is_chinese_char(prev_char) or prev_char.isspace()
def _is_chinese_char(self, char: str) -> bool:
"""
判断字符是否为中文
- 使用Unicode范围判断中文字符
"""
return '\u4e00' <= char <= '\u9fff' or '\u3400' <= char <= '\u4dbf'
def _get_chinese_word_at(self, index: int) -> str:
"""
获取指定位置开始的中文词组
- 从当前位置开始,连续获取中文字符
- 遇到非中文字符或字符串结束则停止
"""
if index < 0 or index >= len(self.learning_content):
return ""
word = ""
current_index = index
while current_index < len(self.learning_content):
char = self.learning_content[current_index]
if self._is_chinese_char(char):
word += char
current_index += 1
else:
break
return word
def _calculate_errors_with_chinese_support(self, user_text: str) -> None:
"""
使用中文整词匹配优化错误计算
- 优先尝试中文整词匹配
- 整词匹配失败时回退到逐字符匹配
"""
i = 0
while i < min(len(user_text), len(self.learning_content)):
# 检查当前位置是否为中文文本开始
if self._is_chinese_text_start(i):
# 获取期望的中文词组
expected_word = self._get_chinese_word_at(i)
# 获取用户输入的对应部分
user_word = user_text[i:min(i + len(expected_word), len(user_text))]
# 如果整词匹配成功,跳过整个词组
if user_word == expected_word:
i += len(expected_word)
continue
else:
# 整词匹配失败,回退到逐字符匹配
if user_text[i] != self.learning_content[i]:
self.error_count += 1
else:
# 非中文文本,使用逐字符匹配
if user_text[i] != self.learning_content[i]:
self.error_count += 1
i += 1