#Wumpus world
#(c) 2016 Shaun Ramsey
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    
import copy
import sys
import time
import readline
import random

starttime = time.clock()
dead = False #hero hasn't died

class Wumpus:
    wumpusAlive = True
    usedArrow = False
    N = 4 #default board size
    x = 0
    y = N-1
    
    def __init__(self, size, mywumps):
        self.N = int(size)
        self.wumpsWorld = copy.deepcopy(mywumps)


    def west(self):
        if self.x > 0:
            return self.wumpsWorld[self.y][self.x-1]
        return '.'

    def east(self):
        if self.x < self.N - 1:
            return self.wumpsWorld[self.y][self.x+1]
        return '.'
    
    def north(self):
        if self.y > 0:
            return self.wumpsWorld[self.y-1][self.x]
        return '.'

    def south(self):
        if self.y < self.N - 1:
            return self.wumpsWorld[self.y+1][self.x]
        return '.'




        #what kinds of things can we tell about teh world
        # Shiny (True or FalsE), Breezy (True or False) and Stinky (true or False)
    def percepts(self):
        shiny = False
        if self.wumpsWorld[self.y][self.x] == 'S' or self.wumpsWorld[self.y][self.x] == 'T':
            shiny = True
        breezy = self.north() == 'H' or self.south() == 'H' or self.east() == 'H' or self.west() == 'H'
        stinky = self.north() == 'W' or self.south() == 'W' or self.east() == 'W' or self.west() == 'W'
        return shiny, breezy, stinky
        


    def display(self):
        global dead
        if dead is True:
            print "You are dead. DON'T PASS GO!"
            return
        print self.percepts()
        for e in self.wumpsWorld:
            str1 = ''.join(e)
            print str1
        if self.usedArrow:
            print "You no longer have an arrow!"
        else:
            print "You still have not used your arrow!"
        if self.wumpusAlive:
            print "You have not heard the Wumpus scream!"
        else:
            print "You have heard the Wumpus scream!"


    def moveTo(self, ix, iy):
        global dead
        if ix < 0 or ix >= self.N:
            print ("You've found a wall.")
            return
        else:
            self.x = ix
        if iy < 0 or iy >= self.N:
            print ("You've found a wall.")
            return
        else:
            self.y = iy
        if self.wumpsWorld[self.y][self.x] == 'W':
            dead = True
            print (" The fuzzy monster cuddles you into starvation. ")
        elif self.wumpsWorld[self.y][self.x] == 'H':
            dead = True
            print (" You fell into a breezy pit and chilled. ")
        elif self.wumpsWorld[self.y][self.x] == 'S':
            print (" The gold is shining before you. ")
            self.wumpsWorld[self.y][self.x] = 'T' #T is you with the shiny
            return True
        else:
            self.wumpsWorld[self.y][self.x] = 'Y'
            return True #move was successful
        return False


    def makeMove(self, num):
        ox,oy = self.x,self.y
        oldValue = self.wumpsWorld[oy][ox]
        nx,ny = self.x,self.y
        moveName = ''
    
        if num == 0 or num == 4: # means move north
            moveName = 'North'
            ny = ny - 1
        elif num == 1 or num == 5: #east
            moveName = 'East'
            nx = nx + 1
        elif num == 2 or num == 6:
            moveName = 'South'
            ny = ny + 1
        elif num == 3 or num == 7:
            moveName = 'West'
            nx = nx - 1
            
        if num < 4:
            print 'You have chosen to move to the', moveName, '!'
            print '*************************************'
            success = self.moveTo(nx,ny) # so x,y is inverted here. north will mean +1 to first coord
            if success:
                if oldValue == 'T':
                    self.wumpsWorld[oy][ox] = 'S'
                else:
                    self.wumpsWorld[oy][ox] = '.'
        else: #then we shot an arrow
            if self.usedArrow: # you already used it
                print 'You fumble with your quiver incessantly.'
            else:
                print 'You fire your arrow off into the darkness. It zooms to the',moveName,'!'
                # walk in the direction, if there was a wumpus, kill it, are neighboring squares still smelly?
                x = self.x
                y = self.y
                addx = 0
                addy = 0
                move = num
                if move == 4: #fire north -1 in y
                    addy = -1
                elif move == 5:
                    addx = 1
                elif move == 6:
                    addy = 1
                elif move == 7:
                    addx = -1
                x = x + addx
                y = y + addy
                killedWumpus = False
                while y < self.N and x < self.N and y >= 0 and x >= 0:
                    if self.valueAt(x,y) == 'W':
                        self.wumpsWorld[y][x] = '.' # it was called remove from board
                        self.wumpusAlive = False
                        killedWumpus = True
                        print 'You hear a loud scream coming from down the corridor!'
                        break
                    x = x + addx
                    y = y + addy
            
                self.usedArrow = True
                # walk through to see if it hits the wumpus
            

    def valueAt(self, i, j):
        if j >= 0 and j < self.N and i >= 0 and i < self.N:
            return self.wumpsWorld[j][i]
        return 'X'                

    def options(self):
        print '[0,1,2,3]: Move to the North, East, South, West'
        print '[4,5,6,7]: Fire Arrow to the North, East, South, West'
            



class knowledge:
    x = 0
    N = 4
    y = N-1
    wumpsWorld = [[ '.', '.', '.', '.'],
                  [ '.', '.', '.', '.'],
                  [ '.', '.', '.', '.'],
                  [ 'Y', '.', '.', '.']]
    usedArrow = False
    deadWumpus = False
    wumpusFound = False
    smellyList = []


    def display(self):
        print 'usedArrow is: ', self.usedArrow
        print 'deadWumpus is: ', self.deadWumpus
        print 'wumpusFound is: ', self.wumpusFound
        print 'I am at location: (',self.x,',',self.y,')'
        for e in self.wumpsWorld:
            str1 = ''.join(e)
            print str1

    def valueAt(self, i, j):
        if j >= 0 and j < self.N and i >= 0 and i < self.N:
            return self.wumpsWorld[j][i]
        return 'X'

    def west(self, value):
        if self.x > 0:
            retval = self.wumpsWorld[self.y][self.x-1]
            if value == 'Move':
                self.x = self.x - 1
                self.wumpsWorld[self.y][self.x] = 'Y'
            elif value != '?':
                self.setValue(self.x-1, self.y, value)                
            return retval
        return 'X'

    def east(self, value):
        if self.x < self.N - 1:
            retval =  self.wumpsWorld[self.y][self.x+1]
            if value == 'Move':
                self.x = self.x + 1
                self.wumpsWorld[self.y][self.x] = 'Y'
            elif value != '?':
                self.setValue(self.x+1, self.y, value)
            return retval
        return 'X'
    
    def north(self, value):
        if self.y > 0:
            retval = self.wumpsWorld[self.y - 1][self.x]
            if value == 'Move':
                self.y = self.y - 1
                self.wumpsWorld[self.y][self.x] = 'Y'
            elif value != '?':
                self.setValue(self.x, self.y-1, value)
            return retval
        return 'X'

    def south(self, value):
        if self.y < self.N - 1:
            retval = self.wumpsWorld[self.y + 1][self.x]
            if value == 'Move':
                self.y = self.y + 1
                self.wumpsWorld[self.y][self.x] = 'Y'
            elif value != '?':
                self.setValue(self.x, self.y+1, value)
            return retval
        return 'X'



    

    def fire(self, move):
        x = self.x
        y = self.y
        addx = 0
        addy = 0
        self.usedArrow = True

        if move == 4: #fire north -1 in y
            addy = -1
        elif move == 5:
            addx = 1
        elif move == 6:
            addy = 1
        elif move == 7:
            addx = -1

        x = x + addx
        y = y + addy
        killedWumpus = False
        while y < self.N and x < self.N and y >= 0 and x >= 0:
            if self.valueAt(x,y) == 'M':
                self.wumpsWorld[y][x] = 'Y'
                self.deadWumpus = True
                killedWumpus = True
                break
            x = x + addx
            y = y + addy
            
        if killedWumpus:
            for i,e in enumerate(self.wumpsWorld):
                for j,f in enumerate(e):
                    if self.valueAt(i,j) == 'M':
                        self.setValue(i,j, 'Y') #make all those squares safe !
        
        

    # wumpus is found when there is a smelly square with three safe sides
    # if self.wumpusFound is True, this function can return False and quit early
    # This function has two jobs when the right conditions are met
    # self.wumpusFound should be set True by this function and this function
    # should return false, but only if this function
    # determines that the wumpus location is definitely known AND its location
    # has been marked in the knowledge base. The function returns False in that instance.
    # If the wumpus location is definitely known and its location has not
    # been marked with an 'M' in the knowledge base, this function should return True

    #how do we know these things?
    #use the self.smellyList for a list of [x,y] locations that are smelly
    #if a smelly square has 3 safe neighbors, the wumpus location is definitely known
    #if a smelly square has 3 safe neighbors and the last neighbor is an 'M' then
    #the wumpus location is definitely known and marked with an 'M'
    
    def checkThree(self): # was the wumpus found somewhere?
        for pos in self.smellyList:
            print "(",pos[0],",",pos[1],")" # prints x,y of all positions in smellyList
        return False
        
                    

#####
    #you need to implement this function
    #doesn't return a value. Your job is to set self.wumpsworld[y][x] appropriately
    #according to the rules presented in the homework txt
#####

    def setValue(self,x,y, value):
        return #doesn't return a value. Your job is to set self.wumpsworld[y][x] appropriately
                    


## choose a random action that makes sense to it.
def selectMove(kself):
    x = kself.x
    y = kself.y
    v = [ '.', '.', '.', '.' ]
    v[0] = kself.valueAt(x,y-1)
    v[2] = kself.valueAt(x,y+1)
    v[1] = kself.valueAt(x+1,y)
    v[3] = kself.valueAt(x-1,y)
    c = []
    for i,p in enumerate(v):
        if p == 'Y':
            c.append(i)
        elif p == 'M' and kself.wumpusFound:
            c.append(4+i)
    return c



print '****************************'
print 'Welcome to the Wumpus World!'
print '****************************'
filename = "wump.txt"
# filename = raw_input('Where is your World Stored?')

print 'You are opening the world at:', filename
wumps =[]
f = open(filename)
wumps = f.readlines()
num = wumps[0]
wumps = [x.strip('\n') for x in wumps[1:]]
newwumps = []
for e in wumps:
    stringlist = []
    for i in e:
        stringlist.append(i)
    newwumps.append(stringlist)
        


world = Wumpus(num, newwumps)
knowledge = knowledge()


displayOn = False

while True: #keep looking, forever, desparately for teh gold

    shiny, breezy, smelly = world.percepts() #what's the world telling us?

    if displayOn:
        world.display()
        world.options()
        knowledge.display()
        print 'Percepts are: shiny,breezy,smelly: ', world.percepts()

    if shiny:
        world.display()
        knowledge.display()        
        print ' ****************************************** '
        print ' YOU HAVE FOUND THE GOAL and GOLD! GRATS!!!'
        print ' ****************************************** '
        break #we quit, we won, bai!


    ########################### your code to add to the knowledge base goes here ###############
    # your code should add to your knowledge base and use the knowledge you have to select a safe space
    # to visit. The goal is to find the shiny
    if not breezy and not smelly:
        knowledge.north('Y')
        knowledge.south('Y')
        knowledge.east('Y')
        knowledge.west('Y')
    if breezy:
        knowledge.north('B')
        knowledge.south('B')
        knowledge.east('B')
        knowledge.west('B')
    if smelly: # try to find wumpus?
        knowledge.smellyList.append( [ knowledge.x, knowledge.y] ) # my knowledge of what x,y coords are smelly!
        wumpfound = knowledge.checkThree()
        knowledge.north('M')  #if we found wumpus because something got safed, then these are all Y's
        knowledge.south('M')  #but if we are in a breezy square and found a wumpus, one of these is an M
        knowledge.east('M')
        knowledge.west('M')
        if wumpfound: #wumpus found next to my current square
            knowledge.wumpusFound = True

        #    world.display()
        
    if  displayOn or True: #the or True makes this display always
        knowledge.display()
    if dead:
        break
    list = selectMove(knowledge)

    if displayOn:
        print "Your list of optional moves are: ", list
        yourMove = input('Give me your move: ')

    if len(list) > 0:
        yourMove = random.choice(list)
    else:
        print "Possible moves is empty :( - Going North. Please Don't Die!"
        yourMove = 0 #just go north - probably dies :P
        
###############################################################################
    if yourMove == 0:
        knowledge.north('Move')
    if yourMove == 1:
        knowledge.east('Move')
    if yourMove == 2:
        knowledge.south('Move')
    if yourMove == 3:
        knowledge.west('Move')
    if yourMove >= 4:
        knowledge.fire(yourMove) # this only happens if we know it'll be successful
    world.makeMove(yourMove)


















