feat(scoring): rescale scores to 0.0-1.0 with 0.05 resolution
Per-question score is now a float in [0.0, 1.0] snapped to a 21-level 0.05 grid, replacing the previous 0-1000 integer scale. Easier to read on a leaderboard, ties become acceptable rather than vanishingly rare, and small clock-skew differences no longer split rankings. DB schema: score is REAL now (SQLite type affinity is loose enough that existing rows still read fine, but new inserts go in as floats). Frontend: added fmtScore() helpers in admin.js and quiz.js to render two decimal places consistently (0.85, 1.20, 5.00) so float-arithmetic sums never display as 0.8500000000000001. Tests: linear_decay/flat/exponential_decay assertions updated; added a snap-to-grid invariant test.
This commit is contained in:
@@ -28,6 +28,13 @@ const store = {
|
||||
|
||||
let countdownTimer = null;
|
||||
|
||||
function fmtScore(value) {
|
||||
// Scores are floats on a 0.05 grid in [0, 1]; sums can run up to N
|
||||
// (one per question). Always render as fixed two-decimal so the
|
||||
// leaderboard reads "0.85" / "1.20" / "5.00" cleanly.
|
||||
return Number(value || 0).toFixed(2);
|
||||
}
|
||||
|
||||
function escapeText(value) {
|
||||
return String(value ?? "").replace(/[&<>"']/g, (c) => ({
|
||||
"&": "&",
|
||||
@@ -377,7 +384,7 @@ function renderLeaderboardList(rows) {
|
||||
<li>
|
||||
<span class="rank">${r.rank}</span>
|
||||
<span class="who"><b>${escapeText(r.name)}</b>${r.student_id ? `<small>${escapeText(r.student_id)}</small>` : ""}</span>
|
||||
<span class="score">${r.score}</span>
|
||||
<span class="score">${fmtScore(r.score)}</span>
|
||||
</li>
|
||||
`).join("")}
|
||||
</ol>
|
||||
|
||||
Reference in New Issue
Block a user