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)
