Are you overconfident? Take the test!
import React, { useMemo, useState } from "react";
import { createRoot } from "react-dom/client";
interface Question {
question: string;
answer: number;
unit: string;
}
interface InsightData {
questions: Question[];
}
const INITIAL_QUESTIONS: Question[] = tb.insight<InsightData>()?.questions ?? [];
type GameState = "start" | "playing" | "finished";
function App() {
const [state, setState] = useState<GameState>("start");
const [currentIndex, setCurrentIndex] = useState(0);
const [inputs, setInputs] = useState({ low: "", high: "" });
const [results, setResults] = useState<{ low: number; high: number; correct: boolean }[]>([]);
const [questions, setQuestions] = useState<Question[]>(INITIAL_QUESTIONS);
const [isInferring, setIsInferring] = useState(false);
const [inferError, setInferError] = useState<string | null>(null);
const handleStart = () => {
setState("playing");
setCurrentIndex(0);
setResults([]);
setInputs({ low: "", high: "" });
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const low = parseFloat(inputs.low);
const high = parseFloat(inputs.high);
const actual = questions[currentIndex].answer;
const isCorrect = actual >= low && actual <= high;
const newResults = [...results, { low, high, correct: isCorrect }];
setResults(newResults);
if (currentIndex < questions.length - 1) {
setCurrentIndex(currentIndex + 1);
setInputs({ low: "", high: "" });
} else {
setState("finished");
}
};
const correctCount = results.filter((r) => r.correct).length;
const targetCount = Math.round(questions.length * 0.9);
const calibrationLevel = () => {
if (correctCount === targetCount) return "Perfectly Calibrated!";
return correctCount > targetCount
? "Underconfident (Too many correct)."
: "Overconfident (Too many incorrect).";
};
const progressLabel = useMemo(
() => `Question ${currentIndex + 1} of ${questions.length}`,
[currentIndex, questions.length]
);
const runInference = async () => {
setIsInferring(true);
setInferError(null);
try {
const insight = await tb.infer<InsightData>();
if (!insight?.questions?.length) return;
setQuestions(insight.questions);
setState("start");
setCurrentIndex(0);
setResults([]);
setInputs({ low: "", high: "" });
} catch (err) {
setInferError("Inference failed. Please try again.");
} finally {
setIsInferring(false);
}
};
if (state === "start") {
return (
<div className="container">
<button className="inference-trigger" onClick={runInference} disabled={isInferring}>
{isInferring ? "Generating..." : "Create new questions for topic..."}
</button>
{inferError && <p className="error">{inferError}</p>}
<h1>Are You Overconfident?</h1>
<p>Provide a range for each such that you are 90% certain the answer is within it.</p>
<p className="goal-text"><b>Goal: Get {targetCount} / {questions.length} correct</b></p>
<button onClick={handleStart}>Start Quiz</button>
</div>
);
}
if (state === "playing") {
const q = questions[currentIndex];
return (
<div className="container">
<button className="inference-trigger" onClick={runInference} disabled={isInferring}>
{isInferring ? "Generating..." : "Create new questions for topic..."}
</button>
<div className="progress">{progressLabel}</div>
<h2>{q.question}</h2>
<form onSubmit={handleSubmit} className="input-group">
<div className="field">
<label>Lower Bound ({q.unit})</label>
<input
type="number"
value={inputs.low}
onChange={(e) => setInputs({ ...inputs, low: e.target.value })}
required
step="any"
autoFocus
/>
</div>
<div className="field">
<label>Upper Bound ({q.unit})</label>
<input
type="number"
value={inputs.high}
onChange={(e) => setInputs({ ...inputs, high: e.target.value })}
required
step="any"
/>
</div>
<button type="submit" disabled={parseFloat(inputs.low) > parseFloat(inputs.high)}>
Submit Range
</button>
</form>
</div>
);
}
return (
<div className="container">
<button className="inference-trigger" onClick={runInference} disabled={isInferring}>
{isInferring ? "Generating..." : "Create new questions for topic..."}
</button>
<h1>Results</h1>
<div className="score-summary">
<div className="score-big">{correctCount} / {questions.length}</div>
<p className="calibration-text">{calibrationLevel()}</p>
</div>
<div className="results-list">
{questions.map((q, i) => (
<div key={i} className={`result-item ${results[i].correct ? "correct" : "incorrect"}`}>
<div className="result-q">{q.question}</div>
<div className="result-details">
<span>Range: {results[i].low} - {results[i].high}</span>
<span className="actual">Actual: <strong>{q.answer}</strong></span>
</div>
</div>
))}
</div>
<button onClick={handleStart} className="restart-btn">Try Again</button>
</div>
);
}
const container = document.getElementById("root");
const root = createRoot(container!);
root.render(<App />);