mathly / mathly2 /comets.py
3v324v23's picture
Add specifications for exercise tests, grades, learning path, lesson quizzes, readiness meter, real tests, rewards, simulation tests, skill mastery, smart board, student home, and test countdown
a7ffb22
Raw History Blame Contribute Delete
4.6 kB
"""The twelve comets (PRD CMT-3) and the rule for when one crosses (CMT-1). The server decides both: the browser only draws."""
import random
from datetime import date
COMETS = [
{"id": "pi", "sym": "ฯ€", "name": "ืคืื™", "fact": "ื”ื™ื—ืก ื‘ื™ืŸ ื”ื”ื™ืงืฃ ืฉืœ ืžืขื’ืœ ืœืงื•ื˜ืจ ืฉืœื•. ื”ืกืคืจื•ืช ืฉืœื• ืื—ืจื™ ื”ื ืงื•ื“ื” ืœื ื ื’ืžืจื•ืช ื•ืœื ื—ื•ื–ืจื•ืช ืขืœ ืขืฆืžืŸ."},
{"id": "fib", "sym": "1,1,2,3,5", "name": "ืคื™ื‘ื•ื ืืฆืณื™", "fact": "ื›ืœ ืžืกืคืจ ื‘ืกื“ืจื” ื”ื•ื ืกื›ื•ื ืฉื ื™ ื”ืงื•ื“ืžื™ื. ื”ืกื“ืจื” ืžื•ืคื™ืขื” ื‘ืกืคื™ืจืœื•ืช ืฉืœ ื’ืจืขื™ื ื™ ื—ืžื ื™ื™ื” ื•ื‘ืืฆื˜ืจื•ื‘ืœื™ื."},
{"id": "phi", "sym": "ฯ†", "name": "ื™ื—ืก ื”ื–ื”ื‘", "fact": "ื‘ืขืจืš 1.618. ืžื—ืœืงื™ื ืงื˜ืข ื›ืš ืฉื”ื™ื—ืก ื‘ื™ืŸ ื”ืฉืœื ืœื—ืœืง ื”ื’ื“ื•ืœ ืฉื•ื•ื” ืœื™ื—ืก ื‘ื™ืŸ ื”ื’ื“ื•ืœ ืœืงื˜ืŸ. ืžื•ืคื™ืข ื‘ืคืจื—ื™ื, ื‘ืฆื“ืคื™ื ื•ื‘ืืžื ื•ืช."},
{"id": "inf", "sym": "โˆž", "name": "ืื™ื ืกื•ืฃ", "fact": "ื™ืฉ ืื™ื ืกื•ืฃ ืžืกืคืจื™ื ืฉืœืžื™ื, ื•ื™ืฉ ื’ื ืื™ื ืกื•ืฃ ืžืกืคืจื™ื ื‘ื™ืŸ 0 ืœึพ1. ืžืชื‘ืจืจ ืฉื”ืื™ื ืกื•ืฃ ื”ืฉื ื™ ื’ื“ื•ืœ ื™ื•ืชืจ."},
{"id": "e", "sym": "e", "name": "ื”ืžืกืคืจ e", "fact": "ื‘ืขืจืš 2.718. ืžื•ืคื™ืข ื‘ื›ืœ ืžืงื•ื ืฉื‘ื• ืžืฉื”ื• ื’ื“ืœ ื‘ืงืฆื‘ ืฉืชืœื•ื™ ื‘ื’ื•ื“ืœ ืฉืœื•: ืจื™ื‘ื™ืช, ืื•ื›ืœื•ืกื™ื•ืช, ื—ื™ื™ื“ืงื™ื."},
{"id": "r2", "sym": "โˆš2", "name": "ืฉื•ืจืฉ 2", "fact": "ื”ืื•ืจืš ืฉืœ ืืœื›ืกื•ืŸ ื‘ืจื™ื‘ื•ืข ืฉืฆืœืขื• 1. ื›ื‘ืจ ื”ื™ื•ื•ื ื™ื ื”ืงื“ืžื•ื ื™ื ื”ื•ื›ื™ื—ื• ืฉืื™ ืืคืฉืจ ืœื›ืชื•ื‘ ืื•ืชื• ื›ืฉื‘ืจ."},
{"id": "zero", "sym": "0", "name": "ืืคืก", "fact": "ื‘ืžืฉืš ืืœืคื™ ืฉื ื™ื ืœื ื”ื™ื” ืกื™ืžืŸ ืœืืคืก. ื”ื•ื ื”ื’ื™ืข ืžื”ื•ื“ื•, ื•ื‘ื–ื›ื•ืชื• ืืคืฉืจ ืœื›ืชื•ื‘ ื›ืœ ืžืกืคืจ ื‘ืขืฉืจ ืกืคืจื•ืช ื‘ืœื‘ื“."},
{"id": "pyth", "sym": "aยฒ+bยฒ=cยฒ", "name": "ืคื™ืชื’ื•ืจืก", "fact": "ื‘ืžืฉื•ืœืฉ ื™ืฉืจ ื–ื•ื•ื™ืช, ืกื›ื•ื ืจื™ื‘ื•ืขื™ ื”ื ื™ืฆื‘ื™ื ืฉื•ื•ื” ืœืจื™ื‘ื•ืข ื”ื™ืชืจ. ื™ื“ื•ืข ืœื™ื•ืชืจ ืžึพ370 ื”ื•ื›ื—ื•ืช ืฉื•ื ื•ืช."},
{"id": "prime", "sym": "2,3,5,7", "name": "ืžืกืคืจื™ื ืจืืฉื•ื ื™ื™ื", "fact": "ืžืกืคืจื™ื ืฉืžืชื—ืœืงื™ื ืจืง ื‘ึพ1 ื•ื‘ืขืฆืžื. ื™ืฉ ืื™ื ืกื•ืฃ ื›ืืœื”, ื•ืขืœื™ื”ื ื‘ื ื•ื™ื” ื”ื”ืฆืคื ื” ืฉืœ ื”ืื™ื ื˜ืจื ื˜."},
{"id": "pascal", "sym": "1 2 1", "name": "ืžืฉื•ืœืฉ ืคืกืงืœ", "fact": "ื›ืœ ืžืกืคืจ ื”ื•ื ืกื›ื•ื ืฉื ื™ ื”ืžืกืคืจื™ื ืฉืžืขืœื™ื•. ื”ืฉื•ืจื•ืช ืฉืœื• ื ื•ืชื ื•ืช ืืช ื”ืžืงื“ืžื™ื ืฉืœ (a + b) ื‘ื›ืœ ื—ื–ืงื”."},
{"id": "euler", "sym": "Vโˆ’E+F=2", "name": "ื ื•ืกื—ืช ืื•ื™ืœืจ", "fact": "ื‘ื›ืœ ื’ื•ืฃ ืžืฉื•ื›ืœืœ, ืงื•ื“ืงื•ื“ื™ื ืคื—ื•ืช ืžืงืฆื•ืขื•ืช ื•ืขื•ื“ ืคืื•ืช ืฉื•ื•ื” ืชืžื™ื“ 2. ื ืกื• ืขืœ ืงื•ื‘ื™ื™ื”: 8 โˆ’ 12 + 6."},
{"id": "mobius", "sym": "โˆž", "name": "ื˜ื‘ืขืช ืžื‘ื™ื•ืก", "fact": "ืจืฆื•ืขืช ื ื™ื™ืจ ืขื ื—ืฆื™ ืกื™ื‘ื•ื‘: ื™ืฉ ืœื” ืจืง ืฆื“ ืื—ื“ ื•ืฉืคื” ืื—ืช. ื ืžืœื” ืฉื”ื•ืœื›ืช ืขืœื™ื” ืชืขื‘ื•ืจ ื‘ืฉื ื™ ื”ืดืฆื“ื“ื™ืืด."},
]
IDS = [c["id"] for c in COMETS]
CHANCE = 0.12 # a right answer's chance of a comet, once the student has been offered their first
def get(comet_id: str) -> dict | None:
return next((c for c in COMETS if c["id"] == comet_id), None)
def due(state: dict | None, caught: set[str], today: date, goal_just_reached: bool, roll: float | None = None) -> bool:
"""Is a comet due after this right answer? At most one a day; none once all are caught; the first one only on the answer that
reaches the daily goal; after any comet was offered, a small chance on each right answer. `roll` (0..1) is injectable for tests."""
if len(caught) >= len(COMETS):
return False
last = (state or {}).get("last_comet_day")
if last == today:
return False
if last is None:
return goal_just_reached
return (random.random() if roll is None else roll) < CHANCE
def surely_not(state: dict | None, today: date, goal_just_reached: bool, roll: float | None) -> bool:
"""True when no comet can be due whatever the student has caught, so the caught list need not be read (the usual case)."""
last = (state or {}).get("last_comet_day")
if last == today:
return True
if last is None:
return not goal_just_reached
return (random.random() if roll is None else roll) >= CHANCE
def pick(caught: set[str], roll: float | None = None) -> dict | None:
"""A comet the student does not have yet, at random."""
left = [c for c in COMETS if c["id"] not in caught]
if not left:
return None
return left[int((random.random() if roll is None else roll) * len(left)) % len(left)]