Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Git
.git/
.gitignore
.github/

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
dist/
build/
*.egg
.pytest_cache/
.mypy_cache/
.ruff_cache/
venv/
env/
ENV/

# IDEs
.vscode/
.idea/
*.swp
*.swo
*~

# Documentation (keep README.md as it's explicitly copied)
*.md
!README.md
docs/

# CI/CD
.travis.yml
.gitlab-ci.yml

# OS
.DS_Store
Thumbs.db

# Test and development files
tests/
*.test
test_*

# Environment files
.env
.env.local
*.log

# Temporary files
tmp/
temp/
*.tmp
16 changes: 9 additions & 7 deletions src/envs/nle_env/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

A reinforcement learning environment based on NetHack 3.6.6, wrapped for the OpenEnv framework.

**Note**: This environment currently uses HTTP-based communication. OpenEnv is transitioning
to WebSocket-based communication for the Gym-like API, so this implementation may need
migration in the future.

## Overview

NetHack is one of the oldest and most challenging roguelike games, featuring:
Expand Down Expand Up @@ -45,8 +49,7 @@ env.close()
### Building the Docker Image

```bash
# Build from repository root (not from server directory)
cd /Users/sanyambhutani/GH/OpenEnv
# Build from repository root
docker build -f src/envs/nle_env/server/Dockerfile -t nle-env:latest .
```

Expand Down Expand Up @@ -94,7 +97,7 @@ For a complete action mapping, see [NLE Actions Documentation](https://github.co

## Observation Space

NLE provides rich observations about the game state. With OpenEnv's beefy compute assumption, all observations are included by default:
NLE provides rich observations about the game state. All observations are included by default:

### Core Observations
- **glyphs** `(21, 79)`: Symbolic dungeon map representation
Expand Down Expand Up @@ -245,11 +248,10 @@ env.close()

## Performance Considerations

With **beefy compute** (64+ cores, 256GB+ RAM, 10Gbps network):
Typical performance characteristics:
- Observation size: ~140KB per step (all observation types)
- Network overhead: Negligible (<1ms on fast network)
- Memory: ~200-500MB per container
- Throughput: 100+ parallel environments easily
- Throughput: 100+ parallel environments on multi-core systems

**Optimizations are NOT needed** - just run it simple with JSON serialization!

Expand Down Expand Up @@ -288,7 +290,7 @@ Common issues:

### Slow Performance

If you experience slowness even with beefy compute:
If you experience slow performance:
1. Check network latency: `ping <server-ip>`
2. Monitor CPU: NLE is CPU-intensive for dungeon generation
3. Check Docker resources: Ensure containers have sufficient CPU allocation
Expand Down
10 changes: 2 additions & 8 deletions src/envs/nle_env/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ class NLEEnv(HTTPEnvClient[NLEAction, NLEObservation]):
This client connects to an NLEEnvironment HTTP server and provides
methods to interact with NetHack: reset(), step(), and state access.

With beefy compute, we use simple JSON serialization. The server sends
all observation arrays as nested lists, which we keep as-is or convert
back to numpy arrays as needed.

Example:
>>> # Connect to a running server
>>> client = NLEEnv(base_url="http://localhost:8000")
Expand Down Expand Up @@ -72,9 +68,7 @@ def _parse_result(self, payload: Dict) -> StepResult[NLEObservation]:
"""
Parse server response into StepResult[NLEObservation].

The server sends all arrays as nested lists. With beefy compute,
we just keep them as lists - no need to convert back to numpy
unless the user specifically needs it.
The server sends all arrays as nested lists for JSON compatibility.

Args:
payload: JSON response from server
Expand All @@ -90,7 +84,7 @@ def _parse_result(self, payload: Dict) -> StepResult[NLEObservation]:
metadata = obs_data.get("metadata", {})

# Build observation with all the array fields
# Keep them as lists - simple and works great with beefy compute
# Keep them as lists for JSON compatibility
observation = NLEObservation(
# Core observations
glyphs=obs_data.get("glyphs"),
Expand Down
7 changes: 5 additions & 2 deletions src/envs/nle_env/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class NLEObservation(Observation):
Contains a subset of NLE's 14+ observation types. All numpy arrays are
serialized as nested lists for JSON compatibility.

Note: All observation fields are optional because NLE supports configurable
observation spaces. The environment can be configured with `observation_keys`
to include only a subset of available observations, allowing flexibility in
balancing information richness with data size.

Observation types (all optional, configured at env creation):
- glyphs: (21, 79) - Symbolic dungeon map representation
- chars: (21, 79) - ASCII character display
Expand All @@ -60,8 +65,6 @@ class NLEObservation(Observation):
- tty_colors: (24, 80) - Full terminal colors
- tty_cursor: (2,) - Terminal cursor position [row, col]
- screen_descriptions: (21, 79, 80) - Text descriptions of dungeon

With beefy compute, we include all observations by default.
"""

# Core observations (always useful)
Expand Down
4 changes: 2 additions & 2 deletions src/envs/nle_env/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ RUN pip install --no-cache-dir \
numpy

# Install NLE (this will compile NetHack from source)
# Using the stable version from PyPI
RUN pip install --no-cache-dir nle gym
# Pin gym version for reproducibility and API compatibility
RUN pip install --no-cache-dir nle gym==0.26.2

# Copy OpenEnv core
COPY src/core/ /app/src/core/
Expand Down
17 changes: 8 additions & 9 deletions src/envs/nle_env/server/nle_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ class NLEEnvironment(Environment):
This environment wraps NLE's gym interface and provides OpenEnv-compatible
reset(), step(), and state access.

With beefy compute, we use simple JSON serialization and include all
observation types by default. No optimization needed - compute handles it.

Example:
>>> env = NLEEnvironment()
>>> obs = env.reset()
Expand Down Expand Up @@ -90,7 +87,6 @@ def __init__(
self._observation_keys = observation_keys

# Create NLE gym environment
# With beefy compute: no ttyrec saving, all observations enabled
self.nle_env = NLE(
character=character,
observation_keys=observation_keys,
Expand All @@ -101,13 +97,17 @@ def __init__(
)

# Episode tracking
self._episode_id: Optional[str] = None
self._episode_id: str = self._generate_episode_id()
self._step_count = 0
self._last_reward = 0.0
self._last_done = False
self._end_status = "RUNNING"
self._in_normal_game = False

def _generate_episode_id(self) -> str:
"""Generate a unique episode ID using timestamp."""
return f"nle_{int(time.time() * 1_000_000)}"

def reset(self) -> NLEObservation:
"""
Reset the environment and return initial observation.
Expand All @@ -126,7 +126,7 @@ def reset(self) -> NLEObservation:
gym_obs = reset_result # Old API

# Initialize episode tracking
self._episode_id = f"nle_{int(time.time() * 1000000)}"
self._episode_id = self._generate_episode_id()
self._step_count = 0
self._last_reward = 0.0
self._last_done = False
Expand Down Expand Up @@ -208,8 +208,7 @@ def _convert_observation(
"""
Convert NLE gym observation to NLEObservation.

With beefy compute, we just convert numpy arrays to lists.
No compression, no optimization - simplicity first.
Converts numpy arrays to lists for JSON serialization.

Args:
gym_obs: Dictionary from NLE gym env
Expand All @@ -226,7 +225,7 @@ def _convert_observation(
}

# Convert each observation type from numpy array to nested list
# This is simple and works perfectly with JSON + beefy compute
# for JSON serialization
for key in self._observation_keys:
if key in gym_obs:
array = gym_obs[key]
Expand Down