# Notes from Wednesday, November 8

# A program to solve the Missionaries & Cannibals problem using
# heuristic search.

from HeuristicSearch import search

class MissionaryState:

    def __init__(self, missionaries, cannibals, boat):
        self.numM = missionaries
        self.numC = cannibals
        self.boat = boat

    def __str__(self):
        hereM = self.numM * 'M'
        hereC = self.numC * 'C'
        thereM = (3 - self.numM) * 'M'
        thereC = (3 - self.numC) * 'C'
        if self.boat == 'here':
            return "%s%s  \__/,,,,,  %s%s" % (hereM, hereC, thereM, thereC)
        else:
            return "%s%s  ,,,,,\__/  %s%s" % (hereM, hereC, thereM, thereC)

    # __eq__ allows two MissionaryState objects to be compared using the == operator
    def __eq__(self, other):
        return self.numM == other.numM and \
               self.numC == other.numC and \
               self.boat == other.boat

    # returns true if this state is valid
    def isValid(self):
        if self.numM < 0 or self.numM > 3: return False
        if self.numC < 0 or self.numC > 3: return False
        if self.numC > self.numM and self.numM > 0: return False
        numMthere = 3 - self.numM
        numCthere = 3 - self.numC
        if numCthere > numMthere and numMthere > 0: return False
        return True

    # operators
    def move1M(self):
        if self.boat == 'here':
            action = "1 missionary crosses the river"
            newState = MissionaryState(self.numM - 1, self.numC, 'there')
        else:
            action = "1 missionary comes back"
            newState = MissionaryState(self.numM + 1, self.numC, 'here')
        if newState.isValid():
            return (action, newState)
        else:
            return None

    def move1C(self):
        if self.boat == 'here':
            action = "1 cannibal crosses the river"
            newState = MissionaryState(self.numM, self.numC - 1, 'there')
        else:
            action = "1 cannibal comes back"
            newState = MissionaryState(self.numM, self.numC + 1, 'here')
        if newState.isValid():
            return (action, newState)
        else:
            return None

    def move2M(self):
        if self.boat == 'here':
            action = "2 missionaries cross the river"
            newState = MissionaryState(self.numM - 2, self.numC, 'there')
        else:
            action = "2 missionaries come back"
            newState = MissionaryState(self.numM + 2, self.numC, 'here')
        if newState.isValid():
            return (action, newState)
        else:
            return None

    def move2C(self):
        if self.boat == 'here':
            action = "2 cannibals cross the river"
            newState = MissionaryState(self.numM, self.numC - 2, 'there')
        else:
            action = "2 cannibals come back"
            newState = MissionaryState(self.numM, self.numC + 2, 'here')
        if newState.isValid():
            return (action, newState)
        else:
            return None

    def move1M1C(self):
        if self.boat == 'here':
            action = "1 missionary and 1 cannibal cross the river"
            newState = MissionaryState(self.numM - 1, self.numC - 1, 'there')
        else:
            action = "1 missionary and 1 cannibal come back"
            newState = MissionaryState(self.numM + 1, self.numC + 1, 'here')
        if newState.isValid():
            return (action, newState)
        else:
            return None

    def applyOperators(self):
        results = [self.move1M(), self.move1C(), self.move2M(), self.move2C(), self.move1M1C()]
        # remove any occurrences of None from results
        return [r for r in results if r is not None]

#----------------------------------------------------------------------
# heuristic function

def distance(state, goal):
    return (state.numM + state.numC) / 2

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

start = MissionaryState(3, 3, 'here')
goal = MissionaryState(0, 0, 'there')

def main():
    search(start, goal, distance)

