镜像自地址
https://github.com/a13zhy/a13rollcall.git
已同步 2026-10-21 03:41:51 +08:00
121 行
4.4 KiB
Python
121 行
4.4 KiB
Python
import random
|
|
import time
|
|
from datetime import datetime
|
|
|
|
|
|
class DrawEngine:
|
|
def __init__(self, data_manager, config_manager):
|
|
self.data = data_manager
|
|
self.config = config_manager
|
|
self.is_drawing = False
|
|
self.current_name = ""
|
|
self.drawn_students = set()
|
|
self.drawn_history = []
|
|
self._start_time = 0
|
|
self._is_manual = False
|
|
self._students = []
|
|
self._available = []
|
|
self._duration = 3
|
|
self._base_interval = 0.05
|
|
|
|
def reset_round(self):
|
|
self.drawn_students.clear()
|
|
self.drawn_history.clear()
|
|
self.current_name = ""
|
|
|
|
def set_student_list(self, students):
|
|
self._students = students
|
|
|
|
def get_available_students(self):
|
|
no_repeat = self.config.get("draw", "no_repeat")
|
|
if no_repeat:
|
|
return [s for s in self._students if s not in self.drawn_students]
|
|
return self._students.copy()
|
|
|
|
def _calculate_weight(self, student_name):
|
|
base_weight = self.data.get_student_weight(student_name)
|
|
dynamic = self.config.get("draw", "dynamic_focus")
|
|
if not dynamic:
|
|
return base_weight
|
|
status = self.data.get_student_status(student_name)
|
|
if status == "已背过":
|
|
rate = self.config.get("draw", "rate_mastered")
|
|
elif status == "未背熟":
|
|
rate = self.config.get("draw", "rate_familiar")
|
|
else:
|
|
rate = self.config.get("draw", "rate_unlearned")
|
|
return base_weight * rate
|
|
|
|
def _weighted_random(self, students):
|
|
if not students:
|
|
return None
|
|
weights = [self._calculate_weight(s) for s in students]
|
|
total = sum(weights)
|
|
if total <= 0:
|
|
return random.choice(students)
|
|
r = random.uniform(0, total)
|
|
cumulative = 0
|
|
for i, w in enumerate(weights):
|
|
cumulative += w
|
|
if r <= cumulative:
|
|
return students[i]
|
|
return students[-1]
|
|
|
|
def start_draw(self, students, mode="auto"):
|
|
if self.is_drawing:
|
|
return False, "", 0
|
|
self._students = students
|
|
self._available = self.get_available_students()
|
|
if not self._available:
|
|
return False, None, 0
|
|
self.is_drawing = True
|
|
self._is_manual = (mode == "manual")
|
|
self._start_time = time.time()
|
|
self._duration = self.config.get("draw", "auto_duration")
|
|
speed = self.config.get("ui", "scroll_speed")
|
|
self._base_interval = 0.05 / speed
|
|
self.current_name = random.choice(self._available)
|
|
self.config.trigger_hooks("draw_start")
|
|
return True, self.current_name, self._base_interval
|
|
|
|
def update(self):
|
|
if not self.is_drawing:
|
|
return False, self.current_name, 0
|
|
elapsed = time.time() - self._start_time
|
|
if not self._is_manual and elapsed >= self._duration:
|
|
return self._finish()
|
|
if self._is_manual:
|
|
interval = self._base_interval
|
|
else:
|
|
progress = elapsed / self._duration
|
|
ease = self.config.get("ui", "ease_strength")
|
|
if progress > 0.7:
|
|
slow_factor = 1 + 3 * ease * ((progress - 0.7) / 0.3)
|
|
interval = self._base_interval * slow_factor
|
|
else:
|
|
interval = self._base_interval
|
|
self.current_name = random.choice(self._available)
|
|
return True, self.current_name, interval
|
|
|
|
def stop_manual(self):
|
|
if not self.is_drawing or not self._is_manual:
|
|
return False, self.current_name, 0
|
|
return self._finish()
|
|
|
|
def _finish(self):
|
|
self.is_drawing = False
|
|
result = self._weighted_random(self._available)
|
|
if result:
|
|
self.drawn_students.add(result)
|
|
self.current_name = result
|
|
time_str = datetime.now().strftime("%H:%M:%S")
|
|
self.drawn_history.append({
|
|
"name": result,
|
|
"time": time_str,
|
|
"status": "待标记"
|
|
})
|
|
limit = self.config.get("draw", "history_limit") or 50
|
|
if len(self.drawn_history) > limit:
|
|
self.drawn_history = self.drawn_history[-limit:]
|
|
self.config.trigger_hooks("draw_finish", result)
|
|
return False, result, 0 |