Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions app/games/otter-stop/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ export function setGoKeys(keys) {
/** Display interval at level 0 (milliseconds). */
const BASE_INTERVAL_MS = 1500;

/** How much the interval shrinks per level (milliseconds). */
const INTERVAL_STEP_MS = 50;
/**
* Geometric decay factor applied to the display interval per level.
* Each level multiplies the current interval by this rate, producing large
* speed jumps at low levels that taper off as the game becomes faster.
*/
const INTERVAL_DECAY_RATE = 0.88;

/** Minimum allowed display interval (milliseconds). */
const MIN_INTERVAL_MS = 150;
Expand Down Expand Up @@ -263,12 +267,14 @@ export function recordResponse(isNoGo, spacePressed) {

/**
* Return the display interval in milliseconds for the current level.
* Starts at 700 ms and decreases by 50 ms per level, with a floor of 150 ms.
* Uses geometric decay: each level multiplies the base interval by
* INTERVAL_DECAY_RATE, producing large speed jumps early and increasingly
* smaller increments as the game gets faster. Floored at MIN_INTERVAL_MS.
*
* @returns {number} Display interval in milliseconds.
*/
export function getCurrentIntervalMs() {
return Math.max(BASE_INTERVAL_MS - level * INTERVAL_STEP_MS, MIN_INTERVAL_MS);
return Math.max(Math.round(BASE_INTERVAL_MS * (INTERVAL_DECAY_RATE ** level)), MIN_INTERVAL_MS);
}

// ── Getters ───────────────────────────────────────────────────────────────────
Expand Down
4 changes: 2 additions & 2 deletions app/games/otter-stop/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
}

.os-stimulus__img {
max-width: min(440px, calc(50vmin - 60px));
max-height: min(440px, calc(50vmin - 60px));
width: 100%;
height: 100%;
object-fit: contain;
transition: opacity 0.08s ease;
pointer-events: none;
Expand Down
8 changes: 4 additions & 4 deletions app/games/otter-stop/tests/game.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -510,16 +510,16 @@ describe('getCurrentIntervalMs()', () => {
expect(getCurrentIntervalMs()).toBe(1500);
});

it('returns 1450 at level 1 (after 3 correct no-go inhibitions)', () => {
it('returns 1320 at level 1 (after 3 correct no-go inhibitions)', () => {
recordResponse(true, false);
recordResponse(true, false);
recordResponse(true, false);
expect(getCurrentIntervalMs()).toBe(1450);
expect(getCurrentIntervalMs()).toBe(1320); // Math.round(1500 * 0.88)
});

it('returns 150 at a high level (floor clamped)', () => {
// 3 no-go correct per level, need level 27 → (1500 - 27*50 = 150)
for (let i = 0; i < 81; i += 1) recordResponse(true, false);
// 3 no-go correct per level; floor is reached at level 19 (Math.round(1500 * 0.88^19) < 150)
for (let i = 0; i < 57; i += 1) recordResponse(true, false);
expect(getCurrentIntervalMs()).toBe(150);
});

Expand Down
Loading