From a0effc5fdcd624665dc94fc2b89e2ee7b13165fd Mon Sep 17 00:00:00 2001 From: bitcraft Date: Thu, 28 Jun 2012 20:03:50 -0500 Subject: [PATCH] added real-time pathfinding and agent mobility --- npc/pirate/actions.py | 27 +++++++++++++++++++++------ pygoap/agent.py | 20 ++++++++++++++------ pygoap/environment.py | 11 ++++++----- pygoap/environment2d.py | 11 +++++++++++ pygoap/planning.py | 7 ++++++- pygoap/tiledenvironment.py | 2 +- test.py | 4 ++-- 7 files changed, 61 insertions(+), 21 deletions(-) diff --git a/npc/pirate/actions.py b/npc/pirate/actions.py index b71d52a..7a66a3b 100644 --- a/npc/pirate/actions.py +++ b/npc/pirate/actions.py @@ -48,22 +48,37 @@ def get_actions(self, caller, bb): return a list of action that this caller is able to move with """ - if not SimpleGoal(is_tired=True).test(bb): - pos = caller.environment.can_move_from(caller, dist=30) - return [ self.build_action(caller, p) for p in pos ] - else: + if SimpleGoal(is_tired=True).test(bb): return [] + else: + pos = caller.environment.can_move_from(caller, dist=100) + return [ self.build_action(caller, p) for p in pos ] + def build_action(self, caller, pos): a = move_action(caller) + a.setEndpoint(pos[1]) a.effects.append(PositionGoal(target=caller, position=pos)) a.effects.append(SimpleGoal(is_tired=True)) return a - class move_action(CallableAction): - pass + def update(self, time): + super(move_action, self).update(time) + if self.caller.position[1] == self.endpoint: + self.finish() + else: + env = self.caller.environment + path = env.pathfind(self.caller.position[1], self.endpoint) + path.pop() # this will always the the starting position + env.move(self.caller, (env, path.pop())) + + def setStartpoint(self, pos): + self.startpoint = pos + + def setEndpoint(self, pos): + self.endpoint = pos exported_actions.append(move) diff --git a/pygoap/agent.py b/pygoap/agent.py index 0494418..e4d4cb4 100644 --- a/pygoap/agent.py +++ b/pygoap/agent.py @@ -4,6 +4,8 @@ from actionstates import * +DEBUG = 0 + NullAction = InstancedAction() @@ -95,7 +97,7 @@ def handle_precept(self, pct): # our filters may have caused us to ignore the precept if pct == None: return None - print "[agent] {} recv'd pct {}".format(self, pct) + if DEBUG: print "[agent] {} recv'd pct {}".format(self, pct) # this line has been added for debugging purposes self.plan = [] @@ -115,7 +117,7 @@ def replan(self): s = [ g for g in s if g[0] > 0 ] s.sort(reverse=True) - print "[agent] goals {}".format(s) + if DEBUG: print "[agent] goals {}".format(s) # starting for the most relevant goal, attempt to make a plan for score, goal in s: @@ -126,13 +128,15 @@ def replan(self): self.bb, goal) - if ok: + if ok and DEBUG: print "[agent] {} has planned to {}".format(self, goal) pretty = list(reversed(plan[:])) print "[agent] {} has plan {}".format(self, pretty) return plan - else: + elif DEBUG: print "[agent] {} cannot {}".format(self, goal) + elif ok: + return plan return [] @@ -143,7 +147,8 @@ def current_action(self): return NullAction def running_actions(self): - return self.current_action() + action = self.current_action() + return action def next_action(self): """ @@ -157,7 +162,10 @@ def next_action(self): # this action is done, so return the next one if current_action.state == ACTIONSTATE_FINISHED: - return self.plan.pop() + if self.plan: + return self.plan.pop() + else: + return None # this action failed somehow elif current_action.state == ACTIONSTATE_FAILED: diff --git a/pygoap/environment.py b/pygoap/environment.py index 942156e..02faa9e 100644 --- a/pygoap/environment.py +++ b/pygoap/environment.py @@ -17,6 +17,7 @@ from itertools import chain, repeat, product, izip +DEBUG = 0 class ObjectBase(object): """ @@ -111,7 +112,7 @@ def add_thing(self, thing, position=None): thing.position = position or self.default_position(thing) self.things.append(thing) - print "[env] adding {}".format(thing) + if DEBUG: print "[env] adding {}".format(thing) # add the agent if isinstance(thing, GoapAgent): @@ -144,13 +145,13 @@ def update(self, time_passed): p = Precept(sense="time", time=self.time) [ a.handle_precept(p) for a in self.agents ] + # get all the running actions for the agents + self.action_que = [ a.running_actions() for a in self.agents ] + # update all the actions that may be running precepts = [ a.update(time_passed) for a in self.action_que ] precepts = [ p for p in precepts if not p == None ] - - # get all the running actions for the agents - self.action_que = chain([ a.running_actions() for a in self.agents ]) - + # start any actions that are not started [ action.start() for action in self.action_que if action.state == ACTIONSTATE_NOT_STARTED ] diff --git a/pygoap/environment2d.py b/pygoap/environment2d.py index 49054df..041fa73 100644 --- a/pygoap/environment2d.py +++ b/pygoap/environment2d.py @@ -11,6 +11,7 @@ from pygoap.agent import GoapAgent from environment import Environment, Precept +from pathfinding.astar import search, Node import random, math @@ -46,6 +47,9 @@ def get_surrounding(self, position): def calc_h(self, position1, position2): return distance(position1, position2) + def factory(self, position): + return Node(position) + class XYEnvironment(Environment, Pathfinding2D): """ @@ -135,3 +139,10 @@ def can_move_from(self, agent, dist=100): pos.append((self, (xx, yy))) return pos + + def pathfind(self, start, finish): + """ + return a path from start to finish + """ + + return search(start, finish, self.factory) diff --git a/pygoap/planning.py b/pygoap/planning.py index 8b55d1d..dc262f4 100644 --- a/pygoap/planning.py +++ b/pygoap/planning.py @@ -1,5 +1,6 @@ from blackboard import Blackboard from heapq import heappop, heappush, heappushpop +from actionstates import * import sys @@ -159,13 +160,17 @@ class InstancedAction(object): builder = None def __init__(self): - self.state = None + self.state = ACTIONSTATE_FINISHED + + def update(self, time): + pass def touch(self, bb): if DEBUG: print "[debug] action {} has no touch method".format(self) def test(self, bb): if DEBUG: print "[debug] action {} has no test method".format(self) + return 1.0 def __repr__(self): return self.__class__.__name__ diff --git a/pygoap/tiledenvironment.py b/pygoap/tiledenvironment.py index bbe6a23..1318d38 100644 --- a/pygoap/tiledenvironment.py +++ b/pygoap/tiledenvironment.py @@ -21,7 +21,7 @@ def render(self, surface): for l in xrange(0, len(self.tiledmap.layers)): for y in xrange(0, self.tiledmap.height): for x in xrange(0, self.tiledmap.width): - tile = self.tiledmap.get_tile_image(x, y, l) + tile = self.tiledmap.getTileImage(x, y, l) xx = x * self.tiledmap.tilewidth yy = y * self.tiledmap.tileheight if not tile == 0: diff --git a/test.py b/test.py index c635e86..a0e3819 100644 --- a/test.py +++ b/test.py @@ -88,7 +88,7 @@ def run_once(): formosa.add_thing(rum) elif time == 5: - formosa.move(rum, pirate.position) + #formosa.move(rum, pirate.position) pass elif time == 6: @@ -134,7 +134,7 @@ def run_once(): stdout.write("\n\n"); time += 1 - if time == 8: run = False + if time == 32: run = False if __name__ == "__main__":