The payoff mechanism builds a recent, weighted estimate of how much pursuing each kind of goal helped. The prior, decay, and floor are supplied choices. This is implementation evidence, not a performance result.
Inspect the source excerpt
class Payoff:
"""How much each kind of want has been worth lately."""
def __init__(self, attempts: list | None = None, method: str | None = None) -> None:
self.by_type: dict[str, float] = {}
self._build(attempts or [], method)
@classmethod
def from_ledger(cls, ledger) -> "Payoff":
return cls(list(getattr(ledger, "attempts", [])),
method=getattr(ledger, "CURRENT_METHOD", None))
def _build(self, attempts: list, method: str | None) -> None:
finished: dict[str, list] = {}
for a in attempts:
helped = getattr(a, "helped", None)
if helped is None:
continue
# Failures from a way of looking she no longer uses say nothing
# about the goal -- the same reasoning as fruitless_run.
if method is not None and getattr(a, "via", None) != method:
continue
finished.setdefault(str(getattr(a, "goal_type", "")), []).append(a)
for kind, group in finished.items():
weighted = 0.0
total = 0.0
# Most recent first, so age is position in the list.
for age, a in enumerate(reversed(group)):
w = 0.5 ** (age / HALF_LIFE)
before = float(getattr(a, "score_before", 0.0) or 0.0)
after = float(getattr(a, "score_after", 0.0) or 0.0)
weighted += w * improvement_of(before, after)
total += w
if total <= 0:
continue
mean = weighted / total
# Shrunk toward the prior, so a single lucky attempt does not
# convince her of anything.
shrunk = ((PRIOR_STRENGTH * PRIOR_IMPROVEMENT + total * mean)
/ (PRIOR_STRENGTH + total))
self.by_type[kind] = _multiplier(shrunk)
def for_type(self, goal_type) -> float:
"""The factor this kind of want has earned. 1.0 when she has no view."""
kind = getattr(goal_type, "value", goal_type)
return self.by_type.get(str(kind), 1.0)
def __bool__(self) -> bool:
return bool(self.by_type)