Pygame button-driven viewer for policy / value iteration. Set `display.buttons = [(label, handler[, enabled]), ...]` (up to 4). `enabled` is an optional zero-arg callable returning bool; when it returns False the button is greyed out and clicks are ignored. `show_values(V)` / `show_
| 267 | |
| 268 | # --------------------------------------------------------------------------- |
| 269 | class GraphicDisplay: |
| 270 | """Pygame button-driven viewer for policy / value iteration. |
| 271 | |
| 272 | Set `display.buttons = [(label, handler[, enabled]), ...]` (up to 4). |
| 273 | `enabled` is an optional zero-arg callable returning bool; when it |
| 274 | returns False the button is greyed out and clicks are ignored. |
| 275 | `show_values(V)` / `show_arrows(policy_table)` overlay; `clear()` |
| 276 | removes them. `move_along_policy(picker)` animates greedy moves. |
| 277 | """ |
| 278 | BAR = 50 |
| 279 | |
| 280 | def __init__(self, agent, title, buttons=None): |
| 281 | self.agent = agent |
| 282 | self.env = PolicyEnv() |
| 283 | self.title = title |
| 284 | self.buttons = buttons or [] |
| 285 | self.agent_pos = [0, 0] |
| 286 | # Per-label click counts, available to button `enabled` predicates. |
| 287 | self.clicks = {} |
| 288 | # Brief "pressed" flash so clicks feel responsive. |
| 289 | self._press_label = None |
| 290 | self._press_frames = 0 |
| 291 | self._screen = None |
| 292 | self._values = None |
| 293 | self._arrows = None |
| 294 | |
| 295 | def click_count(self, label): |
| 296 | return self.clicks.get(label, 0) |
| 297 | |
| 298 | def show_values(self, v): self._values = v |
| 299 | def show_arrows(self, p): self._arrows = p |
| 300 | def clear(self): self._values = self._arrows = None |
| 301 | |
| 302 | def move_along_policy(self, picker): |
| 303 | self.agent_pos = [0, 0] |
| 304 | while True: |
| 305 | self._render(); pygame.time.wait(200) |
| 306 | r, c = self.agent_pos |
| 307 | # Stop at the goal cell — picker may not be defined there |
| 308 | # (policy iteration's get_action crashes on the terminal state). |
| 309 | if self.env.reward[r][c] > 0: |
| 310 | break |
| 311 | a = picker(list(self.agent_pos)) |
| 312 | if a is None or a == [] or a == 0.0: break |
| 313 | if isinstance(a, list): a = a[0] |
| 314 | dx, dy = DP_ACTIONS[a] |
| 315 | self.agent_pos = [max(0, min(WIDTH - 1, self.agent_pos[0] + dx)), |
| 316 | max(0, min(HEIGHT - 1, self.agent_pos[1] + dy))] |
| 317 | |
| 318 | def mainloop(self): |
| 319 | self._screen = _open(self.title, (WIDTH * UNIT, HEIGHT * UNIT + self.BAR)) |
| 320 | self._font = pygame.font.SysFont(None, 22) |
| 321 | self._small = pygame.font.SysFont(None, 16) |
| 322 | while True: |
| 323 | for e in pygame.event.get(): |
| 324 | if e.type == pygame.QUIT: |
| 325 | pygame.quit(); return |
| 326 | if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1: |
no outgoing calls
no test coverage detected