class Cafe:

    def actions(self):
        """ definit l'ensemble d'actions possibles """
        return(['gauche','droite','prendre','poser'])

    def etats(self):
        """ definit l'ensemble d'etats possibles """
        return([(1,0),(1,1),(2,0),(2,1),(3,0),(3,1)])

    def transition(self,s,a):
        """ definit les consequences d'une action """
        pos=s[0];
        cafe=s[1];
        if (a=='gauche'):
            if (pos>1):
                pos=pos-1
        if (a=='droite'):
            if (pos<3):
                pos=pos+1
        if (a=='prendre'):
            if (pos==3):
                cafe=1
        if (a=='poser'):
            cafe=0
        return(pos,cafe);


    def recompense(self,s,a,sarr):
        """ definit les recompenses obtenues """
        if (s[0]==1) and (s[1]==1) and (a=='poser'):
            return(100)
        return(-1)


#************************************************************

class SystemeExecute:

    def __init__(self,pb):
        """ construit un systeme a partir d'un probleme """
        self.pb=pb

    def executerPi(self,pi,depart,nb):
        """ excecute la  politique pi a partir de l'etat depart """
        s=depart
        for i in range(nb):
            action=pi[s]
            sFin=pb.transition(s,action)
            print(s," -> ",action," : ",sFin)
            s=sFin

    def executerPiRec(self,pi,depart,nb):
        """ excecute la  politique pi a partir de l'etat depart et retourne perf"""
        s=depart
        gamma=0.99
        somme=0
        for i in range(nb):
            action=pi[s]
            sFin=pb.transition(s,action)
            r=pb.recompense(s,action,sFin)
            somme=somme+(gamma**i)*r
            print(s," -> ",action," : ",sFin,"<",r,">")
            s=sFin
        return somme



#************************************************************



class Systeme:
    """permet d'executer un probleme
    - pb : attribut du probleme"""

    def __init__(self,pb):
        """construit un systeme a partir d'un probleme"""
        self.pb=pb

    def execute(self,s,a):
        #print ("etat depart: ",s)
        #print ("action: ",a)
        sArriv = self.pb.transition(s,a)
        #print ("etat arrivee: ",sArriv)
        #print ("recompense: ",self.pb.recompense(s,a,sArriv))
        #print("********")
        return(sArriv)

    def intialiseQ(self):
        """construit des Qvaleurs vides"""
        Q = {}
        return Q


    def executerPi(self,pi,depart,nb):
        """permet d'executer la politique pendant nb pas de temps"""
        s=depart
        for i in range(nb):
            action=pi[s]
            sFin=self.execute(s,action)
            r=self.pb.recompense(s,action,sFin)
            print(s," -> ",action," : ",sFin,"<",r,">")
            s=sFin


    def afficherPi(self,pi):
        """ permet d'afficher la politique pi"""
        for etat in self.pb.etats():
            chaine = str(etat)
            chaine+= " -> " + str(pi[etat])
            print(chaine)


    def afficherQ(self,Q):
        """ permet d'afficher les Qvaleurs"""
        for etat in self.pb.etats():
            chaine = str(etat)
            print()
            print(chaine)
            for action in self.pb.actions():
                chaine="   - "+action+" -> "+str(round(Q[(etat,action)],2))
                print(chaine)

    def afficherQS(self,Q,etat):
        """ permet d'afficher les Qvaleurs de l'etat etat"""
        chaine = ""+str(etat)+" - "
        for action in self.pb.actions():
            chaine+=action+" -> "+str(Q[(etat,action)])+", "
        print(chaine)


    def politiqueFromQ(self,Q):
        """ construit la politique a partir de Q """
        pi={}
        for etat in self.pb.etats():
            # cherche max arrivee
            max=-100000
            amax=-1;
            for actionMax in self.pb.actions():
                if (Q[(etat,actionMax)]>max):
                    max=Q[(etat,actionMax)]
                    amax=actionMax
            pi[etat]=amax
        return(pi)


    ####################################################
    # A COMPLETER
    ####################################################
    def valueIteration(self,nb):
        """effectue algorithme value iteration"""
        gamma=0.99
        # initialiser les Q valeurs (utilise methode initialiserQ)
        # [...]
        # faire nb iteration
        # [...]
            # creer une mise a jour de Qvaleurs (avec executerUneIteration)
            # [...]
            # affiche Q
            print('*** iteration '+str(i)+' ****')
            self.afficherQ(Q)
        # retourne Qvaleurs
        return(Q)

    def executerUneIteration(self,Q,gamma):
        """ effectue une iteration de value iteration """
        #initialiser Q2
        # [...]
        # pour chaque etat
        # [...]
            #pour chaque action
            # [...]
                # calculer arrivee et recompense
                # [...]
                # cherche max arrivee (utiliser chercherMax)
                # [...]
                #mise a jour de Q2
                # [...]
        #retourne Q2
        return(Q2)

    def chercherMax(self,Q,sArriv):
        """ cherche la valeur maximale sur un etat"""
        #initialiser max
        # [...]
        #pour chaque action
        # [...]
            #si la clef n'existe pas, on cree
            # [...]
            #si la valeur est plus grande que max, c'est le max
            # [...]
        #retourne max
        return(max)

    ####################################################
    # FIN A COMPLETER
    ####################################################


#************************************************************


print("****************************************")
print("******        probleme cafe         ****")
print("****************************************")


pb=Cafe()
print('etats => ',pb.etats())
print('actions => ', pb.actions())

print()
print("****************************************")
print("*******       planif            ********")
print("****************************************")
print("planification")
systeme=Systeme(pb)
Q=systeme.valueIteration(30)


print()
print("****************************************")
print("***         Affiche Q               ****")
print("****************************************")
#print(Q)
systeme.afficherQ(Q)
#systeme.afficherQS(Q,(3,2))
#systeme.afficherQS(Q,(3,3))


print()
print("****************************************")
print("***         Affiche pi              ****")
print("****************************************")
pi=systeme.politiqueFromQ(Q)
systeme.afficherPi(pi)

print()
print("****************************************")
print("***         Execute pi              ****")
print("****************************************")


systeme.executerPi(pi,(1,0),30)
