Files
PPCA-AIPacMan-2024/logic/logicPlan.py
2024-07-01 08:07:22 +00:00

686 lines
28 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# logicPlan.py
# ------------
# Licensing Information: You are free to use or extend these projects for
# educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
#
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# Student side autograding was added by Brad Miller, Nick Hay, and
# Pieter Abbeel (pabbeel@cs.berkeley.edu).
"""
In logicPlan.py, you will implement logic planning methods which are called by
Pacman agents (in logicAgents.py).
"""
from typing import Dict, List, Tuple, Callable, Generator, Any
import util
import sys
import logic
import game
from logic import conjoin, disjoin
from logic import PropSymbolExpr, Expr, to_cnf, pycoSAT, parseExpr, pl_true
import itertools
import copy
pacman_str = 'P'
food_str = 'FOOD'
wall_str = 'WALL'
pacman_wall_str = pacman_str + wall_str
DIRECTIONS = ['North', 'South', 'East', 'West']
blocked_str_map = dict([(direction, (direction + "_blocked").upper()) for direction in DIRECTIONS])
geq_num_adj_wall_str_map = dict([(num, "GEQ_{}_adj_walls".format(num)) for num in range(1, 4)])
DIR_TO_DXDY_MAP = {'North':(0, 1), 'South':(0, -1), 'East':(1, 0), 'West':(-1, 0)}
#______________________________________________________________________________
# QUESTION 1
def sentence1() -> Expr:
"""Returns a Expr instance that encodes that the following expressions are all true.
A or B
(not A) if and only if ((not B) or C)
(not A) or (not B) or C
"""
A=Expr('A')
B=Expr('B')
C=Expr('C')
return conjoin([A | B, (~A % ( ~B | C)), disjoin([~A , ~B , C])])
def sentence2() -> Expr:
"""Returns a Expr instance that encodes that the following expressions are all true.
C if and only if (B or D)
A implies ((not B) and (not D))
(not (B and (not C))) implies A
(not D) implies C
"""
A,B,C,D=Expr('A'),Expr('B'),Expr('C'),Expr('D')
return conjoin([(C % (B | D)), (A >> (~B & ~D)), (~(B & ~C) >> A), (~D >> C)])
def sentence3() -> Expr:
"""Using the symbols PacmanAlive_1 PacmanAlive_0, PacmanBorn_0, and PacmanKilled_0,
created using the PropSymbolExpr constructor, return a PropSymbolExpr
instance that encodes the following English sentences (in this order):
Pacman is alive at time 1 if and only if Pacman was alive at time 0 and it was
not killed at time 0 or it was not alive at time 0 and it was born at time 0.
Pacman cannot both be alive at time 0 and be born at time 0.
Pacman is born at time 0.
"""
PacmanAlive_0=PropSymbolExpr('PacmanAlive_0')
PacmanAlive_1=PropSymbolExpr('PacmanAlive_1')
PacmanBorn_0=PropSymbolExpr('PacmanBorn_0')
PacmanKilled_0=PropSymbolExpr('PacmanKilled_0')
# the answer should be "((PacmanAlive_1 <=> ((PacmanAlive_0 & ~PacmanKilled_0) | (~PacmanAlive_0 & PacmanBorn_0))) & ~(PacmanAlive_0 & PacmanBorn_0) & PacmanBorn_0)"
return conjoin([(PacmanAlive_1 % ((PacmanAlive_0 & ~PacmanKilled_0) | (~PacmanAlive_0 & PacmanBorn_0))), (~(PacmanAlive_0 & PacmanBorn_0)), PacmanBorn_0])
def findModel(sentence: Expr) -> Dict[Expr, bool]:
"""Given a propositional logic sentence (i.e. a Expr instance), returns a satisfying
model if one exists. Otherwise, returns False.
"""
cnf_sentence = to_cnf(sentence)
return pycoSAT(cnf_sentence)
def findModelUnderstandingCheck() -> Dict[Expr, bool]:
"""Returns the result of findModel(Expr('a')) if lower cased expressions were allowed.
You should not use findModel or Expr in this method.
"""
a = Expr('A')
a.__dict__['op']='a'
return {a:True}
def entails(premise: Expr, conclusion: Expr) -> bool:
"""Returns True if the premise entails the conclusion and False otherwise.
"""
expr=conjoin([premise,~conclusion])
return not pycoSAT(to_cnf(expr))
def plTrueInverse(assignments: Dict[Expr, bool], inverse_statement: Expr) -> bool:
"""Returns True if the (not inverse_statement) is True given assignments and False otherwise.
pl_true may be useful here; see logic.py for its description.
"""
return pl_true(~inverse_statement,assignments)
#______________________________________________________________________________
# QUESTION 2
def atLeastOne(literals: List[Expr]) -> Expr:
"""
Given a list of Expr literals (i.e. in the form A or ~A), return a single
Expr instance in CNF (conjunctive normal form) that represents the logic
that at least one of the literals ist is true.
>>> A = PropSymbolExpr('A');
>>> B = PropSymbolExpr('B');
>>> symbols = [A, B]
>>> atleast1 = atLeastOne(symbols)
>>> model1 = {A:False, B:False}
>>> print(pl_true(atleast1,model1))
False
>>> model2 = {A:False, B:True}
>>> print(pl_true(atleast1,model2))
True
>>> model3 = {A:True, B:True}
>>> print(pl_true(atleast1,model2))
True
"""
return disjoin(literals)
def atMostOne(literals: List[Expr]) -> Expr:
"""
Given a list of CNF Expr literals, return a single Expr instance in
CNF (conjunctive normal form) that represents the logic that at most one of
the expressions in the list is true.
itertools.combinations is used here.
"""
clauses = []
# Generate all pairs of literals
for pair in itertools.combinations(literals, 2):
# Create a clause that says "not both of these can be true"
clause = disjoin(~pair[0], ~pair[1])
clauses.append(clause)
# If there are no clauses (i.e., 0 or 1 literal), return True
if not clauses:
return Expr('True')
# Combine all clauses with AND
return conjoin(clauses)
def exactlyOne(literals: List[Expr]) -> Expr:
"""
Given a list of Expr literals, return a single Expr instance in
CNF (conjunctive normal form)that represents the logic that exactly one of
the expressions in the list is true.
"""
at_least_one = atLeastOne(literals)
at_most_one = atMostOne(literals)
return conjoin([at_least_one, at_most_one])
#______________________________________________________________________________
# QUESTION 3
def pacmanSuccessorAxiomSingle(x: int, y: int, time: int, walls_grid: List[List[bool]]=None) -> Expr:
"""
Successor state axiom for state (x,y,t) (from t-1), given the board (as a
grid representing the wall locations).
Current <==> (previous position at time t-1) & (took action to move to x, y)
Available actions are ['North', 'East', 'South', 'West']
Note that STOP is not an available action.
"""
now, last = time, time - 1
possible_causes: List[Expr] = [] # enumerate all possible causes for P[x,y]_t
# the if statements give a small performance boost and are required for q4 and q5 correctness
if walls_grid[x][y+1] != 1:
possible_causes.append( PropSymbolExpr(pacman_str, x, y+1, time=last)
& PropSymbolExpr('South', time=last))
if walls_grid[x][y-1] != 1:
possible_causes.append( PropSymbolExpr(pacman_str, x, y-1, time=last)
& PropSymbolExpr('North', time=last))
if walls_grid[x+1][y] != 1:
possible_causes.append( PropSymbolExpr(pacman_str, x+1, y, time=last)
& PropSymbolExpr('West', time=last))
if walls_grid[x-1][y] != 1:
possible_causes.append( PropSymbolExpr(pacman_str, x-1, y, time=last)
& PropSymbolExpr('East', time=last))
if not possible_causes:
return None
current_state = PropSymbolExpr(pacman_str, x, y, time=now)
return current_state % disjoin(possible_causes)
def SLAMSuccessorAxiomSingle(x: int, y: int, time: int, walls_grid: List[List[bool]]) -> Expr:
"""
Similar to `pacmanSuccessorStateAxioms` but accounts for illegal actions
where the pacman might not move timestep to timestep.
Available actions are ['North', 'East', 'South', 'West']
"""
now, last = time, time - 1
moved_causes: List[Expr] = [] # enumerate all possible causes for P[x,y]_t, assuming moved to having moved
if walls_grid[x][y+1] != 1:
moved_causes.append( PropSymbolExpr(pacman_str, x, y+1, time=last)
& PropSymbolExpr('South', time=last))
if walls_grid[x][y-1] != 1:
moved_causes.append( PropSymbolExpr(pacman_str, x, y-1, time=last)
& PropSymbolExpr('North', time=last))
if walls_grid[x+1][y] != 1:
moved_causes.append( PropSymbolExpr(pacman_str, x+1, y, time=last)
& PropSymbolExpr('West', time=last))
if walls_grid[x-1][y] != 1:
moved_causes.append( PropSymbolExpr(pacman_str, x-1, y, time=last)
& PropSymbolExpr('East', time=last))
if not moved_causes:
return None
moved_causes_sent: Expr = conjoin([~PropSymbolExpr(pacman_str, x, y, time=last) , ~PropSymbolExpr(wall_str, x, y), disjoin(moved_causes)])
failed_move_causes: List[Expr] = [] # using merged variables, improves speed significantly
auxilary_expression_definitions: List[Expr] = []
for direction in DIRECTIONS:
dx, dy = DIR_TO_DXDY_MAP[direction]
wall_dir_clause = PropSymbolExpr(wall_str, x + dx, y + dy) & PropSymbolExpr(direction, time=last)
wall_dir_combined_literal = PropSymbolExpr(wall_str + direction, x + dx, y + dy, time=last)
failed_move_causes.append(wall_dir_combined_literal)
auxilary_expression_definitions.append(wall_dir_combined_literal % wall_dir_clause)
failed_move_causes_sent: Expr = conjoin([
PropSymbolExpr(pacman_str, x, y, time=last),
disjoin(failed_move_causes)])
return conjoin([PropSymbolExpr(pacman_str, x, y, time=now) % disjoin([moved_causes_sent, failed_move_causes_sent])] + auxilary_expression_definitions)
def pacphysicsAxioms(t: int, all_coords: List[Tuple], non_outer_wall_coords: List[Tuple], walls_grid: List[List] = None, sensorModel: Callable = None, successorAxioms: Callable = None) -> Expr:
"""
Given:
t: timestep
all_coords: list of (x, y) coordinates of the entire problem
non_outer_wall_coords: list of (x, y) coordinates of the entire problem,
excluding the outer border (these are the actual squares pacman can
possibly be in)
walls_grid: 2D array of either -1/0/1 or T/F. Used only for successorAxioms.
Do NOT use this when making possible locations for pacman to be in.
sensorModel(t, non_outer_wall_coords) -> Expr: function that generates
the sensor model axioms. If None, it's not provided, so shouldn't be run.
successorAxioms(t, walls_grid, non_outer_wall_coords) -> Expr: function that generates
the sensor model axioms. If None, it's not provided, so shouldn't be run.
Return a logic sentence containing all of the following:
- for all (x, y) in all_coords:
If a wall is at (x, y) --> Pacman is not at (x, y)
- Pacman is at exactly one of the squares at timestep t.
- Pacman takes exactly one action at timestep t.
- Results of calling sensorModel(...), unless None.
- Results of calling successorAxioms(...), describing how Pacman can end in various
locations on this time step. Consider edge cases. Don't call if None.
"""
pacphysics_sentences = []
# 1. 对于所有坐标如果有墙则Pacman不在该位置
for x, y in all_coords:
pacphysics_sentences.append(PropSymbolExpr(wall_str, x, y) >> ~PropSymbolExpr(pacman_str, x, y, time=t))
# 2. Pacman在t时刻恰好位于一个非外墙坐标
pacman_locations = [PropSymbolExpr(pacman_str, x, y, time=t) for x, y in non_outer_wall_coords]
pacphysics_sentences.append(exactlyOne(pacman_locations))
# 3. Pacman在t时刻恰好执行一个动作
actions = [PropSymbolExpr(action, time=t) for action in DIRECTIONS]
pacphysics_sentences.append(exactlyOne(actions))
# 4. 如果提供了sensorModel则添加传感器公理
if sensorModel:
pacphysics_sentences.append(sensorModel(t, non_outer_wall_coords))
# 5. 如果提供了successorAxioms则添加转移公理
if t!=0 and successorAxioms:
pacphysics_sentences.append(successorAxioms(t, walls_grid, non_outer_wall_coords))
return conjoin(pacphysics_sentences)
def checkLocationSatisfiability(x1_y1: Tuple[int, int], x0_y0: Tuple[int, int], action0, action1, problem):
"""
Given:
- x1_y1 = (x1, y1), a potential location at time t = 1
- x0_y0 = (x0, y0), Pacman's location at time t = 0
- action0 = one of the four items in DIRECTIONS, Pacman's action at time t = 0
- action1 = to ensure match with autograder solution
- problem = an instance of logicAgents.LocMapProblem
Note:
- there's no sensorModel because we know everything about the world
- the successorAxioms should be allLegalSuccessorAxioms where needed
Return:
- a model where Pacman is at (x1, y1) at time t = 1
- a model where Pacman is not at (x1, y1) at time t = 1
"""
walls_grid = problem.walls
walls_list = walls_grid.asList()
all_coords = list(itertools.product(range(problem.getWidth()+2), range(problem.getHeight()+2)))
non_outer_wall_coords = list(itertools.product(range(1, problem.getWidth()+1), range(1, problem.getHeight()+1)))
KB = []
x0, y0 = x0_y0
x1, y1 = x1_y1
# We know which coords are walls:
map_sent = [PropSymbolExpr(wall_str, x, y) for x, y in walls_list]
KB.append(conjoin(map_sent))
# Add pacphysics_axioms
KB.append(pacphysicsAxioms(0, all_coords, non_outer_wall_coords, walls_grid, None, successorAxioms= allLegalSuccessorAxioms))
KB.append(pacphysicsAxioms(1, all_coords, non_outer_wall_coords, walls_grid, None, successorAxioms= allLegalSuccessorAxioms))
# Add initial Pacman location
KB.append(PropSymbolExpr(pacman_str, x0, y0, time=0))
# Add action0 and action1
KB.append(PropSymbolExpr(action0, time=0))
KB.append(PropSymbolExpr(action1, time=1))
# Query for model1: Pacman is at (x1, y1) at time t = 1
query1 = PropSymbolExpr(pacman_str, x1, y1, time=1)
model1 = findModel(conjoin(KB) & query1)
# Query for model2: Pacman is not at (x1, y1) at time t = 1
query2 = ~PropSymbolExpr(pacman_str, x1, y1, time=1)
model2 = findModel(conjoin(KB) & query2)
return (model1, model2)
#______________________________________________________________________________
# QUESTION 4
def positionLogicPlan(problem) -> List:
"""
Given an instance of a PositionPlanningProblem, return a list of actions that lead to the goal.
Available actions are ['North', 'East', 'South', 'West']
Note that STOP is not an available action.
Overview: add knowledge incrementally, and query for a model each timestep. Do NOT use pacphysicsAxioms.
"""
walls_grid = problem.walls
width, height = problem.getWidth(), problem.getHeight()
walls_list = walls_grid.asList()
x0, y0 = problem.startState
xg, yg = problem.goal
# Get lists of possible locations (i.e. without walls) and possible actions
all_coords = list(itertools.product(range(width + 2),
range(height + 2)))
non_wall_coords = [loc for loc in all_coords if loc not in walls_list]
actions = [ 'North', 'South', 'East', 'West' ]
KB = []
"*** BEGIN YOUR CODE HERE ***"
util.raiseNotDefined()
"*** END YOUR CODE HERE ***"
#______________________________________________________________________________
# QUESTION 5
def foodLogicPlan(problem) -> List:
"""
Given an instance of a FoodPlanningProblem, return a list of actions that help Pacman
eat all of the food.
Available actions are ['North', 'East', 'South', 'West']
Note that STOP is not an available action.
Overview: add knowledge incrementally, and query for a model each timestep. Do NOT use pacphysicsAxioms.
"""
walls = problem.walls
width, height = problem.getWidth(), problem.getHeight()
walls_list = walls.asList()
(x0, y0), food = problem.start
food = food.asList()
# Get lists of possible locations (i.e. without walls) and possible actions
all_coords = list(itertools.product(range(width + 2), range(height + 2)))
non_wall_coords = [loc for loc in all_coords if loc not in walls_list]
actions = [ 'North', 'South', 'East', 'West' ]
KB = []
"*** BEGIN YOUR CODE HERE ***"
util.raiseNotDefined()
"*** END YOUR CODE HERE ***"
#______________________________________________________________________________
# QUESTION 6
def localization(problem, agent) -> Generator:
'''
problem: a LocalizationProblem instance
agent: a LocalizationLogicAgent instance
'''
walls_grid = problem.walls
walls_list = walls_grid.asList()
all_coords = list(itertools.product(range(problem.getWidth()+2), range(problem.getHeight()+2)))
non_outer_wall_coords = list(itertools.product(range(1, problem.getWidth()+1), range(1, problem.getHeight()+1)))
KB = []
"*** BEGIN YOUR CODE HERE ***"
util.raiseNotDefined()
for t in range(agent.num_timesteps):
"*** END YOUR CODE HERE ***"
yield possible_locations
#______________________________________________________________________________
# QUESTION 7
def mapping(problem, agent) -> Generator:
'''
problem: a MappingProblem instance
agent: a MappingLogicAgent instance
'''
pac_x_0, pac_y_0 = problem.startState
KB = []
all_coords = list(itertools.product(range(problem.getWidth()+2), range(problem.getHeight()+2)))
non_outer_wall_coords = list(itertools.product(range(1, problem.getWidth()+1), range(1, problem.getHeight()+1)))
# map describes what we know, for GUI rendering purposes. -1 is unknown, 0 is open, 1 is wall
known_map = [[-1 for y in range(problem.getHeight()+2)] for x in range(problem.getWidth()+2)]
# Pacman knows that the outer border of squares are all walls
outer_wall_sent = []
for x, y in all_coords:
if ((x == 0 or x == problem.getWidth() + 1)
or (y == 0 or y == problem.getHeight() + 1)):
known_map[x][y] = 1
outer_wall_sent.append(PropSymbolExpr(wall_str, x, y))
KB.append(conjoin(outer_wall_sent))
"*** BEGIN YOUR CODE HERE ***"
util.raiseNotDefined()
for t in range(agent.num_timesteps):
"*** END YOUR CODE HERE ***"
yield known_map
#______________________________________________________________________________
# QUESTION 8
def slam(problem, agent) -> Generator:
'''
problem: a SLAMProblem instance
agent: a SLAMLogicAgent instance
'''
pac_x_0, pac_y_0 = problem.startState
KB = []
all_coords = list(itertools.product(range(problem.getWidth()+2), range(problem.getHeight()+2)))
non_outer_wall_coords = list(itertools.product(range(1, problem.getWidth()+1), range(1, problem.getHeight()+1)))
# map describes what we know, for GUI rendering purposes. -1 is unknown, 0 is open, 1 is wall
known_map = [[-1 for y in range(problem.getHeight()+2)] for x in range(problem.getWidth()+2)]
# We know that the outer_coords are all walls.
outer_wall_sent = []
for x, y in all_coords:
if ((x == 0 or x == problem.getWidth() + 1)
or (y == 0 or y == problem.getHeight() + 1)):
known_map[x][y] = 1
outer_wall_sent.append(PropSymbolExpr(wall_str, x, y))
KB.append(conjoin(outer_wall_sent))
"*** BEGIN YOUR CODE HERE ***"
util.raiseNotDefined()
for t in range(agent.num_timesteps):
"*** END YOUR CODE HERE ***"
yield (known_map, possible_locations)
# Abbreviations
plp = positionLogicPlan
loc = localization
mp = mapping
flp = foodLogicPlan
# Sometimes the logic module uses pretty deep recursion on long expressions
sys.setrecursionlimit(100000)
#______________________________________________________________________________
# Important expression generating functions, useful to read for understanding of this project.
def sensorAxioms(t: int, non_outer_wall_coords: List[Tuple[int, int]]) -> Expr:
all_percept_exprs = []
combo_var_def_exprs = []
for direction in DIRECTIONS:
percept_exprs = []
dx, dy = DIR_TO_DXDY_MAP[direction]
for x, y in non_outer_wall_coords:
combo_var = PropSymbolExpr(pacman_wall_str, x, y, x + dx, y + dy, time=t)
percept_exprs.append(combo_var)
combo_var_def_exprs.append(combo_var % (
PropSymbolExpr(pacman_str, x, y, time=t) & PropSymbolExpr(wall_str, x + dx, y + dy)))
percept_unit_clause = PropSymbolExpr(blocked_str_map[direction], time = t)
all_percept_exprs.append(percept_unit_clause % disjoin(percept_exprs))
return conjoin(all_percept_exprs + combo_var_def_exprs)
def fourBitPerceptRules(t: int, percepts: List) -> Expr:
"""
Localization and Mapping both use the 4 bit sensor, which tells us True/False whether
a wall is to pacman's north, south, east, and west.
"""
assert isinstance(percepts, list), "Percepts must be a list."
assert len(percepts) == 4, "Percepts must be a length 4 list."
percept_unit_clauses = []
for wall_present, direction in zip(percepts, DIRECTIONS):
percept_unit_clause = PropSymbolExpr(blocked_str_map[direction], time=t)
if not wall_present:
percept_unit_clause = ~PropSymbolExpr(blocked_str_map[direction], time=t)
percept_unit_clauses.append(percept_unit_clause) # The actual sensor readings
return conjoin(percept_unit_clauses)
def numAdjWallsPerceptRules(t: int, percepts: List) -> Expr:
"""
SLAM uses a weaker numAdjWallsPerceptRules sensor, which tells us how many walls pacman is adjacent to
in its four directions.
000 = 0 adj walls.
100 = 1 adj wall.
110 = 2 adj walls.
111 = 3 adj walls.
"""
assert isinstance(percepts, list), "Percepts must be a list."
assert len(percepts) == 3, "Percepts must be a length 3 list."
percept_unit_clauses = []
for i, percept in enumerate(percepts):
n = i + 1
percept_literal_n = PropSymbolExpr(geq_num_adj_wall_str_map[n], time=t)
if not percept:
percept_literal_n = ~percept_literal_n
percept_unit_clauses.append(percept_literal_n)
return conjoin(percept_unit_clauses)
def SLAMSensorAxioms(t: int, non_outer_wall_coords: List[Tuple[int, int]]) -> Expr:
all_percept_exprs = []
combo_var_def_exprs = []
for direction in DIRECTIONS:
percept_exprs = []
dx, dy = DIR_TO_DXDY_MAP[direction]
for x, y in non_outer_wall_coords:
combo_var = PropSymbolExpr(pacman_wall_str, x, y, x + dx, y + dy, time=t)
percept_exprs.append(combo_var)
combo_var_def_exprs.append(combo_var % (PropSymbolExpr(pacman_str, x, y, time=t) & PropSymbolExpr(wall_str, x + dx, y + dy)))
blocked_dir_clause = PropSymbolExpr(blocked_str_map[direction], time=t)
all_percept_exprs.append(blocked_dir_clause % disjoin(percept_exprs))
percept_to_blocked_sent = []
for n in range(1, 4):
wall_combos_size_n = itertools.combinations(blocked_str_map.values(), n)
n_walls_blocked_sent = disjoin([
conjoin([PropSymbolExpr(blocked_str, time=t) for blocked_str in wall_combo])
for wall_combo in wall_combos_size_n])
# n_walls_blocked_sent is of form: (N & S) | (N & E) | ...
percept_to_blocked_sent.append(
PropSymbolExpr(geq_num_adj_wall_str_map[n], time=t) % n_walls_blocked_sent)
return conjoin(all_percept_exprs + combo_var_def_exprs + percept_to_blocked_sent)
def allLegalSuccessorAxioms(t: int, walls_grid: List[List], non_outer_wall_coords: List[Tuple[int, int]]) -> Expr:
"""walls_grid can be a 2D array of ints or bools."""
all_xy_succ_axioms = []
for x, y in non_outer_wall_coords:
xy_succ_axiom = pacmanSuccessorAxiomSingle(
x, y, t, walls_grid)
if xy_succ_axiom:
all_xy_succ_axioms.append(xy_succ_axiom)
return conjoin(all_xy_succ_axioms)
def SLAMSuccessorAxioms(t: int, walls_grid: List[List], non_outer_wall_coords: List[Tuple[int, int]]) -> Expr:
"""walls_grid can be a 2D array of ints or bools."""
all_xy_succ_axioms = []
for x, y in non_outer_wall_coords:
xy_succ_axiom = SLAMSuccessorAxiomSingle(
x, y, t, walls_grid)
if xy_succ_axiom:
all_xy_succ_axioms.append(xy_succ_axiom)
return conjoin(all_xy_succ_axioms)
#______________________________________________________________________________
# Various useful functions, are not needed for completing the project but may be useful for debugging
def modelToString(model: Dict[Expr, bool]) -> str:
"""Converts the model to a string for printing purposes. The keys of a model are
sorted before converting the model to a string.
model: Either a boolean False or a dictionary of Expr symbols (keys)
and a corresponding assignment of True or False (values). This model is the output of
a call to pycoSAT.
"""
if model == False:
return "False"
else:
# Dictionary
modelList = sorted(model.items(), key=lambda item: str(item[0]))
return str(modelList)
def extractActionSequence(model: Dict[Expr, bool], actions: List) -> List:
"""
Convert a model in to an ordered list of actions.
model: Propositional logic model stored as a dictionary with keys being
the symbol strings and values being Boolean: True or False
Example:
>>> model = {"North[2]":True, "P[3,4,0]":True, "P[3,3,0]":False, "West[0]":True, "GhostScary":True, "West[2]":False, "South[1]":True, "East[0]":False}
>>> actions = ['North', 'South', 'East', 'West']
>>> plan = extractActionSequence(model, actions)
>>> print(plan)
['West', 'South', 'North']
"""
plan = [None for _ in range(len(model))]
for sym, val in model.items():
parsed = parseExpr(sym)
if type(parsed) == tuple and parsed[0] in actions and val:
action, _, time = parsed
plan[time] = action
#return list(filter(lambda x: x is not None, plan))
return [x for x in plan if x is not None]
# Helpful Debug Method
def visualizeCoords(coords_list, problem) -> None:
wallGrid = game.Grid(problem.walls.width, problem.walls.height, initialValue=False)
for (x, y) in itertools.product(range(problem.getWidth()+2), range(problem.getHeight()+2)):
if (x, y) in coords_list:
wallGrid.data[x][y] = True
print(wallGrid)
# Helpful Debug Method
def visualizeBoolArray(bool_arr, problem) -> None:
wallGrid = game.Grid(problem.walls.width, problem.walls.height, initialValue=False)
wallGrid.data = copy.deepcopy(bool_arr)
print(wallGrid)
class PlanningProblem:
"""
This class outlines the structure of a planning problem, but doesn't implement
any of the methods (in object-oriented terminology: an abstract class).
You do not need to change anything in this class, ever.
"""
def getStartState(self):
"""
Returns the start state for the planning problem.
"""
util.raiseNotDefined()
def getGhostStartStates(self):
"""
Returns a list containing the start state for each ghost.
Only used in problems that use ghosts (FoodGhostPlanningProblem)
"""
util.raiseNotDefined()
def getGoalState(self):
"""
Returns goal state for problem. Note only defined for problems that have
a unique goal state such as PositionPlanningProblem
"""
util.raiseNotDefined()