// TAP educational scenario v1.0. Dimensionless and deliberately not fitted to physiology. // A fixed-demand task separates external demand, a regulatory response and task function. // Changing these assumptions changes the result; no parameter estimates a person's health. export const adaptationScenarioVersion = "1.0"; export const scenarioIds = ["unexpected", "anticipated", "false-alarm", "persistent"] as const; export type ScenarioId = (typeof scenarioIds)[number]; export type ScenarioPoint = { time: number; demand: number; response: number; function: number }; export const scenarioDefaults: ScenarioId = "unexpected"; export function isScenarioId(value: string): value is ScenarioId { return scenarioIds.some((id) => id === value); } export function adaptationScenario(id: ScenarioId): ScenarioPoint[] { if (!isScenarioId(id)) throw new Error("Unknown educational scenario"); let response = 0; return Array.from({ length: 101 }, (_, time) => { const demand = id !== "false-alarm" && time >= 30 && time < 60 ? 0.65 : 0; // Advance notice starts preparation at t=20. A cancelled task is recognized at t=30. const advance = (id === "anticipated" || id === "false-alarm") && time >= 20 && time < 30; const target = advance ? 0.65 : demand; const tau = target > response ? 4 : id === "persistent" ? 15 : 4; response += (target - response) * (1 - Math.exp(-1 / tau)); // Explicit toy assumption: insufficient matching impairs task function; excess activation // also carries an immediate penalty. Neither term measures allostatic load or reserve. const shortfall = Math.max(0, demand - response); const excess = Math.max(0, response - demand); const taskFunction = 1 - 0.7 * shortfall - 0.22 * excess; return { time, demand, response, function: taskFunction }; }); } export function scenarioPath(points: ScenarioPoint[], key: "demand" | "response" | "function") { return points.map((p, i) => `${i ? "L" : "M"}${(48 + p.time * 7.84).toFixed(2)},${(124 - p[key] * 84).toFixed(2)}`).join(" "); }