11import os
22import re
3+ import typing
34
5+ import pacai .core .action
46import pacai .util .file
57import pacai .util .json
68import pacai .util .reflection
1820class Marker (str ):
1921 """
2022 A marker represents something that can appear on a board.
21- These are similar to a game piece of token in a traditional board game (like the top hat or dog in Monolopy ).
23+ These are similar to a game piece of token in a traditional board game (like the top hat or dog in Monopoly ).
2224 """
2325
2426 pass
2527
28+ class Position (typing .NamedTuple ):
29+ """
30+ A 2-dimension location.
31+ The first value represent row/y/height,
32+ and the second value represents col/x/width.
33+ """
34+
35+ row : int
36+ """ The row / y / height of this position. """
37+
38+ col : int
39+ """ The col / x / width of this position. """
40+
41+ def to_index (self , width : int ) -> int :
42+ """ Convert this position into a 1-dimension index. """
43+ return (self .row * width ) + self .col
44+
45+ @staticmethod
46+ def from_index (index : int , width : int ) -> 'Position' :
47+ """ Convert a 1-dimension index into a 2-dimension position. """
48+ row = index // width
49+ col = index % width
50+
51+ return Position (row , col )
52+
53+ def add (self , other : 'Position' ) -> 'Position' :
54+ """
55+ Add another position (offset) to this one and return the result.
56+ """
57+
58+ return Position (self .row + other .row , self .col + other .col )
59+
2660class Board :
2761 """
2862 A board represents the static (non-agent) components of a game.
@@ -44,9 +78,19 @@ def __init__(self,
4478 extra_markers : list [str ] = [],
4579 strip : bool = True ,
4680 ** kwargs ) -> None :
81+ self ._marker_empty : Marker = Marker (marker_empty )
82+ """
83+ The marker used for empty locations.
84+ """
85+
86+ self ._marker_wall : Marker = Marker (marker_wall )
87+ """
88+ The marker used for wall locations.
89+ """
90+
4791 self ._markers : dict [str , Marker ] = {
48- marker_empty : Marker ( marker_empty ) ,
49- marker_wall : Marker ( marker_wall ) ,
92+ marker_empty : self . _marker_empty ,
93+ marker_wall : self . _marker_wall ,
5094 }
5195 """ Map the text for a marker to the actual marker. """
5296
@@ -104,6 +148,44 @@ def _process_text(self, board_text: str, strip: bool = True) -> tuple[int, int,
104148
105149 return height , width , locations
106150
151+ def _get_index (self , position ):
152+ """
153+ Get the internal 1-d index for this position.
154+ Will raise if this position is not valid.
155+ """
156+
157+ index = position .to_index (self .width )
158+ if ((index < 0 ) or (index >= len (self ._locations ))):
159+ raise ValueError ("Invalid position: %s." , str (position ))
160+
161+ return index
162+
163+ def is_wall (self , position ):
164+ return (self ._locations [self ._get_index (position )] == self ._marker_wall )
165+
166+ def get_neighbors (self , position : Position ) -> list [tuple [pacai .core .action .Action , Position ]]:
167+ """
168+ Get positions that are directly touching (via cardinal directions) the given position
169+ without being inside a wall,
170+ and the action it would take to get there.
171+ """
172+
173+ neighbors = []
174+ for (action , offset ) in CARDINAL_OFFSETS :
175+ neighbor = position .add (offset )
176+
177+ if ((neighbor .row < 0 ) or (neighbor .col < 0 )):
178+ continue
179+
180+ if ((neighbor .row >= self .height ) or (neighbor .col >= self .width )):
181+ continue
182+
183+ if (self .is_wall (neighbor )):
184+ continue
185+
186+ neighbors .append ((action , neighbor ))
187+
188+ return neighbors
107189
108190def load_path (path : str ) -> Board :
109191 """ Load a board from a file. """
@@ -138,3 +220,10 @@ def load_string(text: str) -> Board:
138220
139221 board_class = options .get ('class' , DEFAULT_BOARD_CLASS )
140222 return pacai .util .reflection .new_object (board_class , board_text , ** options )
223+
224+ CARDINAL_OFFSETS : list [tuple [pacai .core .action .Action , Position ]] = [
225+ (pacai .core .action .NORTH , Position (- 1 , 0 )),
226+ (pacai .core .action .EAST , Position (0 , 1 )),
227+ (pacai .core .action .WEST , Position (0 , - 1 )),
228+ (pacai .core .action .SOUTH , Position (1 , 0 )),
229+ ]
0 commit comments