|
|
|
|
@ -19,6 +19,7 @@ class TypingLogic:
|
|
|
|
|
"""
|
|
|
|
|
检查用户输入与学习材料的匹配情况
|
|
|
|
|
- 逐字符比较逻辑
|
|
|
|
|
- 支持中文整词匹配
|
|
|
|
|
- 进度跟踪
|
|
|
|
|
- 准确率计算
|
|
|
|
|
- 返回字典包含:
|
|
|
|
|
@ -42,13 +43,31 @@ class TypingLogic:
|
|
|
|
|
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 len(user_text) > self.current_index and user_text[self.current_index] != expected_char:
|
|
|
|
|
correct = False
|
|
|
|
|
|
|
|
|
|
# 中文整词匹配优化
|
|
|
|
|
# 如果当前位置是中文文本的开始,尝试进行整词匹配
|
|
|
|
|
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值用于准确率计算
|
|
|
|
|
@ -78,21 +97,35 @@ class TypingLogic:
|
|
|
|
|
"accuracy": accuracy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def update_position(self, user_text: str):
|
|
|
|
|
def update_position(self, user_text: str) -> dict:
|
|
|
|
|
"""
|
|
|
|
|
更新当前索引和错误计数
|
|
|
|
|
- 根据用户输入更新当前位置
|
|
|
|
|
- 计算并更新错误计数
|
|
|
|
|
更新当前位置并计算错误数
|
|
|
|
|
- 支持中文整词匹配
|
|
|
|
|
- 逐字符比较逻辑(回退机制)
|
|
|
|
|
- 错误计数
|
|
|
|
|
- 位置更新
|
|
|
|
|
- 返回字典包含:
|
|
|
|
|
* new_position: 整数,新的位置
|
|
|
|
|
* error_count: 整数,错误数
|
|
|
|
|
* completed: 布尔值,是否完成
|
|
|
|
|
"""
|
|
|
|
|
new_position = len(user_text)
|
|
|
|
|
# 更新当前索引位置
|
|
|
|
|
self.current_index = len(user_text)
|
|
|
|
|
|
|
|
|
|
# 重置错误计数
|
|
|
|
|
self.error_count = 0
|
|
|
|
|
|
|
|
|
|
# 使用中文整词匹配优化错误计算
|
|
|
|
|
self._calculate_errors_with_chinese_support(user_text)
|
|
|
|
|
|
|
|
|
|
# 计算新增的错误数
|
|
|
|
|
for i in range(self.current_index, min(new_position, self.total_chars)):
|
|
|
|
|
if user_text[i] != self.learning_content[i]:
|
|
|
|
|
self.error_count += 1
|
|
|
|
|
# 检查是否完成
|
|
|
|
|
completed = self.current_index >= self.total_chars
|
|
|
|
|
|
|
|
|
|
# 更新当前索引
|
|
|
|
|
self.current_index = new_position
|
|
|
|
|
return {
|
|
|
|
|
"new_position": self.current_index,
|
|
|
|
|
"error_count": self.error_count,
|
|
|
|
|
"completed": completed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def get_expected_text(self, length: int = 10) -> str:
|
|
|
|
|
"""
|
|
|
|
|
@ -236,4 +269,85 @@ class TypingLogic:
|
|
|
|
|
return 0.0
|
|
|
|
|
finally:
|
|
|
|
|
# 清除递归保护标志
|
|
|
|
|
self._calculating_accuracy = False
|
|
|
|
|
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
|