Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| import json | |
| from dataclasses import asdict, dataclass, field | |
| from typing import Any, Literal | |
| GoalType = Literal[ | |
| "survive", | |
| "pursue_and_attack_target", | |
| "retaliate", | |
| "call_for_help", | |
| "help_or_defend", | |
| "respond_to_event", | |
| "investigate", | |
| "find_food", | |
| "routine_life", | |
| ] | |
| CitizenMode = Literal[ | |
| "routine", | |
| "threatened", | |
| "hostile_pursuit", | |
| "helping", | |
| "investigating", | |
| "fleeing", | |
| "recovering", | |
| ] | |
| NpcRole = Literal["guard", "builder", "gatherer"] | |
| HouseState = Literal["under_construction", "completed", "destroyed"] | |
| GameStatus = Literal["running", "win_population", "win_survival", "lose"] | |
| class Vec3: | |
| x: float | |
| y: float | |
| z: float | |
| class MemoryEntry: | |
| tick: int | |
| text: str | |
| class MemoryEpisode: | |
| tick: int | |
| kind: str | |
| actor_id: str | |
| target_id: str | None | |
| object_id: str | None | |
| subject_kind: str | |
| perspective: str | |
| summary: str | |
| emotional_weight: float = 0.5 | |
| tags: list[str] = field(default_factory=list) | |
| class NpcMemory: | |
| episodes: list[MemoryEpisode] = field(default_factory=list) | |
| facts: list[str] = field(default_factory=list) | |
| class NpcConversationContext: | |
| messages: list[dict[str, str]] = field(default_factory=list) | |
| max_turns: int = 5 | |
| def add_turn(self, user_content: str, assistant_content: str) -> None: | |
| self.messages.append({"role": "user", "content": user_content}) | |
| self.messages.append({"role": "assistant", "content": assistant_content}) | |
| max_messages = self.max_turns * 2 | |
| if len(self.messages) > max_messages: | |
| self.messages = self.messages[-max_messages:] | |
| class CitizenNeeds: | |
| safety: int = 20 | |
| food: int = 25 | |
| social: int = 30 | |
| status: int = 30 | |
| curiosity: int = 35 | |
| rest: int = 20 | |
| class CitizenEmotions: | |
| fear: int = 0 | |
| anger: int = 0 | |
| trust_baseline: int = 50 | |
| stress: int = 0 | |
| class CitizenGoal: | |
| goal_type: GoalType | |
| priority: int | |
| reason: str | |
| target_npc_id: str | None = None | |
| target_entity_id: str | None = None | |
| expires_at_tick: int | None = None | |
| class Npc: | |
| id: str | |
| name: str | |
| role: str | |
| position: Vec3 | |
| health: int = 100 | |
| attack_damage: int = 10 | |
| personality: str = "neutral" | |
| god_directive: str | None = None | |
| directive_source: str | None = None | |
| directive_issued_tick: int | None = None | |
| directive_ttl_ticks: int | None = None | |
| intention: str = "idle" | |
| # Latest model chain-of-thought ("thinking") and the tick it was produced on, | |
| # surfaced to the UI. Empty for non-thinking turns. | |
| last_reasoning: str | None = None | |
| last_reasoning_tick: int | None = None | |
| memory_summary: str | None = None | |
| # True once the NPC has written its OWN memory_summary (the model owns it). | |
| # While set, the engine never appends raw "Tick N: ..." lines to it. | |
| memory_summary_self_authored: bool = False | |
| memory: list[MemoryEntry] = field(default_factory=list) | |
| structured_memory: NpcMemory = field(default_factory=NpcMemory) | |
| conversation_context: NpcConversationContext = field(default_factory=NpcConversationContext) | |
| relationships: dict[str, float] = field(default_factory=dict) | |
| needs: CitizenNeeds = field(default_factory=CitizenNeeds) | |
| emotions: CitizenEmotions = field(default_factory=CitizenEmotions) | |
| current_goal: CitizenGoal | None = None | |
| mode: CitizenMode = "routine" | |
| # Survival core-loop state (deterministic, engine-owned). | |
| hunger: float = 25.0 | |
| fear: float = 0.0 | |
| safety: float = 0.0 | |
| age: int = 0 | |
| max_age: int = 400 | |
| reproduction_cooldown: int = 0 | |
| importance: float = 1.0 | |
| survival_goal: str = "routine_life" | |
| inventory_food: int = 0 | |
| inventory_herbs: int = 0 | |
| inventory_wood: int = 0 | |
| inventory_weapon: int = 0 | |
| inventory_coins: int = 0 | |
| help_target_id: str | None = None | |
| # Whom the NPC is currently engaging (talk/attack/trade); drives facing in the renderer. | |
| focus_target_id: str | None = None | |
| home_house_id: str | None = None | |
| build_target_house_id: str | None = None | |
| patrol_index: int = 0 | |
| resources_transferred: int = 0 | |
| beasts_killed: int = 0 | |
| children_count: int = 0 | |
| houses_built: int = 0 | |
| # Set when this NPC is controlled by an external agent over MCP rather than | |
| # by the built-in deterministic/LLM planner. | |
| is_player: bool = False | |
| player_owner: str | None = None | |
| country_id: str | None = None | |
| special_status: str | None = None | |
| model_profile_id: str | None = None | |
| # When true, the engine does not role-gate this NPC's actions: it may take | |
| # any action regardless of role. Player NPCs set this, but it can also be | |
| # given to special non-player NPCs. | |
| unrestricted_actions: bool = False | |
| # Routes this NPC to a named connector (from secondary_connectors in config). | |
| # None means the default connector handles this NPC. | |
| connector_id: str | None = None | |
| class ResourceNode: | |
| id: str | |
| resource_type: str # "food" | "herbs" | "wood" | "weapon" | |
| position: Vec3 | |
| amount: int | |
| max_amount: int = 0 # regrowth cap; 0 means "no regrowth" (set to amount at spawn) | |
| class Beast: | |
| id: str | |
| position: Vec3 | |
| health: float = 180.0 | |
| damage: float = 12.0 | |
| speed: float = 1.0 # in tiles per tick | |
| target_npc_id: str | None = None | |
| target_house_id: str | None = None | |
| retreat_ticks: int = 0 | |
| state: str = "hunting" # hunting | attacking | retreating | dead | |
| class House: | |
| id: str | |
| position: Vec3 | |
| owner_ids: list[str] = field(default_factory=list) | |
| hp: float = 60.0 | |
| max_hp: float = 60.0 | |
| state: HouseState = "completed" | |
| build_progress: int = 10 | |
| capacity: int = 3 | |
| occupant_ids: list[str] = field(default_factory=list) | |
| class TreasuryState: | |
| id: str | |
| position: Vec3 | |
| resources: dict[str, int] = field(default_factory=dict) | |
| class CannonState: | |
| id: str | |
| position: Vec3 | |
| operator_id: str | None = None | |
| cooldown_until_tick: int = 0 | |
| damage: int = 35 | |
| radius: int = 5 | |
| class CountryState: | |
| id: str | |
| name: str | |
| color: str | |
| badge: str | |
| citizen_ids: list[str] = field(default_factory=list) | |
| ruler_id: str | None = None | |
| policy: str = "Gather resources, protect citizens, and avoid reckless war." | |
| treasury: TreasuryState | None = None | |
| cannon: CannonState | None = None | |
| next_election_tick: int = 50 | |
| class ElectionState: | |
| country_id: str | |
| start_tick: int | |
| end_tick: int | |
| candidate_ids: list[str] | |
| votes: dict[str, str] = field(default_factory=dict) | |
| completed: bool = False | |
| class WorldLogEvent: | |
| tick: int | |
| type: str | |
| summary: str | |
| severity: Literal["good", "neutral", "warning", "danger"] = "neutral" | |
| actor_id: str | None = None | |
| target_id: str | None = None | |
| object_id: str | None = None | |
| class Terrain: | |
| kind: Literal["plain_green"] | |
| width: int | |
| depth: int | |
| color: str = "#43a047" | |
| class WorldEvent: | |
| tick_created: int | |
| kind: str | |
| description: str | |
| position: Vec3 | None | |
| radius: float = 8.0 | |
| duration_ticks: int = 5 | |
| class WorldState: | |
| tick: int | |
| seed: int | |
| terrain: Terrain | |
| npcs: list[Npc] | |
| last_tick_source: str = "initial" | |
| last_action_debug: list[dict[str, Any]] = field(default_factory=list) | |
| resource_nodes: list[ResourceNode] = field(default_factory=list) | |
| beasts: list[Beast] = field(default_factory=list) | |
| houses: list[House] = field(default_factory=list) | |
| active_events: list[WorldEvent] = field(default_factory=list) | |
| event_log: list[WorldLogEvent] = field(default_factory=list) | |
| countries: list[CountryState] = field(default_factory=list) | |
| elections: list[ElectionState] = field(default_factory=list) | |
| game_status: GameStatus = "running" | |
| population: int = 0 | |
| population_cap: int = 12 | |
| total_births: int = 0 | |
| deaths_by_cause: dict[str, int] = field(default_factory=dict) | |
| houses_built: int = 0 | |
| beasts_killed: int = 0 | |
| peak_population: int = 0 | |
| overseer_score: int = 0 | |
| chaos_score: int = 0 | |
| chaos_intervention_until: int = -1 | |
| famine_until: int = -1 | |
| famine_saved_max: dict[str, int] = field(default_factory=dict) | |
| overseer_mode: Literal["off", "advisor", "autopilot"] = "off" | |
| overseer_cycle_ticks: int = 8 | |
| overseer_last_tick: int | None = None | |
| overseer_last_thoughts: str | None = None | |
| overseer_priority_alert: str | None = None | |
| overseer_last_directives: list[dict[str, Any]] = field(default_factory=list) | |
| overseer_status: str = "idle" | |
| def living_npcs(self) -> list[Npc]: | |
| return [npc for npc in self.npcs if npc.health > 0] | |
| def to_dict(self) -> dict[str, Any]: | |
| return asdict(self) | |
| def to_json(self, *, indent: int | None = None) -> str: | |
| return json.dumps(self.to_dict(), indent=indent, sort_keys=True) | |