In [1]:
    
from pyknow import *
import schema
    
In [2]:
    
class Monkey(Fact):
    location = Field(str, default="green-couch")
    on_top_of = Field(str, default="floor")
    holding = Field(str, default="blank")
    
class Thing(Fact):
    name = Field(str, mandatory=True)
    location = Field(str, mandatory=True)
    on_top_of = Field(str, default="floor")
    weight = Field(schema.Or("light", "heavy"), default="light")
class Chest(Fact):
    name = Field(str, mandatory=True)
    contents = Field(str, mandatory=True)
    unlocked_by = Field(str, mandatory=True)
class GoalIsTo(Fact):
    action = Field(schema.Or("hold", "unlock", "eat", "move", "on", "walk-to"),
                   mandatory=True)
    arguments = Field(schema.Or(str, [str]), mandatory=True)
    
In [3]:
    
class ChestUnlockingRules:
    """CHEST UNLOCKING RULES"""
    @Rule(
         GoalIsTo(action="unlock", arguments=MATCH.chest),
         Thing(name=MATCH.chest, on_top_of=NE("floor"), weight="light"),
         Monkey(holding=~MATCH.chest),
         NOT(GoalIsTo(action="hold", arguments=MATCH.chest)))
    def hold_chest_to_put_on_floor(self, chest):
        self.declare(GoalIsTo(action="hold", arguments=chest))
    @Rule(
        GoalIsTo(
            action="unlock",
            arguments=MATCH.chest),
        AS.monkey << Monkey(
                         location=MATCH.place,
                         on_top_of=MATCH.on,
                         holding=MATCH.chest),
        AS.thing  << Thing(
                         name=MATCH.chest))
    def put_chest_on_floor(self, monkey, thing, place, on, chest):
        print("Monkey throws the %s off the %s onto the floor." % (chest, on))
        self.modify(monkey, holding="blank")
        self.modify(thing, location=place, on_top_of="floor")
    @Rule(
        GoalIsTo(action="unlock", arguments=MATCH.obj),
        Thing(name=MATCH.obj, on_top_of="floor"),
        Chest(name=MATCH.obj, unlocked_by=MATCH.key),
        Monkey(holding=~MATCH.key),
        NOT(GoalIsTo(action="hold", arguments=MATCH.key)))
    def get_key_to_unlock(self, key):
        self.declare(GoalIsTo(action="hold", arguments=key))
    @Rule(
        GoalIsTo(
            action="unlock",
            arguments=MATCH.chest),
        Monkey(
            location=MATCH.mplace,
            holding=MATCH.key),
        Thing(
            name=MATCH.chest,
            location=MATCH.cplace & ~MATCH.mplace,
            on_top_of="floor"),
        Chest(
            name=MATCH.chest,
            unlocked_by=MATCH.key),
        NOT(
            GoalIsTo(
                action="walk-to",
                arguments=MATCH.cplace)))
    def move_to_chest_with_key(self, cplace):
        self.declare(GoalIsTo(action="walk-to", arguments=cplace))
    
    @Rule(
        AS.goal << GoalIsTo(action="unlock", arguments=MATCH.name),
        AS.chest << Chest(name=MATCH.name, contents=MATCH.contents, unlocked_by=MATCH.key),
        Thing(name=MATCH.name, location=MATCH.place, on_top_of=MATCH.on),
        Monkey(location=MATCH.place, on_top_of=MATCH.on, holding=MATCH.key))
    def unlock_chest_with_key(self, goal, name, key, contents, place, chest):
        print("Monkey opens the %s with the %s revealing the %s." % (name, key, contents))
        self.modify(chest, contents="nothing")
        self.declare(Thing(name=contents, location=place, on_top_of=name, weight="light"))
        self.retract(goal)
    
In [4]:
    
class HoldObjectRules:
    """HOLD OBJECT RULES"""
    @Rule(
        GoalIsTo(action="hold", arguments=MATCH.obj),
        Chest(name=MATCH.chest, contents=MATCH.obj),
        NOT(GoalIsTo(action="unlock", arguments=MATCH.chest)))
    def unlock_chest_to_hold_object(self, chest):
        self.declare(GoalIsTo(action="unlock", arguments=chest))
    @Rule(
        GoalIsTo(action="hold", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of="ceiling", weight="light"),
        NOT(Thing(name="ladder", location=MATCH.place)),
        NOT(GoalIsTo(action="move", arguments__0="ladder", arguments__1=MATCH.place)))
    def use_ladder_to_hold(self, place):
        self.declare(GoalIsTo(action="move", arguments=["ladder", place]))
    @Rule(
        GoalIsTo(action="hold", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of="ceiling", weight="light"),
        Thing(name="ladder", location=MATCH.place, on_top_of="floor"),
        Monkey(on_top_of=NE("ladder")),
        NOT(GoalIsTo(action="on", arguments="ladder")))
    def climb_ladder_to_hold(self):
        self.declare(GoalIsTo(action="on", arguments="ladder"))
    @Rule(
        AS.goal << GoalIsTo(action="hold", arguments=MATCH.name),
        AS.thing << Thing(name=MATCH.name, location=MATCH.place, on_top_of="ceiling", weight="light"),
        Thing(name="ladder", location=MATCH.place),
        AS.monkey << Monkey(location=MATCH.place, on_top_of="ladder", holding="blank"))
    def grab_object_from_ladder(self, thing, monkey, goal, name):
        print("Monkey grabs the %s." % name)
        self.modify(thing, location="held", on_top_of="held")
        self.modify(monkey, holding=name)
        self.retract(goal)
    @Rule(
        GoalIsTo(action="hold", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of=MATCH.on & NE("ceiling"), weight="light"),
        Monkey(location=MATCH.place, on_top_of=~MATCH.on),
        NOT(GoalIsTo(action="on", arguments=MATCH.on)))
    def climb_to_hold(self, on):
        self.declare(GoalIsTo(action="on", arguments=on))
    @Rule(
        GoalIsTo(action="hold", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of=NE("ceiling"), weight="light"),
        Monkey(location=~MATCH.place),
        NOT(GoalIsTo(action="walk-to", arguments=MATCH.place)))
    def walk_to_hold(self, place):
        self.declare(GoalIsTo(action="walk-to", arguments=place))
    @Rule(
        GoalIsTo(action="hold", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of=MATCH.on, weight="light"),
        Monkey(location=MATCH.place, on_top_of=MATCH.on, holding=NE("blank")),
        NOT(GoalIsTo(action="hold", arguments="blank")))
    def drop_to_hold(self):
        self.declare(GoalIsTo(action="hold", arguments="blank"))
    @Rule(
      AS.goal << GoalIsTo(action="hold", arguments=MATCH.name),
      AS.thing << Thing(name=MATCH.name, location=MATCH.place, on_top_of=MATCH.on, weight="light"),
      AS.monkey << Monkey(location=MATCH.place, on_top_of=MATCH.on, holding="blank"))
    def grab_object(self, name, monkey, thing, goal):
        print("Monkey grabs the %s." % name)
        self.modify(thing, location="held", on_top_of="held")
        self.modify(monkey, holding=name)
        self.retract(goal)
    @Rule(
        AS.goal << GoalIsTo(action="hold", arguments="blank"),
        AS.monkey << Monkey(location=MATCH.place, on_top_of=MATCH.on, holding=MATCH.name & NE("blank")),
        AS.thing << Thing(name=MATCH.name))
    def drop_object(self, monkey, thing, place, name, on, goal):
        print("Monkey drops the %s." % name)
        self.modify(monkey, holding="blank")
        self.modify(thing, location=place, on_top_of=on)
        self.retract(goal)
    
In [5]:
    
class MoveObjectRules:
    """MOVE OBJECT RULES"""
    @Rule(
        GoalIsTo(action="move", arguments__0=MATCH.obj),
        Chest(name=MATCH.chest, contents=MATCH.obj),
        NOT(GoalIsTo(action="unlock", arguments=MATCH.chest)))
    def unlock_chest_to_move_object(self, chest):
        self.declare(GoalIsTo(action="unlock", arguments=chest))
    @Rule(
        GoalIsTo(action="move", arguments__0=MATCH.obj, arguments__1=MATCH.place),
        Thing(name=MATCH.obj, location=~MATCH.place, weight="light"),
        Monkey(holding=~MATCH.obj),
        NOT(GoalIsTo(action="hold", arguments=MATCH.obj)))
    def hold_object_to_move(self, obj):
        self.declare(GoalIsTo(action="hold", arguments=obj))
    @Rule(
        GoalIsTo(action="move", arguments__0=MATCH.obj, arguments__1=MATCH.place),
        Monkey(location=~MATCH.place, holding=MATCH.obj),
        NOT(GoalIsTo(action="walk-to", arguments=MATCH.place)))
    def move_object_to_place(self, place):
        self.declare(GoalIsTo(action="walk-to", arguments=place))
    @Rule(
        AS.goal << GoalIsTo(action="move", arguments__0=MATCH.name, arguments__1=MATCH.place),
        AS.monkey << Monkey(location=MATCH.place, holding=MATCH.obj),
        AS.thing << Thing(name=MATCH.name, weight="light"))
    def drop_object_once_moved(self, name, monkey, thing, goal, place):
        print("Monkey drops the %s." % name)
        self.modify(monkey, holding="blank")
        self.modify(thing, location=place, on_top_of="floor")
        self.retract(goal)
    @Rule(
        AS.goal << GoalIsTo(action="move", arguments__0=MATCH.obj, arguments__1=MATCH.place),
        Thing(name=MATCH.obj, location=MATCH.place))
    def already_moved_object(self, goal):
        self.retract(goal)
    
In [6]:
    
class WalkToPlaceRules:
    """WALK TO PLACE RULES"""
    @Rule(
        AS.goal << GoalIsTo(action="walk-to", arguments=MATCH.place),
        Monkey(location=MATCH.place))
    def already_at_place(self, goal):
        self.retract(goal)
    @Rule(
        GoalIsTo(action="walk-to", arguments=MATCH.place),
        Monkey(location=~MATCH.place, on_top_of=NE("floor")),
        NOT(GoalIsTo(action="on", arguments="floor")))
    def get_on_floor_to_walk(self):
        self.declare(GoalIsTo(action="on", arguments="floor"))
    @Rule(
        AS.goal << GoalIsTo(action="walk-to", arguments=MATCH.place),
        AS.monkey << Monkey(location=~MATCH.place, on_top_of="floor", holding="blank"))
    def walk_holding_nothing(self, place, monkey, goal):
        print("Monkey walks to %s." % place)
        self.modify(monkey, location=place)
        self.retract(goal)
    @Rule(
        AS.goal << GoalIsTo(action="walk-to", arguments=MATCH.place),
        AS.monkey << Monkey(location=~MATCH.place, on_top_of="floor", holding=MATCH.obj & NE("blank")))
    def walk_holding_object(self, place, obj, goal, monkey):
        print("Monkey walks to %s holding the %s." % (place, obj))
        self.modify(monkey, location=place)
        self.retract(goal)
    
In [7]:
    
class GetOnObjectRules:
    """GET ON OBJECT RULES"""
    @Rule(
        AS.goal << GoalIsTo(action="on", arguments="floor"),
        AS.monkey << Monkey(on_top_of=MATCH.on & NE("floor")))
    def jump_onto_floor(self, on, monkey, goal):
        print("Monkey jumps off the %s onto the floor." % on)
        self.modify(monkey, on_top_of="floor")
        self.retract(goal)
    @Rule(
        GoalIsTo(action="on", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place),
        Monkey(location=~MATCH.place),
        NOT(GoalIsTo(action="walk-to", arguments=MATCH.place)))
    def walk_to_place_to_climb(self, place):
        self.declare(GoalIsTo(action="walk-to", arguments=place))
    @Rule(
        GoalIsTo(action="on", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place),
        Monkey(location=MATCH.place, holding=NE("blank")),
        NOT(GoalIsTo(action="hold", arguments="blank")))
    def drop_to_climb(self):
        self.declare(GoalIsTo(action="hold", arguments="blank"))
    @Rule(
        GoalIsTo(action="on", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of=MATCH.on),
        Monkey(location=MATCH.place, on_top_of=~MATCH.on & ~MATCH.obj, holding="blank"),
        NOT(GoalIsTo(action="on", arguments=MATCH.on)))
    def climb_indirectly(self, on):
        self.declare(GoalIsTo(action="on", arguments=on))
    @Rule(
        AS.goal << GoalIsTo(action="on", arguments=MATCH.obj),
        Thing(name=MATCH.obj, location=MATCH.place, on_top_of=MATCH.on),
        AS.monkey << Monkey(location=MATCH.place, on_top_of=MATCH.on, holding="blank"))
    def climb_directly(self, obj, monkey, goal):
        print("Monkey climbs onto the %s." % obj)
        self.modify(monkey, on_top_of=obj)
        self.retract(goal)
    @Rule(
        AS.goal << GoalIsTo(action="on", arguments=MATCH.obj),
        Monkey(on_top_of=MATCH.obj))
    def already_on_object(self, goal):
        self.retract(goal)
    
In [8]:
    
class EatObjectRules:
    """EAT OBJECT RULES"""
    @Rule(
        GoalIsTo(action="eat", arguments=MATCH.obj),
        Monkey(holding=~MATCH.obj),
        NOT(GoalIsTo(action="hold", arguments=MATCH.obj)))
    def hold_to_eat(self, obj):
        self.declare(GoalIsTo(action="hold", arguments=obj))
    
    @Rule(
        AS.goal << GoalIsTo(action="eat", arguments=MATCH.name),
        AS.monkey << Monkey(holding=MATCH.name),
        AS.thing << Thing(name=MATCH.name))
    def satisfy_hunger(self, goal, thing, monkey, name):
        print("Monkey eats the %s." % name)
        self.modify(monkey, holding="blank")
        self.retract(goal)
        self.retract(thing)
    
In [9]:
    
class MonkeesAndBananas(ChestUnlockingRules,
                        HoldObjectRules,
                        MoveObjectRules,
                        WalkToPlaceRules,
                        GetOnObjectRules,
                        EatObjectRules,
                        KnowledgeEngine):
    @DefFacts()
    def startup(self):
        """INITIAL STATE"""
        yield Monkey(location="t5-7", on_top_of="green-couch", holding="blank")
        yield Thing(name="green-couch", location="t5-7", on_top_of="floor", weight="heavy")
        yield Thing(name="red-couch", location="t2-2", on_top_of="floor", weight="heavy")
        yield Thing(name="big-pillow", location="t2-2", on_top_of="red-couch", weight="light")
        yield Thing(name="red-chest", location="t2-2", on_top_of="big-pillow", weight="light")
        yield Chest(name="red-chest", contents="ladder", unlocked_by="red-key")
        yield Thing(name="blue-chest", location="t7-7", on_top_of="ceiling", weight="light")
        yield Chest(name="blue-chest", contents="bananas", unlocked_by="blue-key")
        yield Thing(name="blue-couch", location="t8-8", on_top_of="floor", weight="heavy")
        yield Thing(name="green-chest", location="t8-8", on_top_of="ceiling", weight="light")
        yield Chest(name="green-chest", contents="blue-key", unlocked_by="red-key")
        yield Thing(name="red-key", location="t1-3", on_top_of="floor", weight="light")
        yield GoalIsTo(action="eat", arguments="bananas")
    
In [10]:
    
mab = MonkeesAndBananas()
mab.reset()
mab.run()