ソースを参照

Optimisation du pathfinding
Creations des classes Actions

unknown 10 年 前
コミット
185ac8be88
3 ファイル変更308 行追加31 行削除
  1. 42 26
      lib/AEtoile.py
  2. 265 5
      lib/Actions.py
  3. 1 0
      lib/Plateau.py

+ 42 - 26
lib/AEtoile.py

@@ -2,6 +2,9 @@
 # -*- coding: utf-8 -*-
 """algorithme de recherche du plus court chemin entre deux cases"""
 from math import *
+import time
+
+#pour des performances correctes, ne pas utiliser pour de deplacements de plus de 100 cases
 
 
 def distance(coord1, coord2):
@@ -46,22 +49,23 @@ def chemin(plateau, origine, cible):
                     noeud = N(coord)
                     
                     #on calcule le cout de la case
-                    noeud.creer(position, cible)
+                    noeud.creer(position, cible, plateau.cases[coord].coutDep())
 
                     #si le noeud est trouve dans la liste ouverte, on le compare a celui-ci
-                    # si ce nouveau noeud a un meilleur cout, on remplace
+                    # si ce nouveau noeud a un cout moindre, on remplace
                     # sinon on ajoute ce noeud a la liste ouverte
                     trouve = False
                     for nTest in filOuvert:
                         if nTest.coord == noeud.coord:
-                            nTest.parent = noeud.parent
-                            nTest.cout = noeud.cout
-                            trouve = True
-                            print " | teste", coord, "maj", noeud.coutG, noeud.coutH
+                            if noeud.cout < nTest.cout:
+                                nTest.parent = noeud.parent
+                                nTest.cout = noeud.cout
+                                trouve = True
+                                print " | teste", coord, "maj", noeud.coutG**2, noeud.coutH, noeud.cout
                             
                     if not trouve:
                         filOuvert.append(noeud)
-                        print " | teste", coord, "ajout", noeud.coutG, noeud.coutH
+                        print " | teste", coord, "ajout", noeud.coutG**2, noeud.coutH, noeud.cout
 
         #on parcourt les noeuds de la liste ouverte
         #et on cherche le meilleur noeud (le cout le plus faible)
@@ -84,8 +88,12 @@ def chemin(plateau, origine, cible):
         else:
             echec = True
 
-        for n in reversed(filFerme):    
-            chemin.append(n.coord)
+    #on revient de parent en parent jusqu'a l'arrivee
+    if not echec:
+        while position.coord != origine:
+            chemin.insert(0, position.coord)
+            position = position.parent
+            print "retour", position.coord
             
     return chemin                    
                     
@@ -94,6 +102,7 @@ class N():
     def __init__(self, coord):
         self.parent = None    #coord du noeud qui a amene a ce noeud-ci
         self.coord = coord
+        self.kDep = 1
         self.coutG = 0
         self.coutH = 0
         self.cout = 0
@@ -101,15 +110,16 @@ class N():
 ##    def __repr__(self):
 ##        return "{} ({}+{}= {}) |".format(self.coord, self.coutG, self.coutH, self.cout)
 
-    def creer(self, parent, cible):
+    def creer(self, parent, cible, kDep):
         self.parent = parent
+        self.kDep = kDep
         self.coutH = distanceCarree(self.coord, cible)
-        self.coutG = self.parent.coutG + 1
-        self.cout =  self.coutG + self.coutH
+        self.coutG = self.parent.coutG + self.kDep
+        self.cout =  (self.coutG)**2 + self.coutH
 
 
 ### pour les tests
-##
+
 ##class Plateau():
 ##    def __init__(self):
 ##        self.cases = {}
@@ -121,29 +131,35 @@ class N():
 ##        self.majVoisins()
 ##        
 ##    def majVoisins(self):
-##        for x in range(1, 6):
-##            for y in range(1, 6):
-##                if x in [self.coord[0] - 1, self.coord[0], self.coord[0] + 1] and \
-##                   y in [self.coord[1] - 1, self.coord[1], self.coord[1] + 1] and \
-##                   (x, y) != self.coord:
-##                    self.voisins.append((x, y))
-##
+##        voisins = []
+##        lst = [(x, y-1), (x+1, y-1), (x+1, y), (x+1, y+1), (x,   y+1), (x-1, y+1), (x-1, y), (x-1, y-1)]
+##        for coord in lst: 
+##            if (coord[0] > 0 and coord[1] > 0 and coord[0] <= 5 and coord[1] <= 5):
+##                self.voisins.append(coord)
+##                
 ##    def estFranchissable(self):
-##        return not (self.coord[0]==2 and self.coord[1] in [2, 3, 4, 5])
+##        return not (self.coord[0]==2 and self.coord[1] in [3, 4])
 ##
+##    def coutDep(self):
+##        cout = 1
+##        if self.coord in [(3,2), (3,3), (3, 4), (4,3), (4,4)]:
+##            cout = 2
+##        return cout
 ##
+##    
 ##plateau = Plateau()
 ##
 ##for x in range(1, 6):
 ##    for y in range(1, 6):
 ##        plateau.cases[(x, y)] = Case((x, y))
-##
-##
 ##depart = (1, 2)
 ##arrivee = (5, 5)
-##        
-##print chemin(plateau, depart, arrivee)
-##
+##    
+##for essai in range(1,2):
+##    t0 = time.time()
+##    c = chemin(plateau, depart, arrivee)
+##    print (time.time() - t0)
+##    print c
 
 
 

+ 265 - 5
lib/Actions.py

@@ -1,6 +1,7 @@
 #from __future__ import unicode_literals
 # -*- coding: utf-8 -*-
-
+import regles
+import AEtoile
 
 class Action():
     """action effectuee par un combattant sur le plateau de jeu"""
@@ -12,6 +13,7 @@ class Action():
 
     def activer(self, plateau):
         self.plateau = plateau
+        self.creerItemsGraphiques()
 
     def majCoordCible(self, coord):
         """met a jour les coordonnees de la cible,
@@ -20,8 +22,14 @@ class Action():
             self._coordCible = coord
             self.maj()
 
+##    def majCible(self, item):
+##        """met a jour la cible,
+##            (item actuellement actuellement survole par la souris)"""
+##        self._cible = item
+##        self.maj()
+
     def desactiver(self):
-        pass
+        self.detruireItemsGraphiques()
 
     def valider(self):
         #envoyer signal
@@ -49,21 +57,30 @@ class Action():
     def detruireItemsGraphiques(self):
         pass
 
+    def activerCurseur(self):
+        pass
+
     #affichage des cibles
     def afficherCibles(self, actif):
         pass
 
 
 class Deplacement(Action):
+    ### a completer avec des icones de deplacement,
+    #la prise en compte de la nage et de l'escalade
+    #et le calcul du cout de deplacement
     def __init__(self):
         super(Deplacement, self).__init__(self)
         self._chemin = []  #liste des coord des cases a traverser
         self._cout = 0     #cout en points de dep
 
-
     def activer(self, plateau):
         super(Deplacement, self).activer(self, plateau)
 
+    def desactiver(self):
+        self.afficherCibles(False)
+        super(Deplacement, self).desactiver(self)
+
     def majCoordCible(self, coord):
         if coord != self.coordActeur():
             super(Deplacement, self).majCoordCible(self, coord)
@@ -75,20 +92,263 @@ class Deplacement(Action):
         self._cout = 0
 
         coordInit = self.coordActeur()
-        ki = 0
-        fil = []
+
+        self._chemin = AEtoile.chemin(self.plateau, coordInit, self._coordCible)
+
+        self.afficherCibles(True)
+
+    def afficherCibles(self, actif):
+        for coord in self._chemin:
+            self.plateau.cases[coord].majEstDansChampDeplacement(actif)
+            
+
+class Attaque(Action):
+    """attaque pre-parametree affectee a un pion, un personnage ou une creature"""
+    def __init__(self):
+        super(Attaque, self).__init__(self)
+        self.nom = "Attaque"
+        self.portee = 1   #portee max en cases
+        self.attributs = regles.listeAttributsAttaques()
+        self.notes = ""
         
+
+
+class Cac(Attaque):
+    """attaque au corps a corps"""
+    def __init__(self):
+        super(Cac, self).__init__(self)
+        self.nom = "Attaque au corps-à-corps"
+        self._pionCible = None
+
+    def activer(self, plateau):
+        super(Deplacement, self).activer(self, plateau)
+
+    def desactiver(self):
+        self.afficherCibles(False)
+        super(Deplacement, self).desactiver(self)
+      
+    def maj(self):
+        self.afficherCibles(False)
+        pionCible = self_plateau.cases[self._coordCible].pionOccupant()
+        if pionCible != None and pionCible != self._plateau.pionSelectionne():
+            self._pionCible = pionCible
+        else:
+            self._pionCible = None
+        self.afficherCibles(True)
+
+    def estValide(self):
+        return (self._coordCible in self.plateau.zone(self.plateau.pionSelectionne().position, self.portee, 0, False, True))
+
+    def afficherCibles(self, actif):
+        if self._pionCible:
+            self._pionCible.estCibleAttaque(actif, self.estValide())
+
+
+class Distance(Attaque):
+    """attaque a distance"""
+    def __init__(self):
+        super(Distance, self).__init__(self)
+        self.nom = "Attaque à distance"
+        self._itemLigne = None
+        self._pionCible = None
+
+    def activer(self, plateau):
+        #creation de la ligne de mire
+        super(Distance, self).activer(self, plateau)
+
+    def majCoordCible(self, coord):
+        if self._pionCible:
+            self._pionCible.estCibleAttaque(False, self.estValide())
+        if self._coordCible in self.plateau.cases:    
+            self.plateau.cases[self._coordCible].majEstCibleCurseur(False)
+        super(Distance, self).majCoord(self, coord)        
+        
+    def desactiver(self):
+        super(Distance, self).desactiver(self)
+
         
+    def maj(self):
+        """met a jour la ligne de mire representant l'attaque a distance"""
+        self.afficherCibles(False)
+        pionCible = self_plateau.cases[self._coordCible].pionOccupant()
+
+        self.majItemsGraphiques()
+        if pionCible != None and pionCible != self._plateau.pionSelectionne():
+            self._pionCible = pionCible
+        else:
+            self._pionCible = None
 
         self.afficherCibles(True)
 
+    def estValide(self):
+        return self.plateau.estCibleAttaqueDistValide(self.plateau.pionSelectionne().position, self._coordCible, 0)
 
+    def afficherCibles(self, actif):
+        if self._pionCible:
+            self._pionCible.estCibleAttaque(actif, self.estValide())
+        else:
+            #si pas de pion vise, on affiche la case cible comme visee
+            self.plateau.cases[self._coordCible].majEstCibleCurseur(True, self.estValide())            
 
+    def creerItemsGraphiques(self):
+        self._itemLigne = QGraphicsLineItem()
+        self._itemLigne.setZValue(100)
+        pinceau = QPen()
+        pinceau.setWidth(6)
+        self._itemLigne.setPen(pinceau)
+        self._itemLigne.prepareGeometryChange()
+        self.plateau.addItem(self._itemLigne)
 
+    def majItemsGraphiques(self):
+        self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
+                                       self.plateau.cases[self._coordCible].centreGraphique))
 
+    def detruireItemsGraphiques(self):
+        if self._itemLigne != None:
+            self._itemLigne.prepareGeometryChange()
+            self.removeItem(self._itemLigne)
+            self._itemLigne = None
 
 
+class Zone(Attaque):
+    """attaque de zone de base"""
+    def __init__(self):
+        super(Zone, self).__init__(self)    
+        self.nom = "Attaque de zone"
+        self._itemLigne = None
+        self._itemCible = None
+        self._casesCibles = []
+
+    def maj(self):
+        """maj la forme de l'attaque de zone et les items cibles"""
+        self.afficherCibles(False)
+        self.majItemsGraphiques()
+        self.majCibles()
+        self.afficherCibles(True)
+
+    def majCibles(self):
+        """met a jour la liste des cases cibles"""
+        self._casesCibles = []
+        if self.estValide():
+            for coord in self.plateau.casesSousForme(self._itemCible, True, True):
+                if coord != self.plateau.pionSelectionne().position:
+                    self._casesCibles.append(coord)            
+
+    def afficherCibles(self, actif):
+        for coord in self._casesCibles:
+            self.plateau.cases[coord].majEstCibleAttaque(actif)
+        for numCombattant in self.plateau.pionsSurListeCase(self._casesCibles):
+            self.plateau.combattants[numCombattant].estCibleAttaque(actif)    
+
+    def estValide(self):
+        return self.plateau.estCibleAttaqueDistValide(self.plateau.pionSelectionne().position, self._coordCible)
+
+    def creerItemsGraphiques(self):
+        self._itemLigne = QGraphicsLineItem()
+        self._itemLigne.setPen(QPen(QColor("black")))
+        self._itemLigne.prepareGeometryChange()
+        self.plateau.addItem(self._itemLigne)
 
+        self._itemCible = QGraphicsEllipseItem()
+        self._itemCible.setPen(QPen(QColor("black")))
+        self._itemCible.prepareGeometryChange()
+        self.plateau.addItem(self._itemCible)
+
+    def detruireItemsGraphiques(self):
+        if self._itemCible != None:
+            self._itemCible.prepareGeometryChange()
+            self.removeItem(self._itemCible)
+            self._itemCible = None
+
+        if self._itemLigne != None:
+            self._itemLigne.prepareGeometryChange()
+            self.removeItem(self._itemLigne)
+            self._itemLigne = None
+
+
+
+class Ligne(Attaque):
+    """attaque de zone de forme lineaire"""
+    def __init__(self):
+        super(Ligne, self).__init__(self)
+        self.nom = "Attaque de zone: ligne"
+
+    def majItemsGraphiques(self):
+        self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
+                                       self.plateau.cases[self._coordCible].centreGraphique))
+
+
+class Disque(Attaque):
+    """attaque de zone de forme circulaire"""
+    def __init__(self):
+        super(Disque, self).__init__(self)
+        self.nom = "Attaque de zone: disque"
+        self.rayon = 1
+
+    def majCibles(self):
+        self._casesCibles = self.plateau.zone(self._coordCible, self.rayon, 0)
+
+    def afficherCibles(self, actif):
+        super(Disque, self).afficherCibles(self, actif)
+        #si on affiche une attaque invalide
+        if actif and not self.estValide():
+            self.plateau.cases[self._coordCible].majEstCibleCurseur(True, False)
+
+    def majItemsGraphiques(self):
+        self._itemLigne.setLine(QLineF(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
+                                       self.plateau.cases[self._coordCible].centreGraphique))
+        if self.estValide():
+            rect = self.rectEllipseCirculaire(self.plateau.cases[self._coordCible].centreGraphique, self.rayon)
+            if rect != None:
+                self._itemCible.setRect(rect)
+            self._itemCible.setVisible(self.estvalide() and rect != None)
+
+    def rectEllipseCirculaire(self, centre, rayon):
+        """renvoie le QRectF definissant une ellipse ayant le QPointF pour centre et le rayon en cases entres en param
+           attention: l'ellipse n'est pas tout a fait circulaire, elle couvre horizontalement et
+           verticalement le nombre de cases demandees"""
+        rect = None
+        if rayon > 0:
+            p1 = QPointF((centre.x() - (rayon*self.hCase)), (centre.y() - (rayon*self.hCase)))
+            p2 = QPointF((centre.x() + (rayon*self.hCase)), (centre.y() + (rayon*self.hCase)))
+            if p1 != p2:
+                rect = QRectF()
+                rect.setTopLeft(p1)
+                rect.setBottomRight(p2)
+        return rect
+
+
+class Cone(Attaque):
+    """attaque de zone de forme conique"""
+    def __init__(self):
+        super(Cone, self).__init__(self)
+        self.nom = "Attaque de zone: cône"
+
+    def creerItemsGraphiques(self):
+        self._itemCible = QGraphicsPolygonItem()
+        self._itemCible.setPen(QPen(QColor("black")))
+        self._itemCible.prepareGeometryChange()
+        self.plateau.addItem(self._itemCible)
+
+    def majItemsGraphiques(self):        
+        self._itemCible.setPolygon(self.polygoneCone(self.plateau.cases[self.plateau.pionSelectionne().position].centreGraphique, \
+                                                     self.plateau.cases[self._coordCible].centreGraphique))
+
+
+    def polygoneCone(self, point1, point2):
+        """renvoie le polygone du cone defini par les deux points (origine, distance)"""
+        ligne1 = QLineF(point1, point2)
+        longueur = ligne1.length()
+        ligne1.setAngle(ligne1.angle() + 22.5)
+        ligne1.setLength(1.1547*longueur)
+        ligne2 = QLineF(point1, point2)    
+        ligne2.setAngle(ligne2.angle() - 22.5)
+        ligne2.setLength(1.1547*longueur)
+        polygone = QPolygonF()
+        polygone.append(point1)
+        polygone.append(ligne1.p2())
+        polygone.append(ligne2.p2())
+        return polygone
 
 
 

+ 1 - 0
lib/Plateau.py

@@ -13,6 +13,7 @@ from PyQt4 import QtOpenGL
 ##from ui.ecran_editionAttaques import Ui_editionAttaques
 
 import Modes
+import Actions
 from Case import Case
 from Combattant import Combattant
 from Decor import Decor