(self)
| 348 | for i in range(len(self.buttons))] |
| 349 | |
| 350 | def _render(self): |
| 351 | s = self._screen |
| 352 | s.fill(WHITE) |
| 353 | _grid_lines(s, UNIT) |
| 354 | # PolicyEnv state is [row, col] but our draw helpers take [col, row] — swap. |
| 355 | _circle(s, 2, 2, UNIT, GOAL_COLOR) |
| 356 | for row, col in [(1, 2), (2, 1)]: |
| 357 | _triangle(s, col, row, UNIT, OBSTACLE_COLOR) |
| 358 | label = self._small.render("R : -1.0", True, TEXT_COLOR) |
| 359 | s.blit(label, (col * UNIT + 6, row * UNIT + 4)) |
| 360 | s.blit(self._small.render("R : +1.0", True, TEXT_COLOR), (2 * UNIT + 6, 2 * UNIT + 4)) |
| 361 | |
| 362 | # Agent first (filled), so V text and arrows render on top of it. |
| 363 | r, c = self.agent_pos |
| 364 | sz = int(UNIT * 0.55) |
| 365 | cx, cy = c * UNIT + UNIT // 2, r * UNIT + UNIT // 2 |
| 366 | pygame.draw.rect(s, AGENT_COLOR, pygame.Rect(cx - sz // 2, cy - sz // 2, sz, sz)) |
| 367 | |
| 368 | if self._values is not None: |
| 369 | for r in range(HEIGHT): |
| 370 | for c in range(WIDTH): |
| 371 | t = self._font.render(f"{self._values[r][c]:.2f}", True, TEXT_COLOR) |
| 372 | cx, cy = c * UNIT + UNIT // 2, r * UNIT + UNIT // 2 |
| 373 | s.blit(t, t.get_rect(center=(cx, cy + UNIT // 4))) |
| 374 | |
| 375 | if self._arrows is not None: |
| 376 | edge = [(0, -UNIT * 0.32), (0, UNIT * 0.32), |
| 377 | (-UNIT * 0.32, 0), (UNIT * 0.32, 0)] |
| 378 | for r in range(HEIGHT): |
| 379 | for c in range(WIDTH): |
| 380 | probs = self._arrows[r][c] |
| 381 | if not probs: continue |
| 382 | cx, cy = c * UNIT + UNIT // 2, r * UNIT + UNIT // 2 |
| 383 | for i, p in enumerate(probs): |
| 384 | if p > 0: |
| 385 | self._arrow(cx, cy, cx + edge[i][0], cy + edge[i][1]) |
| 386 | |
| 387 | for rect, btn in zip(self._btn_rects(), self.buttons): |
| 388 | enabled = self._btn_enabled(btn) |
| 389 | pressed = btn[0] == self._press_label and self._press_frames > 0 |
| 390 | if not enabled: |
| 391 | bg, fg, border = (245, 245, 245), (170, 170, 170), (200, 200, 200) |
| 392 | elif pressed: |
| 393 | bg, fg, border = (160, 180, 220), BLACK, BLACK # blue tint while held |
| 394 | else: |
| 395 | bg, fg, border = (220, 220, 220), BLACK, BLACK |
| 396 | pygame.draw.rect(s, bg, rect) |
| 397 | pygame.draw.rect(s, border, rect, 1) |
| 398 | t = self._font.render(btn[0], True, fg) |
| 399 | # Offset text slightly when pressed for a "depressed" feel. |
| 400 | center = (rect.centerx + (1 if pressed else 0), rect.centery + (1 if pressed else 0)) |
| 401 | s.blit(t, t.get_rect(center=center)) |
| 402 | if self._press_frames > 0: |
| 403 | self._press_frames -= 1 |
| 404 | |
| 405 | pygame.display.flip() |
| 406 | |
| 407 | def _arrow(self, x0, y0, x1, y1): |
no test coverage detected