It was run in the sandbox and it failed. run output shows an error/traceback — the artifact does NOT run clean.
$ python3 contract_bound_action_graph.py
Traceback (most recent call last):
File "/work/contract_bound_action_graph.py", line 121, in <module>
plan = graph.plan(initial_state, goal_state)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/work/contract_bound_action_graph.py", line 64, in plan
if goal_check(current_state):
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/work/contract_bound_action_graph.py", line 119, in <lambda>
goal_state = lambda s: s['position'] >= 10
~^^^^^^^^^^^^
TypeError: 'State' object is not subscriptableNo screenshot — there is nothing working to show. This is recorded as an unfinished sketch so the attempt stays visible instead of being quietly dropped.
All of it — 128 lines, one file, standard library only.
from heapq import heappush, heappop
class Contract:
def __init__(self, preconditions, invariants, postconditions):
self.preconditions = preconditions
self.invariants = invariants
self.postconditions = postconditions
def validate_preconditions(self, state):
return all(condition(state) for condition in self.preconditions)
def validate_invariants(self, state):
return all(invariant(state) for invariant in self.invariants)
def validate_postconditions(self, state):
return all(postcondition(state) for postcondition in self.postconditions)
class Action:
def __init__(self, name, preconditions, effects, contract=None):
self.name = name
self.preconditions = preconditions
self.effects = effects
self.contract = contract
class State:
def __init__(self, **kwargs):
self.variables = kwargs
def __eq__(self, other):
return self.variables == other.variables
def __hash__(self):
return hash(frozenset(self.variables.items()))
class ContractBoundActionGraph:
def __init__(self):
self.actions = {}
self.states = {}
def add_action(self, action_name, action):
self.actions[action_name] = action
def get_available_actions(self, state):
return [action for action in self.actions.values()
if (action.contract is None or
action.contract.validate_preconditions(state))]
def execute_action(self, state, action):
new_state = State(**state.variables)
for eff in action.effects:
eff(new_state)
if action.contract and not action.contract.validate_postconditions(new_state):
raise ValueError("Contract postconditions not satisfied after action")
return new_state
def plan(self, initial_state, goal_check):
open_set = [(0, initial_state)]
came_from = {}
g_score = {initial_state: 0}
while open_set:
current_cost, current_state = heappop(open_set)
if goal_check(current_state):
return self.reconstruct_path(came_from, current_state)
for action in self.plan_graph.get_available_actions(current_state):
next_state = self.plan_graph.execute_action(current_state, action)
tentative_g = g_score[current_state] + 1 # Uniform cost
if next_state not in g_score or tentative_g < g_score[next_state]:
came_from[next_state] = (current_state, action)
g_score[next_state] = tentative_g
heappush(open_set, (tentative_g, next_state))
return None
def reconstruct_path(self, came_from, current_state):
path = []
while current_state in came_from:
current_state, action = came_from[current_state]
path.append(action)
return list(reversed(path))
# Example usage
if __name__ == "__main__":
graph = ContractBoundActionGraph()
# Define simple contracts
battery_condition = lambda s: s['battery'] > 0
charging_condition = lambda s: s['charging']
charge_action = Action(
name="charge",
preconditions=[charging_condition],
effects=[lambda s: s.__setitem__('battery', s['battery'] + 10)],
contract=Contract(
preconditions=[charging_condition],
invariants=[battery_condition],
postconditions=[lambda s: s['battery'] > s['battery'] - 10]
)
)
move_action = Action(
name="move",
preconditions=[battery_condition],
effects=[lambda s: s.__setitem__('position', s['position'] + 1)],
contract=Contract(
preconditions=[battery_condition],
invariants=[lambda s: s['battery'] >= 0],
postconditions=[lambda s: s['position'] == s['position'] - 1 + 1]
)
)
graph.add_action("charge", charge_action)
graph.add_action("move", move_action)
initial_state = State(battery=50, position=0, charging=True)
goal_state = lambda s: s['position'] >= 10
plan = graph.plan(initial_state, goal_state)
if plan:
print("Plan found:")
for action in plan:
print(f"- {action.name}")
else:
print("No plan found")