Anshuman Singh
Initial implementation of minimum-violation LTL planner
ba4f7c6
"""
Grid world transition system.
Each cell is labeled with a set of atomic propositions.
Robots move N/S/E/W; obstacles block movement.
"""
from dataclasses import dataclass, field
from typing import Dict, FrozenSet, List, Set, Tuple
MOVES = {"N": (-1, 0), "S": (1, 0), "E": (0, 1), "W": (0, -1)}
# Cell type → set of atomic propositions true at that cell
CELL_PROPS = {
"free": frozenset(),
"obstacle": frozenset({"obstacle"}),
"zone_a": frozenset({"zone_a"}),
"zone_b": frozenset({"zone_b"}),
"zone_c": frozenset({"zone_c"}),
"danger": frozenset({"danger"}),
"goal": frozenset({"goal"}),
"start": frozenset(),
}
@dataclass
class GridWorld:
n: int
grid: List[List[str]] = field(default_factory=list)
start: Tuple[int, int] = (0, 0)
def __post_init__(self):
if not self.grid:
self.grid = [["free"] * self.n for _ in range(self.n)]
def label(self, pos: Tuple[int, int]) -> FrozenSet[str]:
r, c = pos
return CELL_PROPS.get(self.grid[r][c], frozenset())
def successors(self, pos: Tuple[int, int]) -> List[Tuple[str, Tuple[int, int]]]:
r, c = pos
result = []
for action, (dr, dc) in MOVES.items():
nr, nc = r + dr, c + dc
if 0 <= nr < self.n and 0 <= nc < self.n:
if self.grid[nr][nc] != "obstacle":
result.append((action, (nr, nc)))
# also allow staying in place (needed for sync / waiting)
result.append(("stay", pos))
return result
def all_positions(self) -> List[Tuple[int, int]]:
return [
(r, c)
for r in range(self.n)
for c in range(self.n)
if self.grid[r][c] != "obstacle"
]
def make_scenario(name: str) -> GridWorld:
"""Built-in demo scenarios."""
if name == "road":
# 8×8 road network
# danger=double-line zone, zone_a=pickup, zone_b=dropoff, goal=destination
n = 8
g = GridWorld(n=n)
g.start = (0, 0)
# vertical danger strip (double line)
for r in range(n):
g.grid[r][3] = "danger"
# obstacle block
for r in range(2, 5):
g.grid[r][5] = "obstacle"
g.grid[1][6] = "zone_a"
g.grid[6][1] = "zone_b"
g.grid[7][7] = "goal"
return g
if name == "patrol":
# 6×6 warehouse patrol
n = 6
g = GridWorld(n=n)
g.start = (0, 0)
g.grid[0][5] = "zone_a"
g.grid[5][0] = "zone_b"
g.grid[2][2] = "danger"
g.grid[2][3] = "danger"
g.grid[3][2] = "danger"
g.grid[3][3] = "danger"
return g
if name == "rescue":
# 7×7 rescue mission
n = 7
g = GridWorld(n=n)
g.start = (3, 0)
for c in range(1, 6):
g.grid[3][c] = "obstacle"
g.grid[3][3] = "free"
g.grid[0][6] = "zone_a"
g.grid[6][6] = "zone_b"
for r in [1, 2, 4, 5]:
g.grid[r][2] = "danger"
g.grid[0][0] = "zone_c"
return g
raise ValueError(f"Unknown scenario: {name}")