Spaces:
Paused
Paused
Download doom_runtime.py from AvaSiG/ARGOS-MUKHTAR-Lab: direct link, hf CLI and curl.
- Browser
- Download file 6.17 kB
-
https://huggingface.co/spaces/AvaSiG/ARGOS-MUKHTAR-Lab/resolve/main/doom_runtime.py
- Command line
-
hf download hf://spaces/AvaSiG/ARGOS-MUKHTAR-Lab/doom_runtime.py
-
curl -L -o doom_runtime.py https://huggingface.co/spaces/AvaSiG/ARGOS-MUKHTAR-Lab/resolve/main/doom_runtime.py
6.17 kB
| """Live ViZDoom/Freedoom runtime for ARGOS-MUKHTAR Space. | |
| The Space owns the game world. A remote controller may only submit actions | |
| when MUKHTAR_BRIDGE_KEY is configured and the caller supplies the same key. | |
| No original DOOM assets are bundled; ViZDoom uses its free Freedoom assets. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import threading | |
| import time | |
| from typing import Any | |
| import numpy as np | |
| ACTION_NAMES = ("NOOP", "FORWARD", "BACK", "TURN_LEFT", "TURN_RIGHT", "FIRE") | |
| _ACTIONS = { | |
| "NOOP": [0, 0, 0, 0, 0], | |
| "FORWARD": [1, 0, 0, 0, 0], | |
| "BACK": [0, 1, 0, 0, 0], | |
| "TURN_LEFT": [0, 0, 1, 0, 0], | |
| "TURN_RIGHT": [0, 0, 0, 1, 0], | |
| "FIRE": [0, 0, 0, 0, 1], | |
| } | |
| _LOCK = threading.RLock() | |
| class DoomRuntime: | |
| def __init__(self) -> None: | |
| self.game = None | |
| self.seed = 0 | |
| self.episode = 0 | |
| self.step_count = 0 | |
| self.total_reward = 0.0 | |
| self.last_reward = 0.0 | |
| self.last_action = "NOOP" | |
| self.started_at = None | |
| def _build(self, seed: int): | |
| import vizdoom as vzd | |
| game = vzd.DoomGame() | |
| game.set_screen_resolution(vzd.ScreenResolution.RES_320X240) | |
| game.set_screen_format(vzd.ScreenFormat.RGB24) | |
| game.set_window_visible(False) | |
| game.set_mode(vzd.Mode.PLAYER) | |
| game.set_doom_map("map01") | |
| game.set_doom_skill(3) | |
| game.set_seed(int(seed)) | |
| game.set_available_buttons([ | |
| vzd.Button.MOVE_FORWARD, | |
| vzd.Button.MOVE_BACKWARD, | |
| vzd.Button.TURN_LEFT, | |
| vzd.Button.TURN_RIGHT, | |
| vzd.Button.ATTACK, | |
| ]) | |
| game.set_available_game_variables([ | |
| vzd.GameVariable.HEALTH, | |
| vzd.GameVariable.ARMOR, | |
| vzd.GameVariable.SELECTED_WEAPON_AMMO, | |
| vzd.GameVariable.KILLCOUNT, | |
| vzd.GameVariable.DEATHCOUNT, | |
| ]) | |
| game.set_living_reward(-0.001) | |
| game.init() | |
| return game | |
| def start(self, seed: int = 42): | |
| with _LOCK: | |
| if self.game is not None: | |
| try: | |
| self.game.close() | |
| except Exception: | |
| pass | |
| self.seed = int(seed) | |
| self.game = self._build(self.seed) | |
| self.game.new_episode() | |
| self.episode += 1 | |
| self.step_count = 0 | |
| self.total_reward = 0.0 | |
| self.last_reward = 0.0 | |
| self.last_action = "NOOP" | |
| self.started_at = time.time() | |
| return self.snapshot() | |
| def _ensure(self): | |
| if self.game is None: | |
| return self.start(self.seed or 42) | |
| if self.game.is_episode_finished(): | |
| self.game.new_episode() | |
| self.episode += 1 | |
| self.step_count = 0 | |
| self.total_reward = 0.0 | |
| return None | |
| def step(self, action: str, tics: int = 4): | |
| name = str(action or "NOOP").upper() | |
| if name not in _ACTIONS: | |
| raise ValueError(f"unknown Doom action: {name}") | |
| frames = max(1, min(int(tics), 12)) | |
| with _LOCK: | |
| self._ensure() | |
| reward = float(self.game.make_action(_ACTIONS[name], frames)) | |
| self.step_count += 1 | |
| self.total_reward += reward | |
| self.last_reward = reward | |
| self.last_action = name | |
| if self.game.is_episode_finished(): | |
| return self.snapshot(finished=True) | |
| return self.snapshot() | |
| def _var(self, name: str, default: float = 0.0) -> float: | |
| import vizdoom as vzd | |
| try: | |
| return float(self.game.get_game_variable(getattr(vzd.GameVariable, name))) | |
| except Exception: | |
| return float(default) | |
| def snapshot(self, finished: bool = False): | |
| with _LOCK: | |
| if self.game is None: | |
| return None, self.telemetry(state="not_started") | |
| state = None if self.game.is_episode_finished() else self.game.get_state() | |
| frame = None if state is None else np.asarray(state.screen_buffer).copy() | |
| info = self.telemetry(state="finished" if finished else "running") | |
| return frame, info | |
| def telemetry(self, state: str = "running") -> dict[str, Any]: | |
| return { | |
| "engine": "ViZDoom", | |
| "assets": "Freedoom (free content)", | |
| "state": state, | |
| "seed": self.seed, | |
| "episode": self.episode, | |
| "step": self.step_count, | |
| "last_action": self.last_action, | |
| "last_reward": round(self.last_reward, 6), | |
| "total_reward": round(self.total_reward, 6), | |
| "health": self._var("HEALTH"), | |
| "armor": self._var("ARMOR"), | |
| "ammo": self._var("SELECTED_WEAPON_AMMO"), | |
| "kills": self._var("KILLCOUNT"), | |
| "deaths": self._var("DEATHCOUNT"), | |
| "uptime_s": round(time.time() - self.started_at, 2) | |
| if self.started_at else 0.0, | |
| "remote_bridge": bridge_status(), | |
| } | |
| _RUNTIME = DoomRuntime() | |
| def bridge_status() -> dict[str, Any]: | |
| configured = bool(os.getenv("MUKHTAR_CONTROL_CODE", "").strip()) | |
| return { | |
| "configured": configured, | |
| "remote_write": "enabled" if configured else "disabled", | |
| "policy": "controller-code protected; observation is read-only", | |
| } | |
| def doom_start(seed: int = 42): | |
| return _RUNTIME.start(seed) | |
| def doom_step(action: str, tics: int = 4): | |
| return _RUNTIME.step(action, tics) | |
| def doom_snapshot(): | |
| return _RUNTIME.snapshot() | |
| def doom_observe(): | |
| return _RUNTIME.snapshot() | |
| def doom_remote_step(action: str, tics: int, control_code: str): | |
| expected = os.getenv("MUKHTAR_CONTROL_CODE", "").strip() | |
| supplied = str(control_code or "") | |
| frame, info = _RUNTIME.snapshot() | |
| if not expected: | |
| info = dict(info) | |
| info["remote_error"] = "control_not_configured" | |
| return frame, info | |
| import secrets | |
| if not secrets.compare_digest(expected, supplied): | |
| info = dict(info) | |
| info["remote_error"] = "unauthorized" | |
| return frame, info | |
| return _RUNTIME.step(action, tics) | |