pacai.gridworld.board
1import typing 2 3import pacai.core.board 4 5MARKER_TERMINAL: pacai.core.board.Marker = pacai.core.board.Marker('T') 6MARKER_DISPLAY_VALUE: pacai.core.board.Marker = pacai.core.board.Marker('V') 7MARKER_DISPLAY_QVALUE: pacai.core.board.Marker = pacai.core.board.Marker('Q') 8MARKER_SEPARATOR: pacai.core.board.Marker = pacai.core.board.Marker('X') 9 10AGENT_MARKER: pacai.core.board.Marker = pacai.core.board.MARKER_AGENT_0 11""" The fixed marker of the only agent. """ 12 13BOARD_COL_DELIM: str = ',' 14 15class Board(pacai.core.board.Board): 16 """ 17 A board for GridWorld. 18 19 In addition to walls, grid worlds also have terminal states. 20 These will be represented as `T-?\\d`, so a 'T' followed by the value of the terminal state, 21 e.g., `T1`, `T100`, `T-5`. 22 """ 23 24 def __init__(self, *args: typing.Any, 25 additional_markers: list[str] | None = None, 26 qdisplay: bool = False, 27 **kwargs: typing.Any) -> None: 28 """ 29 Construct a GridWorld board. 30 If qdisplay is true, then the existing board will be extended to display Q-Values and policies. 31 """ 32 33 if (additional_markers is None): 34 additional_markers = [] 35 36 additional_markers += [ 37 MARKER_TERMINAL, 38 ] 39 40 kwargs['strip'] = False 41 42 self._terminal_values: dict[pacai.core.board.Position, int] = {} 43 """ Values for each terminal position. """ 44 45 # Ensure that super's init() is called after self._terminal_values exists. 46 super().__init__(*args, additional_markers = additional_markers, **kwargs) # type: ignore 47 48 self._original_height: int = self.height 49 """ The original height for this board. """ 50 51 self._original_width: int = self.width 52 """ The original width for this board. """ 53 54 if (qdisplay): 55 self._add_qvalue_display() 56 57 def _split_line(self, line: str) -> list[str]: 58 # Skip empty lines. 59 if (len(line.strip()) == 0): 60 return [] 61 62 return line.split(BOARD_COL_DELIM) 63 64 def _translate_marker(self, text: str, position: pacai.core.board.Position) -> pacai.core.board.Marker | None: 65 # GridWorld markers may have additional whitespace or repeated characters. 66 # Only simple markers (not terminals) can have repeated characters. 67 68 text = text.strip() 69 if (len(text) == 0): 70 text = ' ' 71 72 if (not text.startswith(MARKER_TERMINAL)): 73 return super()._translate_marker(text[0], position) 74 75 value = int(text[1:]) 76 self._terminal_values[position] = value 77 78 return MARKER_TERMINAL 79 80 def is_terminal_position(self, position: pacai.core.board.Position) -> bool: 81 """ Check if the given position is a terminal. """ 82 83 return (position in self._terminal_values) 84 85 def get_terminal_value(self, position: pacai.core.board.Position) -> int: 86 """ Get the value of this terminal position (or raise an exception if the position is not a terminal). """ 87 88 return self._terminal_values[position] 89 90 def display_qvalues(self) -> bool: 91 """ Check if this board is displaying Q-Values. """ 92 93 return len(self.get_marker_positions(MARKER_SEPARATOR)) > 0 94 95 def _add_qvalue_display(self) -> None: 96 """ 97 Add a Q-Value display, which includes a section for values and q-values. 98 The board will be doubled in size (along each dimension). 99 The top-left will have the original board (with agent), 100 the top-right will have the values and policies, 101 and the bottom-left will have the q-values. 102 """ 103 104 if (len(self.get_marker_positions(MARKER_SEPARATOR)) > 0): 105 raise ValueError("Already added Q-Value display.") 106 107 self._original_height = self.height 108 self._original_width = self.width 109 base_walls = self._walls.copy() 110 111 # Grow the board. 112 self.height = (self._original_height * 2) + 1 113 self.width = (self._original_width * 2) + 1 114 115 offset_values = pacai.core.board.Position(0, self._original_width + 1) # Values (Top-Right) 116 offset_qvalues = pacai.core.board.Position(self._original_height + 1, 0) # Q-Values (Bottom-Left) 117 118 # Add separators to quarter off sections of the new board. 119 120 for row in range(self.height): 121 self.place_marker(MARKER_SEPARATOR, pacai.core.board.Position(row, self._original_width)) 122 123 for col in range(self.width): 124 self.place_marker(MARKER_SEPARATOR, pacai.core.board.Position(self._original_height, col)) 125 126 # Empty out the blank section. 127 for row in range(self._original_height): 128 for col in range(self._original_width): 129 position = pacai.core.board.Position(self._original_height + 1 + row, self._original_width + 1 + col) 130 self.place_marker(MARKER_SEPARATOR, position) 131 132 # Duplicate the walls on the new sections. 133 134 for offset in [offset_values, offset_qvalues]: 135 for base_wall in base_walls: 136 self._walls.add(base_wall.add(offset)) 137 138 # Place the markers to display values/q-values. 139 140 for base_row in range(self._original_height): 141 for base_col in range(self._original_width): 142 base_position = pacai.core.board.Position(base_row, base_col) 143 if (self.is_wall(base_position) or self.is_marker(MARKER_TERMINAL, base_position)): 144 continue 145 146 self.place_marker(MARKER_DISPLAY_VALUE, base_position.add(offset_values)) 147 self.place_marker(MARKER_DISPLAY_QVALUE, base_position.add(offset_qvalues)) 148 149 # Copy terminal markers. 150 151 for (base_position, value) in list(self._terminal_values.items()): 152 for offset in [offset_values, offset_qvalues]: 153 position = base_position.add(offset) 154 self._terminal_values[position] = value 155 self.place_marker(MARKER_TERMINAL, position) 156 157 def to_dict(self) -> dict[str, typing.Any]: 158 data = super().to_dict() 159 data['_terminal_values'] = [(position.to_dict(), value) for (position, value) in self._terminal_values.items()] 160 return data 161 162 @classmethod 163 def from_dict(cls, data: dict[str, typing.Any]) -> typing.Any: 164 board = super().from_dict(data) 165 board._terminal_values = {pacai.core.board.Position.from_dict(raw): value for (raw, value) in data['_terminal_values']} 166 return board
The fixed marker of the only agent.
16class Board(pacai.core.board.Board): 17 """ 18 A board for GridWorld. 19 20 In addition to walls, grid worlds also have terminal states. 21 These will be represented as `T-?\\d`, so a 'T' followed by the value of the terminal state, 22 e.g., `T1`, `T100`, `T-5`. 23 """ 24 25 def __init__(self, *args: typing.Any, 26 additional_markers: list[str] | None = None, 27 qdisplay: bool = False, 28 **kwargs: typing.Any) -> None: 29 """ 30 Construct a GridWorld board. 31 If qdisplay is true, then the existing board will be extended to display Q-Values and policies. 32 """ 33 34 if (additional_markers is None): 35 additional_markers = [] 36 37 additional_markers += [ 38 MARKER_TERMINAL, 39 ] 40 41 kwargs['strip'] = False 42 43 self._terminal_values: dict[pacai.core.board.Position, int] = {} 44 """ Values for each terminal position. """ 45 46 # Ensure that super's init() is called after self._terminal_values exists. 47 super().__init__(*args, additional_markers = additional_markers, **kwargs) # type: ignore 48 49 self._original_height: int = self.height 50 """ The original height for this board. """ 51 52 self._original_width: int = self.width 53 """ The original width for this board. """ 54 55 if (qdisplay): 56 self._add_qvalue_display() 57 58 def _split_line(self, line: str) -> list[str]: 59 # Skip empty lines. 60 if (len(line.strip()) == 0): 61 return [] 62 63 return line.split(BOARD_COL_DELIM) 64 65 def _translate_marker(self, text: str, position: pacai.core.board.Position) -> pacai.core.board.Marker | None: 66 # GridWorld markers may have additional whitespace or repeated characters. 67 # Only simple markers (not terminals) can have repeated characters. 68 69 text = text.strip() 70 if (len(text) == 0): 71 text = ' ' 72 73 if (not text.startswith(MARKER_TERMINAL)): 74 return super()._translate_marker(text[0], position) 75 76 value = int(text[1:]) 77 self._terminal_values[position] = value 78 79 return MARKER_TERMINAL 80 81 def is_terminal_position(self, position: pacai.core.board.Position) -> bool: 82 """ Check if the given position is a terminal. """ 83 84 return (position in self._terminal_values) 85 86 def get_terminal_value(self, position: pacai.core.board.Position) -> int: 87 """ Get the value of this terminal position (or raise an exception if the position is not a terminal). """ 88 89 return self._terminal_values[position] 90 91 def display_qvalues(self) -> bool: 92 """ Check if this board is displaying Q-Values. """ 93 94 return len(self.get_marker_positions(MARKER_SEPARATOR)) > 0 95 96 def _add_qvalue_display(self) -> None: 97 """ 98 Add a Q-Value display, which includes a section for values and q-values. 99 The board will be doubled in size (along each dimension). 100 The top-left will have the original board (with agent), 101 the top-right will have the values and policies, 102 and the bottom-left will have the q-values. 103 """ 104 105 if (len(self.get_marker_positions(MARKER_SEPARATOR)) > 0): 106 raise ValueError("Already added Q-Value display.") 107 108 self._original_height = self.height 109 self._original_width = self.width 110 base_walls = self._walls.copy() 111 112 # Grow the board. 113 self.height = (self._original_height * 2) + 1 114 self.width = (self._original_width * 2) + 1 115 116 offset_values = pacai.core.board.Position(0, self._original_width + 1) # Values (Top-Right) 117 offset_qvalues = pacai.core.board.Position(self._original_height + 1, 0) # Q-Values (Bottom-Left) 118 119 # Add separators to quarter off sections of the new board. 120 121 for row in range(self.height): 122 self.place_marker(MARKER_SEPARATOR, pacai.core.board.Position(row, self._original_width)) 123 124 for col in range(self.width): 125 self.place_marker(MARKER_SEPARATOR, pacai.core.board.Position(self._original_height, col)) 126 127 # Empty out the blank section. 128 for row in range(self._original_height): 129 for col in range(self._original_width): 130 position = pacai.core.board.Position(self._original_height + 1 + row, self._original_width + 1 + col) 131 self.place_marker(MARKER_SEPARATOR, position) 132 133 # Duplicate the walls on the new sections. 134 135 for offset in [offset_values, offset_qvalues]: 136 for base_wall in base_walls: 137 self._walls.add(base_wall.add(offset)) 138 139 # Place the markers to display values/q-values. 140 141 for base_row in range(self._original_height): 142 for base_col in range(self._original_width): 143 base_position = pacai.core.board.Position(base_row, base_col) 144 if (self.is_wall(base_position) or self.is_marker(MARKER_TERMINAL, base_position)): 145 continue 146 147 self.place_marker(MARKER_DISPLAY_VALUE, base_position.add(offset_values)) 148 self.place_marker(MARKER_DISPLAY_QVALUE, base_position.add(offset_qvalues)) 149 150 # Copy terminal markers. 151 152 for (base_position, value) in list(self._terminal_values.items()): 153 for offset in [offset_values, offset_qvalues]: 154 position = base_position.add(offset) 155 self._terminal_values[position] = value 156 self.place_marker(MARKER_TERMINAL, position) 157 158 def to_dict(self) -> dict[str, typing.Any]: 159 data = super().to_dict() 160 data['_terminal_values'] = [(position.to_dict(), value) for (position, value) in self._terminal_values.items()] 161 return data 162 163 @classmethod 164 def from_dict(cls, data: dict[str, typing.Any]) -> typing.Any: 165 board = super().from_dict(data) 166 board._terminal_values = {pacai.core.board.Position.from_dict(raw): value for (raw, value) in data['_terminal_values']} 167 return board
A board for GridWorld.
In addition to walls, grid worlds also have terminal states.
These will be represented as T-?\d, so a 'T' followed by the value of the terminal state,
e.g., T1, T100, T-5.
25 def __init__(self, *args: typing.Any, 26 additional_markers: list[str] | None = None, 27 qdisplay: bool = False, 28 **kwargs: typing.Any) -> None: 29 """ 30 Construct a GridWorld board. 31 If qdisplay is true, then the existing board will be extended to display Q-Values and policies. 32 """ 33 34 if (additional_markers is None): 35 additional_markers = [] 36 37 additional_markers += [ 38 MARKER_TERMINAL, 39 ] 40 41 kwargs['strip'] = False 42 43 self._terminal_values: dict[pacai.core.board.Position, int] = {} 44 """ Values for each terminal position. """ 45 46 # Ensure that super's init() is called after self._terminal_values exists. 47 super().__init__(*args, additional_markers = additional_markers, **kwargs) # type: ignore 48 49 self._original_height: int = self.height 50 """ The original height for this board. """ 51 52 self._original_width: int = self.width 53 """ The original width for this board. """ 54 55 if (qdisplay): 56 self._add_qvalue_display()
Construct a GridWorld board. If qdisplay is true, then the existing board will be extended to display Q-Values and policies.
81 def is_terminal_position(self, position: pacai.core.board.Position) -> bool: 82 """ Check if the given position is a terminal. """ 83 84 return (position in self._terminal_values)
Check if the given position is a terminal.
86 def get_terminal_value(self, position: pacai.core.board.Position) -> int: 87 """ Get the value of this terminal position (or raise an exception if the position is not a terminal). """ 88 89 return self._terminal_values[position]
Get the value of this terminal position (or raise an exception if the position is not a terminal).
91 def display_qvalues(self) -> bool: 92 """ Check if this board is displaying Q-Values. """ 93 94 return len(self.get_marker_positions(MARKER_SEPARATOR)) > 0
Check if this board is displaying Q-Values.
158 def to_dict(self) -> dict[str, typing.Any]: 159 data = super().to_dict() 160 data['_terminal_values'] = [(position.to_dict(), value) for (position, value) in self._terminal_values.items()] 161 return data
Return a dict that can be used to represent this object. If the dict is passed to from_dict(), an identical object should be reconstructed.
A general (but inefficient) implementation is provided by default.
163 @classmethod 164 def from_dict(cls, data: dict[str, typing.Any]) -> typing.Any: 165 board = super().from_dict(data) 166 board._terminal_values = {pacai.core.board.Position.from_dict(raw): value for (raw, value) in data['_terminal_values']} 167 return board
Return an instance of this subclass created using the given dict. If the dict came from to_dict(), the returned object should be identical to the original.
A general (but inefficient) implementation is provided by default.
Inherited Members
- pacai.core.board.Board
- source
- height
- width
- search_target
- copy
- size
- get_corners
- agent_count
- agent_indexes
- get
- get_marker_positions
- get_marker_count
- get_walls
- remove_agent
- remove_marker
- place_marker
- get_neighbors
- get_adjacency
- get_adjacent_walls
- is_empty
- is_marker
- is_wall
- get_agent_position
- get_agent_initial_position
- get_nonwall_string
- to_grid