From 74c585727564e57752a17cc700a6dfe8425382f8 Mon Sep 17 00:00:00 2001 From: Leif Theden Date: Sun, 25 Nov 2012 15:56:21 +0800 Subject: [PATCH] fixed the rum drinking problem --- npc/pirate/actions.py | 50 ++++++++++++++++--------------------------- pygoap/agent.py | 30 +++++++++++--------------- pygoap/blackboard.py | 36 +++++++------------------------ pygoap/goals.py | 18 ++++++++++------ pygoap/planning.py | 4 ++-- test.py | 6 ------ 6 files changed, 52 insertions(+), 92 deletions(-) diff --git a/npc/pirate/actions.py b/npc/pirate/actions.py index 5a35476..0abecc3 100644 --- a/npc/pirate/actions.py +++ b/npc/pirate/actions.py @@ -16,9 +16,9 @@ def get_position(entity, memory): """ Return the position of [entity] according to memory. """ - for precept in memory.of_class(PositionPrecept): - if precept.entity is entity: - return precept.position + for pct in memory.of_class(PositionPrecept): + if pct.entity is entity: + return pct.position class MoveAction(ActionContext): @@ -46,7 +46,7 @@ class PickupAction(CalledOnceContext): pass -class DrinkRunAction(ActionContext): +class DrinkRumAction(ActionContext): def start(self): self.caller.drunkness = 1 super(drink_rum, self).start() @@ -77,15 +77,15 @@ class move_to_entity(ActionBuilder): def get_actions(self, caller, memory): visited = [] - for precept in memory.of_class(PositionPrecept): - if precept.entity is caller or precept.position in visited: + for pct in memory.of_class(PositionPrecept): + if pct.entity is caller or pct.position in visited: continue - visited.append(precept.position) + visited.append(pct.position) action = MoveAction(caller) - action.setEndpoint(precept.position[1]) - action.effects.append(PositionGoal(caller, precept.position)) + action.setEndpoint(pct.position[1]) + action.effects.append(PositionGoal(caller, pct.position)) yield action exported_actions.append(move_to_entity) @@ -98,42 +98,28 @@ def get_actions(self, caller, memory): """ here = get_position(caller, memory) - for precept in memory.of_class(PositionPrecept): - if here == precept.position and precept.entity is not caller: + for pct in memory.of_class(PositionPrecept): + if here == pct.position and pct.entity is not caller: action = PickupAction(caller) - action.effects.append(HasItemGoal(precept.entity)) + action.effects.append(HasItemGoal(pct.entity)) yield action exported_actions.append(pickup) - class drink_rum(ActionBuilder): """ drink rum that is in caller's inventory """ - def make_action(self, caller, tag, memory): - a = drink_rum_action(caller) - a.effects.append(SimpleGoal(is_drunk=True)) - a.effects.append(EvalGoal("charisma = charisma + 10")) - - return a - def get_actions(self, caller, memory): - """ - return list of actions that will drink rum from player's inv - """ - - a = [] - for precept in memory.of_class(PositionPrecept): - if precept.position[0] == caller: - if DEBUG: print "[drink rum] 1 {}".format(tag) - if tag['obj'].name=="rum": - if DEBUG: print "[drink rum] 2 {}".format(tag) - a.append(self.make_action(caller, tag, memory)) + for pct in memory.of_class(PositionPrecept): + if pct.position[0] == 'self' and pct.entity.name == "rum": + action = DrinkRumAction(caller) + action.effects.append(SimpleGoal(is_drunk=True)) + action.effects.append(EvalGoal("charisma = charisma + 10")) + yield action - return a exported_actions.append(drink_rum) diff --git a/pygoap/agent.py b/pygoap/agent.py index 23e3d4d..ae393b8 100644 --- a/pygoap/agent.py +++ b/pygoap/agent.py @@ -1,7 +1,7 @@ from environment import ObjectBase from planning import plan from actions import ActionContext -from blackboard import Blackboard, MemoryManager +from blackboard import MemoryManager from actionstates import * from precepts import * import logging @@ -88,30 +88,26 @@ def replan(self): """ # get the relevancy of each goal according to the state of the agent - s = [ (g.get_relevancy(self.memory), g) for g in self.goals ] - s = [ g for g in s if g[0] > 0 ] + s = ( (g.get_relevancy(self.memory), g) for g in self.goals ) + s = [ g for g in s if g[0] > 0.0 ] s.sort(reverse=True) debug("[agent] goals %s", s) - # starting for the most relevant goal, attempt to make a plan + # starting for the most relevant goal, attempt to make a plan + plan = [] for score, goal in s: - ok, plan = self.planner( - self, - self.actions, - self.current_action, - self.memory, - goal) - - if ok: - pretty = list(reversed(plan[:])) + tentative_plan = self.planner(self, self.actions, + self.current_action, self.memory, goal) + + if tentative_plan: + pretty = list(reversed(tentative_plan[:])) debug("[agent] %s has planned to %s", self, goal) debug("[agent] %s has plan %s", self, pretty) - return plan - else: - debug("[agent] %s cannot %s", self, goal) + plan = tentative_plan + break - return [] + return plan @property def current_action(self): diff --git a/pygoap/blackboard.py b/pygoap/blackboard.py index e23f8f4..797cb5d 100644 --- a/pygoap/blackboard.py +++ b/pygoap/blackboard.py @@ -1,34 +1,13 @@ """ Memories are stored precepts. -A blackboard is a device to share information amongst actions. -This implementation uses sqlite3 as a backend for storing memories. - -The MemManager class is not used. """ class MemoryManager(set): """ - a memory manager's purpose is to store and manage stored precepts. - """ - - def of_class(self, klass): - for i in self: - if isinstance(i, klass): - yield i - -class Blackboard(dict): - """ - a blackboard is an abstract memory device. - - alternative, more robust solution would be a xml backend or database - - tags belong to a set of values that are known by the actions that an actor - would find useful. tag names are simple strings and have a value - associated with them. for simplicity, a blackboard could be used like a - standard python dictionary. - + Store and manage precepts. + shared blackboards violate reality in that multiple agents share the same thoughts, to extend the metaphore. but, the advantage of this is that in a real-time simulation, it gives the player the impression that the agents @@ -38,9 +17,10 @@ class Blackboard(dict): that being said, i have chosen to restrict blackboards to a per-agent basis. this library is meant for rpgs, where the action isn't real-time and would require a more realistic simulation of intelligence. - - however, i will still develop blackboards with the intention that they are - shared, so that in the future, it will be easier to simulate agents with - shared knowledge and subtle communication. """ - pass + + def of_class(self, klass): + for i in self: + if isinstance(i, klass): + yield i + diff --git a/pygoap/goals.py b/pygoap/goals.py index 02d20bd..83b7790 100644 --- a/pygoap/goals.py +++ b/pygoap/goals.py @@ -25,8 +25,8 @@ class GoalBase(object): """ Goals: - can be satisfied. - can be valid + can be tested + can be relevant Goals, ActionPrereqs and ActionEffects are now that same class. They share so much functionality that they have been combined into one class. @@ -42,12 +42,10 @@ def __init__(self, *args, **kwargs): except IndexError: self.condition = None - self.value = 1.0 + self.weight = 1.0 self.args = args self.kw = kwargs - self.self_test() - def touch(self, memory): pass @@ -61,7 +59,8 @@ def get_relevancy(self, memory): as a general rule, the return value here should never equal what is returned from test() """ - return self.value if not self.test(memory) else 0.0 + score = 1 - self.test(memory) + return self.weight * score def self_test(self): """ @@ -153,7 +152,11 @@ def do_it(expr, d): d = {} d['__builtins__'] = None - memory.post(Tag(kw=do_it(self.args[0], d))) + for k, v in d.items(): + if k == '__builtins__': + continue + + memory.add(DatumPrecept(k, v)) return True @@ -208,6 +211,7 @@ def test(self, memory): return 1.0 else: + d = distance(position, target_position) if d > self.dist: return (float(self.dist / d)) * float(self.dist) diff --git a/pygoap/planning.py b/pygoap/planning.py index 66c94b2..3dfcf3c 100644 --- a/pygoap/planning.py +++ b/pygoap/planning.py @@ -154,9 +154,9 @@ def plan(caller, actions, start_action, start_memory, goal): keyNode = keyNode.parent path0.append(keyNode.action) - return True, path0 + return path0 else: - return False, [] + return [] diff --git a/test.py b/test.py index 221a2ce..6d589ac 100644 --- a/test.py +++ b/test.py @@ -81,21 +81,15 @@ def run_once(): if time == 1: pirate = Human("Male", "jack") load_commands(pirate, os.path.join("npc", "pirate")) - #pirate.add_goal(SimpleGoal(is_idle=True)) pirate.add_goal(SimpleGoal(is_drunk=True)) formosa.add(pirate) formosa.set_position(pirate, (formosa, (0,0))) elif time == 3: rum = ObjectBase("rum") - pirate.add_goal(HasItemGoal(rum)) formosa.add(rum) formosa.set_position(rum, (formosa, (0,2))) - elif time == 5: - #formosa.move(rum, pirate.position) - pass - elif time == 6: wench = Human("Female", "wench") formosa.add(wench)