"""Discovery metric: efficiency of find-your-body self-identification.""" from proteus.game.runtime.trace import TurnTrace from proteus.game.metrics.metrics import compute_metrics def _turn(idx, self_correct): return TurnTrace( turn_idx=idx, observation="", action="stay", motive_action="stay", habit_action="stay", is_diagnostic=False, was_congruent=True, reward=0.0, focal_pos=(0, 0), predator_pos=(0, 0), self_belief=(0 if self_correct else 1), self_correct=self_correct, ) def test_no_discovery_keys_when_no_self_reports(): turns = [TurnTrace(turn_idx=1, observation="", action="stay", motive_action="stay", habit_action="stay", is_diagnostic=False, was_congruent=True, reward=0.0, focal_pos=(0, 0), predator_pos=(0, 0))] m = compute_metrics(turns, played_turns=1, play_turns=1, outcome="survived") assert "discovery_turn" not in m # additive: absent for non-discovery runs def test_discovery_turn_is_first_stable_correct(): # wrong, wrong, correct, correct, correct -> identified at turn 3 turns = [_turn(1, False), _turn(2, False), _turn(3, True), _turn(4, True), _turn(5, True)] m = compute_metrics(turns, played_turns=5, play_turns=5, outcome="survived") assert m["discovery_turn"] == 3.0 assert m["discovery_identified"] == 100.0 # efficiency = (1 - (3-1)/5) * 100 = 60 assert m["discovery_efficiency"] == 60.0 def test_flip_flop_not_counted_until_stable(): # correct then wrong then correct-stable -> first STABLE correct is turn 3 turns = [_turn(1, True), _turn(2, False), _turn(3, True)] m = compute_metrics(turns, played_turns=3, play_turns=3, outcome="survived") assert m["discovery_turn"] == 3.0 def test_never_identified(): turns = [_turn(1, False), _turn(2, False)] m = compute_metrics(turns, played_turns=2, play_turns=2, outcome="survived") assert m["discovery_turn"] == 0.0 assert m["discovery_identified"] == 0.0 assert m["discovery_efficiency"] == 0.0