After using Kahoot in a training session, I wanted to replay the questions. What I missed was the rhythm: answer, watch the score move, try one more round. In a solo session, there was nobody to compete with.
Quick answer
I started a small quiz prototype with a CPU rival. The useful design decision was to separate three things:
- Simulate the rival's correctness and response time.
- Calculate both scores with the same fixed function.
- Treat generated dialogue as a possible future feature, outside scoring.
This is a prototype, not evidence that competition improves learning. Kahoot already described study modes with AI players in 2020. My goal was to explore that experience with my own material and timing rules.
A simulated rival does not have to understand the question
My CPU samples correctness from a configured probability and picks a virtual response time. It does not ask a language model to solve the question.
The code has 60%, 78%, and 92% accuracy profiles. The current UI uses the 78% profile; a difficulty selector is not implemented. These are prototype settings, not validated learning targets.
This gives me an opponent to test before adding conversational behavior.
Random answers, fixed scoring
The player answers against an answer key. The CPU's correctness is sampled. Once correctness is known, both use the same scoring rule.
Here is that rule extracted into a standalone function. Inputs assume nonnegative elapsed time and streak, and a positive time limit.
function score({ correct, elapsedMs, limitMs, streak }) {
if (!correct) return 0;
const remaining = Math.max(0, Math.min(1, 1 - elapsedMs / limitMs));
const speed = 0.5 + remaining * 0.5;
const combo = 1 + Math.min(streak, 5) * 0.05;
return Math.round(1000 * speed * combo);
}
At half the time limit, a correct answer with streak: 0 scores 750. With streak: 2, it scores 825. These are arithmetic examples, not learner measurements.
The rival's result varies; the scoring rule does not. I checked the extracted function with seven assertions covering these examples, incorrect answers, time boundaries, and the streak cap.
To repeat that check, save the function as score.mjs, add export { score };, and run:
node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { score } from './score.mjs';
const cases = [
[true, 5000, 0, 750], [true, 5000, 2, 825],
[false, 0, 0, 0], [true, 0, 0, 1000],
[true, 10000, 0, 500], [true, 12000, 0, 500],
[true, 0, 8, 1250],
];
for (const [correct, elapsedMs, streak, expected] of cases) {
assert.equal(score({ correct, elapsedMs, limitMs: 10000, streak }), expected);
}
console.log('7 assertions passed');
JS
Observed output: 7 assertions passed. This checks the extracted formula, not the full game.
Reveal an answer state before revealing a result
If the CPU answers first, the player sees only answered. Its correctness and points stay hidden until the player's answer is locked.
Question starts
CPU finishes -> show "answered", keep result hidden
Player answers or times out -> lock player input
Finish the rival's waiting display
Reveal correctness and points
Next question or replay
The boundary is information: a player who can still answer must not see the rival's result. Locking also gives the game one place to reject repeated submissions.
Virtual response time is not display waiting time
When the player finishes first, the prototype caps the remaining on-screen wait. It still scores the CPU using the virtual response time chosen for that question.
For example, suppose the CPU's planned time is eight seconds and the player answers after two. Shortening the waiting animation must not replace the CPU's eight-second scoring input with the shorter display duration.
That is an illustrative timeline. The general boundary is that presentation changes should not silently change an already chosen scoring input.
FAQ: what belongs outside the game loop?
Where would I use an LLM?
The current scoring path makes no external LLM calls. Rival dialogue, encouragement, and rephrased explanations are future ideas. If added, I want fixed messages to keep the game moving when generation fails.
Does fixed scoring make the quiz educationally correct?
Question authoring is another responsibility. An incorrect answer key can be scored consistently and still teach the wrong thing. The current prototype reads questions from a lesson page; support for a common import format is still being designed.
What should I check next?
Before adding dialogue, trace these cases in your own quiz: CPU first, player first, timeout, repeated clicks, and replay while an old timer is pending. The seven function assertions above do not establish that this full browser test matrix passes.
Then measure completion, replay, and learning separately. Speed bonuses may reward fast reading or guessing, and more replays do not prove better retention.
Start by naming one piece of information that must stay hidden before an answer, and one scoring input that an animation must never change.
Based on the author's training experience and prototype. AI assisted with implementation review, English recomposition, and the code explanation.
Top comments (0)