def check_action(self, node: _Node, state: State, action: Action) -> bool: # Find the last action before a navigation action # TODO: Fold this behaviour into ChainingOptions.check_action() nav_parent = node while nav_parent.action is not None and self._is_navigation( nav_parent.action): # HACK: Going through a door is always considered navigation unless the previous action was to open that door. parent = nav_parent.parent if parent.action is not None and parent.action.name == "open/d": break if self.backward and action.name == "open/d": break nav_parent = parent if nav_parent.action is not None and not self._is_navigation(action): if self.backward: recent = action.inverse() pre_navigation = recent post_navigation = nav_parent.action.inverse() else: recent = node.action pre_navigation = nav_parent.action post_navigation = action relevant = set(post_navigation.preconditions) if len(recent.added & relevant) == 0 or len(pre_navigation.added & relevant) == 0: return False return self.options.check_action(state, action)
def apply(self, node: _Node, action: Action) -> Optional[State]: """Attempt to apply an action to the given state.""" new_state = node.state.copy() for prop in action.preconditions: new_state.add_fact(prop) # Make sure new_state still respects the constraints if not self.check_state(new_state): return None new_state.apply(action) # Detect cycles state = new_state.copy() state.apply(action.inverse()) while node.action: state.apply(node.action.inverse()) if new_state == state: return None node = node.parent return new_state