def test_scan_tokens_domain(self): parser = PDDL_Parser() self.assertEqual(parser.scan_tokens('examples/dinner/dinner.pddl'), [ 'define', ['domain', 'dinner'], [':requirements', ':strips'], [ ':predicates', ['clean'], ['dinner'], ['quiet'], ['present'], ['garbage'] ], [ ':action', 'cook', ':precondition', ['clean'], ':effect', ['dinner'] ], [ ':action', 'wrap', ':precondition', ['quiet'], ':effect', ['present'] ], [ ':action', 'carry', ':precondition', ['garbage'], ':effect', ['and', ['not', ['garbage']], ['not', ['clean']]] ], [ ':action', 'dolly', ':precondition', ['garbage'], ':effect', ['and', ['not', ['garbage']], ['not', ['quiet']]] ] ])
def test_parse_predicates(self): parser = PDDL_Parser() parser.predicates = {} parser.parse_predicates( [['untyped_pred', '?v1', '?v2', '?v3'], [ 'typed_pred', '?v1', '-', 'type1', '?v2', '-', 'type1', '?v3', '-', 'object' ], ['shared_type_pred', '?v1', '?v2', '-', 'type1', '?v3']]) self.assertEqual( parser.predicates, { 'untyped_pred': { '?v1': 'object', '?v2': 'object', '?v3': 'object' }, 'typed_pred': { '?v1': 'type1', '?v2': 'type1', '?v3': 'object' }, 'shared_type_pred': { '?v1': 'type1', '?v2': 'type1', '?v3': 'object' } })
def test_parse_undefined_types(self): parser = PDDL_Parser() parser.types = {} parser.parse_types(['location', 'pile', 'robot', 'crane', 'container']) self.assertEqual( parser.types, {'object': ['location', 'pile', 'robot', 'crane', 'container']})
def __init__(self, filename, domainName='reconstructed'): self.domainName = domainName self.parser = PDDL_Parser() self.tokens = self.parser.scan_tokens(filename) pprint.pprint(self.tokens) print('=== Objects ===') self.parseObjects(self.tokens[1]) pprint.pprint(self.types2objs) print('=== States ===') self.parseStates(self.tokens) pprint.pprint(self.states) print('=== Predicates ===') pprint.pprint(self.predicates) print('=== Actions ===') self.parseActions(self.tokens) # print(self.tokens[3]) # p, n = self.actions[0].assignPrecons(self.tokens[3][1][1:]) # print('Grounded Positive preconditions') # print(p) # print('Grounded Negative preconditions') # print(n) # print('Before State') # print(self.states[0]) # self.actions[0].prunePrecons(self.states[0], self.tokens[3][1][1:]) # print(self.actions[0]) self.refineActions(self.tokens) pprint.pprint(self.actions) self.genTypeclasses()
def solve(self, domain, problem): # Parser parser = PDDL_Parser() parser.parse_domain(domain) parser.parse_problem(problem) # Parsed data actions = parser.actions state = parser.state goal_pos = parser.positive_goals goal_not = parser.negative_goals # Do nothing if self.applicable(state, goal_pos, goal_not): return [] # Search visited = [state] fringe = [state, None] while fringe: state = fringe.pop(0) plan = fringe.pop(0) for act in actions: if self.applicable(state, act.positive_preconditions, act.negative_preconditions): new_state = self.apply(state, act.add_effects, act.del_effects) if new_state not in visited: if self.applicable(new_state, goal_pos, goal_not): full_plan = [act] while plan: act, plan = plan full_plan.insert(0, act) return full_plan visited.append(new_state) fringe.append(new_state) fringe.append((act, plan)) return None
def test_scan_tokens_problem(self): parser = PDDL_Parser() self.assertEqual(parser.scan_tokens('examples/dinner/pb1.pddl'), [ 'define', ['problem', 'pb1'], [':domain', 'dinner'], [':init', ['garbage'], ['clean'], ['quiet']], [':goal', ['and', ['dinner'], ['present'], ['not', ['garbage']]]] ])
def test_scan_tokens_problem(self): parser = PDDL_Parser() self.assertEqual(parser.scan_tokens('dinner/pb1.pddl'), ['define', ['problem', 'pb1'], [':domain', 'dinner'], [':init', ['garbage'], ['clean'], ['quiet']], [':goal', ['and', ['dinner'], ['present'], ['not', ['garbage']]]]] )
def test_parse_problem(self): parser = PDDL_Parser() parser.parse_domain('dinner/dinner.pddl') parser.parse_problem('dinner/pb1.pddl') self.assertEqual(parser.problem_name, 'pb1') self.assertEqual(parser.objects, []) self.assertEqual(parser.state, [['garbage'],['clean'],['quiet']]) self.assertEqual(parser.positive_goals, [['dinner'], ['present']]) self.assertEqual(parser.negative_goals, [['garbage']])
def test_parse_domain(self): parser = PDDL_Parser() parser.parse_domain('dinner/dinner.pddl') self.assertEqual(parser.domain_name, 'dinner') self.assertEqual(parser.actions, [ Action('cook', [], [['clean']], [], [['dinner']], []), Action('wrap', [], [['quiet']], [], [['present']], []), Action('carry', [], [['garbage']], [], [], [['garbage'], ['clean']]), Action('dolly', [], [['garbage']], [], [], [['garbage'], ['quiet']]) ] )
def __init__(self, domainfile, problemfile): self.parser_help = PDDL_Parser() self.parser_help.parse_domain(domainfile) self.parser_help.parse_problem(problemfile) self.domprob = DomainProblem(domainfile, problemfile) self.current_state = None # State object self.pos_goal = None # tuple of literals that need to be true at goal self.neg_goal = None # tuple of literals that need to be false at goal self.action_dic = None # list of actions self.objects = None # list of world objects self.reversed_objects = None # for type of object, list of instances self.parser()
def test_parse_defined_types(self): parser = PDDL_Parser() parser.types = {} parser.parse_types([ 'place', 'locatable', 'level', '-', 'object', 'depot', 'market', '-', 'place', 'truck', 'goods', '-', 'locatable' ]) self.assertEqual( parser.types, { 'object': ['place', 'locatable', 'level'], 'place': ['depot', 'market'], 'locatable': ['truck', 'goods'] })
def test_parse_domain(self): parser = PDDL_Parser() parser.parse_domain('dinner/dinner.pddl') self.assertEqual(parser.domain_name, 'dinner') self.assertEqual(parser.requirements, [':strips']) self.assertEqual(parser.types, []) self.assertEqual(parser.actions, [ Action('cook', [], [['clean']], [], [['dinner']], []), Action('wrap', [], [['quiet']], [], [['present']], []), Action('carry', [], [['garbage']], [], [], [['garbage'], ['clean']]), Action('dolly', [], [['garbage']], [], [], [['garbage'], ['quiet']]) ])
def __init__(self, domain, problem, logfile='/dev/null'): # Parser self.parser = PDDL_Parser() self.parser.parse_domain(domain) self.parser.parse_problem(problem) # Parsed data self.state = self.parser.state self.goal_pos = self.parser.positive_goals self.goal_not = self.parser.negative_goals self.history = [] self.movelog = [] self.logfile = logfile self.planner = Planner() # Do nothing if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Puzzle is already solved! Double-check your problem file!')
def constraints(domain, problem): clause_list = [] # Parser parser = PDDL_Parser() parser.parse_domain(domain) parser.parse_problem(problem) state = parser.state # Initial state clauses: initial_clauses = list(state) #print(initial_clauses) goal_pos = parser.positive_goals goal_not = parser.negative_goals goal_clauses = [goal_pos, goal_not] action_list = [] # Grounding process ground_actions = [] for action in parser.actions: for act in action.groundify(parser.objects): ground_actions.append(act) # Appending grounded actions: for act in ground_actions: action_list.append(act) #print(act) action_list.append(Action('noop', (), [], [], [], [])) # Appending to one list: clause_list.append(initial_clauses) clause_list.append(goal_clauses) clause_list.extend(action_list) return clause_list
def test_scan_tokens_domain(self): parser = PDDL_Parser() self.assertEqual(parser.scan_tokens('dinner/dinner.pddl'), ['define', ['domain', 'dinner'], [':requirements', ':strips'], [':predicates', ['clean'], ['dinner'], ['quiet'], ['present'], ['garbage']], [':action', 'cook', ':parameters', [], ':precondition', ['and', ['clean']], ':effect', ['and', ['dinner']]], [':action', 'wrap', ':parameters', [], ':precondition', ['and', ['quiet']], ':effect', ['and', ['present']]], [':action', 'carry', ':parameters', [], ':precondition', ['and', ['garbage']], ':effect', ['and', ['not', ['garbage']], ['not', ['clean']]]], [':action', 'dolly', ':parameters', [], ':precondition', ['and', ['garbage']], ':effect', ['and', ['not', ['garbage']], ['not', ['quiet']]]]] )
def test_parse_problem(self): parser = PDDL_Parser() parser.parse_domain('examples/dinner/dinner.pddl') parser.parse_problem('examples/dinner/pb1.pddl') self.assertEqual(parser.problem_name, 'pb1') self.assertEqual(parser.objects, {}) self.assertEqual(parser.state, [['garbage'],['clean'],['quiet']]) self.assertEqual(parser.positive_goals, [['dinner'], ['present']]) self.assertEqual(parser.negative_goals, [['garbage']])
def test_parse_objects(self): parser = PDDL_Parser() parser.types = {} parser.objects = {} parser.parse_types( ['airplane', 'segment', 'direction', 'airplanetype', 'a']) parser.parse_objects([ 'b', '-', 'a', 'a', '-', 'a', 'north', 'south', '-', 'direction', 'light', 'medium', 'heavy', '-', 'airplanetype', 'element1', '-', 'object', 'seg_pp_0_60', 'seg_ppdoor_0_40', '-', 'segment', 'airplane_CFBEG', '-', 'airplane', 'element2' ], 'test') self.assertEqual( parser.objects, { 'a': ['b', 'a'], 'object': ['element1', 'element2'], 'direction': ['north', 'south'], 'airplanetype': ['light', 'medium', 'heavy'], 'segment': ['seg_pp_0_60', 'seg_ppdoor_0_40'], 'airplane': ['airplane_CFBEG'] })
def test_parse_problem(self): def frozenset_of_tuples(data): return frozenset([tuple(t) for t in data]) parser = PDDL_Parser() parser.parse_domain('examples/dinner/dinner.pddl') parser.parse_problem('examples/dinner/pb1.pddl') self.assertEqual(parser.problem_name, 'pb1') self.assertEqual(parser.objects, {}) self.assertEqual( parser.state, frozenset_of_tuples([['garbage'], ['clean'], ['quiet']])) self.assertEqual(parser.positive_goals, frozenset_of_tuples([['dinner'], ['present']])) self.assertEqual(parser.negative_goals, frozenset_of_tuples([['garbage']]))
def constraints(domain, problem): gate_list = [] # Parser parser = PDDL_Parser() parser.parse_domain(domain) parser.parse_problem(problem) state = parser.state # Initial state gate: initial_gate = list(state) #initial_gate = refine_gate(initial_gate) #print(initial_gate) goal_pos = parser.positive_goals goal_not = parser.negative_goals goal_gate = [goal_pos, goal_not] #goal_gate = [refine_gate(goal_pos), refine_gate(goal_not)] #print(goal_gate) #for act in parser.actions: # #print(act) # act.positive_preconditions = refine_gate(act.positive_preconditions) # act.negative_preconditions = refine_gate(act.negative_preconditions) # act.add_effects = refine_gate(act.add_effects) # act.del_effects = refine_gate(act.del_effects) # #print(act) action_list = [] # Grounding process ground_actions = [] for action in parser.actions: for act in action.groundify(parser.objects): ground_actions.append(act) # Appending grounded actions: for act in ground_actions: action_list.append(act) #print(act) # Appending to one list: gate_list.append(initial_gate) gate_list.append(goal_gate) gate_list.extend(action_list) return gate_list
def construct(self, domain, problem): # Parser parser = PDDL_Parser() parser.parse_domain(domain) parser.parse_problem(problem) # Parsed data state = parser.state initial_state = convert(state) goal_pos = parser.positive_goals goal_not = parser.negative_goals # Do nothing if self.applicable(state, goal_pos, goal_not): return [] # Grounding process ground_actions = [] for action in parser.actions: for act in action.groundify(parser.objects): ground_actions.append(act) # Search visited = [state] need_visit = [state] transitions = dict() while need_visit: state = need_visit.pop(0) transitions[convert(state)] = dict() for act in ground_actions: if self.applicable(state, act.positive_preconditions, act.negative_preconditions): new_state = self.apply(state, act.add_effects, act.del_effects) if new_state not in visited: visited.append(new_state) need_visit.append(new_state) transitions[convert(state)][act.name] = convert( new_state) return [transitions, initial_state]
class Engine: def __init__(self, domain, problem, logfile='/dev/null'): # Parser self.parser = PDDL_Parser() self.parser.parse_domain(domain) self.parser.parse_problem(problem) # Parsed data self.state = self.parser.state self.goal_pos = self.parser.positive_goals self.goal_not = self.parser.negative_goals self.history = [] self.movelog = [] self.logfile = logfile self.planner = Planner() # Do nothing if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Puzzle is already solved! Double-check your problem file!') def parseCellName(self, cellName): x, y = cellName[4:].split('_') # print(cellName) # print(out) return int(x), int(y) def findPlayer(self): for pred in self.state: if pred[0] == 'player': return self.parseCellName(pred[1]) raise ValueError('Player not found!') def formatPos(self, coords): return 'cell{}_{}'.format(coords[0], coords[1]) def groundAction(self, act, assignment): variables = [] for v, _ in act.parameters: variables.append(v) pps = act.replace(act.positive_preconditions, variables, assignment) nps = act.replace(act.negative_preconditions, variables, assignment) aes = act.replace(act.add_effects, variables, assignment) des = act.replace(act.del_effects, variables, assignment) return Action(act.name, assignment, pps, nps, aes, des) def addVec(self, *vecs): x = 0 y = 0 for u, v in vecs: x += u y += v return (x, y) # act must already be grounded (e.g. by self.groundAction) def tryAction(self, act, log): # print(self.state) # print(act.positive_preconditions) # print(act.negative_preconditions) # print(self.planner.applicable(self.state, act.positive_preconditions, act.negative_preconditions)) if self.planner.applicable(self.state, act.positive_preconditions, act.negative_preconditions): # log.write(str(self.state) + '\n') # log.write(str(act)) act_str = '(:action ({}))'.format(' '.join( [act.name, *act.parameters])) self.history.append(self.state) self.state = self.planner.apply(self.state, act.add_effects, act.del_effects) # log.write(self.lispState() + '\n\n') self.movelog.append('{}\n\n{}\n\n'.format(act_str, self.lispState())) return True else: return False def lookupAction(self, actName): for act in self.parser.actions: if act.name == actName: return act return None def doMove(self, key, log): delta = None actions = [] if key == 'w': delta = (0, -1) actions = ['movenorth', 'pushnorth'] elif key == 'a': delta = (-1, 0) actions = ['movewest', 'pushwest'] elif key == 's': delta = (0, 1) actions = ['movesouth', 'pushsouth'] elif key == 'd': delta = (1, 0) actions = ['moveeast', 'pusheast'] elif key == 'u': if len(self.history) >= 1: self.state = self.history.pop() self.movelog.pop() return True elif key == 'r': self.state = self.parser.state # self.history.append(self.state) self.history = [self.state] self.movelog = [] return True else: # log.write('Unparseable input: {}\n\n'.format(key)) return False # print(key, delta, actions) playerPos = self.findPlayer() playerCell = self.formatPos(playerPos) nextCell = self.formatPos(self.addVec(playerPos, delta)) afterCell = self.formatPos(self.addVec(playerPos, delta, delta)) # print(playerPos, playerCell, nextCell, afterCell) act = self.lookupAction(actions[0]) gact = self.groundAction(act, [playerCell, nextCell]) # print(gact) if self.tryAction(gact, log): return True else: act = self.lookupAction(actions[1]) gact = self.groundAction(act, [playerCell, nextCell, afterCell]) # print(gact) if self.tryAction(gact, log): return True # log.write('Blocked move: {}\n\n'.format(actions)) return False tiles = { -1: '[]', # Wall 1: ' ', # Floor 3: '()', # Boulder on floor 5: '//', # Goal on floor 7: '{}', # Goal and boulder on floor 9: ':)', # Player on floor 13: '%)' } # Goal and player on floor def render(self): w, h = 0, 0 for cell in self.parser.objects['object']: x, y = self.parseCellName(cell) w = max(w, x + 1) h = max(h, y + 1) for y in range(h): for x in range(w): cell = self.formatPos((x, y)) code = 0 # print(cell) # print(self.state) if self.planner.applicable(self.state, [['wall', cell]], []): code = -1 else: if self.planner.applicable(self.state, [['floor', cell]], []): code = 1 if self.planner.applicable(self.state, [['ball', cell]], []): code += 2 if self.planner.applicable(self.goal_pos, [['ball', cell]], []): code += 4 if self.planner.applicable(self.state, [['player', cell]], []): code += 8 print(self.tiles[code], end='') print() def lispState(self, word=':state'): out = [] out.append('({}'.format(word)) for pred in self.state: out.append(' (') out.append(' '.join(pred)) out.append(')') out.append(')') return ''.join(out) def gameloop(self): with open(self.logfile, 'w') as log: # log.write('{}\n\n'.format(datetime.datetime.now())) log.write('(trajectory\n\n') log.write('(:objects ') for t, os in self.parser.objects.items(): for o in os: log.write('{} - {} '.format(o, t)) log.write(')\n\n') log.write(self.lispState(':init')) log.write('\n\n') while True: self.render() if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Winningness!') log.write(''.join(self.movelog)) log.write(')') return prevTime = time.time() key = input('Choose direction (wasdur, followed by Enter): ') # log.write('{}\n\n'.format(time.time() - prevTime)) self.doMove(key, log)
class Domain(): def __init__(self, domainfile, problemfile): self.parser_help = PDDL_Parser() self.parser_help.parse_domain(domainfile) self.parser_help.parse_problem(problemfile) self.domprob = DomainProblem(domainfile, problemfile) self.current_state = None # State object self.pos_goal = None # tuple of literals that need to be true at goal self.neg_goal = None # tuple of literals that need to be false at goal self.action_dic = None # list of actions self.objects = None # list of world objects self.reversed_objects = None # for type of object, list of instances self.parser() def parser(self): world_dic = self.domprob.worldobjects() # object list obj_dic = dict() for obj in world_dic.keys(): wo = WorldObject(name=obj) wo.family = world_dic[obj] obj_dic[obj] = wo # reversed objects self.reversed_objects = defaultdict(list) for obj, typ in self.domprob.problem.objects.items(): self.reversed_objects[typ].append(obj) for type, subtypes in self.parser_help.types.items( ): # add reference to subtypes for sub in subtypes: self.reversed_objects[type] += self.reversed_objects[sub] # action list action_dic = dict() for op in self.domprob.operators(): my_op = Action(name=op) action_dic[op] = my_op for op in action_dic.keys(): operator = self.domprob.domain.operators[op] var_names = operator.variable_list.keys() var_types = operator.variable_list.values() var_combinations = itertools.product( *[self.reversed_objects[typ] for typ in var_types]) for var_list in var_combinations: value = dict() # ground preconditions and effects value["preconditions_pos"] = { prec.ground({ name: inst for (name, inst) in zip(var_names, var_list) }) for prec in operator.precondition_pos } value["preconditions_neg"] = { prec.ground({ name: inst for (name, inst) in zip(var_names, var_list) }) for prec in operator.precondition_neg } value["effets_pos"] = { prec.ground({ name: inst for (name, inst) in zip(var_names, var_list) }) for prec in operator.effect_pos } value["effets_neg"] = { prec.ground({ name: inst for (name, inst) in zip(var_names, var_list) }) for prec in operator.effect_neg } action_dic[op].action_dic[tuple(var_list)] = value init_state_set = set() init_state = self.domprob.initialstate() init_state_set = set() for state in init_state: init_state_set.add(tuple(state.predicate)) # goal self.pos_goal = set(self.parser_help.positive_goals) self.neg_goal = set(self.parser_help.negative_goals) ## Update ## self.current_state = State(true_predicates=init_state_set, pos_goal_state=self.pos_goal, neg_goal_state=self.neg_goal) self.action_dic = action_dic self.objects = obj_dic
class Engine: # Params: # domain: Name of domain file; should be 'soko3/soko3/pddl' # problem: Name of problem file encoding the level to be played, e.g. soko3/levelp3.pddl # logfile: Name of file the trajectory should be written to; defaults to /dev/null # verbose: If true, will write restarts, undoes, and failed moves to the trajectory file def __init__(self, domain, problem, logfile='/dev/null', verbose=False): # Parser self.parser = PDDL_Parser() self.parser.parse_domain(domain) self.parser.parse_problem(problem) # Parsed data self.state = self.parser.state self.goal_pos = self.parser.positive_goals self.goal_not = self.parser.negative_goals self.history = [] self.movelog = [] self.logfile = logfile self.verbose = verbose self.planner = Planner() # Do nothing if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Puzzle is already solved! Double-check your problem file!') def parseCellName(self, cellName): _, x, y = cellName.split('-') # print(cellName) # print(out) return int(x), int(y) def findPlayer(self): for pred in self.state: if pred[0] == 'at' and pred[1] == 'player-01': return self.parseCellName(pred[2]) raise ValueError('Player not found!') def formatPos(self, coords): return 'pos-{:02}-{:02}'.format(coords[0], coords[1]) def groundAction(self, act, assignment): variables = [] for v, _ in act.parameters: variables.append(v) pps = act.replace(act.positive_preconditions, variables, assignment) nps = act.replace(act.negative_preconditions, variables, assignment) aes = act.replace(act.add_effects, variables, assignment) des = act.replace(act.del_effects, variables, assignment) return Action(act.name, assignment, pps, nps, aes, des) def addVec(self, *vecs): x = 0 y = 0 for u, v in vecs: x += u y += v return (x, y) # act must already be grounded (e.g. by self.groundAction) def tryAction(self, act, log): # print(self.state) # print(act.positive_preconditions) # print(act.negative_preconditions) # print(self.planner.applicable(self.state, act.positive_preconditions, act.negative_preconditions)) if self.planner.applicable(self.state, act.positive_preconditions, act.negative_preconditions): success = True suffix = '' else: success = False suffix = '-failed' if success or self.verbose: # log.write(str(self.state) + '\n') # log.write(str(act)) print('Action: {} {}'.format(act.name, act.parameters)) try: act_str = '(:action{} ({}))'.format( suffix, ' '.join([act.name, *act.parameters])) if success: self.history.append(self.state) self.state = self.planner.apply(self.state, act.add_effects, act.del_effects) # log.write(self.lispState() + '\n\n') self.movelog.append('{}\n\n{}\n\n'.format( act_str, self.lispState())) except TypeError: # Tried to move or push a boulder off the grid or into a wall (in sokoban-sequential, those are the same thing). # This can only be a failed action, but trying to log it crashes this script (hence this try-except block), and would cause problems for trajectory.py down the line. # So, don't attempt to log this action. # This might come back to bite me later, but I'll cross that bridge when I get there. pass return success def lookupAction(self, actName): for act in self.parser.actions: if act.name == actName: return act return None # Finds the next cell adjacent to curCell in direction direc def findNextCell(self, curCell, direc): for cell in self.parser.objects['location']: if self.planner.applicable(self.state, [['move-dir', curCell, cell, direc]], []): return cell return None def getStone(self, cell): for stone in self.parser.objects['stone']: if self.planner.applicable(self.state, [['at', stone, cell]], []): return stone return None def doMove(self, key, log): direc = None if key == 'u': if len(self.history) >= 1: self.state = self.history.pop() if self.verbose: self.movelog.append('(:undo)\n\n{}\n\n'.format( self.lispState())) else: self.movelog.pop() return True elif key == 'r': self.state = self.parser.state # self.history.append(self.state) self.history = [self.state] if self.verbose: self.movelog.append('(:restart)\n\n{}\n\n'.format( self.lispState())) else: self.movelog = [] return True elif key == 'w': direc = 'dir-up' elif key == 's': direc = 'dir-down' elif key == 'a': direc = 'dir-left' elif key == 'd': direc = 'dir-right' else: # log.write('Unparseable input: {}\n\n'.format(key)) return False # print(key, delta, actions) playerPos = self.findPlayer() # print(key, currentCell) # Put player's cell, next cell, and cell after in a list cells = [self.formatPos(playerPos)] cells.append(self.findNextCell(cells[0], direc)) cells.append(self.findNextCell(cells[1], direc)) # Try move action assignment = ['player-01'] assignment.extend(cells[0:2]) assignment.append(direc) gact = self.groundAction(self.lookupAction('move'), assignment) if self.tryAction(gact, log): return True # If that failed, try push-to-nongoal assignment.insert(1, self.getStone(cells[1])) assignment.insert(4, cells[2]) gact = self.groundAction(self.lookupAction('push-to-goal'), assignment) if self.tryAction(gact, log): return True # And if that failed, try push-to-goal gact = self.groundAction(self.lookupAction('push-to-nongoal'), assignment) if self.tryAction(gact, log): return True return False # for act in self.parser.actions: # currentCell = self.formatPos(playerPos) # assignment = [key] # while len(assignment) < len(act.parameters): # assignment.append(currentCell) # currentCell = self.findNextCell(currentCell, key) # Having to do this for every cell parameter in every action is inefficient; maybe improve later? # gact = self.groundAction(act, assignment) # # print(gact) # if self.tryAction(gact, log): # return True # return False # nextCell = self.formatPos(self.addVec(playerPos, delta)) # afterCell = self.formatPos(self.addVec(playerPos, delta, delta)) # # print(playerPos, playerCell, nextCell, afterCell) # act = self.lookupAction(actions[0]) # gact = self.groundAction(act, [playerCell, nextCell]) # # print(gact) # if self.tryAction(gact, log): # return True # else: # act = self.lookupAction(actions[1]) # gact = self.groundAction(act, [playerCell, nextCell, afterCell]) # # print(gact) # if self.tryAction(gact, log): # return True # # log.write('Blocked move: {}\n\n'.format(actions)) # return False predIDs = {'wall': 1, 'player': 2, 'ball': 4, 'pit': 8, 'goal': 16} tiles = { 0: ' ', # Floor 1: '[]', # Wall 2: ':)', # Player 4: '()', # Boulder 8: '\/', # Pit 16: '//', # Goal 18: '%)', # Goal and player 20: '{}' } # Goal and boulder nonwalls = set() def findNonWalls(self): for pred in self.state: # print('Checking {}'.format(pred)) if pred[0] == 'move-dir': self.nonwalls.add(pred[1]) self.nonwalls.add(pred[2]) def renderCell(self, cell): if cell not in self.nonwalls: return '[]' elif self.formatPos(self.findPlayer()) == cell: if self.planner.applicable(self.state, [['is-goal', cell]], []): return '%)' else: return ':)' elif self.getStone(cell) is not None: if self.planner.applicable(self.state, [['is-goal', cell]], []): return '{}' else: return '()' else: if self.planner.applicable(self.state, [['is-goal', cell]], []): return '//' else: return ' ' def render(self): w, h = 0, 0 for cell in self.parser.objects['location']: x, y = self.parseCellName(cell) w = max(w, x + 1) h = max(h, y + 1) for y in range(1, h): for x in range(1, w): cell = self.formatPos((x, y)) # code = 0 # # print(cell) # # print(self.state) # for pred, pid in self.predIDs.items(): # if self.planner.applicable(self.state, [[pred, cell]], []): # code += pid # if self.planner.applicable(self.goal_pos, [['ball', cell]], []): # code += self.predIDs['goal'] print(self.renderCell(cell), end='') # if self.planner.applicable(self.state, [['wall', cell]], []): # code = -1 # else: # if self.planner.applicable(self.state, [['floor', cell]], []): # code = 1 # if self.planner.applicable(self.state, [['ball', cell]], []): # code += 2 # if self.planner.applicable(self.goal_pos, [['ball', cell]], []): # code += 4 # if self.planner.applicable(self.state, [['player', cell]], []): # code += 8 print() def lispState(self, word=':state'): out = [] out.append('({}'.format(word)) for pred in self.state: out.append(' (') out.append(' '.join(pred)) out.append(')') out.append(')') return ''.join(out) def gameloop(self): with open(self.logfile, 'w') as log: # log.write('{}\n\n'.format(datetime.datetime.now())) log.write('(trajectory\n\n') log.write('(:objects ') for t, os in self.parser.objects.items(): for o in os: log.write('{} - {} '.format(o, t)) log.write(')\n\n') log.write(self.lispState(':init')) log.write('\n\n') self.findNonWalls() while True: self.render() if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Winningness!') log.write(''.join(self.movelog)) log.write(')') return prevTime = time.time() key = input('Choose direction (wasdur, followed by Enter): ') # log.write('{}\n\n'.format(time.time() - prevTime)) self.doMove(key, log)
class Engine: def __init__(self, domain, problem, logfile='/dev/null'): # Parser self.parser = PDDL_Parser() self.parser.parse_domain(domain) self.parser.parse_problem(problem) # Parsed data self.state = self.parser.state self.goal_pos = self.parser.positive_goals self.goal_not = self.parser.negative_goals self.history = [] self.movelog = [] self.logfile = logfile self.planner = Planner() # Do nothing if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Puzzle is already solved! Double-check your problem file!') def parseCellName(self, cellName): x, y = cellName[4:].split('_') # print(cellName) # print(out) return int(x), int(y) def findPlayer(self): for pred in self.state: if pred[0] == 'player': return self.parseCellName(pred[1]) raise ValueError('Player not found!') def formatPos(self, coords): return 'cell{}_{}'.format(coords[0], coords[1]) def groundAction(self, act, assignment): variables = [] for v, _ in act.parameters: variables.append(v) pps = act.replace(act.positive_preconditions, variables, assignment) nps = act.replace(act.negative_preconditions, variables, assignment) aes = act.replace(act.add_effects, variables, assignment) des = act.replace(act.del_effects, variables, assignment) return Action(act.name, assignment, pps, nps, aes, des) def addVec(self, *vecs): x = 0 y = 0 for u, v in vecs: x += u y += v return (x, y) # act must already be grounded (e.g. by self.groundAction) def tryAction(self, act, log): # print(self.state) # print(act.positive_preconditions) # print(act.negative_preconditions) # print(self.planner.applicable(self.state, act.positive_preconditions, act.negative_preconditions)) if self.planner.applicable(self.state, act.positive_preconditions, act.negative_preconditions): # log.write(str(self.state) + '\n') # log.write(str(act)) act_str = '(:action ({}))'.format(' '.join( [act.name, *act.parameters])) self.history.append(self.state) self.state = self.planner.apply(self.state, act.add_effects, act.del_effects) # log.write(self.lispState() + '\n\n') self.movelog.append('{}\n\n{}\n\n'.format(act_str, self.lispState())) return True else: return False def lookupAction(self, actName): for act in self.parser.actions: if act.name == actName: return act return None # Finds the next cell adjacent to curCell in direction direc def findNextCell(self, curCell, direc): for cell in self.parser.objects['cell']: if self.planner.applicable(self.state, [['in-dir', direc, curCell, cell]], []): return cell return None def doMove(self, key, log): if key == 'u': if len(self.history) >= 1: self.state = self.history.pop() self.movelog.pop() return True elif key == 'r': self.state = self.parser.state # self.history.append(self.state) self.history = [self.state] self.movelog = [] return True # else: # # log.write('Unparseable input: {}\n\n'.format(key)) # return False # print(key, delta, actions) playerPos = self.findPlayer() # print(key, currentCell) for act in self.parser.actions: currentCell = self.formatPos(playerPos) assignment = [key] while len(assignment) < len(act.parameters): assignment.append(currentCell) currentCell = self.findNextCell( currentCell, key ) # Having to do this for every cell parameter in every action is inefficient; maybe improve later? gact = self.groundAction(act, assignment) # print(gact) if self.tryAction(gact, log): return True return False # nextCell = self.formatPos(self.addVec(playerPos, delta)) # afterCell = self.formatPos(self.addVec(playerPos, delta, delta)) # # print(playerPos, playerCell, nextCell, afterCell) # act = self.lookupAction(actions[0]) # gact = self.groundAction(act, [playerCell, nextCell]) # # print(gact) # if self.tryAction(gact, log): # return True # else: # act = self.lookupAction(actions[1]) # gact = self.groundAction(act, [playerCell, nextCell, afterCell]) # # print(gact) # if self.tryAction(gact, log): # return True # # log.write('Blocked move: {}\n\n'.format(actions)) # return False predIDs = {'wall': 1, 'player': 2, 'ball': 4, 'pit': 8, 'goal': 16} tiles = { 0: ' ', # Floor 1: '[]', # Wall 2: ':)', # Player 4: '()', # Boulder 8: '\/', # Pit 16: '//', # Goal 18: '%)', # Goal and player 20: '{}' } # Goal and boulder def render(self): w, h = 0, 0 for cell in self.parser.objects['cell']: x, y = self.parseCellName(cell) w = max(w, x + 1) h = max(h, y + 1) for y in range(h): for x in range(w): cell = self.formatPos((x, y)) code = 0 # print(cell) # print(self.state) for pred, pid in self.predIDs.items(): if self.planner.applicable(self.state, [[pred, cell]], []): code += pid if self.planner.applicable(self.goal_pos, [['ball', cell]], []): code += self.predIDs['goal'] print(self.tiles[code], end='') # if self.planner.applicable(self.state, [['wall', cell]], []): # code = -1 # else: # if self.planner.applicable(self.state, [['floor', cell]], []): # code = 1 # if self.planner.applicable(self.state, [['ball', cell]], []): # code += 2 # if self.planner.applicable(self.goal_pos, [['ball', cell]], []): # code += 4 # if self.planner.applicable(self.state, [['player', cell]], []): # code += 8 print() def lispState(self, word=':state'): out = [] out.append('({}'.format(word)) for pred in self.state: out.append(' (') out.append(' '.join(pred)) out.append(')') out.append(')') return ''.join(out) def gameloop(self): with open(self.logfile, 'w') as log: # log.write('{}\n\n'.format(datetime.datetime.now())) log.write('(trajectory\n\n') log.write('(:objects ') for t, os in self.parser.objects.items(): for o in os: log.write('{} - {} '.format(o, t)) log.write(')\n\n') log.write(self.lispState(':init')) log.write('\n\n') while True: self.render() if self.planner.applicable(self.state, self.goal_pos, self.goal_not): print('Winningness!') log.write(''.join(self.movelog)) log.write(')') return prevTime = time.time() key = input( 'Choose direction ({}ur, followed by Enter): '.format( ''.join(self.parser.objects['dir']))) # log.write('{}\n\n'.format(time.time() - prevTime)) self.doMove(key, log)
class trajectory: def parseObjects(self, objTokens): self.types2objs = { } # Map of types -> objects; useful when you need to know what objects have a specified type self.objs2types = { } # Map of objects -> types; useful when you want to know what the type of a specific object is objs = [] isType = False for token in objTokens[ 1:]: # Throw out objTokens[0], which will be the string ':objects' if isType: if token not in self.types2objs: self.types2objs[token] = [] self.types2objs[token].extend(objs) for obj in objs: self.objs2types[obj] = token objs = [] isType = False elif token == '-': isType = True else: objs.append(token) # Parses the :init and :state blocks in the trajectory file, and also makes a dictionary of all the predicates that appear in the states, along with the types of their peremeters def parseStates(self, tokens): self.states = [] self.predicates = {} for block in tokens: if block[0] == ':state' or block[0] == ':init': self.states.append(block[1:]) for pred in block[1:]: pName = pred[0] pTypes = [] for arg in pred[1:]: pTypes.append( [self.objs2types[arg]] ) # Predicate type lists are now lists of lists of strings- each inner list representing the types observed for one parameter if pName in self.predicates: for oldParamTypes, newType in zip( self.predicates[pName], pTypes): if newType[0] not in oldParamTypes: oldParamTypes.append(newType[0]) # if self.predicates[pName] != pTypes: # raise TypeError('Predicate {} believed to have parameter types {}; inconsistent with observed parameters {}'.format(pName, self.predicates[pName], pTypes)) else: self.predicates[pName] = pTypes def parseActions(self, tokens): self.actions = {} for block in tokens: if block[0] == ':action': act_in = block[1] # print('Parsing action {}'.format(act_in)) parTypes = [] for param in act_in[1:]: parTypes.append( self.objs2types[param] ) # Not doing list-of-lists here- that'll be handled in actionCandidate constructor if act_in[0] in self.actions: # print ('Found action with same name: {}'.format(act_in[0])) for oldTypes, newType in zip( self.actions[act_in[0]].parameterTypes, parTypes): if newType not in oldTypes: oldTypes.append(newType) self.actions[act_in[0]].updateParameterTypes(parTypes) # if act.parameterTypes != parTypes: # raise TypeError('Action {} found using parameters {}; inconsistent with earlier {}. Are you using type inheritance?'.format(act_in[0], parTypes, act.parameterTypes)) else: newAct = actionCandidate(act_in[0], parTypes, self) self.actions[newAct.name] = newAct def refineActions(self, tokens): assignments = [((n - 1) // 2 - 1, block[1]) for (n, block) in enumerate(tokens) if block[0] == ':action'] # pprint.pprint(assignments) for i, agmt in assignments: assignedTypes = [self.objs2types[par] for par in agmt[1:]] self.actions[agmt[0]].updateParameterTypes(assignedTypes) for act in self.actions.values(): act.createPrecons(self) needsDoubleChecking = False for i, agmt in assignments: # print('State {}: {}'.format(i, self.states[i])) # print('Action {}: {}'.format(i, agmt)) # print('State {}: {}'.format(i+1, self.states[i+1])) # print() act = self.actions[agmt[0]] assignment = agmt[1:] # assignedTypes = [self.objs2types[par] for par in assignment] act.prunePrecons(self.states[i], assignment) needsDoubleChecking |= act.updateEffects(self.states[i], assignment, self.states[i + 1]) if needsDoubleChecking: # print('Double-checking action effects\n') needsDoubleChecking = False for i, agmt in assignments: act = self.actions[agmt[0]] assignment = agmt[1:] # assignedTypes = [self.objs2types[par] for par in assignment] needsDoubleChecking |= act.updateEffects( self.states[i], assignment, self.states[i + 1]) if needsDoubleChecking: raise ValueError( "Some actions still had unexpected effects during the second pass. This shouldn't be possible. If you see this message, please let me know. --SE" ) def genTypeclasses(self): #TODO Add code to deal with deeper hierarchies classList = [] for typesOuter in self.predicates.values(): for types in typesOuter: # print(types) sorTypes = list(sorted(types)) if sorTypes not in classList: classList.append(sorTypes) for act in self.actions: for types in self.actions[act].parameterTypes: sorTypes = list(sorted(types)) if sorTypes not in classList: classList.append(sorTypes) # print(classList) self.typeclasses = {} for tclass in classList: self.typeclasses['_'.join(tclass)] = 'object' for typ in self.types2objs.keys(): container = 'object' for tclass in classList: if typ in tclass and typ != '_'.join(tclass): container = '_'.join(tclass) self.typeclasses[typ] = container def __init__(self, filename, domainName='reconstructed'): self.domainName = domainName self.parser = PDDL_Parser() self.tokens = self.parser.scan_tokens(filename) pprint.pprint(self.tokens) print('=== Objects ===') self.parseObjects(self.tokens[1]) pprint.pprint(self.types2objs) print('=== States ===') self.parseStates(self.tokens) pprint.pprint(self.states) print('=== Predicates ===') pprint.pprint(self.predicates) print('=== Actions ===') self.parseActions(self.tokens) # print(self.tokens[3]) # p, n = self.actions[0].assignPrecons(self.tokens[3][1][1:]) # print('Grounded Positive preconditions') # print(p) # print('Grounded Negative preconditions') # print(n) # print('Before State') # print(self.states[0]) # self.actions[0].prunePrecons(self.states[0], self.tokens[3][1][1:]) # print(self.actions[0]) self.refineActions(self.tokens) pprint.pprint(self.actions) self.genTypeclasses() def __repr__(self): fmtTypeclasses = [ '{} - {}'.format(k, v) for k, v in self.typeclasses.items() ] fmtPredicates = [] for name, types in self.predicates.items(): fmtParams = [name] for i, typ in enumerate(types): fmtParams.append('?{} - {}'.format(i, '_'.join(typ))) fmtPredicates.append('({})'.format(' '.join(fmtParams))) fmtActions = [str(act) for act in self.actions.values()] return '''(define (domain {}) (:requirements :typing :negative-preconditions) (:types {}) (:predicates {}) {}) '''.format(self.domainName, ' '.join(fmtTypeclasses), ' '.join(fmtPredicates), ''.join(fmtActions))