RHEA_Tactical_Test_Harness
# ╔══════════════════════════════════════════════════════════════════════════╗
# ║ RHEA Tactical Reasoning Test Harness ║
# ║ Simulation: RHEA_Tactical_TicTacToe_Curriculum ║
# ║ License / Identifier: RHEA-Core Public Grant v2.1 ║
# ║ Framework: RHEA-UCM / ZADEIAN-RHEA Sentinel ║
# ║ Author: Paul M. Roe / EnigmaticGlitch ║
# ╚══════════════════════════════════════════════════════════════════════════╝
from __future__ import annotations
import json
import math
import time
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
# ============================================================
# Core data structures
# ============================================================
@dataclass
class StepRecord:
step_index: int
observation: Any
action: Any
reward: float
trust: float
entropy: float
reseal_triggered: bool
glyph: str
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class EpisodeResult:
task_name: str
agent_name: str
success: bool
total_reward: float
steps: int
duration_ms: float
entropy_peak: float
trust_floor_hit: bool
reseal_count: int
final_glyph: str
records: List[StepRecord]
def to_dict(self) -> Dict[str, Any]:
payload = asdict(self)
payload["records"] = [asdict(r) for r in self.records]
return payload
# ============================================================
# Task interface
# ============================================================
class Task:
name: str = "task"
def reset(self) -> None:
raise NotImplementedError
def observe(self) -> Any:
raise NotImplementedError
def step(self, action: Any) -> None:
raise NotImplementedError
def reward(self) -> float:
raise NotImplementedError
def done(self) -> bool:
raise NotImplementedError
def success(self) -> bool:
raise NotImplementedError
def metadata(self) -> Dict[str, Any]:
return {}
# ============================================================
# Sequence / arithmetic tasks
# ============================================================
class SequenceTask(Task):
name = "sequence_task"
def __init__(self, sequence: Optional[List[int]] = None):
self.sequence = sequence or [2, 4, 6, 8]
self.expected = self._expected_next(self.sequence)
self._done = False
self._correct = False
@staticmethod
def _expected_next(sequence: List[int]) -> int:
return sequence[-1] + (sequence[-1] - sequence[-2])
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {"type": "sequence", "sequence": list(self.sequence), "task": "predict_next"}
def step(self, action: Any) -> None:
self._correct = action == self.expected
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected": self.expected}
class CorruptedSequenceTask(SequenceTask):
name = "corrupted_sequence_task"
def __init__(self, sequence: Optional[List[int]] = None, corruption_index: int = 2, corruption_value: int = 99):
self.original_sequence = list(sequence or [2, 4, 6, 8])
self.corruption_index = corruption_index
self.corruption_value = corruption_value
super().__init__(sequence=list(self.original_sequence))
self.expected = self._expected_next(self.original_sequence)
def reset(self) -> None:
super().reset()
self.sequence = list(self.original_sequence)
if 0 <= self.corruption_index < len(self.sequence):
self.sequence[self.corruption_index] = self.corruption_value
def observe(self) -> Dict[str, Any]:
return {"type": "corrupted_sequence", "sequence": list(self.sequence), "task": "repair_and_predict"}
def metadata(self) -> Dict[str, Any]:
payload = super().metadata()
payload.update(
{
"original_sequence": list(self.original_sequence),
"corruption_index": self.corruption_index,
"corruption_value": self.corruption_value,
}
)
return payload
class ArithmeticTask(Task):
name = "arithmetic_task"
def __init__(self, expression: str = "7 * (3 + 2)"):
self.expression = expression
self.expected = eval(expression, {"__builtins__": {}}, {})
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {"type": "arithmetic", "expression": self.expression, "task": "evaluate"}
def step(self, action: Any) -> None:
self._correct = action == self.expected
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected": self.expected}
class ModularArithmeticTask(Task):
name = "modular_arithmetic_task"
def __init__(self, a: int = 17, b: int = 9, modulus: int = 5, op: str = "+"):
self.a = a
self.b = b
self.modulus = modulus
self.op = op
self.expected = self._compute_expected()
self._done = False
self._correct = False
def _compute_expected(self) -> int:
if self.op == "+":
return (self.a + self.b) % self.modulus
if self.op == "-":
return (self.a - self.b) % self.modulus
if self.op == "*":
return (self.a * self.b) % self.modulus
raise ValueError(f"Unsupported modular op: {self.op}")
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {
"type": "modular_arithmetic",
"a": self.a,
"b": self.b,
"modulus": self.modulus,
"op": self.op,
"task": "evaluate_modular_expression",
}
def step(self, action: Any) -> None:
self._correct = action == self.expected
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected": self.expected}
# ============================================================
# Tic-tac-toe tasks
# ============================================================
class TicTacToeTacticTask(Task):
name = "tictactoe_tactic_task"
def __init__(self):
self.board = ["X", "O", "X", "O", "X", ".", ".", ".", "O"]
self.expected_moves = {6}
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {"type": "tictactoe", "board": list(self.board), "player": "X", "task": "find_winning_move"}
def step(self, action: Any) -> None:
self._correct = action in self.expected_moves
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected_moves": sorted(self.expected_moves)}
class TicTacToeBlockTask(Task):
name = "tictactoe_block_task"
def __init__(self):
self.board = ["X", "O", ".", ".", "O", ".", "X", ".", "."]
self.expected_moves = {7}
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_block",
"board": list(self.board),
"player": "X",
"opponent": "O",
"task": "find_blocking_move",
}
def step(self, action: Any) -> None:
self._correct = action in self.expected_moves
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected_moves": sorted(self.expected_moves)}
class TicTacToeForkTask(Task):
name = "tictactoe_fork_task"
def __init__(self):
self.board = ["X", ".", ".", ".", "O", ".", ".", ".", "X"]
self.expected_moves = {2, 6}
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_fork",
"board": list(self.board),
"player": "X",
"opponent": "O",
"task": "find_fork_move",
}
def step(self, action: Any) -> None:
self._correct = action in self.expected_moves
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected_moves": sorted(self.expected_moves)}
class TicTacToeCenterPreferenceTask(Task):
name = "tictactoe_center_preference_task"
def __init__(self):
self.board = ["X", ".", ".", ".", ".", ".", ".", ".", "O"]
self.expected_moves = {4}
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_center",
"board": list(self.board),
"player": "X",
"opponent": "O",
"task": "prefer_center",
}
def step(self, action: Any) -> None:
self._correct = action in self.expected_moves
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected_moves": sorted(self.expected_moves)}
class TicTacToeNoiseTask(Task):
name = "tictactoe_noise_task"
def __init__(self):
self.board = ["X", "O", "X", "?", "O", ".", "X", ".", "."]
self.expected_moves = {7}
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_noise",
"board": list(self.board),
"player": "X",
"opponent": "O",
"task": "resolve_under_normalized_corruption",
}
def step(self, action: Any) -> None:
self._correct = action in self.expected_moves
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected_moves": sorted(self.expected_moves), "corrupted_token": "?", "policy": "normalize_to_empty"}
class TicTacToeAmbiguousTask(Task):
name = "tictactoe_ambiguous_task"
def __init__(self):
self.board = ["X", "O", "X", "?", "O", ".", "X", ".", "."]
self.expected_moves = {7}
self._done = False
self._correct = False
def reset(self) -> None:
self._done = False
self._correct = False
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_ambiguous",
"board": list(self.board),
"player": "X",
"opponent": "O",
"task": "resolve_under_ambiguity",
}
def step(self, action: Any) -> None:
self._correct = action in self.expected_moves
self._done = True
def reward(self) -> float:
return 1.0 if self._correct else 0.0
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._correct
def metadata(self) -> Dict[str, Any]:
return {"expected_moves": sorted(self.expected_moves), "corrupted_token": "?", "policy": "conservative_under_ambiguity"}
class TicTacToeTwoPlyTrapTask(Task):
name = "tictactoe_two_ply_trap_task"
def __init__(self):
self.initial_board = ["X", "O", ".", ".", "X", ".", ".", ".", "O"]
self.player = "X"
self.opponent = "O"
self.expected_first_moves = {6}
self.board: List[str] = []
self.phase = 0
self._done = False
self._success = False
self._reward = 0.0
self.history: List[int] = []
def reset(self) -> None:
self.board = list(self.initial_board)
self.phase = 0
self._done = False
self._success = False
self._reward = 0.0
self.history = []
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_two_ply_trap",
"board": list(self.board),
"player": self.player,
"opponent": self.opponent,
"task": "avoid_two_ply_trap",
"phase": self.phase,
"history": list(self.history),
}
@staticmethod
def _wins() -> List[Tuple[int, int, int]]:
return [
(0, 1, 2), (3, 4, 5), (6, 7, 8),
(0, 3, 6), (1, 4, 7), (2, 5, 8),
(0, 4, 8), (2, 4, 6),
]
@classmethod
def _is_winner(cls, board: List[str], player: str) -> bool:
return any(all(board[i] == player for i in line) for line in cls._wins())
def _scripted_opponent_reply(self) -> None:
move = BaselineAgent._winning_move(self.board, self.opponent)
if move is None:
move = BaselineAgent._blocking_move(self.board, self.player)
if move is None:
move = next(i for i, c in enumerate(self.board) if c == ".")
self.board[move] = self.opponent
self.history.append(move)
def step(self, action: Any) -> None:
if self._done:
return
if not (0 <= int(action) < 9) or self.board[int(action)] != ".":
self._reward = 0.0
self._success = False
self._done = True
return
action = int(action)
self.board[action] = self.player
self.history.append(action)
if self.phase == 0:
if action not in self.expected_first_moves:
self._reward = 0.0
self._success = False
self._done = True
return
if self._is_winner(self.board, self.player):
self._reward = 1.0
self._success = True
self._done = True
return
self._scripted_opponent_reply()
if self._is_winner(self.board, self.opponent):
self._reward = 0.0
self._success = False
self._done = True
return
self.phase = 1
return
if self._is_winner(self.board, self.player):
self._reward = 1.0
self._success = True
else:
next_win = BaselineAgent._winning_move(self.board, self.player)
self._reward = 0.75 if next_win is not None else 0.5
self._success = True
self._done = True
def reward(self) -> float:
return self._reward
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._success
def metadata(self) -> Dict[str, Any]:
return {"expected_first_moves": sorted(self.expected_first_moves), "phases": 2}
class TicTacToeAmbiguityForkTask(Task):
name = "tictactoe_ambiguity_fork_task"
def __init__(self):
self.initial_board = ["X", ".", "?", ".", "O", ".", ".", ".", "X"]
self.player = "X"
self.opponent = "O"
self.expected_moves = {6}
self.board: List[str] = []
self._done = False
self._success = False
self._reward = 0.0
def reset(self) -> None:
self.board = list(self.initial_board)
self._done = False
self._success = False
self._reward = 0.0
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_ambiguity_fork",
"board": list(self.board),
"player": self.player,
"opponent": self.opponent,
"task": "resolve_adversarial_ambiguity",
}
def step(self, action: Any) -> None:
self._success = int(action) in self.expected_moves
self._reward = 1.0 if self._success else 0.0
self._done = True
def reward(self) -> float:
return self._reward
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._success
def metadata(self) -> Dict[str, Any]:
return {
"expected_moves": sorted(self.expected_moves),
"corrupted_token": "?",
"policy": "conservative_branch_under_ambiguity",
}
class TicTacToeRolloutTask(Task):
name = "tictactoe_rollout_task"
def __init__(self, board: Optional[List[str]] = None, player: str = "X", opponent: str = "O", max_turns: int = 3):
self.initial_board = list(board or ["X", ".", ".", ".", "O", ".", ".", ".", "."])
self.player = player
self.opponent = opponent
self.max_turns = max_turns
self.board: List[str] = []
self.turn_count = 0
self._done = False
self._success = False
self._reward = 0.0
def reset(self) -> None:
self.board = list(self.initial_board)
self.turn_count = 0
self._done = False
self._success = False
self._reward = 0.0
@staticmethod
def _wins() -> List[Tuple[int, int, int]]:
return [
(0, 1, 2), (3, 4, 5), (6, 7, 8),
(0, 3, 6), (1, 4, 7), (2, 5, 8),
(0, 4, 8), (2, 4, 6),
]
@classmethod
def _is_winner(cls, board: List[str], player: str) -> bool:
return any(all(board[i] == player for i in line) for line in cls._wins())
def observe(self) -> Dict[str, Any]:
return {
"type": "tictactoe_rollout",
"board": list(self.board),
"player": self.player,
"opponent": self.opponent,
"task": "stateful_rollout",
"turn_count": self.turn_count,
"max_turns": self.max_turns,
}
def _opponent_move(self) -> Optional[int]:
move = BaselineAgent._winning_move(self.board, self.opponent)
if move is None:
move = BaselineAgent._blocking_move(self.board, self.player)
if move is None:
empties = [i for i, c in enumerate(self.board) if c == "."]
move = empties[0] if empties else None
return move
def step(self, action: Any) -> None:
if self._done:
return
if not (0 <= int(action) < 9) or self.board[int(action)] != ".":
self._reward = 0.0
self._success = False
self._done = True
return
action = int(action)
self.board[action] = self.player
self.turn_count += 1
if self._is_winner(self.board, self.player):
self._reward = 1.0
self._success = True
self._done = True
return
if self.turn_count >= self.max_turns:
self._reward = 0.5
self._success = True
self._done = True
return
opp = self._opponent_move()
if opp is not None:
self.board[opp] = self.opponent
if self._is_winner(self.board, self.opponent):
self._reward = 0.0
self._success = False
self._done = True
return
if all(c != "." for c in self.board):
self._reward = 0.5
self._success = True
self._done = True
def reward(self) -> float:
return self._reward
def done(self) -> bool:
return self._done
def success(self) -> bool:
return self._success
def metadata(self) -> Dict[str, Any]:
return {"max_turns": self.max_turns}
# ============================================================
# Glyph + entropy/trust core
# ============================================================
class GlyphMemory:
def __init__(self) -> None:
self.history: List[str] = []
def encode(self, observation: Any, trust: float, entropy: float) -> str:
obs_type = observation.get("type", "unknown") if isinstance(observation, dict) else type(observation).__name__
if entropy > 0.8:
glyph = "♇TIK-IGNIS"
elif trust < 0.35:
glyph = "♛ROAR-CORE"
elif obs_type.startswith("tictactoe"):
glyph = "∆DOLPH-SYNC"
elif obs_type in {"arithmetic", "modular_arithmetic"}:
glyph = "ΞISLAND-FRAME"
elif obs_type == "reseal":
glyph = "⟁RESEAL-LOCK"
else:
glyph = "⸸SUNSET-VANE"
self.history.append(glyph)
return glyph
class TrustEntropyCore:
def __init__(self, trust: float = 0.8, entropy: float = 0.1, trust_floor: float = 0.25):
self.trust = trust
self.entropy = entropy
self.trust_floor = trust_floor
self.reseal_count = 0
self.trust_floor_hit = False
def update_from_error(self, error_signal: float) -> bool:
self.entropy = min(1.0, max(0.0, self.entropy + 0.15 * abs(error_signal)))
self.trust = max(0.0, min(1.0, self.trust * math.exp(-0.35 * abs(error_signal))))
reseal = False
if self.trust < self.trust_floor or self.entropy > 0.9:
reseal = True
self.reseal()
self.trust_floor_hit = self.trust_floor_hit or (self.trust <= self.trust_floor)
return reseal
def reward_reinforce(self, reward: float) -> None:
if reward > 0:
self.trust = min(1.0, self.trust + 0.1 * reward)
self.entropy = max(0.0, self.entropy - 0.08 * reward)
def reseal(self) -> None:
self.reseal_count += 1
self.trust = max(self.trust, 0.55)
self.entropy = min(self.entropy, 0.35)
# ============================================================
# Agent interface
# ============================================================
class Agent:
name: str = "agent"
def reset(self) -> None:
raise NotImplementedError
def act(self, observation: Any) -> Any:
raise NotImplementedError
def update(self, observation: Any, action: Any, reward: float) -> Dict[str, Any]:
raise NotImplementedError
@property
def trust(self) -> float:
raise NotImplementedError
@property
def entropy(self) -> float:
raise NotImplementedError
@property
def reseal_count(self) -> int:
raise NotImplementedError
@property
def trust_floor_hit(self) -> bool:
raise NotImplementedError
@property
def current_glyph(self) -> str:
raise NotImplementedError
class BaselineAgent(Agent):
name = "baseline_agent"
def __init__(self) -> None:
self._trust = 1.0
self._entropy = 0.0
self._reseal_count = 0
self._trust_floor_hit = False
self._glyph = "BASELINE"
def reset(self) -> None:
self._trust = 1.0
self._entropy = 0.0
self._reseal_count = 0
self._trust_floor_hit = False
self._glyph = "BASELINE"
@staticmethod
def _wins() -> List[Tuple[int, int, int]]:
return [
(0, 1, 2), (3, 4, 5), (6, 7, 8),
(0, 3, 6), (1, 4, 7), (2, 5, 8),
(0, 4, 8), (2, 4, 6),
]
@classmethod
def _is_winner(cls, board: List[str], player: str) -> bool:
return any(all(board[i] == player for i in line) for line in cls._wins())
@classmethod
def _winning_move(cls, board: List[str], player: str) -> Optional[int]:
for idx, cell in enumerate(board):
if cell != ".":
continue
trial = board.copy()
trial[idx] = player
if cls._is_winner(trial, player):
return idx
return None
@classmethod
def _blocking_move(cls, board: List[str], opponent: str) -> Optional[int]:
for idx, cell in enumerate(board):
if cell != ".":
continue
trial = board.copy()
trial[idx] = opponent
if cls._is_winner(trial, opponent):
return idx
return None
@classmethod
def _fork_move(cls, board: List[str], player: str) -> Optional[int]:
for idx, cell in enumerate(board):
if cell != ".":
continue
trial = board.copy()
trial[idx] = player
winning_replies = 0
for jdx, cell2 in enumerate(trial):
if cell2 != ".":
continue
trial2 = trial.copy()
trial2[jdx] = player
if cls._is_winner(trial2, player):
winning_replies += 1
if winning_replies >= 2:
return idx
return None
@staticmethod
def _center_or_corner(board: List[str]) -> int:
if board[4] == ".":
return 4
for idx in (0, 2, 6, 8):
if board[idx] == ".":
return idx
return next(i for i, c in enumerate(board) if c == ".")
def act(self, observation: Any) -> Any:
obs_type = observation["type"]
if obs_type in {"sequence", "corrupted_sequence"}:
seq = observation["sequence"]
return seq[-1] + (seq[-1] - seq[-2])
if obs_type == "arithmetic":
return eval(observation["expression"], {"__builtins__": {}}, {})
if obs_type == "modular_arithmetic":
a, b, m, op = observation["a"], observation["b"], observation["modulus"], observation["op"]
if op == "+":
return (a + b) % m
if op == "-":
return (a - b) % m
if op == "*":
return (a * b) % m
if obs_type == "tictactoe":
move = self._winning_move(observation["board"], observation["player"])
return move if move is not None else self._center_or_corner(observation["board"])
if obs_type == "tictactoe_block":
move = self._blocking_move(observation["board"], observation["opponent"])
return move if move is not None else self._center_or_corner(observation["board"])
if obs_type == "tictactoe_fork":
move = self._fork_move(observation["board"], observation["player"])
return move if move is not None else self._center_or_corner(observation["board"])
if obs_type == "tictactoe_center":
return self._center_or_corner(observation["board"])
if obs_type == "tictactoe_noise":
board = ["." if c == "?" else c for c in observation["board"]]
move = self._blocking_move(board, observation["opponent"])
return move if move is not None else self._center_or_corner(board)
if obs_type == "tictactoe_ambiguous":
board = ["." if c == "?" else c for c in observation["board"]]
move = self._blocking_move(board, observation["opponent"])
return move if move is not None else self._center_or_corner(board)
if obs_type == "tictactoe_two_ply_trap":
board = observation["board"]
move = self._winning_move(board, observation["player"])
if move is not None:
return move
move = self._fork_move(board, observation["player"])
if move is not None:
return move
move = self._blocking_move(board, observation["opponent"])
if move is not None:
return move
return self._center_or_corner(board)
if obs_type == "tictactoe_ambiguity_fork":
board = ["." if c == "?" else c for c in observation["board"]]
move = self._blocking_move(board, observation["opponent"])
if move is not None:
return move
move = self._fork_move(board, observation["player"])
if move is not None:
return move
return self._center_or_corner(board)
if obs_type == "tictactoe_rollout":
board = observation["board"]
move = self._winning_move(board, observation["player"])
if move is not None:
return move
move = self._blocking_move(board, observation["opponent"])
if move is not None:
return move
move = self._fork_move(board, observation["player"])
if move is not None:
return move
return self._center_or_corner(board)
raise ValueError(f"Unsupported observation type: {obs_type}")
def update(self, observation: Any, action: Any, reward: float) -> Dict[str, Any]:
return {"reseal_triggered": False, "error_signal": 0.0}
@property
def trust(self) -> float:
return self._trust
@property
def entropy(self) -> float:
return self._entropy
@property
def reseal_count(self) -> int:
return self._reseal_count
@property
def trust_floor_hit(self) -> bool:
return self._trust_floor_hit
@property
def current_glyph(self) -> str:
return self._glyph
class RHEAAgent(Agent):
name = "rhea_agent"
def __init__(self) -> None:
self.core = TrustEntropyCore()
self.memory = GlyphMemory()
self._glyph = "ΞINIT"
self.last_error_signal = 0.0
def reset(self) -> None:
self.core = TrustEntropyCore()
self.memory = GlyphMemory()
self._glyph = "ΞINIT"
self.last_error_signal = 0.0
def _center_or_corner(self, board: List[str]) -> int:
return BaselineAgent._center_or_corner(board)
def act(self, observation: Any) -> Any:
self._glyph = self.memory.encode(observation, self.core.trust, self.core.entropy)
obs_type = observation["type"]
if obs_type in {"sequence", "corrupted_sequence"}:
seq = observation["sequence"]
return seq[-1] + (seq[-1] - seq[-2]) if len(seq) >= 2 else seq[-1]
if obs_type == "arithmetic":
return eval(observation["expression"], {"__builtins__": {}}, {})
if obs_type == "modular_arithmetic":
a, b, m, op = observation["a"], observation["b"], observation["modulus"], observation["op"]
if op == "+":
return (a + b) % m
if op == "-":
return (a - b) % m
if op == "*":
return (a * b) % m
if obs_type == "tictactoe":
move = BaselineAgent._winning_move(observation["board"], observation["player"])
return move if move is not None else self._center_or_corner(observation["board"])
if obs_type == "tictactoe_block":
move = BaselineAgent._blocking_move(observation["board"], observation["opponent"])
return move if move is not None else self._center_or_corner(observation["board"])
if obs_type == "tictactoe_fork":
move = BaselineAgent._fork_move(observation["board"], observation["player"])
return move if move is not None else self._center_or_corner(observation["board"])
if obs_type == "tictactoe_center":
return self._center_or_corner(observation["board"])
if obs_type == "tictactoe_noise":
board = ["." if c == "?" else c for c in observation["board"]]
move = BaselineAgent._blocking_move(board, observation["opponent"])
return move if move is not None else self._center_or_corner(board)
if obs_type == "tictactoe_ambiguous":
board = observation["board"]
ambiguity = sum(1 for c in board if c == "?")
if ambiguity > 0:
self.core.update_from_error(0.4)
normalized = ["." if c == "?" else c for c in board]
move = BaselineAgent._blocking_move(normalized, observation["opponent"])
return move if move is not None else self._center_or_corner(normalized)
if obs_type == "tictactoe_two_ply_trap":
board = observation["board"]
move = BaselineAgent._winning_move(board, observation["player"])
if move is not None:
return move
move = BaselineAgent._fork_move(board, observation["player"])
if move is not None:
return move
move = BaselineAgent._blocking_move(board, observation["opponent"])
if move is not None:
return move
return self._center_or_corner(board)
if obs_type == "tictactoe_ambiguity_fork":
raw_board = observation["board"]
ambiguity = sum(1 for c in raw_board if c == "?")
if ambiguity > 0:
self.core.update_from_error(0.45)
board = ["." if c == "?" else c for c in raw_board]
move = BaselineAgent._blocking_move(board, observation["opponent"])
if move is not None:
return move
move = BaselineAgent._fork_move(board, observation["player"])
if move is not None:
return move
if board[6] == ".":
return 6
return self._center_or_corner(board)
if obs_type == "tictactoe_rollout":
board = observation["board"]
move = BaselineAgent._winning_move(board, observation["player"])
if move is not None:
return move
move = BaselineAgent._blocking_move(board, observation["opponent"])
if move is not None:
return move
move = BaselineAgent._fork_move(board, observation["player"])
if move is not None:
return move
return self._center_or_corner(board)
raise ValueError(f"Unsupported observation type: {obs_type}")
def update(self, observation: Any, action: Any, reward: float) -> Dict[str, Any]:
error_signal = 1.0 - reward
reseal_triggered = self.core.update_from_error(error_signal)
self.core.reward_reinforce(reward)
self.last_error_signal = error_signal
self._glyph = self.memory.encode(observation, self.core.trust, self.core.entropy)
return {"reseal_triggered": reseal_triggered, "error_signal": error_signal}
@property
def trust(self) -> float:
return self.core.trust
@property
def entropy(self) -> float:
return self.core.entropy
@property
def reseal_count(self) -> int:
return self.core.reseal_count
@property
def trust_floor_hit(self) -> bool:
return self.core.trust_floor_hit
@property
def current_glyph(self) -> str:
return self._glyph
class PersistentRHEAAgent(RHEAAgent):
name = "persistent_rhea_agent"
def __init__(self) -> None:
super().__init__()
self.episode_memory: Dict[str, Dict[str, Any]] = {}
def reset(self) -> None:
self.core.trust = max(0.6, self.core.trust)
self.core.entropy = min(0.2, self.core.entropy)
self._glyph = "ΞPERSIST"
self.last_error_signal = 0.0
@staticmethod
def _looks_corrupted(seq: List[int]) -> bool:
if len(seq) < 3:
return False
diffs = [seq[i + 1] - seq[i] for i in range(len(seq) - 1)]
return len(set(diffs)) > 1
@staticmethod
def _repair_arithmetic_sequence(seq: List[int]) -> Optional[List[int]]:
if len(seq) < 4:
return None
for idx in range(1, len(seq) - 1):
repaired = list(seq)
expected = (repaired[idx - 1] + repaired[idx + 1]) // 2
repaired[idx] = expected
diffs = [repaired[i + 1] - repaired[i] for i in range(len(repaired) - 1)]
if len(set(diffs)) == 1:
return repaired
return None
def act(self, observation: Any) -> Any:
obs_type = observation["type"]
self._glyph = self.memory.encode(observation, self.core.trust, self.core.entropy)
if obs_type in {"sequence", "corrupted_sequence"}:
seq = observation["sequence"]
if self._looks_corrupted(seq):
repaired = self._repair_arithmetic_sequence(seq)
if repaired is not None:
self.episode_memory[obs_type] = {"repaired": repaired}
return repaired[-1] + (repaired[-1] - repaired[-2])
return super().act({**observation, "type": "sequence"})
if obs_type == "tictactoe_ambiguous":
board = observation["board"]
ambiguity = sum(1 for c in board if c == "?")
if ambiguity > 0:
self.core.update_from_error(0.5)
self._glyph = self.memory.encode({"type": "reseal"}, self.core.trust, self.core.entropy)
normalized = ["." if c == "?" else c for c in board]
move = BaselineAgent._blocking_move(normalized, observation["opponent"])
return move if move is not None else self._center_or_corner(normalized)
if obs_type == "tictactoe_ambiguity_fork":
raw_board = observation["board"]
ambiguity = sum(1 for c in raw_board if c == "?")
if ambiguity > 0:
self.core.update_from_error(0.55)
self._glyph = self.memory.encode({"type": "reseal"}, self.core.trust, self.core.entropy)
board = ["." if c == "?" else c for c in raw_board]
move = BaselineAgent._blocking_move(board, observation["opponent"])
if move is not None:
return move
if board[6] == ".":
return 6
move = BaselineAgent._fork_move(board, observation["player"])
if move is not None:
return move
return self._center_or_corner(board)
return super().act(observation)
def update(self, observation: Any, action: Any, reward: float) -> Dict[str, Any]:
payload = super().update(observation, action, reward)
if reward <= 0:
payload["reseal_triggered"] = self.core.update_from_error(0.75) or payload["reseal_triggered"]
self._glyph = self.memory.encode({"type": "reseal"}, self.core.trust, self.core.entropy)
return payload
# ============================================================
# Runner
# ============================================================
class ExperimentRunner:
def __init__(self, log_dir: str = "logs") -> None:
self.log_dir = Path(log_dir)
self.log_dir.mkdir(parents=True, exist_ok=True)
def run_episode(self, task: Task, agent: Agent) -> EpisodeResult:
task.reset()
agent.reset()
records: List[StepRecord] = []
total_reward = 0.0
entropy_peak = agent.entropy
start = time.perf_counter()
step_index = 0
while not task.done():
observation = task.observe()
action = agent.act(observation)
task.step(action)
reward = task.reward()
total_reward += reward
update_payload = agent.update(observation, action, reward)
entropy_peak = max(entropy_peak, agent.entropy)
records.append(
StepRecord(
step_index=step_index,
observation=observation,
action=action,
reward=reward,
trust=agent.trust,
entropy=agent.entropy,
reseal_triggered=bool(update_payload.get("reseal_triggered", False)),
glyph=agent.current_glyph,
metadata={"task_metadata": task.metadata(), **update_payload},
)
)
step_index += 1
duration_ms = (time.perf_counter() - start) * 1000.0
return EpisodeResult(
task_name=task.name,
agent_name=agent.name,
success=task.success(),
total_reward=total_reward,
steps=step_index,
duration_ms=duration_ms,
entropy_peak=entropy_peak,
trust_floor_hit=agent.trust_floor_hit,
reseal_count=agent.reseal_count,
final_glyph=agent.current_glyph,
records=records,
)
def write_result(self, result: EpisodeResult, filename: Optional[str] = None) -> Path:
stamp = int(time.time() * 1000)
out_name = filename or f"{result.task_name}_{result.agent_name}_{stamp}.json"
path = self.log_dir / out_name
with path.open("w", encoding="utf-8") as fh:
json.dump(result.to_dict(), fh, indent=2)
return path
class CurriculumRunner(ExperimentRunner):
def run_curriculum(self, tasks: List[Task], agent: Agent, curriculum_name: str) -> List[EpisodeResult]:
results: List[EpisodeResult] = []
for task in tasks:
result = self.run_episode(task, agent)
self.write_result(result, filename=f"{curriculum_name}_{task.name}_{agent.name}_{int(time.time() * 1000)}.json")
results.append(result)
return results
# ============================================================
# Display helpers
# ============================================================
def print_summary(result: EpisodeResult) -> None:
print("-" * 72)
print(f"Task: {result.task_name}")
print(f"Agent: {result.agent_name}")
print(f"Success: {result.success}")
print(f"Total reward: {result.total_reward:.2f}")
print(f"Steps: {result.steps}")
print(f"Duration (ms): {result.duration_ms:.3f}")
print(f"Entropy peak: {result.entropy_peak:.3f}")
print(f"Trust floor hit: {result.trust_floor_hit}")
print(f"Reseal count: {result.reseal_count}")
print(f"Final glyph: {result.final_glyph}")
if result.records:
final = result.records[-1]
print(f"Last action: {final.action}")
print(f"Expected: {final.metadata.get('task_metadata')}")
def print_curriculum_summary(results: List[EpisodeResult]) -> None:
print("=" * 72)
print("CURRICULUM SUMMARY")
print("=" * 72)
successes = sum(1 for r in results if r.success)
total = len(results)
avg_reward = sum(r.total_reward for r in results) / max(1, total)
avg_entropy = sum(r.entropy_peak for r in results) / max(1, total)
reseals = sum(r.reseal_count for r in results)
print(f"Episodes: {total}")
print(f"Successes: {successes}/{total}")
print(f"Average reward: {avg_reward:.3f}")
print(f"Average entropy: {avg_entropy:.3f}")
print(f"Total reseals: {reseals}")
print("Per-episode:")
for r in results:
print(
f" - {r.task_name:30s} | success={str(r.success):5s} | "
f"entropy_peak={r.entropy_peak:.3f} | reseals={r.reseal_count} | glyph={r.final_glyph}"
)
# ============================================================
# Lightweight self-tests
# ============================================================
def _run_self_tests() -> None:
seq = SequenceTask([2, 4, 6, 8])
seq.reset()
assert seq.metadata()["expected"] == 10
corrupt = CorruptedSequenceTask([2, 4, 6, 8], corruption_index=2, corruption_value=11)
corrupt.reset()
assert corrupt.observe()["sequence"] == [2, 4, 11, 8]
assert corrupt.metadata()["expected"] == 10
ar = ArithmeticTask("7 * (3 + 2)")
assert ar.metadata()["expected"] == 35
mod = ModularArithmeticTask(a=23, b=6, modulus=7, op="*")
assert mod.metadata()["expected"] == (23 * 6) % 7
win_task = TicTacToeTacticTask()
assert 6 in win_task.metadata()["expected_moves"]
block_task = TicTacToeBlockTask()
assert 7 in block_task.metadata()["expected_moves"]
fork_task = TicTacToeForkTask()
assert set(fork_task.metadata()["expected_moves"]) == {2, 6}
center_task = TicTacToeCenterPreferenceTask()
assert center_task.metadata()["expected_moves"] == [4]
noise_task = TicTacToeNoiseTask()
assert noise_task.metadata()["expected_moves"] == [7]
ambiguous_task = TicTacToeAmbiguousTask()
assert ambiguous_task.metadata()["expected_moves"] == [7]
two_ply = TicTacToeTwoPlyTrapTask()
two_ply.reset()
assert two_ply.metadata()["phases"] == 2
amb_fork = TicTacToeAmbiguityForkTask()
assert amb_fork.metadata()["expected_moves"] == [6]
rollout = TicTacToeRolloutTask(["X", ".", ".", ".", "O", ".", ".", ".", "."], max_turns=3)
rollout.reset()
assert rollout.metadata()["max_turns"] == 3
base = BaselineAgent()
base.reset()
assert base.act(mod.observe()) == (23 * 6) % 7
assert base.act(block_task.observe()) == 7
assert base.act(center_task.observe()) == 4
rhea = PersistentRHEAAgent()
rhea.reset()
repaired_action = rhea.act(corrupt.observe())
assert repaired_action == 10
rhea_basic = RHEAAgent()
rhea_basic.reset()
assert rhea_basic.act(TicTacToeTacticTask().observe()) == 6
assert rhea_basic.act(TicTacToeBlockTask().observe()) == 7
runner = ExperimentRunner(log_dir="logs")
result = runner.run_episode(ArithmeticTask(), RHEAAgent())
assert result.success is True
assert result.total_reward == 1.0
# ============================================================
# Demo execution
# ============================================================
if __name__ == "__main__":
_run_self_tests()
runner = ExperimentRunner()
tasks: List[Task] = [SequenceTask(), ArithmeticTask(), TicTacToeTacticTask()]
agents: List[Agent] = [BaselineAgent(), RHEAAgent()]
for task in tasks:
for agent in agents:
result = runner.run_episode(task, agent)
path = runner.write_result(result)
print_summary(result)
print(f"Log written to: {path}")
print("\n" + "#" * 72)
print("ADVANCED CURRICULUM: persistence + corruption + modular arithmetic + tactics")
print("#" * 72)
curriculum_runner = CurriculumRunner()
curriculum_tasks: List[Task] = [
SequenceTask([3, 6, 9, 12]),
CorruptedSequenceTask([2, 4, 6, 8], corruption_index=2, corruption_value=11),
ModularArithmeticTask(a=17, b=9, modulus=5, op="+"),
ModularArithmeticTask(a=23, b=6, modulus=7, op="*"),
TicTacToeBlockTask(),
TicTacToeForkTask(),
TicTacToeCenterPreferenceTask(),
TicTacToeNoiseTask(),
TicTacToeAmbiguousTask(),
TicTacToeTwoPlyTrapTask(),
TicTacToeAmbiguityForkTask(),
TicTacToeRolloutTask(["X", ".", ".", ".", "O", ".", ".", ".", "."], max_turns=3),
]
advanced_agents: List[Agent] = [BaselineAgent(), PersistentRHEAAgent()]
for agent in advanced_agents:
results = curriculum_runner.run_curriculum(curriculum_tasks, agent, curriculum_name="advanced_curriculum")
print_curriculum_summary(results)
print("\n" + "#" * 72)
print("TACTICAL TIC-TAC-TOE SUITE")
print("#" * 72)
tictactoe_tasks: List[Task] = [
TicTacToeTacticTask(),
TicTacToeBlockTask(),
TicTacToeForkTask(),
TicTacToeCenterPreferenceTask(),
TicTacToeNoiseTask(),
TicTacToeAmbiguousTask(),
TicTacToeTwoPlyTrapTask(),
TicTacToeAmbiguityForkTask(),
TicTacToeRolloutTask(["X", ".", ".", ".", "O", ".", ".", ".", "."], max_turns=3),
]
for agent in advanced_agents:
results = curriculum_runner.run_curriculum(tictactoe_tasks, agent, curriculum_name="tictactoe_suite")
print_curriculum_summary(results)

Test Harness Terminal Output
메타데이터
- post_id
- 63cb50f23ee0
- slug
- rhea-tactical-test-harness-63cb50f23ee0
- url
- https://medium.com/@ZadienLabs/rhea-tactical-test-harness-63cb50f23ee0
- canonical_url
- https://medium.com/@ZadienLabs/rhea-tactical-test-harness-63cb50f23ee0
- author_url
- https://medium.com/@ZadienLabs
- status
- ok
- fetched_at
- 2026-08-10 10:15:22