| from collections import OrderedDict |
| from typing import Tuple, Union |
|
|
| import numpy as np |
| import pygame |
| from PIL import Image |
|
|
| from .dataset_env import DatasetEnv |
| from .keymap import Keymap |
| from .play_env import PlayEnv |
|
|
|
|
| class Game: |
| def __init__( |
| self, |
| play_env: Union[PlayEnv, DatasetEnv], |
| keymap: Keymap, |
| size: Tuple[int, int], |
| fps: int, |
| verbose: bool, |
| ) -> None: |
| self.env = play_env |
| self.keymap = keymap |
| self.height, self.width = size |
| self.fps = fps |
| self.verbose = verbose |
| self.keymap = OrderedDict() |
| for keys, act in sorted(keymap.items(), key=lambda keys_act: -len(keys_act[0])): |
| self.keymap[keys] = act |
|
|
| print("\nControls (general):\n") |
| print("⏎ : reset env") |
| print(". : pause/unpause") |
| print("e : step-by-step (when paused)") |
| self.env.print_controls() |
| print("\n") |
|
|
| def run(self) -> None: |
| pygame.init() |
|
|
| header_height = 150 if self.verbose else 0 |
| font_size = 16 |
| screen = pygame.display.set_mode((self.width, self.height + header_height)) |
| clock = pygame.time.Clock() |
| font = pygame.font.SysFont("mono", font_size) |
| header_rect = pygame.Rect(0, 0, self.width, header_height) |
|
|
| def clear_header(): |
| pygame.draw.rect(screen, pygame.Color("black"), header_rect) |
| pygame.draw.rect(screen, pygame.Color("white"), header_rect, 1) |
|
|
| def draw_text(text, idx_line, idx_column, num_cols): |
| pos = (5 + idx_column * int(self.width // num_cols), 5 + idx_line * font_size) |
| assert (0 <= pos[0] <= self.width) and (0 <= pos[1] <= header_height) |
| screen.blit(font.render(text, True, pygame.Color("white")), pos) |
|
|
| def draw_game(obs): |
| assert obs.ndim == 4 and obs.size(0) == 1 |
| img = Image.fromarray(obs[0].add(1).div(2).mul(255).byte().permute(1, 2, 0).cpu().numpy()) |
| pygame_image = np.array(img.resize((self.width, self.height), resample=Image.NEAREST)).transpose((1, 0, 2)) |
| surface = pygame.surfarray.make_surface(pygame_image) |
| screen.blit(surface, (0, header_height)) |
|
|
| def reset(): |
| nonlocal obs, info, do_reset, ep_return, ep_length |
| obs, info = self.env.reset() |
| do_reset = False |
| ep_return = 0 |
| ep_length = 0 |
|
|
| obs, info, do_reset, ep_return, ep_length = (None,) * 5 |
|
|
| reset() |
| do_wait = False |
| should_stop = False |
|
|
| while not should_stop: |
| do_one_step = False |
| action = 0 |
| pygame.event.pump() |
|
|
| for event in pygame.event.get(): |
| if event.type == pygame.QUIT: |
| should_stop = True |
|
|
| if event.type != pygame.KEYDOWN: |
| continue |
|
|
| if event.key == pygame.K_RETURN: |
| do_reset = True |
|
|
| if event.key == pygame.K_PERIOD: |
| do_wait = not do_wait |
| print("Game paused." if do_wait else "Game resumed.") |
|
|
| if event.key == pygame.K_e: |
| do_one_step = True |
|
|
| if event.key == pygame.K_m: |
| do_reset = self.env.next_mode() |
|
|
| if event.key == pygame.K_UP: |
| do_reset = self.env.next_axis_1() |
|
|
| if event.key == pygame.K_DOWN: |
| do_reset = self.env.prev_axis_1() |
|
|
| if event.key == pygame.K_RIGHT: |
| do_reset = self.env.next_axis_2() |
|
|
| if event.key == pygame.K_LEFT: |
| do_reset = self.env.prev_axis_2() |
|
|
| if action == 0: |
| pressed = pygame.key.get_pressed() |
| for keys, action in self.keymap.items(): |
| if all([pressed[key] for key in keys]): |
| break |
| else: |
| action = 0 |
|
|
| if do_reset: |
| reset() |
|
|
| if do_wait and not do_one_step: |
| continue |
|
|
| next_obs, rew, end, trunc, info = self.env.step(action) |
|
|
| ep_return += rew.item() |
| ep_length += 1 |
|
|
| draw_game(obs) |
|
|
| if self.verbose and info is not None: |
| clear_header() |
| assert isinstance(info, dict) and "header" in info |
| header = info["header"] |
| num_cols = len(header) |
| for j, col in enumerate(header): |
| for i, row in enumerate(col): |
| draw_text(row, idx_line=i, idx_column=j, num_cols=num_cols) |
|
|
| pygame.display.flip() |
| clock.tick(self.fps) |
|
|
| if end or trunc: |
| reset() |
|
|
| else: |
| obs = next_obs |
|
|
| pygame.quit() |
|
|