Skip to content
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ To install Crafter, run `pip3 install crafter`. The environment follows the
outputs are one of 17 categorical actions.

```py
import gym
import gymnasium as gym
import crafter

env = gym.make('CrafterReward-v1') # Or CrafterNoReward-v1
Expand All @@ -93,7 +93,7 @@ obs = env.reset()
done = False
while not done:
action = env.action_space.sample()
obs, reward, done, info = env.step(action)
obs, reward, terminated, truncated, info = env.step(action)
```

[gym]: https://github.com/openai/gym
Expand Down
2 changes: 1 addition & 1 deletion crafter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .recorder import Recorder

try:
import gym
import gymnasium as gym
gym.register(
id='CrafterReward-v1',
entry_point='crafter:Env',
Expand Down
19 changes: 12 additions & 7 deletions crafter/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

# Gym is an optional dependency.
try:
import gym
import gymnasium as gym
DiscreteSpace = gym.spaces.Discrete
BoxSpace = gym.spaces.Box
DictSpace = gym.spaces.Dict
Expand All @@ -36,7 +36,6 @@ def __init__(
self._reward = reward
self._length = length
self._seed = seed
self._episode = 0
self._world = engine.World(area, constants.materials, (12, 12))
self._textures = engine.Textures(constants.root / 'assets')
item_rows = int(np.ceil(len(constants.items) / view[0]))
Expand Down Expand Up @@ -67,18 +66,22 @@ def action_space(self):
def action_names(self):
return constants.actions

def reset(self):
def reset(self, seed=None, options={}):
center = (self._world.area[0] // 2, self._world.area[1] // 2)
self._episode += 1
self._step = 0
self._world.reset(seed=hash((self._seed, self._episode)) % (2 ** 31 - 1))
if not seed:
seed = self._seed
if not seed:
seed = 0
self._world.reset(seed=seed)
self._update_time()
self._player = objects.Player(self._world, center)
self._last_health = self._player.health
self._world.add(self._player)
self._unlocked = set()
worldgen.generate_world(self._world, self._player)
return self._obs()
info = {"options": options}
return self._obs(), info

def step(self, action):
self._step += 1
Expand Down Expand Up @@ -115,7 +118,9 @@ def step(self, action):
}
if not self._reward:
reward = 0.0
return obs, reward, done, info
terminated = done
truncated = False
return obs, reward, terminated, truncated, info

def render(self, size=None):
size = size or self._size
Expand Down
50 changes: 31 additions & 19 deletions crafter/recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import imageio
import numpy as np

import gymnasium

class Recorder:

class Recorder(gymnasium.Env):

def __init__(
self, env, directory, save_stats=True, save_video=True,
Expand All @@ -24,6 +26,12 @@ def __getattr__(self, name):
raise AttributeError(name)
return getattr(self._env, name)

def reset(self, seed=None, options={}):
return self._env.reset(seed, options)

def step(self, action):
return self._env.step(action)


class StatsRecorder:

Expand All @@ -42,24 +50,25 @@ def __getattr__(self, name):
raise AttributeError(name)
return getattr(self._env, name)

def reset(self):
obs = self._env.reset()
def reset(self, seed=None, options={}):
obs = self._env.reset(seed=seed, options=options)
self._length = 0
self._reward = 0
self._unlocked = None
self._stats = None
return obs

def step(self, action):
obs, reward, done, info = self._env.step(action)
obs, reward, terminated, truncated, info = self._env.step(action)
done = terminated or truncated
self._length += 1
self._reward += info['reward']
if done:
self._stats = {'length': self._length, 'reward': round(self._reward, 1)}
for key, value in info['achievements'].items():
self._stats[f'achievement_{key}'] = value
self._save()
return obs, reward, done, info
return obs, reward, terminated, truncated, info

def _save(self):
self._file.write(json.dumps(self._stats) + '\n')
Expand All @@ -70,7 +79,7 @@ class VideoRecorder:

def __init__(self, env, directory, size=(512, 512)):
if not hasattr(env, 'episode_name'):
env = EpisodeName(env)
env = EpisodeName
self._env = env
self._directory = pathlib.Path(directory).expanduser()
self._directory.mkdir(exist_ok=True, parents=True)
Expand All @@ -82,17 +91,18 @@ def __getattr__(self, name):
raise AttributeError(name)
return getattr(self._env, name)

def reset(self):
obs = self._env.reset()
def reset(self, seed=None, options={}):
obs = self._env.reset(seed=seed, options=options)
self._frames = [self._env.render(self._size)]
return obs

def step(self, action):
obs, reward, done, info = self._env.step(action)
obs, reward, terminated, truncated, info = self._env.step(action)
done = terminated or truncated
self._frames.append(self._env.render(self._size))
if done:
self._save()
return obs, reward, done, info
return obs, reward, terminated, truncated, info

def _save(self):
filename = str(self._directory / (self._env.episode_name + '.mp4'))
Expand All @@ -103,7 +113,7 @@ class EpisodeRecorder:

def __init__(self, env, directory):
if not hasattr(env, 'episode_name'):
env = EpisodeName(env)
env = EpisodeName
self._env = env
self._directory = pathlib.Path(directory).expanduser()
self._directory.mkdir(exist_ok=True, parents=True)
Expand All @@ -114,16 +124,17 @@ def __getattr__(self, name):
raise AttributeError(name)
return getattr(self._env, name)

def reset(self):
obs = self._env.reset()
def reset(self, seed=None, options={}):
obs = self._env.reset(seed=seed, options=options)
self._episode = [{'image': obs}]
return obs

def step(self, action):
# Transitions are defined from the environment perspective, meaning that a
# transition contains the action and the resulting reward and next
# observation produced by the environment in response to said action.
obs, reward, done, info = self._env.step(action)
obs, reward, terminated, truncated, info = self._env.step(action)
done = terminated or truncated
transition = {
'action': action, 'image': obs, 'reward': reward, 'done': done,
}
Expand All @@ -138,7 +149,7 @@ def step(self, action):
self._episode.append(transition)
if done:
self._save()
return obs, reward, done, info
return obs, reward, terminated, truncated, info

def _save(self):
filename = str(self._directory / (self._env.episode_name + '.npz'))
Expand All @@ -165,20 +176,21 @@ def __getattr__(self, name):
raise AttributeError(name)
return getattr(self._env, name)

def reset(self):
obs = self._env.reset()
def reset(self, seed=None, options={}):
obs = self._env.reset(seed=seed, options=options)
self._timestamp = None
self._unlocked = None
self._length = 0
return obs

def step(self, action):
obs, reward, done, info = self._env.step(action)
obs, reward, terminated, truncated, info = self._env.step(action)
done = terminated or truncated
self._length += 1
if done:
self._timestamp = datetime.datetime.now().strftime('%Y%m%dT%H%M%S')
self._unlocked = sum(int(v >= 1) for v in info['achievements'].values())
return obs, reward, done, info
return obs, reward, terminated, truncated, info

@property
def episode_name(self):
Expand Down
13 changes: 9 additions & 4 deletions crafter/run_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ def main():
size[1] = size[1] or args.window[1]

env = crafter.Env(
area=args.area, view=args.view, length=args.length, seed=args.seed)
area=args.area, view=args.view, length=args.length)
env = crafter.Recorder(env, args.record)
env.reset()

seed = args.seed

env.reset(seed=seed)
achievements = set()
duration = 0
return_ = 0
Expand Down Expand Up @@ -108,7 +111,8 @@ def main():
action = 'noop'

# Environment step.
_, reward, done, _ = env.step(env.action_names.index(action))
_, reward, terminated, truncated, _ = env.step(env.action_names.index(action))
done = terminated or truncated
duration += 1

# Achievements.
Expand All @@ -135,7 +139,8 @@ def main():
running = False
if args.death == 'reset':
print('\nStarting a new episode.')
env.reset()
seed = hash(seed) % (2 ** 31 - 1)
env.reset(seed=seed)
achievements = set()
was_done = False
duration = 0
Expand Down
8 changes: 6 additions & 2 deletions crafter/run_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def main():
parser.add_argument('--episodes', type=int, default=1)
args = parser.parse_args()

seed = args.seed

random = np.random.RandomState(args.seed)
crafter.constants.items['health']['max'] = args.health
crafter.constants.items['health']['initial'] = args.health
Expand All @@ -26,7 +28,8 @@ def main():
for _ in range(args.episodes):

start = time.time()
obs = env.reset()
seed = hash(seed) % (2 ** 31 - 1)
obs = env.reset(seed=seed)
print('')
print(f'Reset time: {1000*(time.time()-start):.2f}ms')
print('Coal exist: ', env._world.count('coal'))
Expand All @@ -37,7 +40,8 @@ def main():
done = False
while not done:
action = random.randint(0, env.action_space.n)
obs, reward, done, info = env.step(action)
obs, reward, terminated, truncated, info = env.step(action)
done = terminated or truncated
duration = time.time() - start
step = env._step
print(f'Step time: {1000*duration/step:.2f}ms ({int(step/duration)} FPS)')
Expand Down
5 changes: 4 additions & 1 deletion crafter/run_terrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ def main():
parser.add_argument('--filename', type=str, default='terrain.png')
args = parser.parse_args()

seed = args.seed

env = crafter.Env(args.area, args.area, args.size, seed=args.seed)
images = []
for index in range(args.amount):
images.append(env.reset())
seed = hash(seed) % (2 ** 31 - 1)
images.append(env.reset(seed))
diamonds = env._world.count('diamond')
print(f'Map: {index:>2}, diamonds: {diamonds:>2}')

Expand Down
9 changes: 7 additions & 2 deletions examples/run_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
parser = argparse.ArgumentParser()
parser.add_argument('--outdir', default='logdir/crafter_noreward-random/0')
parser.add_argument('--steps', type=float, default=1e6)
parser.add_argument('--seed', type=int, default=None)
args = parser.parse_args()

seed = args.seed

env = crafter.Env()
env = crafter.Recorder(
env, args.outdir,
Expand All @@ -22,8 +25,10 @@
bar = tqdm.tqdm(total=args.steps, smoothing=0)
while step < args.steps or not done:
if done:
env.reset()
seed = hash(seed) % (2 ** 31 - 1)
env.reset(seed)
done = False
_, _, done, _ = env.step(action_space.sample())
_, _, terminated, truncated, _ = env.step(action_space.sample())
done = terminated or truncated
step += 1
bar.update(1)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

setuptools.setup(
name='crafter',
version='1.8.3',
version='1.8.4',
description='Open world survival game for reinforcement learning.',
url='http://github.com/danijar/crafter',
long_description=pathlib.Path('README.md').read_text(),
Expand Down