Spaces:
Running
Running
File size: 1,416 Bytes
3c124f3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import type { Decision, EventDef, EventScores, FairyState, PuckMode } from "./types";
import { clamp } from "./util";
/** Personality bends the raw event scores before the decision bands see them. */
export function score(ev: EventDef, fs: FairyState): EventScores {
const s = { ...ev.base };
// protectiveness raises the bar for annoyance; mischief lowers Puck's filter
s.annoyance = clamp(s.annoyance + (fs.protectiveness - 50) * 0.25 - (fs.mischief - 50) * 0.15);
s.interest = clamp(s.interest + (fs.curiosity - 50) * 0.2 + (fs.mischief - 50) * 0.15);
s.relevance = clamp(s.relevance + (fs.protectiveness - 50) * 0.1);
return s;
}
/** Decision bands → rungs of the interruption ladder. */
export function decide(s: EventScores): Decision {
const worth = s.relevance + s.urgency + s.interest - s.annoyance;
if (s.urgency >= 80 && s.relevance >= 80) return "interrupt";
if (s.relevance < 35 && s.annoyance > 60) return "ignore";
if (worth > 180) return "notify";
if (worth > 120) return "glow";
if (worth > 70) return "scroll";
return "ignore";
}
/** Mode overrides applied after scoring + learned bias (mirrors applyBias). */
export function applyMode(decision: Decision, mode: PuckMode): Decision {
if (mode === "silent" && (decision === "notify" || decision === "interrupt")) return "scroll";
if (mode === "goblin" && decision === "glow") return "notify";
return decision;
}
|