Skip to content

Implementation of the water jugs problem #26

@versae

Description

@versae

Hi, thanks for this interesting library!

I tried implementing a version of the water jugs problem (of which a version was seen on the Die Hard movie), but it gets stuck in loop. The problem is usually formulated as this:

Suppose we have two jugs. One jug is capable of holding 3 gallons of water. A second jug can hold up to 4 gallons of water. There are no measurement lines on either jug. Therefore we can never determine the exact amount of water in either jug. However, by looking in either jug, we can determine if the jug is empty, full, or contains some water. We can empty a jug, fill a jug, or pour water from one jug into the other.

I thought the implementation would be straightforward, but after many tries I was unable to make work. I'm sure I'm missing something.

from experta import KnowledgeEngine, Fact, Field, Rule, DefFacts, AS, MATCH, TEST, P, W, L


class Jug(Fact):
    content = Field(int, default=0, mandatory=True)


class Jug3(Jug): pass
class Jug4(Jug): pass


class Jugs(KnowledgeEngine):

    @DefFacts()
    def init(self):
        yield Jug3(content=0)
        yield Jug4(content=0)

    @Rule(Jug4(content=L(2)))
    def goal(self):
        print("Done")
        self.halt()

    @Rule(
        AS.jug3 << Jug3(content=MATCH.content3),
        AS.jug4 << Jug4(content=MATCH.content4),
        TEST(lambda content3, content4: content3 < 3 and content4 > 0),
    )
    def pour_jug4_into_jug3(self, jug3, jug4, content3, content4):
        content_to_pour = min(3 - content3, content4, 3)
        jug3_content = content3 + content_to_pour
        jug4_content = content4 - content_to_pour
        self.modify(jug3, content=jug3_content)
        self.modify(jug4, content=jug4_content)
        print("Pour jug4 into jug3")

    @Rule(
        AS.jug3 << Jug3(content=MATCH.content3),
        AS.jug4 << Jug4(content=MATCH.content4),
        TEST(lambda content3, content4: content3 > 0 and content4 < 4),
    )
    def pour_jug3_into_jug4(self, jug3, jug4, content3, content4):
        content_to_pour = min(4 - content4, content3, 4)
        jug3_content = content3 - content_to_pour
        jug4_content = content4 + content_to_pour
        self.modify(jug3, content=jug3_content)
        self.modify(jug4, content=jug4_content)
        print("Pour jug3 into jug4")

    @Rule(AS.jug3 << Jug3(content=P(lambda x: x > 0)))
    def empty_jug3(self, jug3):
        self.modify(jug3, content=0)
        print("Empty jug3")

    @Rule(AS.jug4 << Jug4(content=P(lambda x: x > 0)))
    def empty_jug4(self, jug4):
        self.modify(jug4, content=0)
        print("Empty jug4")

    @Rule(AS.jug3 << Jug3(content=P(lambda x: x < 3)))
    def fill_jug3(self, jug3):
        self.modify(jug3, content=3)
        print("Fill jug3")

    @Rule(AS.jug4 << Jug4(content=P(lambda x: x < 4)))
    def fill_jug4(self, jug4):
        self.modify(jug4, content=4)
        print("Fill jug4")


engine = Jugs()
engine.reset()
engine.run()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions