wordle-five: the engine, the reward, the solver and the probe that checks them
The Python is the source of truth; src/demos/wordle/engine.ts will be a port of it, and CI gates the two against a SHA-256 over all 21.2M (guess, answer) pattern pairs rather than a hand-picked vector file — a vector file only ever catches the cases somebody thought of. The reward is three weighted components, and the third one is the reason this demo is worth building. `solved` and `economy` pull toward winning. `consistency` pulls against them, because a player maximising information deliberately guesses words that cannot win — a word that splits the remaining candidates evenly teaches more than a word that might happen to be right. That is good play, and it costs consistency. The probe ladder proves the tension is real rather than asserted: inaction 0.0000 crude 0.0111 plausible 0.1224 candidate_only 0.8925 exhaustive 0.9031 oracle 0.9458 The two good policies are 0.05 apart and neither dominates — the entropy oracle takes 1.00 economy and 0.73 consistency, the candidate-only player takes 0.75 and 1.00. Which one wins is a decision about what you want, which is the whole argument the site exists to make. probe.py fails CI if either starts dominating. Two traps found by building it. `consistency` is scored over turns SPENT, not guesses accepted: counting only legal guesses hands a free 1.0 to a policy that plays one word and then jams the parser five times — one guess, no contradictions, perfect score. And `economy`'s denominator is the depth the SHIPPED solver reaches, not a depth-optimal search: entropy-greedy is not depth-optimal, so grading it against an exact optimum would make the oracle rung fail its own assertion on some seeds. The word lists are built from Wordnik (MIT) intersected with SCOWL, never from the original game's 2,315 answers. 4,603 answers makes this materially harder than the original, so the published SALET/3.4212 results are cited as belonging to that list and our own reference player's TARES/3.72 is measured here. verifiers is an optional extra. The engine, reward, solver and probe all run — and gate — without an RL stack resolvable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { Component } from 'react';
|
||||
import type { ErrorInfo, ReactNode } from 'react';
|
||||
import { AlertTriangle, ExternalLink, RotateCcw } from 'lucide-react';
|
||||
|
||||
const REPO_URL = 'https://github.com/karti-ai/PIG-Demo';
|
||||
|
||||
export interface DemoErrorBoundaryProps {
|
||||
children: ReactNode;
|
||||
/** Named in the fallback copy, so the visitor knows what broke. */
|
||||
demoTitle?: string;
|
||||
/** Link to the exact source, if the caller knows it. Falls back to the repo. */
|
||||
sourceHref?: string;
|
||||
/** Called when the visitor asks to try again; use it to reset shell state. */
|
||||
onReset?: () => void;
|
||||
}
|
||||
|
||||
interface DemoErrorBoundaryState {
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* One broken demo must never take the site down.
|
||||
*
|
||||
* This is a class because there is still no hook for `componentDidCatch`; that
|
||||
* is the entire reason for the exception to the function-component rule here.
|
||||
*
|
||||
* The fallback is deliberately calm and specific. A site whose pitch is
|
||||
* "here are the receipts" cannot answer a crash with a shrug: it names the
|
||||
* demo, links the source, and lets the visitor retry without a full reload.
|
||||
*/
|
||||
export class DemoErrorBoundary extends Component<DemoErrorBoundaryProps, DemoErrorBoundaryState> {
|
||||
override state: DemoErrorBoundaryState = { error: null };
|
||||
|
||||
static getDerivedStateFromError(error: Error): DemoErrorBoundaryState {
|
||||
return { error };
|
||||
}
|
||||
|
||||
override componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
// No telemetry endpoint on a static site, and none is wanted. The console
|
||||
// is the only place a maintainer can see this, so keep the component stack.
|
||||
console.error('[pig-demo] a demo surface threw', error, info.componentStack);
|
||||
}
|
||||
|
||||
private handleReset = () => {
|
||||
this.setState({ error: null });
|
||||
this.props.onReset?.();
|
||||
};
|
||||
|
||||
override render() {
|
||||
const { error } = this.state;
|
||||
if (!error) return this.props.children;
|
||||
|
||||
const { demoTitle, sourceHref } = this.props;
|
||||
return (
|
||||
<div role="alert" className="card mx-auto my-10 max-w-xl p-6">
|
||||
<div className="flex items-center gap-2 text-warning">
|
||||
<AlertTriangle className="h-5 w-5" aria-hidden="true" />
|
||||
<h2 className="text-base font-semibold">
|
||||
{demoTitle ? `${demoTitle} failed to render` : 'This demo failed to render'}
|
||||
</h2>
|
||||
</div>
|
||||
<p className="mt-3 text-sm leading-relaxed text-muted">
|
||||
Something in this demo threw while drawing. The rest of the site is unaffected — every
|
||||
other demo is a separate module. The environment and the recorded runs behind this page
|
||||
are in the repository either way, and you can run them yourself.
|
||||
</p>
|
||||
<p className="mt-3 break-words rounded-lg bg-surface-2 px-3 py-2 font-mono text-xs text-muted">
|
||||
{error.message || 'Unknown error'}
|
||||
</p>
|
||||
<div className="mt-5 flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.handleReset}
|
||||
className="tap inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors duration-2 ease-enter hover:bg-primary/90"
|
||||
>
|
||||
<RotateCcw className="h-4 w-4" aria-hidden="true" />
|
||||
Try again
|
||||
</button>
|
||||
<a
|
||||
href={sourceHref ?? REPO_URL}
|
||||
className="tap inline-flex items-center gap-2 rounded-lg border border-border px-4 py-2 text-sm font-medium transition-colors duration-2 ease-enter hover:bg-surface-2"
|
||||
>
|
||||
Read the source
|
||||
<ExternalLink className="h-4 w-4" aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user