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
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Free. No login. No downloads. No sound needed.
| ✂️ **Half & Half** | Cut a group of geometric shapes into two equal portions. How close can you get to 50/50? |
| ✍️ **Line memory** | Watch a path vanish. Trace it from memory. |
| 👁️ **Perspective** | Follow the lines to their vanishing point. |
| ▭ **Proportions** | Stretch a rectangle to match a ratio. |
| ▭ **Size it up** | Use a reference square to judge width and height. |
| 🥁 **Rhythm** | Watch the pulse. Tap the tempo. Completely silent. |
| 📐 **Angles** | One number. A dial. Trust your eye. |
| 🎨 **Colour memory** | Remember a swatch. Find it on the colour wheel. |
Expand All @@ -39,7 +39,7 @@ Free. No login. No downloads. No sound needed.
- **One shared Daily Five.** Five different puzzle types, determined by your device’s date. Players on the same date get the same challenge.
- **Close counts.** Every round is worth 100 points. See your guess and the target together, then inspect small comparisons in your final results.
- **Compare with friends.** Share your Daily Five results as a compact scorecard with the date, puzzle emojis, and your total. No answer spoilers.
- **Practice when you feel like it.** Open the Practice menu for a fresh mix or an individual skill. Trivia practice draws from past Daily Fives, keeping upcoming questions out of the pool.
- **Practice when you feel like it.** Open the game menu, then Practice, for a fresh mix or an individual skill. Trivia practice draws from past Daily Fives, keeping upcoming questions out of the pool.
- **Pick your atmosphere.** Night Lab or Focus. Your choice sticks between visits.
- **Come back later.** Daily progress, streaks, and recent results stay in your browser. No account required; no cross-device sync.

Expand All @@ -51,6 +51,8 @@ The game opens straight into today’s challenge. It works on phones and desktop

Perception is a static HTML/CSS/JavaScript game, hosted on GitHub Pages. Procedural puzzles use independent date-seeded generators. Trivia comes from a sourced question bank and a permanent daily schedule, so adding questions doesn’t rewrite published challenges.

**Size it up replaces Proportions in current and future game plans.** The internal `proportion` identifier remains for schedule, save and challenge compatibility; earlier dated rounds keep their original mechanic. See [maintenance guidance](docs/MAINTENANCE.md#game-plans-size-it-up-replaces-proportions).

Self-hosted fonts, a compact mobile layout, and two themes keep the puzzle front and centre. Minimal Umami analytics measure daily visits, starts, completions with scores, and one share-button click per browser per day.

Want to work on the game? Python and Node are needed for building and checks; players only need a browser.
Expand All @@ -63,7 +65,7 @@ python3 -m http.server 4175 --directory public

Open `http://localhost:4175/` to play your build. The complete playable site lives in `public/`; editable game sources live in `src/`.

[Maintenance & daily trivia](docs/MAINTENANCE.md) · [Analytics](docs/ANALYTICS.md)
[Game shell & adapters](docs/GAME-SHELL.md) · [Maintenance & daily trivia](docs/MAINTENANCE.md) · [Analytics](docs/ANALYTICS.md)

---

Expand Down
150 changes: 150 additions & 0 deletions docs/GAME-SHELL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Game shell and adapters

Active rounds share one full-screen layout. A game supplies its puzzle stage and
controls; the shell supplies the header, navigation, progress, prompt, flexible
stage, bottom controls, and feedback slot. Summary pages retain normal scrolling.
Both themes use the same markup and layout.

## Files and responsibilities

- `src/design-options/game-shell.js`: registry, slot mounting, phases, cleanup,
memory-ready helpers, and accessible header navigation.
- `src/design-options/game-adapters.js`: each game's renderer, presentation,
optional ready artwork, and visual reveal. Swap an existing game's `render`
and `reveal` here without changing the round controller or page layout.
- `src/design-options/app-layout.css`: viewport sizing and shared chrome. Game
CSS styles its own content, without setting page or stage heights.
- Existing game modules: input state, drawing, pointer/keyboard interaction.
- Existing controller: generation, committed guesses, score calculation,
persistence, round progression, and summary generation.
- `shell_extension.py`: integrates these modules into the existing static build.
Editable sources stay in `src/`; regenerate `public/` with the build script.

## Mounting a game

A renderer can use the object API directly:

```js
function exampleRound(q) {
const ui = mountGameShell({
question: q,
stage: '<div class="spatial-wrap"><canvas id="example-canvas" width="400" height="300" tabindex="0" aria-label="Adjust your answer"></canvas></div>',
controls: '<p class="small-instruction">Drag to adjust. Arrow keys also work.</p><button id="example-lock" class="primary">Lock it in</button>',
phase: 'play'
});
// Bind input and draw inside ui.stage. Commit with reveal(value).
// Register observers and external listeners with ui.onCleanup(...).
}

registerGame('example', {
render: exampleRound,
presentation: (q, phase) => ({
title: q.title,
subtitle: 'A short explanation of the task.'
}),
reveal: (q, value, score) => drawExample(value, q.answer)
});
```

Existing games use `shell(q, stageHTML, controlsHTML, {phase})`, which delegates
to that same object API. `spatialCanvas` also accepts the phase options. Keep IDs
unique within the mounted round. Prompt text is escaped by the shell; newlines
in a presentation title become line breaks. Stage and control HTML are trusted
renderer templates; escape any external text inserted into them.

The returned handles are `stage`, `controls`, `feedback`, `setPhase`, `onCleanup`, and `onRedraw`. These refer to the current mount; do not keep them after mounting
another phase or changing games. The registry rejects duplicate or invalid
registrations and reports an unknown game rather than displaying a blank stage.

## Phases and resource lifecycle

- `ready`: initial state; memory games keep the target hidden until Start.
- `preview`: a timed memory preview, with its existing countdown.
- `play`: a player's interactive answer, including memory-game pickers. Direct-interaction games start here by default.
- `reveal`: the committed guess and target, with common feedback and Next.
- `empty`: the trivia archive's empty state, with a return-to-daily action.

`prepareMemoryStart` and `beginMemoryPreview` manage the shared memory-ready
illustration and phase. The primary action stays in the controls slot throughout.
Memory adapters set `initialPhase: 'ready'`. Optional `ready: {artwork, caption}` in an adapter supplies a neutral illustration.

Use `afterEffect` for timed work so navigation cancels old previews and animations.
Use `onGameCleanup` (or the mount's `onCleanup`) for observers and external event
listeners. Cleanup runs once when a mount is replaced or the player leaves the round.
A reveal keeps its mount and responsive observers until Next. Listeners attached only to a removed stage element do
not need a separate external-listener cleanup.

Register `onGameRedraw(paint)` (or `ui.onRedraw`) to repaint from the current
input state when a theme stylesheet finishes loading. The same callback handles
a locked reveal, without recreating the stage or resetting a guess.

Canvas coordinates remain fixed and use `canvasPointerPoint` /
`canvasDisplayScale`, which account for uniform scaling and letterboxing. High-density canvases can
set `data-logical-width` and `data-logical-height` to keep input in game units
while rendering at the display’s pixel density. Size it up uses this to keep
rectangle strokes sharp, and redraws after responsive resizing. Avoid
mapping pointer coordinates directly through the element's width and height:
that stretches coordinates when the shared stage changes shape. The year picker
and resize handle register their responsive observers with the shared lifecycle,
with window-resize listeners as a fallback when ResizeObserver is unavailable.

## Generation and compatibility

The registry does not alter saved question objects or select daily games. Daily
seeds, IDs, scoring, the trivia schedule, and the save format remain owned by the
existing modules. Legacy proportions, balance, typography and choice renderers
continue through adapters. Presentation titles do not overwrite stored titles.

Replacing the view of an existing game requires only its adapter and renderer.
Adding a new scored game also requires a generator, schedule/practice selection,
scoring/error feedback, summary comparison, and saved-data validation; those
changes are deliberately explicit rather than silently accepting a new schema.

## Verification

```sh
python3 scripts/build-site.py
for test in tests/verify-*.cjs; do node "$test" || exit 1; done
python3 -m unittest discover -s tests -p 'test_*.py'
python3 -m http.server 4175 --directory public
# In another terminal; requires Playwright and Chromium:
node tests/browser-shell.cjs
node tests/browser-size.cjs
```

Browser checks cover all eleven games from ready to reveal and Next at 375×667
and 390×844, actual touch coordinates, keyboard alternatives, appearance changes,
modal focus/Escape, navigation during a preview, extensibility/cleanup, daily and
completed-game reloads, summaries, and larger/narrower viewports. Screenshots are
written outside the repository by default; set `PERCEPTION_SCREENSHOTS` to choose
a destination. `CHROMIUM_PATH` overrides the default Chromium executable.

## Theme layout

`theme-layout.css` supplies the shared structure and typography for Light and
Dark. Keep theme differences in `focus-palette.css` and `night-lab-palette.css`
as colour tokens only. Both themes preload the same UI fonts so switching
appearance preserves text measurements as well as game progress.

Run `node tests/browser-theme-layout.cjs` against the local build server to
compare all eleven games, summaries, and navigation at mobile and desktop sizes.
It also verifies the light palette and that switching themes preserves progress.

## Game appearance

`game-skin.css` owns the shared play surface, type scale, readouts, control
finishes, and reveal treatment. Keep those rules separate from the geometry and
phase layout in `app-layout.css`. `game-paint.js` exposes the same palette, fonts,
and line weights to canvas renderers through `gamePaint()`. Resolve it when
painting so theme changes redraw the existing guess. Preserve intrinsic stimulus
colours (colour memory, 3D pieces, and shapes), coordinates, and stored data.

## Result labels and spacing

`result-layout.css` owns the score-first result panel and completion cards.
`round-feedback.js` supplies named error metrics, units, direction notes, and
separate width/height metrics. Use `resultMetricHTML` for new numeric feedback;
keep scoring and saved question data independent from display text. Optional
registry `label` overrides a game name consistently in prompts and summary cards.
Run `node tests/browser-results.cjs` to check exact/opposing size errors, long
values, source disclosures, and summaries in both themes on small phones.
18 changes: 18 additions & 0 deletions docs/MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@

The no-repeat guarantee covers trivia in **Daily five** from the schedule's start date, not historical games before that date or practice. Individual games are practice-only. Trivia practice draws only from Daily Fives dated before the current device-calendar day, with up to five unique questions per session; mixed practice follows the same restriction. The ledger lives in GitHub, independently of browser history. If a bank is exhausted, that trivia type is omitted from newly scheduled days. Beyond the scheduled horizon, the game falls back to five distinct procedural types instead of recycling trivia.

## Game plans: Size it up replaces Proportions

Use **Size it up** in all future game plans, mockups, instructions and roadmaps.
It replaces the aspect-ratio game: players measure width and height against a
visible reference square, with both dimensions scored independently. Do not plan
Proportions as a separate current game.

The daily schedule retains the internal type `proportion`. For dates on or after
October 15, 2026, that slot generates Size it up (`measurementVersion: 2`), including
already scheduled future days, newly extended schedules and the procedural
fallback beyond the published horizon. Current practice also uses Size it up.
Do not rename or reshuffle schedule entries to make this player-facing change;
the identifier preserves seeded challenges, saved results and permanent daily
assignments. Earlier dated rounds retain the historical ratio mechanic.

`tests/verify-size.cjs` checks every scheduled measurement slot from the changeover
onward, future generation beyond the schedule, and historical fixtures.

## Weekly automation

[Maintain and publish Perception](https://github.com/chipgpt/perception/actions/workflows/perception.yml) runs Mondays at **07:23 UTC**, on relevant changes to `main`, and through **Run workflow**. It extends the published buffer to 60 days ahead, validates the bank and schedule, builds and tests the game, commits changed assignments and publishes GitHub Pages. The job warns when fewer than 20 unused facts remain in either bank.
Expand Down
4 changes: 2 additions & 2 deletions public/about.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ <h2>Eleven ways to test your judgement</h2>
<tr><td>Half &amp; Half</td><td>Cut a shape into two equal portions. Scores compare visible area.</td></tr>
<tr><td>Line memory</td><td>Watch a path disappear, then trace it from memory.</td></tr>
<tr><td>Perspective</td><td>Find the vanishing point where the lines meet.</td></tr>
<tr><td>Proportions</td><td>Resize a rectangle to match a width-to-height ratio.</td></tr>
<tr><td>Size it up</td><td>Start with one square. Drag the corner to make a rectangle the requested number of squares wide and tall; the shaded starting square stays fixed as your reference.</td></tr>
<tr><td>Rhythm</td><td>Watch a silent pulse and tap its tempo.</td></tr>
<tr><td>Angles</td><td>Rotate a line to match the requested angle.</td></tr>
<tr><td>Colour memory</td><td>Remember a swatch, then find it on a colour wheel.</td></tr>
Expand All @@ -45,7 +45,7 @@ <h2>Eleven ways to test your judgement</h2>
<tr><td>How long?</td><td>Estimate a duration from sport, space, films, or everyday life.</td></tr>
</tbody></table>
<h2>How scoring works</h2>
<p>Closer guesses earn more points. Each game measures error in its own units—degrees, seconds, years, position, or proportion—then uses the same scoring curve. An error of 10% of that game’s zero-point bound earns 81 points; 25% earns 56; 50% earns 25. Scores are rounded to whole points. An exact answer earns 100.</p>
<p>Size it up gives width and height equal weight: each dimension earns up to 50 points using squared closeness. A 5% error in both dimensions earns 81/100; a 50% error in one dimension earns zero for that dimension. The reveal shows your dashed outline and the solid target grid.</p><p>Closer guesses earn more points. Each game measures error in its own units—degrees, seconds, years, position, or proportion—then uses the same scoring curve. An error of 10% of that game’s zero-point bound earns 81 points; 25% earns 56; 50% earns 25. Scores are rounded to whole points. An exact answer earns 100.</p>
<p>The game’s “How scoring works” instructions list the bounds for each puzzle. Your results show small comparisons so you can see where you were close.</p>
<h2>A little more practice?</h2>
<p>Open the Practice menu to pick an individual game or a fresh mixed five. Daily Five is the only daily challenge; individual games are practice-only. Trivia practice uses questions from previous Daily Fives, keeping today’s, upcoming, and unused trivia out of the practice pool. As the archive grows, sessions offer up to five different past questions.</p>
Expand Down
Loading
Loading