# # Module: strips # # This module provides classes for representing STRIPS-based # planning domains: # Predicate - used to represent conditions in states and operators # Operator - used to represent STRIPS operators # STRIPS - a "SearchDomain" for planning with STRIPS operators # # (c) Luis Seabra Lopes # InteligĂȘncia Artificial & Introducao a Inteligencia Artificial, 2019 # from tree_search import * from functools import reduce from itertools import product # Predicates used to describe states, preconditions and effects class Predicate: def __str__(self): argsstr = args2string(self.args) return type(self).__name__ + "(" + argsstr + ")" def __repr__(self): return str(self) def __eq__(self,predicate): # allows for comparisons with "==", etc. return str(self)==str(predicate) def substitute(self,assign): # Substitute the arguments in a predicate la = self.args # by constants according to a given if len(la)==0: # assignment (i.e. a dictionary) return type(self)() if len(la)==1: return type(self)(assign[la[0]]) # add other cases if needed return type(self)(assign[la[0]],assign[la[1]]) # STRIPS operators # -- operators for a specific domain will be subclasses # -- concrete actions will be instances of specific operators class Operator: def __init__(self,args,pc,neg,pos): self.args = args self.pc = pc self.neg = neg self.pos = pos def __str__(self): return type(self).__name__ + '([' + args2string(self.args) + "]," + \ str(self.pc) + ',' + str(self.neg) + ',' + \ str(self.pos) + ')' def __repr__(self): argsstr = args2string(self.args) return type(self).__name__ + "(" + argsstr + ")" # Produce a concrete action by instanciating a specific # operator (i.e. the "Operator" subclass where the method was # called) for the arguments given in "args" # ( returns None if the action is not applicable in the given "state" ) @classmethod def instanciate(cls,args): if len(args)!=len(cls.args): return None assign = dict(zip(cls.args, args)) pc = [ p.substitute(assign) for p in cls.pc ] neg = [ p.substitute(assign) for p in cls.neg ] pos = [ p.substitute(assign) for p in cls.pos ] return cls(args,pc,neg,pos) # Search domains based on STRIPS actions class STRIPS(SearchDomain): # constructor def __init__(self): pass # list of applicable actions in a given "state" def actions(self, state): constants = state_constants(state) operators = Operator.__subclasses__() actions = [] for op in operators: lassign = assignments(op.args,constants) for assign in lassign: argvalues = [assign[a] for a in op.args] action = op.instanciate(argvalues) if all(c in state for c in action.pc): actions.append(action) return actions # Result of a given "action" in a given "state" # ( returns None, if the action is not applicable in the state) def result(self, state, action): pass def cost(self, state, action): return 1 def heuristic(self, state, goal): return 0 # Checks if a given "goal" is satisfied in a given "state" def satisfies(self, state, goal): pass # Auxiliary functions def state_constants(state): return list(set(reduce(lambda r,h : h.args+r, state,[]))) def assignments(lvars,lconsts): lcombs = product(lconsts,repeat=len(lvars)) makeassign = lambda comb : dict(zip(lvars,comb)) return list(map(makeassign,lcombs)) def args2string(args): if args == []: return "" return reduce(lambda r,h : r+','+str(h), args[1:],str(args[0]))