# deux groupes d'agent
# chaque groupe a son propre objecti
# modifiable avec un click de souris (click gauche/ click droit)
#
# tous les agents se repoussent mutuellement



#objectif a atteindre
class Objectif():
    
    def __init__(self,x,y):
        self.x=x
        self.y=y


#agent avec un objectif
class Agent():

    
    
    def __init__(self,dx,dy,obj):
        self.objectif=obj
        self.x=dx
        self.y=dy

        #par defaut pas de dynamique
        self.vx=20
        self.vy=0
        self.ax=0
        self.ay=0
        self.dt=0.1
        
        #acceleration maximale
        self.aMax=20        

    def evoluer(self):
        self.vx=self.vx+self.ax*self.dt
        self.vy=self.vy+self.ay*self.dt
        self.x=self.x+self.vx*self.dt
        self.y=self.y+self.vy*self.dt


    def afficher(self):
        print("(x,y): "+str(self.x)+","+str(self.y)+"  vx,vy: "+str(self.vx)+","+str(self.vy))


    #retourne la norme de la force de repulsion fonction distance
    def normeSociale(self,distance):
        force=5
        if (distance>100):
            return(0)
        return force*((100)/distance-1)

    # met a jour son acceleration en fonction objectif
    # et des autres agents
    def calculerObj(self,agents):
        from math import sqrt
        
        #vitesse souhaitee (maximisee)
        vx=(self.objectif.x-self.x)
        vy=(self.objectif.y-self.y)
        vMax=50
        vnorme=sqrt(vx*vx+vy*vy)
        if (vnorme>vMax):
            vx=vx*vMax/vnorme
            vy=vy*vMax/vnorme
            
        

        #calcule la commande à donner
        dvx=vx-self.vx
        dvy=vy-self.vy

        #norme avec aMax       
        norme=sqrt(dvx*dvx+dvy*dvy)
        if (norme>self.aMax):
            dvx=dvx*self.aMax/norme
            dvy=dvy*self.aMax/norme
        self.ax=dvx
        self.ay=dvy


        #prise en compte autres agents
        for agent in agents:
            if (agent!=self):
                dx=agent.x-self.x
                dy=agent.y-self.y
                distance=self.distance(agent)
    
                #met a jour la force
                normeForce=self.normeSociale(distance)
                self.ax-=normeForce*dx/distance
                self.ay-=normeForce*dy/distance


    
        
        
            
    def distance(self,O):
        dx=(O.x-self.x)
        dy=(O.y-self.y)
        d2=dx*dx+dy*dy
        from math import sqrt
        return sqrt(d2)


# systeme c'est un agent et un objectif
class Systeme():

    #initialise le systeme avec les coordonnees de l'agent
    def __init__(self,num):

        #creation du repere
        self.repere=Repere()
            
        from random import uniform
        self.num=num
        self.objectif1=Objectif(200,200)
        self.objectif2=Objectif(400,400)
        self.agents=[]
        
        for i in range(num):
            x=uniform(0,500)
            y=uniform(0,700)
            ag=Agent(x,y,self.objectif1)
            ag.groupe=1
            self.agents+=[ag]
        for i in range(num):
            x=uniform(0,500)
            y=uniform(0,700)
            ag=Agent(x,y,self.objectif2)
            ag.groupe=2
            self.agents+=[ag]

    #evoluer le jeu
    def evoluer(self):
        for agent in self.agents:
            agent.calculerObj(self.agents)
            agent.evoluer()
        

    def dessiner(self):
        WHITE = (0xFF, 0xFF, 0xFF)
        RED = (0xFF, 0x00, 0x00)
        GREEN= (0x00, 0xFF, 0x00)
        PURPLE= (0xFF, 0x00, 0xFF)
        BLUE = (0x00, 0x00, 0xFF)
        BLACK = (0x00, 0x00, 0x00)
        screen.fill(WHITE)

        
        #changement de repere
        taille=5*self.repere.zoom
        ncoord=self.transformCoord(self.objectif1.x,self.objectif1.y)
        pygame.draw.ellipse(screen,GREEN,(ncoord[0]-taille,ncoord[1]-taille,taille*2,taille*2))

        ncoord=self.transformCoord(self.objectif2.x,self.objectif2.y)
        pygame.draw.ellipse(screen,PURPLE,(ncoord[0]-taille,ncoord[1]-taille,taille*2,taille*2))

        #dessiner agents
        taille=5*self.repere.zoom
        for agent in self.agents:
            ncoord=self.transformCoord(agent.x,agent.y)
            if (agent.groupe==1):
                pygame.draw.ellipse(screen,RED,(ncoord[0]-taille,ncoord[1]-taille,taille*2,taille*2))
            if (agent.groupe==2):
                pygame.draw.ellipse(screen,BLUE,(ncoord[0]-taille,ncoord[1]-taille,taille*2,taille*2))
            ncoordV=self.transformCoord(agent.x+agent.vx,agent.y+agent.vy)
            pygame.draw.line(screen,BLACK,ncoord,ncoordV)

        #dessin texte explications
        myfont = pygame.font.SysFont("monospace", 12)
        label = myfont.render("Controles:", 1, BLUE)
        screen.blit(label, (10, 10))
        label = myfont.render("  - a/q : zoom +/-", 1, BLUE)
        screen.blit(label, (10,25))
        label = myfont.render("  - fleches : deplacement camera", 1, BLUE)
        screen.blit(label, (10,40))
        label = myfont.render("  - click gauxhe/droite: designer objectif", 1, BLUE)
        screen.blit(label, (10,55))


    def transformCoord(self,x,y):
        return(self.repere.transformCoord(x,y))
   

class Repere():

    def __init__(self):
        self.tailleFenetreX=700
        self.tailleFenetreY=500
        
        self.x0=100
        self.y0=100
        self.zoom=1
        
        self.dep_HAUT=False
        self.dep_BAS=False
        self.dep_GAUCHE=False
        self.dep_DROITE=False

    #passe du monde à l'ecran
    def transformCoord(self,xm,ym):
        tx=self.tailleFenetreX/2
        ty=self.tailleFenetreY/2
        
        return(tx+(xm-self.x0)*self.zoom,ty+(ym-self.y0)*self.zoom)

    #passe de l'ecran au monde(ex gerer click souris)
    def coord_inverse(self,xe,ye):
        tx=self.tailleFenetreX/2
        ty=self.tailleFenetreY/2
        return((xe-tx)/self.zoom+self.x0,(ye-ty)/self.zoom+self.y0)  
       

    #permet de gerer le changeemnt de repere
    def gererTouches(self,event):
        #gestion du changement de repere
        if event.type == pygame.KEYDOWN:
            #si c'est haut
            if event.key==pygame.K_UP:
                self.dep_HAUT=True
            if event.key==pygame.K_LEFT:
                self.dep_GAUCHE=True
            if event.key==pygame.K_RIGHT:
                self.dep_DROITE=True
            #si c'est bas
            if event.key==pygame.K_DOWN:
                self.dep_BAS=True

            #si c'est bas
            if event.key==pygame.K_q:
                self.zoom=self.zoom*1.5
            if event.key==pygame.K_a:
                self.zoom=self.zoom/1.5
                
        if event.type == pygame.KEYUP:
            #si c'est haut
            if event.key==pygame.K_UP:
                self.dep_HAUT=False
            if event.key==pygame.K_DOWN:
                self.dep_BAS=False
            if event.key==pygame.K_LEFT:
                self.dep_GAUCHE=False
            if event.key==pygame.K_RIGHT:
                self.dep_DROITE=False


    def update(self):
        #met à jour le repere
        if self.dep_HAUT:
            jeu.repere.y0-=5/self.zoom
        if self.dep_BAS:
            jeu.repere.y0+=5/self.zoom
        if self.dep_GAUCHE:
            jeu.repere.x0-=5/self.zoom
        if self.dep_DROITE:
            jeu.repere.x0+=5/self.zoom

    




print("controle de deux groupes d'agents")
print("click de souris modifie le but")
print("- bouton 1 - but 1")
print("- bouton 2 - but 2")


           
import pygame
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Moteur")


# boolean boucle
done = False
jeu=Systeme(10)
dep_HAUT=False

# horloge
clock = pygame.time.Clock()
# -------- boucle de jeu
while not done:
    
    # --- gestion evenements
    for event in pygame.event.get(): 
        # quitter
        if event.type == pygame.QUIT: 
            done = True

        #si on appui souris: change objectif
        if event.type == pygame.MOUSEBUTTONDOWN:
            #on transforme les coordonnees en coordonnes du monde
            coord=jeu.repere.coord_inverse(pygame.mouse.get_pos()[0],pygame.mouse.get_pos()[1])
            #si c'est bouton 1 - objectif 1
            if (pygame.mouse.get_pressed()[0]):
                jeu.objectif1.x=coord[0]
                jeu.objectif1.y=coord[1]
            if (pygame.mouse.get_pressed()[2]):
                jeu.objectif2.x=coord[0]
                jeu.objectif2.y=coord[1]

        #on gere les touches du repere
        jeu.repere.gererTouches(event)
        
            
    # --- evolution
    jeu.evoluer()
    
    # --- dessin
    #met a jour le repere
    jeu.repere.update()
    #affiche
    jeu.dessiner()
        
    # --- mise a jour graphique
    pygame.display.flip()
    # --- attente
    clock.tick(60)

pygame.quit()
   

