# -*- coding: utf-8 -*-
"""
Created on Thu Nov 28 10:39:22 2013

@author: Lunaria
"""
from copy import deepcopy,copy
from Objet import ArbreChemin
from Resolution import *

def ReflexionIa(_game):
    """
    fonction permettant de trouver le meilleur chemin possible en reflechissant à long terme.
    @type _game: TourDeJeu.Jeu
    @param _game: objet contenant l'integralite des elements du jeu
    
    @rtype:list,fonction,list,int
    @return: liste des deplacements a realiser, sens dans lequel on doit pousser la case restante pour avoir la possibilite de faire le chemin, position d'insertion de la case restante ,nombre de rotation  à 90° afin de pouvoir suivre le chemin donner
    """
    #initialisation des variables de sortie et copie du jeu pour ne pas le modifier
    game = deepcopy(_game)
    meilleurGain = -1
    meilleurDeplacement=list([game.listeJoueur[game.listeJoueur['joueurActif']].position])
    meilleurPosition ={"ligne":0,"colonne":1}
    meilleurFonction = 1
    meilleurRotation = 0
    fonction = [_game.deplacementVersLeHaut,_game.deplacementVersLeBas,_game.deplacementVersLaGauche,_game.deplacementVersLaDroite]
                
    #test sur les cases ou l'on peut poser la case restante
    for indice in xrange (1,game.configuration["nbCase"],2):
        fonction_Parametre =["Haut","Bas","Gauche","Droite"]
        ligne = 0
        colonne = 0
        index = 0
        #test sur les sens d'insertion de la case restante
        while index <4 :
            
             #test pour toutes les rotations de la case restante
             for nbRotation in xrange(4):
                game.CaseRestante.Carte.orientation = (game.CaseRestante.Carte.orientation +(-90*nbRotation))%360
                game.CaseRestante.Carte.rotation90(nbRotation) 
                if fonction_Parametre[index]== "Haut":
                    game.deplacementVersLeHaut(6,indice,False)
                    ligne=6
                    colonne=indice
                elif fonction_Parametre[index] == "Bas":
                    game.deplacementVersLeBas(0,indice,False)
                    ligne=0
                    colonne=indice
                elif fonction_Parametre[index]== "Gauche":
                    game.deplacementVersLaGauche(indice,6,False)
                    ligne=indice
                    colonne=6
                    
                elif fonction_Parametre[index] == "Droite":
                    game.deplacementVersLaDroite(indice,0,False)
                    ligne=indice
                    colonne=0
                
                #initialisation des informations pour la generation de l'arbre des chemin possible
                positionDuJoueur = list(game.listeJoueur[game.listeJoueur['joueurActif']].position)
                joueur = game.listeJoueur[game.listeJoueur['joueurActif']]
                valeurPassage,valeurArret = calculeValeurIADifficile(game,positionDuJoueur,game.fantomePouvantEtreMange,joueur)
                initArbreChemin = ArbreChemin(list([positionDuJoueur]),valeurPassage,valeurArret)
                
                #generation complete de l'arbre des chemins
                arbreChemin = generationArbreIADifficile(game,copy(positionDuJoueur),initArbreChemin,copy(game.fantomePouvantEtreMange),joueur)
                
                #recherche du meilleur chemin dans l'arbre
                game.liste_deplacement,gain = meilleurChemin(arbreChemin)
                
                #on met a jour le meilleur chemin si le gain de se chemin est plus grand et que le chemin final ne se termine pas par une case ou se situ un autre joueur
                if game.plateauDeJeu[game.liste_deplacement[-1][0]][game.liste_deplacement[-1][1]].Joueur == None and (meilleurGain < gain or (meilleurGain == gain and len(meilleurDeplacement)>len(game.liste_deplacement))):
                    meilleurGain = gain
                    meilleurDeplacement = game.liste_deplacement
                    meilleurPosition = {"ligne":ligne,"colonne":colonne}
                    meilleurFonction = index
                    meilleurRotation = nbRotation
                #cas ou le meilleur chemin est celui de rester sur place
                elif meilleurGain < gain and len(game.liste_deplacement)==1:
                    meilleurGain = gain
                    meilleurDeplacement = game.liste_deplacement
                    meilleurPosition = {"ligne":ligne,"colonne":colonne}
                    meilleurFonction = index
                    meilleurRotation = nbRotation
                
                #apres le test du placement d'une carte et de calcule du gain on reinitialise le jeu pour tester une nouvelle option
                game = deepcopy(_game)
               
             index+=1
    
        
    return meilleurDeplacement,fonction[meilleurFonction],meilleurPosition,meilleurRotation


def generationArbreIADifficile(_game,_positionDepart,_arbreChemin,_fantomePouvantEtreMange,_joueur):
        """
        fonction donnant toutes les cases accessible à partir d'un etat du plateau de jeu et d'un position de depart pour un joueur donne
        @type _positionDepart: list
        @param _positionDepart: liste contenant les coordonnes a partir de laquelle on cherche les cases accessibles
        @type _arbreChemin: Objet.arbre
        @param _arbreChemin: liste des coordonnées accessible a partir de la case de depart range sous forme d'arbre
        @type _fantomePouvantEtreMange: int
        @param _fantomePouvantEtreMange: Prochain fantome pouvant etre mange
        @type _joueur: Objet.joueur
        @param _joueur: joueur pour lequel on souaite realiser l'arbre
        
        @rtype:Objet.arbre
        @return: liste des coordonnées accessible a partir de la case de depart range sous forme d'arbre
        """
        
        #generation des positions des cartes accessible depuis la case
        nouvellepositionUp = [(_positionDepart[0]-1) % _game.configuration['nbCase'],_positionDepart[1]]
        nouvellepositionDown = [(_positionDepart[0]+1) % _game.configuration['nbCase'],_positionDepart[1]]
        nouvellepositionLeft = [_positionDepart[0],(_positionDepart[1]-1) % _game.configuration['nbCase']]
        nouvellepositionRight =[_positionDepart[0],(_positionDepart[1]+1) % _game.configuration['nbCase']]
        
        for index,item in enumerate([[nouvellepositionUp,_arbreChemin.set_ArbreUp,_arbreChemin.get_ArbreUp],
                                     [nouvellepositionDown,_arbreChemin.set_ArbreDown,_arbreChemin.get_ArbreDown],
                                     [nouvellepositionRight,_arbreChemin.set_ArbreRight,_arbreChemin.get_ArbreRight],
                                     [nouvellepositionLeft,_arbreChemin.set_ArbreLeft,_arbreChemin.get_ArbreLeft]]):
                                         
            #si la case teste est accessible alors on complete l'arbre en generant l'arbre suivant sur une branche de l'arbre                            
            if (mouvementPossible(_game.plateauDeJeu[_positionDepart[0]][_positionDepart[1]].Carte,index,_game.plateauDeJeu[item[0][0]][item[0][1]].Carte)
                and not(item[0] in _arbreChemin.chemin)):
                    
                nouveauChemin = copy(_arbreChemin.chemin)
                nouveauChemin.append(item[0])
                valeurPassage,valeurArret = calculeValeurIADifficile(_game,[item[0][0],item[0][1]],_fantomePouvantEtreMange,_joueur)
                item[1](ArbreChemin(nouveauChemin,_arbreChemin.valeur+valeurPassage,_arbreChemin.valeur+valeurArret))
                #exploration en profondeur avec une generation recursive de l'arbre
                generationArbreIADifficile(_game,item[0],item[2](),_fantomePouvantEtreMange,_joueur)
        return _arbreChemin

    
def calculeValeurIADifficile(_Game,_positionJoueur,_fantomePouvantEtreMange,_Joueur) :
    """
    fonction permattant de calculer la valeur obtenu en allant sur une case donnee pour un joueur donne
    @type _Game: TourDeJeu.Jeu
    @param _Game: objet contenant l'integralite des elements du jeu
    @type _positionJoueur: list
    @param _positionJoueur: position du joueur
    @type _fantomePouvantEtreMange: int
    @param _fantomePouvantEtreMange: Prochain fantome pouvant etre mange
    @type _Joueur: Objet.Joueur
    @param _Joueur: joueur pour lequel on souaite realiser le calcule de la valeur de la case
   
    @rtype:int, int
    @return: valeur obtenu en passant par cette case, valeur obtenu en s'arretant sur cette case
    """
    #initialisation des variables 
    compteurPassage = 0
    compteurArret = 0
    gainPepite = 0
    
    #calcule du gain pour avoir complete la mission
    gainMission =  valeurMissionComplete(_Joueur,_Game.plateauDeJeu[_positionJoueur[0]][_positionJoueur[1]])
    
    #calcule du gain pour avoir pris des fantomes appartenant a la mission du joueur
    gainRecupFantomeJoueurPasage , gainRecupFantomeJoueurArret = valeurRecupererFantomJoueur(_Game,_Joueur,_positionJoueur)
    
    #calcule du gain pour avoir pris des fantomes appartenant a la mission des autres joueur
    gainRecupFantomeAutrePasage , gainRecupFantomeAutreArret = valeurRecupererFantomAutre(_Game,_Joueur,_positionJoueur)
    
    #calcule du gain pour la recuperation de pepite sur la case
    if hasattr(_Game.plateauDeJeu[_positionJoueur[0]][_positionJoueur[1]].Pepite,"valeur"):
        gainPepite =_Game.plateauDeJeu[_positionJoueur[0]][_positionJoueur[1]].Pepite.valeur
    
    #generation des variables de sortie
    compteurPassage = gainMission + gainRecupFantomeJoueurPasage + gainRecupFantomeAutrePasage + gainPepite
    compteurArret = gainMission + gainRecupFantomeJoueurArret + gainRecupFantomeAutreArret + gainPepite
    return compteurPassage,compteurArret
    
def valeurMissionComplete(_Joueur,_Case):
    """
    fonction permattant de calculer le gain pour avoir complete la mission
    @type _Joueur: Objet.Joueur
    @param _Joueur: joueur pour lequel on souaite realiser le calcule de la valeur de la case
    @type _Case: Objet.Case
    @param _Case: represente une case du plateau de jeu
   
    @rtype:int
    @return: gain pour avoir fini la mission
    """
    missionFini = True
    if hasattr(_Case.Fantome,"numero") and _Case.Fantome.numero == _Joueur.tabNumMission[-1]:
        #pour chaque fantome dans la mission s'ils ne sont pas dans la liste des fantomes recupere alors la mission n'est pas reussi
        for numero in _Joueur.tabNumMission[:-1]:
            if numero not in _Joueur.tabNumFantome:
                missionFini = False
                break
        
        if missionFini:
            print "OUIIIIIIIIIIIIIII --- IA_difficile/valeurMissionComplete ----"
            return 40
    return 0
        
def valeurRecupererFantomJoueur(_Game,_Joueur,_positionJoueur):
    """
    fonction permattant de calculer le gain pour avoir pris des fantomes appartenant a la mission du joueur
    @type _Game: TourDeJeu.Jeu
    @param _Game: objet contenant l'integralite des elements du jeu
    @type _Joueur: Objet.Joueur
    @param _Joueur: joueur pour lequel on souaite realiser le calcule de la valeur de la case
    @param _positionJoueur: position du joueur
    
    @rtype:int, int
    @return: gain pour passer sur la case ou se trouve le joueur, gain pour s'arreter sur cette case
    """
    #initialisation
    valeurPassage = 0
    valeurArret = 0   
    positionFamtome_actuel=positionFantom(_Game,_Game.fantomePouvantEtreMange)
    #la recuperation des fantomes apporte un gain pour l'arret sur la case mais aussi pour le passage
    if positionFamtome_actuel != None and positionFamtome_actuel == _positionJoueur and _Game.fantomePouvantEtreMange in _Joueur.tabNumMission :
        valeurPassage += 20
        valeurArret +=20
    
    #on cherche la position du prochain fantome sur la mission mais il ne faut pas qu'il soit plus grand de 4 par rapport au fantome en cours
    prochainFantomeMission = float("inf") 
    for mission in _Joueur.tabNumMission:
        if mission >=_Game.fantomePouvantEtreMange and mission < prochainFantomeMission:
            prochainFantomeMission = mission
    #calcul d'un gain potentiel si le joueur s'arete sur la case par rapport a la distance avec le prochain fantome sur la mission
    positionFantome = positionFantom (_Game,prochainFantomeMission)
    if positionFantome!= None:
        distanceALaProchaineMission = calculeDistance(_positionJoueur,positionFantome,_Game.configuration['nbCase'])
        valeurArret = valeurArret + (20/((distanceALaProchaineMission+1)))
    
    return valeurPassage,valeurArret
    
def valeurRecupererFantomAutre(_Game,_Joueur,_positionJoueur):
    """
    fonction permattant de calculer le gain pour avoir pris des fantomes appartenant a la mission des autres joueur
    @type _Game: TourDeJeu.Jeu
    @param _Game: objet contenant l'integralite des elements du jeu
    @type _Joueur: Objet.Joueur
    @param _Joueur: joueur pour lequel on souaite realiser le calcule de la valeur de la case
    @param _positionJoueur: position du joueur
    
    @rtype:int, int
    @return: gain pour passer sur la case ou se trouve le joueur,  gain pour s'arreter sur cette case
    """
    #initialisation des variables 
    valeurPassage = 0
    valeurArret = 0    
    positionFamtome_actuel=positionFantom(_Game,_Game.fantomePouvantEtreMange)
    #la recuperation des fantomes apporte un gain pour l'arret sur la case mais aussi pour le passage
    if positionFamtome_actuel !=None and positionFamtome_actuel== _positionJoueur:
        valeurPassage += 5
        valeurArret +=5
    
    #si le joueur s'arrete sur le fantome pouvant etre mange suivant alors il gagne un gain potentiel. celui ci n'est valable que pour l'arret sur la case
    FantomeSurLaCaseDuJoueur = _Game.plateauDeJeu[_positionJoueur[0]][_positionJoueur[1]].Fantome    
    if (_Game.fantomePouvantEtreMange+1) in _Joueur.tabNumMission:
        if hasattr(FantomeSurLaCaseDuJoueur,"numero") and FantomeSurLaCaseDuJoueur.numero == (_Game.fantomePouvantEtreMange+1):
            valeurArret +=2
    
    return valeurPassage,valeurArret
