a13rollcall/draw_engine.py
2026-08-27 21:23:43 +08:00

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