diff --git a/reasoning_gym/games/rush_hour.py b/reasoning_gym/games/rush_hour.py index 0257abe6..1c0bd713 100644 --- a/reasoning_gym/games/rush_hour.py +++ b/reasoning_gym/games/rush_hour.py @@ -19,13 +19,14 @@ MaxWalls = 0 BoardSize2 = BoardSize * BoardSize Target = PrimaryRow * BoardSize + BoardSize - PrimarySize -H = 1 # horizontal stride -V = BoardSize # vertical stride +H = 1 # horizontal stride +V = BoardSize # vertical stride DoWalls = MinPieceSize == 1 -# board boundry limits -def create_row_masks(): - row_masks = [] + +# board boundary limits +def create_row_masks() -> list[int]: + row_masks: list[int] = [] for y in range(BoardSize): mask = 0 for x in range(BoardSize): @@ -35,8 +36,8 @@ def create_row_masks(): return row_masks -def create_column_masks(): - column_masks = [] +def create_column_masks() -> list[int]: + column_masks: list[int] = [] for x in range(BoardSize): mask = 0 for y in range(BoardSize): @@ -45,52 +46,53 @@ def create_column_masks(): column_masks.append(mask) return column_masks -RowMasks = create_row_masks() -TopRow = RowMasks[0] -BottomRow = RowMasks[-1] -ColumnMasks = create_column_masks() -LeftColumn = ColumnMasks[0] -RightColumn = ColumnMasks[-1] +ROW_MASKS = create_row_masks() +TOP_ROW = ROW_MASKS[0] +BOTTOM_ROW = ROW_MASKS[-1] +COLUMNS_MASKS = create_column_masks() +LEFT_COLUMN = COLUMNS_MASKS[0] +RIGHT_COLUMN = COLUMNS_MASKS[-1] class Piece: def __init__(self, position: int, size: int, stride: int): - self.Position = position - self.Size = size - self.Stride = stride - self.Mask = 0 + self.position = position + self.size = size + self.stride = stride + self.mask = 0 p = position for i in range(size): - self.Mask |= 1 << p + self.mask |= 1 << p p += stride - - def Fixed(self): - return self.Size == 1 - - def Move(self, steps: int): - d = self.Stride * steps - self.Position += d - if (steps > 0): - self.Mask <<= d + + @property + def fixed(self) -> bool: + return self.size == 1 + + def move(self, steps: int) -> None: + d = self.stride * steps + self.position += d + if steps > 0: + self.mask <<= d else: - self.Mask >>= -d + self.mask >>= -d class Board: - def __init__(self, desc): - self.m_HorzMask = 0 - self.m_VertMask = 0 - self.m_Pieces = [] + def __init__(self, desc: str): + self._horz_mask = 0 + self._vert_mask = 0 + self._pieces: list[Piece] = [] if len(desc) != BoardSize2: raise ValueError("board string is wrong length") - positions = {} + positions: dict[str, int] = {} for i, label in enumerate(desc): - if label == 'x' or label == 'o': + if label == "x" or label == "o": continue if label not in positions: positions[label] = [] @@ -113,145 +115,146 @@ class Board: for i in range(2, len(ps)): if ps[i] - ps[i - 1] != stride: raise ValueError("invalid piece shape") - self.AddPiece(Piece(ps[0], len(ps), stride)) + self.add_piece(Piece(ps[0], len(ps), stride)) - def AddPiece(self, piece): - self.m_Pieces.append(piece) - if (piece.Stride == H): - self.m_HorzMask |= piece.Mask + def add_piece(self, piece) -> None: + self._pieces.append(piece) + if piece.stride == H: + self._horz_mask |= piece.mask else: - self.m_VertMask |= piece.Mask + self._vert_mask |= piece.mask + + def mask(self) -> int: + return self._horz_mask | self._vert_mask - def Mask(self): - return self.m_HorzMask | self.m_VertMask - # DoMove has no bounds checking - def DoMove(self, i: int, steps: int): - piece = self.m_Pieces[i] - if (piece.Stride == H): + def do_move(self, i: int, steps: int) -> None: + piece = self._pieces[i] + if piece.stride == H: # Clears the current position from the horizontal mask - self.m_HorzMask &= ~piece.Mask - piece.Move(steps) + self._horz_mask &= ~piece.mask + piece.move(steps) # Adds the new position to the horizontal mask - self.m_HorzMask |= piece.Mask + self._horz_mask |= piece.mask else: - self.m_VertMask &= ~piece.Mask - piece.Move(steps) - self.m_VertMask |= piece.Mask + self._vert_mask &= ~piece.mask + piece.move(steps) + self._vert_mask |= piece.mask - def Moves(self, target: str = "A", dir: str = "up"): - # The position of piecs are stored as bits, + def moves(self, target: str = "A", dir: str = "up") -> None: + # The position of piecs are stored as bits, # it is compaired with the barrier (row/column) to confim the move being made is valid. # Example format: # 1000001000000000000 Piece Mask # 1000000000000000000000000 Move Mask - #111000111100111101001111011111011110 Puzzle Board + # 111000111100111101001111011111011110 Puzzle Board # - boardMask = self.Mask() - i = ord(target) - ord('A') + boardMask = self.mask() + i = ord(target) - ord("A") if i < 0 or i > 13: return - - piece = self.m_Pieces[i] + + piece = self._pieces[i] # boards incress difficulty by having unmovable blocks - if piece.Fixed(): + if piece.fixed: return - if piece.Stride == H: + if piece.stride == H: # reverse / left (negative steps) - if ((piece.Mask & LeftColumn) == 0) and dir.lower() == "left": - mask = (piece.Mask >> H) & ~piece.Mask + if ((piece.mask & LEFT_COLUMN) == 0) and dir.lower() == "left": + mask = (piece.mask >> H) & ~piece.mask # check pieces are intersected on a position if (boardMask & mask) == 0: - self.DoMove(i, -1) + self.do_move(i, -1) return - + # forward / right (positive steps) - if ((piece.Mask & RightColumn) == 0) and dir.lower() == "right": - mask = (piece.Mask << H) & ~piece.Mask + if ((piece.mask & RIGHT_COLUMN) == 0) and dir.lower() == "right": + mask = (piece.mask << H) & ~piece.mask if (boardMask & mask) == 0: - self.DoMove(i, 1) + self.do_move(i, 1) return - + # print("NOOP") else: # reverse / up (negative steps) - if ((piece.Mask & TopRow) == 0) and dir.lower() == "up": - mask = (piece.Mask >> V) & ~piece.Mask + if ((piece.mask & TOP_ROW) == 0) and dir.lower() == "up": + mask = (piece.mask >> V) & ~piece.mask if (boardMask & mask) == 0: - self.DoMove(i, -1) + self.do_move(i, -1) return # forward / down (positive steps) - if ((piece.Mask & BottomRow) == 0) and dir.lower() == "down": - mask = (piece.Mask << V) & ~piece.Mask + if ((piece.mask & BOTTOM_ROW) == 0) and dir.lower() == "down": + mask = (piece.mask << V) & ~piece.mask if (boardMask & mask) == 0: # print("{0:36b}".format(piece.Mask)) # print("{0:36b}".format(mask)) # print("{0:36b}".format(boardMask)) - self.DoMove(i, 1) + self.do_move(i, 1) return - + # print("NOOP") - def Solved(self): - return self.m_Pieces[0].Position == Target + @property + def solved(self) -> bool: + return self._pieces[0].position == Target - def String(self): - s = ['.'] * (BoardSize * (BoardSize + 1)) - for i in range(len(self.m_Pieces)): - piece = self.m_Pieces[i] - c = 'x' if piece.Fixed() else chr(ord('A') + i) - p = piece.Position - for i in range(piece.Size): + def __str__(self) -> str: + s = ["."] * (BoardSize * (BoardSize + 1)) + for i in range(len(self._pieces)): + piece = self._pieces[i] + c = "x" if piece.fixed else chr(ord("A") + i) + p = piece.position + for i in range(piece.size): s[p] = c - p += piece.Stride - return ''.join(s) + p += piece.stride + return "".join(s) - def String2D(self): - s = ['.'] * (BoardSize * (BoardSize + 1)) + def board_str(self) -> str: + s = ["."] * (BoardSize * (BoardSize + 1)) for y in range(BoardSize): p = y * (BoardSize + 1) + BoardSize - s[p] = '\n' - for i in range(len(self.m_Pieces)): - piece = self.m_Pieces[i] - c = 'x' if piece.Fixed() else chr(ord('A') + i) - stride = piece.Stride - if (stride == V): + s[p] = "\n" + for i in range(len(self._pieces)): + piece = self._pieces[i] + c = "x" if piece.fixed else chr(ord("A") + i) + stride = piece.stride + if stride == V: stride += 1 - y = piece.Position // BoardSize - x = piece.Position % BoardSize + y = piece.position // BoardSize + x = piece.position % BoardSize p = y * (BoardSize + 1) + x - for i in range(piece.Size): + for i in range(piece.size): s[p] = c p += stride - return ''.join(s) - + return "".join(s) + if __name__ == "__main__": # See it in action b = Board(TEST_STRING) print("-= Board String =-") - print(b.String()) - - print('\n' + "-= Board 2D =-") - print("{}".format(b.String2D())) + print(b) + + print("\n" + "-= Board 2D =-") + print("{}".format(b.board_str())) print("Number of Pieces") - print(len(b.m_Pieces)) - - print('\n' + "Lets move some pieces to test") - b.Moves("B", "RIGHT") - b.Moves("F", "right") - b.Moves("F", "RIGHT") - b.Moves("G", "down") - b.Moves("G", "DOWN") + print(len(b._pieces)) + + print("\n" + "Lets move some pieces to test") + b.moves("B", "RIGHT") + b.moves("F", "right") + b.moves("F", "RIGHT") + b.moves("G", "down") + b.moves("G", "DOWN") # moves that should not update the board - b.Moves("G", "down") - b.Moves("J", "left") - b.Moves("K", "right") + b.moves("G", "down") + b.moves("J", "left") + b.moves("K", "right") print("\n" + "-= Updated Board2D =-") - print("{}".format(b.String2D() )) + print("{}".format(b.board_str())) print("Solved the board?") - print(b.Solved()) \ No newline at end of file + print(b.solved)