# read in definitions from Search module
from Search import *

class PitcherState:
    """
    This class implements a state representation for the Water Jug
    Puzzle: Suppose that you are given a 3-quart pitcher and a 4-quart
    pitcher.  Either pitcher can be filled up completely from a
    faucet.  The entire contents of either pitcher can be poured down
    a drain.  Water may be poured from one pitcher to the other.  When
    pouring, as soon as the pitcher being poured into is full, the
    pouring stops.  There is no additional measuring device and the
    pitchers have no markings to show partial quantities.

    Each operator returns a new instance of this class, representing a
    successor state.  The instance variables self.P3 and self.P4
    represent the amount of water in the 3-quart and 4-quart pitchers,
    respectively.
    """
    def __init__(self, P3, P4):
        self.P3 = P3
        self.P4 = P4

    def __str__(self):
        # returns a string representation of the pitcher state
        return '(%d, %d)' % (self.P3, self.P4)

    def __eq__(self, other):
        # returns True if this pitcher state is equivalent to other
        return self.P3 == other.P3 and self.P4 == other.P4

    #-------------------------------------------------------
    # state operators

    def fillP3(self):
        return PitcherState(3, self.P4)

    def fillP4(self):
        return PitcherState(self.P3, 4)

    def drainP3(self):
        return PitcherState(0, self.P4)

    def drainP4(self):
        return PitcherState(self.P3, 0)

    def pourP3toP4(self):
        # space available in the 4-quart pitcher
        capacity = 4 - self.P4
        if self.P3 > capacity:
            # do not overfill
            return PitcherState(self.P3 - capacity, 4)
        else:
            return PitcherState(0, self.P4 + self.P3)

    def pourP4toP3(self):
        # space available in the 3-quart pitcher
        capacity = 3 - self.P3
        if self.P4 > capacity:
            # do not overfill
            return PitcherState(3, self.P4 - capacity)
        else:
            return PitcherState(self.P3 + self.P4, 0)

    #-------------------------------------------------------

    def isValid(self):
        # returns True for a valid state configuration
        return 0 <= self.P3 <= 3 and 0 <= self.P4 <= 4

    def applyOperators(self):
        # Functions and methods in Python are "first class": you can
        # treat them just like any other values.  Here we store them
        # in a list of tuples, paired up with strings that describe
        # what each one does.
        operators = [(self.fillP3, 'Fill P3'),
                     (self.fillP4, 'Fill P4'),
                     (self.drainP3, 'Drain P3'),
                     (self.drainP4, 'Drain P4'),
                     (self.pourP3toP4, 'Pour from P3 to P4'),
                     (self.pourP4toP3, 'Pour from P4 to P3')]
        results = []
        for (operator, action) in operators:
            # here we call the operator method to create the new state
            newState = operator()
            if newState.isValid():
                results.append((newState, action))
        # results is a list of tuples of the form (state, string)
        return results


#-------------------------------------------------------
# Solve the puzzle!

def main():
    BreadthFirst(PitcherState(0, 0), PitcherState(0, 2))
