##################
# Cups Tester Program
# Example for CSC 396 Spring 2008
# Mark Goadrich
##################

class Cup:
    """Keeps track of one cup"""
    def __init__(self, size, contents):
        self.size = size
        self.contents = contents

    def getSize(self):
        return self.size

    def getContents(self):
        return self.contents

    def setContents(self, contents):
        self.contents = contents

    def clone(self):
        return Cup(self.size, self.contents)

    def fill(self):
        self.contents = self.size

    def empty(self):
        self.contents = 0

    def pourFrom(self, c):
        if (c.getContents() + self.contents < self.size):
            self.contents += c.getContents()
            c.setContents(0)
        else:
            c.setContents(c.getContents() - (self.size - self.contents))
            self.contents = self.size

class CupsNode:
    """Holds the state, parent, action, level and hvalue"""
    
    goalValue = 0;  # Global for knowing what the goal is

    def __init__(self, cups, parent, action):
        self.cups = cups;
        self.parent = parent;
        self.action = action;
        if (parent != None):
            self.level = parent.getLevel() + 1;
        else:
            self.level = 0
        self.hvalue = 0

    def getLevel(self):
        return self.level

    def gethValue(self):
        return self.hValue

    def getCup(self, i):
        return self.cups[i]

    def __eq__(self, other):
        for i in range(len(self.cups)):
            if (self.cups[i].getContents() != other.cups[i].getContents()):
                return False
        return True;

    def isGoal(self):
        if (self.cups[0].getContents() == CupsNode.goalValue):
            return True
        return False;
        
    def generateChildren(self):
    
        cns = []
        for i in range(len(self.cups)):
            
            # First generate the fill children
            ca = []
            for j in range(len(self.cups)):
                ca.append(self.cups[j].clone())
        
            ca[i].fill()
            cns.append(CupsNode(ca, self, "Fill " + str(ca[i].getSize())))
        
            # Then generate the empty children
            ca = []
            for j in range(len(self.cups)):
                ca.append(self.cups[j].clone())
        
            ca[i].empty()
            cns.append(CupsNode(ca, self, "Empty " + str(ca[i].getSize())))
        
            # Then generate all the pour children
            for k in range(len(self.cups)):
                if (k != i):
                    ca = []
                    for j in range(len(self.cups)):
                        ca.append(self.cups[j].clone())
            
                    ca[i].pourFrom(ca[k])
                    cns.append(CupsNode(ca, self, "Pour From " + \
                                        str(ca[k].getSize()) + " into " + \
                                        str(ca[i].getSize())))
        return cns

    def __str__(self):
        s = ""
        for i in range(len(self.cups)):
            s += str(self.cups[i].getContents()) + " "
        return s;
    
    def printPath(self):
        if (self.parent != None):
            self.parent.printPath()
        print str(self) + " " + self.action

# A Tester program to get you started.  Here is where you would implement the 
# Search algorithm discussed in class with open and closed lists
def main():
    cups = []
    CupsNode.goalValue = input("What is the goal amount? ")
    numCups = input("How many cups are there? ")
    for i in range(numCups):
        cups.append(Cup(input("What is the size of cup " + str(i) + " "), 0))
    initial = CupsNode(cups, None, "Initial")

    kids = initial.generateChildren()
    for i in range(len(kids)):
        kids[i].printPath();
        print "Goal? " + str(kids[i].isGoal())
        print "Same as initial? " + str(initial == kids[i])
        print
            
main()