From 151f8f4762fce65d6796eac0a4a0a6e224a36b2f Mon Sep 17 00:00:00 2001 From: Tomasz Sternal Date: Sat, 27 Jan 2024 21:06:05 +0100 Subject: [PATCH] Add a margin at the top of the window --- .../envs/fruit_tree/assets/node_blue.png | Bin 0 -> 34166 bytes mo_gymnasium/envs/fruit_tree/fruit_tree.py | 61 ++++++++++++------ 2 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 mo_gymnasium/envs/fruit_tree/assets/node_blue.png diff --git a/mo_gymnasium/envs/fruit_tree/assets/node_blue.png b/mo_gymnasium/envs/fruit_tree/assets/node_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..176457809c20a1f08b6569ab957c637fd78e0a9b GIT binary patch literal 34166 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 QSO5S307*qoM6N<$f`h9z$p8QV literal 0 HcmV?d00001 diff --git a/mo_gymnasium/envs/fruit_tree/fruit_tree.py b/mo_gymnasium/envs/fruit_tree/fruit_tree.py index 7ec4e8c2..bdf794c0 100644 --- a/mo_gymnasium/envs/fruit_tree/fruit_tree.py +++ b/mo_gymnasium/envs/fruit_tree/fruit_tree.py @@ -270,6 +270,8 @@ class FruitTreeEnv(gym.Env, EzPickle): The episode terminates when the agent reaches a leaf node. """ + metadata = {"render_modes": ["human", "rgb_array"], "render_fps": 4} + def __init__(self, depth=6, render_mode: Optional[str] = None): assert depth in [5, 6, 7], "Depth must be 5, 6 or 7." EzPickle.__init__(self, depth) @@ -296,6 +298,8 @@ def __init__(self, depth=6, render_mode: Optional[str] = None): # pygame self.row_height = 20 + self.top_margin = 15 + # Add margin at the bottom to account for the node rewards self.window_size = (1200, self.row_height * self.tree_depth + 150) self.node_square_size = np.array([10, 10], dtype=np.int32) @@ -305,6 +309,7 @@ def __init__(self, depth=6, render_mode: Optional[str] = None): self.font = pygame.font.SysFont(None, self.font_size) self.window = None + self.clock = None self.node_img = None self.agent_img = None @@ -375,6 +380,9 @@ def render(self): ) return + if self.clock is None and self.render_mode == "human": + self.clock = pygame.time.Clock() + if self.window is None: pygame.init() @@ -382,53 +390,70 @@ def render(self): pygame.display.init() pygame.display.set_caption("Fruit Tree") self.window = pygame.display.set_mode(self.window_size) + self.clock.tick(self.metadata["render_fps"]) else: self.window = pygame.Surface(self.window_size) if self.node_img is None: - filename = path.join(path.dirname(__file__), "assets", "node.png") + filename = path.join(path.dirname(__file__), "assets", "node_blue.png") self.node_img = pygame.transform.scale(pygame.image.load(filename), self.node_square_size) self.node_img = pygame.transform.flip(self.node_img, flip_x=True, flip_y=False) + if self.agent_img is None: filename = path.join(path.dirname(__file__), "assets", "agent.png") self.agent_img = pygame.transform.scale(pygame.image.load(filename), self.node_square_size) canvas = pygame.Surface(self.window_size) - canvas.fill((0, 0, 0)) + canvas.fill((255, 255, 255)) # White - self.window.blit(canvas, (0, 0)) + # draw branches + for ind, node in enumerate(self.tree): + row, index_in_row = self.ind_to_state(ind) + node_pos = self.get_pos_in_window(row, index_in_row) + if row < self.tree_depth: + # Get childerns' positions and draw branches + child1_pos = self.get_pos_in_window(row + 1, 2 * index_in_row) + child2_pos = self.get_pos_in_window(row + 1, 2 * index_in_row + 1) + half_square = self.node_square_size / 2 + pygame.draw.line(canvas, (90, 82, 85), node_pos + half_square, child1_pos + half_square, 1) + pygame.draw.line(canvas, (90, 82, 85), node_pos + half_square, child2_pos + half_square, 1) for ind, node in enumerate(self.tree): row, index_in_row = self.ind_to_state(ind) if (row, index_in_row) == tuple(self.current_state): img = self.agent_img - font_color = (255, 0, 0) # Red digits for agent node + font_color = (164, 0, 0) # Red digits for agent node else: img = self.node_img - font_color = (0, 255, 0) # Green digits for non-agent nodes + if ind % 2: + font_color = (250, 128, 114) # Green + else: + font_color = (45, 72, 101) # Dark Blue node_pos = self.get_pos_in_window(row, index_in_row) - self.window.blit(img, np.array(node_pos)) + canvas.blit(img, np.array(node_pos)) - if row < self.tree_depth: - # Get childerns' positions and draw branches - child1_pos = self.get_pos_in_window(row + 1, 2 * index_in_row) - child2_pos = self.get_pos_in_window(row + 1, 2 * index_in_row + 1) - half_square = self.node_square_size / 2 - pygame.draw.line(self.window, (255, 255, 255), node_pos + half_square, child1_pos + half_square, 1) - pygame.draw.line(self.window, (255, 255, 255), node_pos + half_square, child2_pos + half_square, 1) - else: - # Print node values at the bottom of the tree + # Print node values at the bottom of the tree + if row == self.tree_depth: + odd_nodes_values_offset = 0.5 * (ind % 2) values_imgs = [self.font.render(f"{val:.2f}", True, font_color) for val in node] for i, val_img in enumerate(values_imgs): - self.window.blit(val_img, node_pos + np.array([-5, (i + 1) * self.font_size])) + canvas.blit(val_img, node_pos + np.array([-5, (i + 1 + odd_nodes_values_offset) * 1.5 * self.font_size])) if self.render_mode == "human": pygame.event.pump() pygame.display.update() + self.clock.tick(self.metadata["render_fps"]) elif self.render_mode == "rgb_array": - return np.transpose(np.array(pygame.surfarray.pixels3d(self.window)), axes=(1, 0, 2)) + return np.transpose(np.array(pygame.surfarray.pixels3d(canvas)), axes=(1, 0, 2)) + + background = pygame.Surface(self.window_size) + background.fill((255, 255, 255)) # White + + background.blit(canvas, (0, self.top_margin)) + + self.window.blit(background, (0, 0)) if __name__ == "__main__": @@ -436,7 +461,7 @@ def render(self): import mo_gymnasium as mo_gym - env = mo_gym.make("fruit-tree", depth=6, render_mode="human") + env = mo_gym.make("fruit-tree", depth=7, render_mode="human") env.reset() while True: env.render()