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;
}