Tice / core /t_cell.py
fierce74's picture
Upload folder using huggingface_hub
cc6e5ac verified
from __future__ import annotations
import numpy as np
INITIAL_ATTACK_STRENGTH = 0.8
INITIAL_ENERGY = 1.0
INITIAL_FATIGUE = 0.0
VALUE_RANGE = (0.0, 1.0)
FATIGUE_EFFECT_FACTOR = 0.8
TISSUE_DAMAGE_FACTOR = 0.3
EXHAUSTION_ON_THRESHOLD = 0.8
EXHAUSTION_OFF_THRESHOLD = 0.2
T_CELL_TRANSITIONS = {
"ATTACK_HIGH": {"base": 0.18, "energy_delta": -0.22, "fatigue_delta": 0.14},
"ATTACK_MEDIUM": {"base": 0.08, "energy_delta": -0.10, "fatigue_delta": 0.05},
"ATTACK_LOW": {"base": 0.05, "energy_delta": -0.06, "fatigue_delta": 0.02},
"REST": {"base": 0.00, "energy_delta": 0.16, "fatigue_delta": -0.15},
}
EXHAUSTED_ACTION_MAP = {
"ATTACK_HIGH": "ATTACK_MEDIUM",
"ATTACK_MEDIUM": "REST",
"ATTACK_LOW": "REST",
"REST": "REST",
}
def _clip_unit(value: float) -> float:
return float(np.clip(value, *VALUE_RANGE))
class TCellAgent:
def __init__(self) -> None:
self.reset()
def reset(self) -> None:
self.attack_strength = _clip_unit(INITIAL_ATTACK_STRENGTH)
self.energy = _clip_unit(INITIAL_ENERGY)
self.fatigue = _clip_unit(INITIAL_FATIGUE)
self.exhausted = False
def step(
self,
action: str,
b_detection: float,
pdl1_suppression: float,
tumor_resistance: float,
) -> dict:
if self.exhausted:
# Exhaustion is a hard constraint: once fatigued past a threshold, the environment
# automatically downgrades aggression to enforce recovery behavior.
action = EXHAUSTED_ACTION_MAP.get(action, "REST")
transition = T_CELL_TRANSITIONS.get(action, T_CELL_TRANSITIONS["ATTACK_LOW"])
base = float(transition["base"])
self.energy = _clip_unit(self.energy + float(transition["energy_delta"]))
self.fatigue = _clip_unit(self.fatigue + float(transition["fatigue_delta"]))
# Multiplicative effective damage makes coordination real: weak detection, high suppression,
# or high fatigue can collapse damage even when the agent chooses "ATTACK_HIGH".
effective_damage = (
base
* float(np.clip(b_detection, *VALUE_RANGE))
* (1.0 - float(np.clip(pdl1_suppression, *VALUE_RANGE)))
* (1.0 - (self.fatigue * FATIGUE_EFFECT_FACTOR))
* (1.0 - float(np.clip(tumor_resistance, *VALUE_RANGE)))
)
tissue_damage = base * self.fatigue * TISSUE_DAMAGE_FACTOR
if self.fatigue >= EXHAUSTION_ON_THRESHOLD:
self.exhausted = True
if self.fatigue <= EXHAUSTION_OFF_THRESHOLD:
self.exhausted = False
self.attack_strength = _clip_unit(base)
return {
"effective_damage": _clip_unit(effective_damage),
"tissue_damage": _clip_unit(tissue_damage),
"base_damage": _clip_unit(base),
}
def get_state(self) -> dict:
return {
"attack_strength": float(self.attack_strength),
"energy": float(self.energy),
"fatigue": float(self.fatigue),
"exhausted": bool(self.exhausted),
}