Billard français


Description

Trois boules : blanche, rouge et grise sur un tapis vert ... et deux joueurs.
Après avoir placé les boules sur le billard et réglé la direction, la vitesse,
l'effet et la hauteur de tir, le premier joueur pourra "continuer", "rejouer le coup" ou
"passer la main", en fonction des résultats de son score.
L'algorithme de choc passe par le calcul du recul des boules après intersection de
leurs contours, pour revenir au strict contact, puis par la détermination des nouveaux
vecteurs vitesses selon les lois de la mécanique des chocs inélastiques.
Ce script devrait intéresser les débutants qui cherchent à mettre en oeuvre les interfaces
graphiques Tkinter, avec les widgets Canvas, Button, Text, Scale et Label.

Source / Exemple :


#! /usr/bin/env python
# -*- coding: Latin-1 -*-

# <<<     Billard     >>>

# REMERCIEMENTS : ce projet est très largement inspiré des exemples fournis par Mr Gérard Swinnen dans la deuxième édition de son ouvrage intitulé "Apprendre à programmer avec Python".

from Tkinter import *
from math import *

# CONVENTIONS
# boule BLANCHE....(coordonnées:x1,y1):B1  
# boule ROUGE......(coordonnées:x2,y2):B2
# boule GRISE......(coordonnées:x3,y3):B3
# bille bleue......(coordonnées:x4,y4):b4(Réglage de l'effet latéral et de la hauteur de tir)
# cercle blanc.....(coordonnées:x5,y5):b5(Réglage de l'orientation et de la vitesse de tir)
# pupitre de simulation : indice "S"

########## Commande "préparer le jeu" ####################################

def jouer():
    "Préparer le jeu"
    global flag,texte
    if flag==0:flag=1
    jouer;config_init()                                         # mise en place de la configuration initiale (boules,flèches,réglages)
    can.bind("<Button-1>", mouseDown)                           # commandes avec le clic gauche de la souris
    can.bind("<Button1-Motion>", mouseMove)
    can.bind("<Button1-ButtonRelease>", mouseUp)
    T.pack(),J.pack_forget()                                    # gestion des boutons de commande
    can.delete(texte)

########## Commandes de réglage du tir ###################################

def mouseDown( event):
    "Opérations à effectuer quand le bouton gauche de la souris est enfoncé"
    global selObject,X,Y,x5,y5,v,o
    can.currObject=None
    X,Y=event.x,event.y                                         # coordonnées du clic                                        
    D1=hypot(X-x1-r,Y-y1-r)                                     # distance entre le clic et le centre de B1
    D2=hypot(X-x2-r,Y-y2-r)                                     # idem B2
    D3=hypot(X-x3-r,Y-y3-r)                                     # idem B3
    D4=hypot(X-x4-R,Y-y4-R)                                     # idem bille bleue
    D5=hypot(X-x5-R,Y-y5-R)                                     # idem cercle blanc
    X5,Y5=x5-7L/8+R,y5-H-h/2+R
    H5=hypot(X5,Y5)
    if D1<r:selObject=b1                                        # sélection de B1 si le clic est à l'intérieur de B1
    if D2<r:selObject=b2                                        # idem B2
    if D3<r:selObject=b3                                        # idem B3
    if D4<R:selObject=b4                                        # idem bille bleue
    if D5<4*R:selObject=b5                                      # plage de sélection du cercle blanc: cercle de rayon 4R
    if selObject==b5:                                           # procédure de réglage fin, par clic sussessifs à proximité
        dx,dy=(X-x5-R)/40,(Y-y5-R)/40                           # pas = 1/40 ème de la distance du clic au centre du cercle
        x5,y5=x5+dx,y5+dy
        can.move(selObject,dx,dy)                               # déplacement du cercle blanc   
        C5=can.coords(b5)                               
        x5,y5=C5[0],C5[1]
        o=-atan2(y5-H-h/2+R,x5-7*L/8+R)                         # récupération de l'angle de tir (o)       
        wo=round(float(o*180/pi),1)
        can.orientation.config(text='%s'%wo)                    # valeur d'affichage de (o)
        v=1+14*hypot(x5+R-7*L/8-2*R*cos(o),y5+R-H-h/2+2*R*sin(o))/(3*h/8-3*R) # récupération de la vitessede tir (v)
        wv=round(float(v),1)
        can.vitesse.config(text='%s'%wv)                        # valeur d'affichage de (v)
        if B1==1:fl(flèche1,o,x1,y1)
        if B3==1:fl(flèche3,o,x3,y3)
        can.coords(curseur5,7*L/8+R*cos(o),H+h/2-R*sin(o),x5+R-R*cos(o),y5+R+R*sin(o))
    can.itemconfig(selObject,width=2)                           # le contour de l'objet sélectionné double d'épaisseur

def mouseMove(event):
    "Opérations à effectuer quand la souris se déplace, bouton gauche enfoncé"
    global O,X,Y
    global x1,y1,x2,y2,x3,y3,x4,y4,x5,y5                          
    global e,ha,v,o,o4
    XX,YY=event.x,event.y
    dx,dy=XX-X,YY-Y
    if selObject==b1:  
        if dx+x1>0 and dx+x1<L-2*r and dy+y1>0 and dy+y1<H-2*r:
            can.move(selObject,dx,dy);X,Y=XX,YY                 # déplacement de B1
            C1=can.coords(b1)                                   
            x1,y1=C1[0],C1[1]                                   # récupération des coordonnées du coin supérieur gauche
            if B1==1:fl(flèche1,pi,x1,y1)                       # déplacement de la flèche de B1
    if selObject==b2 :
        if dx+x2>0 and dx+x2<L-2*r and dy+y2>0 and dy+y2<H-2*r:
            can.move(selObject,dx,dy);X,Y=XX,YY                 # déplacement de B2
            C2=can.coords(b2)                                   
            x2,y2=C2[0],C2[1]
    if selObject==b3 :
        if dx+x3>0 and dx+x3<L-2*r and dy+y3>0 and dy+y3<H-2*r:
            can.move(selObject,dx,dy);X,Y=XX,YY                 # déplacement de B3
            C3=can.coords(b3)                                   
            x3,y3=C3[0],C3[1]
            if B3==1:fl(flèche3,pi,x3,y3)                       # déplacement de la flèche de B3
    if selObject==b4: 
        DX4,DY4=dx+x4-L/8+R,dy+y4-H-h/2+R
        HD4=hypot(DX4,DY4)
        if HD4<R4-R:
            can.move(selObject,dx,dy);X,Y=XX,YY                 # déplacement de la bille bleue 
            C4=can.coords(b4)                               
            x4,y4=C4[0],C4[1]
            o4=atan2(DY4,DX4)
            e=(x4-L/8+R)*2/(R4-R)*pi/180                        # calcul de l'effet (-2<e<+2)
            we=round(float(e*180/pi),1);can.effet.config(text='%s'%we)
            ha=-2*(y4-H-h/2+R)/(R4-R)                           # calcul de la hauteur (-2<h<+2)
            wha=round(float(ha),1);can.hauteur.config(text='%s'%wha)
            can.coords(curseur4,L/8+R4*cos(o4),H+h/2+R4*sin(o4),L/8-R4*cos(o4),H+h/2-R4*sin(o4))
    if selObject==b5: 
        DX5,DY5=dx+x5-7*L/8+R,dy+y5-H-h/2+R
        HD5=hypot(DX5,DY5)
        if HD5<3*h/8-R and HD5>2*R:
            can.move(selObject,dx,dy);X,Y=XX,YY                 # déplacement du cercle blanc dans l'espace d'orientation et de vitesse
            C5=can.coords(b5)                               
            x5,y5=C5[0],C5[1]
            o=-atan2(y5-H-h/2+R,x5-7*L/8+R)                     # récupération de l'angle de tir (o)       
            wo=round(float(o*180/pi),1)
            can.orientation.config(text='%s'%wo)
            v=1+14*hypot(x5+R-7*L/8-2*R*cos(o),y5+R-H-h/2+2*R*sin(o))/(3*h/8-3*R)# récupération de la vitesse de tir (v)
            wv=round(float(v),1)
            can.vitesse.config(text='%s'%wv)
            can.coords(curseur5,7*L/8+R*cos(o),H+h/2-R*sin(o),x5+R-R*cos(o),y5+R+R*sin(o))
            if B1==1:fl(flèche1,o,x1,y1)
            if B3==1:fl(flèche3,o,x3,y3)

def mouseUp(event):
    "Opérations à effectuer quand le bouton gauche de la souris est relâché"
    global selObject
    can.itemconfig(selObject,width=1)                           # le contour de l'objet sélectionné revient à son épaisseur initiale
    if selObject==b1:selObject=None
    if selObject==b2:selObject=None
    if selObject==b3:selObject=None
    if selObject==b4:selObject=None
    if selObject==b5:selObject=None

########## Commande "tirer" ##############################################    
 
def tirer():
    "tirer "
    global T1,T3,t1,t3,S1,S3,E1,E3,R1,R3,PM1,PM3
    global x1,y1,x3,y3,dx1,dy1,dx3,dy3                       
    global xx1,yy1,xx2,yy2,xx3,yy3
    global dxx1,dyy1,dxx2,dyy2,dxx3,dyy3
    global xx4,yy4,xx5,yy5
    global ee,hh,oo,vv,oo4
    if B1==1:                  
        T1=1;t1+=1                                              # indicateur et compteur de tir
        dx1,dy1=v*cos(o),-v*sin(o)                              # coordonnées du vecteur vitesse
        can.coords(flèche1,x1+r,y1+r,x1+r,y1+r)                 # origine de la flèche
        can.itemconfig(b4,fill='white',outline='white')         # effacement de la bille bleue
        can.itemconfig(curseur4,fill='white')                   # effacement du diamètre de la bille bleue
    if B3==1:                                                   
        T3=1;t3+=1
        dx3,dy3=v*cos(o),-v*sin(o)
        can.coords(flèche3,x3+r,y3+r,x3+r,y3+r)                 # idem
        can.itemconfig(b4,fill='grey',outline='grey')           # idem
        can.itemconfig(curseur4,fill='grey')
    xx1,yy1,xx2,yy2,xx3,yy3=x1,y1,x2,y2,x3,y3                   # mémorisation de la position des trois boules
    dxx1,dyy1,dxx2,dyy2,dxx3,dyy3=dx1,dy1,dx2,dy2,dx3,dy3       # mémorisation de la vitesse des trois boules
    xx4,yy4,xx5,yy5=x4,y4,x5,y5                                 # mémorisation de la position de la bille et du cercle
    ee,hh,oo,vv,oo4=e,ha,o,v,o4                                 # mémorisation des réglages du tir
    S1,S3,E1,E3,R1,R3,PM1,PM3,CT1,CT3=0,0,0,0,0,0,0,0,0,0       # mise à zéro des indicateurs
    T.pack_forget();FI.pack()                                   # gestion des boutons de commande
    can.itemconfig(texte1,fill="brown")
    can.delete(texte2);can.delete(texte3)
    can.itemconfig(b5,outline='dark green')                     # effacement du cercle blanc
    can.itemconfig(curseur5,fill='dark green')                  # effacement de la flèche
    move()                                                      # appel de la fonction "mouvement" des boules
                       
###### Commandes "continuer/rejouer/passer la main/figer/relancer" #######    

def continuer():
    "continuer"
    global flag,CT1,CT3,B1,B3
    global x1,y1,x2,y2,x3,y3,x4,y4,x5,y5
    global e,ha,o,v,o4,o6                            
    if flag==0:flag=1
    e,ha,o,v,o4=0,0,pi,8,pi/2                                   # réglages de départ
    x4,y4,x5,y5=x4o,y4o,x5o,y5o                                 # coordonnées de départ de la bille et du cercle
    effacer_simulation()
    continuer;config()                                          # mise en place de la configuration
    if S1==1:CT1=1;B1,B3=1,0;fl(flèche1,o,x1,y1)            
    if S3==1:CT3=1;B1,B3=0,1;fl(flèche3,o,x3,y3)
    T.pack();CT.pack_forget();RJ.pack_forget()
    effacer_texteR() 
    
def rejouer():
    "rejouer le coup"
    global flag,R1,R3,r1,r3
    global x1,y1,x2,y2,x3,y3
    global e,ha,o,v,o4                                  
    if flag==0:flag=1
    e,ha,o,v,o4 =ee,hh,oo,vv,oo4                                # récupération des réglages avant tir
    x1,y1,x2,y2,x3,y3=xx1,yy1,xx2,yy2,xx3,yy3                   # récupération des positions avant tir des boules 
    x4,y4,x5,y5=xx4,yy4,xx5,yy5                                 # récupération des positions avant tir de la bille et du cercle
    rejouer;config()                                            # mise en place de la configuration
    if B1==1 :R1=1;fl(flèche1,o,x1,y1);r1+=1                  
    if B3==1 :R3=1;fl(flèche3,o,x3,y3);r3+=1
    T.pack();RJ.pack_forget();PM.pack_forget();CT.pack_forget()
    effacer_texteR() 

def passer():
    "passer la main"
    global flag,PM1,PM3,B1,B3
    global x1,y1,x2,y2,x3,y3,x4,y4,x5,y5
    global e,ha,o,v,o4                                  
    if flag==0:flag=1
    e,ha,o,v,o4=0,0,pi,8,pi/2                                   # réglages de départ
    x4,y4,x5,y5=x4o,y4o,x5o,y5o                                 # coordonnées de départ de la bille et du cercle
    effacer_simulation()
    passer;config()                                             # mise en place de la configuration
    if E1==1:PM1=1;B1,B3=0,1;fl(flèche3,o,x3,y3)                # changement de boule (B3 prend la main)
    if E3==1:PM3=1;B1,B3=1,0;fl(flèche1,o,x1,y1)
    if B3==1:can.itemconfig(REG4,fill='grey');can.itemconfig(REG5B,fill='grey')
    if B1==1:can.itemconfig(REG4,fill='white');can.itemconfig(REG5B,fill='white')
    T.pack();RJ.pack_forget();PM.pack_forget();CT.pack_forget()
    effacer_texteR() 

def stop():
    "Figer le jeu"
    global flag,figer
    figer=1;flag=0
    dxx1,dyy1,dxx2,dyy2,dxx3,dyy3=dx1,dy1,dx2,dy2,dx3,dy3       # mémorisation de la vitesse des trois boules
    RL.pack();FI.pack_forget()

def go():
    "Relancer le jeu"
    global flag,figer
    flag=1;figer=0
    dx1,dy1,dx2,dy2,dx3,dy3=dxx1,dyy1,dxx2,dyy2,dxx3,dyy3       # récupération de la vitesse  avant arrêt des trois boules
    FI.pack();RL.pack_forget();RJ.pack_forget();PM.pack_forget()
    move()                                                      # redémarrage du mouvement d'ensemble

########## Commande "score" ##############################################

def score():
    "Détermination du score" 
    global score1,score3,texte1S,texte1E,texte3S,texte3E,S1,S3,E1,E3
    if B1==1:
        if figer==1:RJ.pack_forget();PM.pack_forget()
        else:
            if point1==1:                                                
                S1=1;score1+=1                                  # Constat de succès : le score de B1 augmente d'un point
                texte1S=can3.create_text(2*h/3,h/4,text='La BLANCHE continue\nou\nrejoue le coup',fill ="red",font ="Arial 8")
                CT.pack();RJ.pack();FI.pack_forget()
            if point1==0:                                              
                E1=1;score1+=0                                  # Constat d'échec                                               
                texte1E=can3.create_text(2*h/3,h/4,text='La BLANCHE rejoue le coup\nou\npasse la main',fill ="red",font ="Arial 8")
                RJ.pack();PM.pack();FI.pack_forget()
                if R1==1:score1+=-1
            can.score1.config(text='%s'%score1) 
            
    if B3==1:
        if figer==1:RJ.pack_forget();PM.pack_forget()
        else:
            if point3==1:                               
                S3=1;score3+=1  
                texte3S=can3.create_text(2*h/3,h/4,text='La GRISE continue\nou\nrejoue le coup',fill ="red",font ="Arial 8")
                CT.pack();RJ.pack();FI.pack_forget()             
            if point3==0:                              
                E3=1;score3+=0  
                texte3E=can3.create_text(2*h/3,h/4,text='La GRISE rejoue le coup\nou\npasse la main',fill ="red",font ="Arial 8")
                RJ.pack();PM.pack();FI.pack_forget()
                if R3==1:score3+=-1
            can.score3.config(text='%s'%score3)
       

########## Fonction "mouvement" ##########################################

def move():
    "mouvement des trois boules au fur et à mesure des chocs"
    global flag
    global x1,y1,x2,y2,x3,y3
    global dx1,dy1,dx2,dy2,dx3,dy3
    global c12,c23,c31,point1,point3
    global T1,T3,RT1,RT3,RN1,RN3,rt
    global sim1,sim3

    x1,y1,x2,y2,x3,y3=x1+dx1,y1+dy1,x2+dx2,y2+dy2,x3+dx3,y3+dy3 # création du mouvement
    dx1,dy1,dx2,dy2,dx3,dy3=k*dx1,k*dy1,k*dx2,k*dy2,k*dx3,k*dy3 # freinage du tapis (k)

    if hypot(dx1,dy1)<s:dx1,dy1=0,0                             # arrêt de B1 lorsque la vitesse de B1 devient trop faible (<s)
    if hypot(dx2,dy2)<s:dx2,dy2=0,0                             # idem B2
    if hypot(dx3,dy3)<s:dx3,dy3=0,0                             # idem B3 
                                                                
    X12,Y12,X23,Y23,X31,Y31=x2-x1,y2-y1,x3-x2,y3-y2,x1-x3,y1-y3
    Z12,Z23,Z31=hypot(X12,Y12),hypot(X23,Y23),hypot(X31,Y31)
    dx12,dy12,dx23,dy23,dx31,dy31=dx2-dx1,dy2-dy1,dx3-dx2,dy3-dy2,dx1-dx3,dy1-dy3
    dz12,dz23,dz31=hypot(dx12,dy12),hypot(dx23,dy23),hypot(dx31,dy31)
    k12,K12,k23,K23,k31,K31=dz12*dz12,X12*dx12+Y12*dy12,dz23*dz23,X23*dx23+Y23*dy23,dz31*dz31,X31*dx31+Y31*dy31 
    u12,u23,u31=Z12-2*r,Z23-2*r,Z31-2*r

    # Choc de B1 sur B2 :    
    if u12<=0:                                                  # la distance entre centres est <= à la somme des rayons
        # algorithme de recul des deux boules :
        c12+=1                                                  # compteur de chocs B1/B2
        m12=(K12+sqrt(K12*K12-k12*(Z12*Z12-4*r*r)))/k12                   
        x1,y1,x2,y2=x1-m12*dx1,y1-m12*dy1,x2-m12*dx2,y2-m12*dy2 # recul des deux boules sur leurs directions initales
        # algorithme de modification des vecteurs vitesse après le choc:
        A12,B12,C12,D12=(X12*dx1+Y12*dy1)/Z12,(-Y12*dx1+X12*dy1)/Z12,(X12*dx2+Y12*dy2)/Z12,(-Y12*dx2+X12*dy2)/Z12
        dx1,dy1,dx2,dy2=(-Y12*B12+X12*C12)/Z12,(X12*B12+Y12*C12)/Z12,(-Y12*D12+X12*A12)/Z12,(X12*D12+Y12*A12)/Z12
        if T1==1 or T3==1 and RT1==0 and RN1 ==0:
            effet_choc(dx1,dy1,dx2,dy2)
            dx1,dy1,dx2,dy2=dxAE,dyAE,dxBE,dyBE                 # vitesse corrigée de l'effet
        if B1==1 or B3==1 and c12==1:
            hauteur(dxx1,dyy1,dxx2,dyy2)                        # vitesse corrigée de la hauteur de tir
            dx1,dy1,dx2,dy2=dx1+dxACR,dy1+dyACR,dx2+dxBCR,dy2+dyBCR
    # Choc de B2 sur B3 :    
    if u23<=0:
        c23+=1                                                  # compteur de chocs B2/B3
        m23=(K23+sqrt(K23*K23-k23*(Z23*Z23-4*r*r)))/k23       
        x2,y2,x3,y3=x2-m23*dx2,y2-m23*dy2,x3-m23*dx3,y3-m23*dy3
        A23,B23,C23,D23=(X23*dx2+Y23*dy2)/Z23,(-Y23*dx2+X23*dy2)/Z23,(X23*dx3+Y23*dy3)/Z23,(-Y23*dx3+X23*dy3)/Z23
        effet_choc(dx2,dy2,dx3,dy3)
        dx2,dy2,dx3,dy3=dxAE,dyAE,dxBE,dyBE
        dx2,dy2,dx3,dy3=(-Y23*B23+X23*C23)/Z23,(X23*B23+Y23*C23)/Z23,(-Y23*D23+X23*A23)/Z23,(X23*D23+Y23*A23)/Z23    
        if B3==1 and c23==1:
            hauteur(dxx3,dyy3,dxx2,dyy2)
            dx3,dy3,dx2,dy2=dx3+dxACR,dy3+dyACR,dx2+dxBCR,dy2+dyBCR
    # Choc de B3 sur B1 :   
    if u31<=0:
        c31+=1                                                  # compteur de chocs B3/B1
        m31=(K31+sqrt(K31*K31-k31*(Z31*Z31-4*r*r)))/k31      
        x3,y3,x1,y1=x3-m31*dx3,y3-m31*dy3,x1-m31*dx1,y1-m31*dy1
        A31,B31,C31,D31=(X31*dx3+Y31*dy3)/Z31,(-Y31*dx3+X31*dy3)/Z31,(X31*dx1+Y31*dy1)/Z31,(-Y31*dx1+X31*dy1)/Z31
        dx3,dy3,dx1,dy1=(-Y31*B31+X31*C31)/Z31,(X31*B31+Y31*C31)/Z31,(-Y31*D31+X31*A31)/Z31,(X31*D31+Y31*A31)/Z31
        if T3==1 or T1==1 and RT3==0 and RN3==0:
            effet_choc(dx3,dy3,dx1,dy1)
            dx3,dy3,dx1,dy1=dxAE,dyAE,dxBE,dyBE
        if B3==1 or B3==1 and c31==1:
            hauteur(dxx3,dyy3,dxx1,dyy1)
            dx3,dy3,dx1,dy1=dx3+dxACR,dy3+dyACR,dx1+dxBCR,dy1+dyBCR
      
    if c12*c31>0:point1=1                                       # succès=1 point 
    else:point1=0                                               # échec
    if c23*c31>0:point3=1                                      
    else:point3=0                                               
    
    if x1>L-2*r or x1<0 or y1>H-2*r or y1<0:                    # Rebonds de la BLANCHE sur les bandes: 
        if T1==1 and c12==0 and c31==0:
            rebond_tir(x1,y1,dx1,dy1)
            x1,y1,dx1,dy1,RT1=xR,yR,dxR,dyR,RT
        else :
            rebond_normal(x1,y1,dx1,dy1)
            x1,y1,dx1,dy1,RN1=xR,yR,dxR,dyR,RN
            T1=0
    if x2>L-2*r or x2<0 or y2>H-2*r or y2<0:                    # Rebonds de la ROUGE sur les bandes:  
        rebond_normal(x2,y2,dx2,dy2)
        x2,y2,dx2,dy2=xR,yR,dxR,dyR
    if x3>L-2*r or x3<0 or y3>H-2*r or y3<0:                    # Rebonds de la GRISE sur les bandes:   
        if T3==1 and c31==0 and c23==0:
            rebond_tir(x3,y3,dx3,dy3)
            x3,y3,dx3,dy3,RT3=xR,yR,dxR,dyR,RT
            T3=0
        else:
            rebond_normal(x3,y3,dx3,dy3)
            x3,y3,dx3,dy3,RN3=xR,yR,dxR,dyR,RN

    Cboule(b1,x1,y1,r);Cboule(b2,x2,y2,r);Cboule(b3,x3,y3,r)    # Nouvelles coordonnées de B1,B2,B3
    CbouleS(b1S,x1,y1,RS);CbouleS(b2S,x2,y2,RS);CbouleS(b3S,x3,y3,RS)

    if B1==1:sm(x1,y1,dx1,dy1);sim1=sim;can.itemconfig(sim1,fill='white')
    if B3==1:sm(x3,y3,dx3,dy3);sim3=sim;can.itemconfig(sim3,fill='grey')

    # Algorithmes à l'arrêt des trois boules
    if flag==0:
        c12+=-c12                                               # mise à zéro des compteurs de choc
        c23+=-c23
        c31+=-c31
        score()                                                 # appel de la commande "score"
        rt=0
    if flag>0:
        root.after(10,move)                                      # déclenchement du mouvement des boules et des flèches       
    if dx1==0 and dy1==0 and dx2==0 and dy2==0 and dx3==0 and dy3==0:flag=0     # arrêt complet
        
########## Procédures diverses ###########################################

def config_init():
    "Mise en place de la configuration de départ (à partir des 'données initiales)"
    global B1,B3,b1,b2,b3,b4,b5,b1S,b2S,b3S
    B1,B3=1,0                                                   # Sélection de la boule à tirer (B1)
    boule(x1,y1,r,color='white');b1=b                           # Mise en place de B1
    boule(x2,y2,r,color='red');b2=b                             # idem B2
    boule(x3,y3,r,color='grey');b3=b                            # idem B3
    bouleS(x1,y1,RS,color='white');b1S=bS                       # idem B1/Pupitre
    bouleS(x2,y2,RS,color='red');b2S=bS                         # idem B2/Pupitre
    bouleS(x3,y3,RS,color='grey');b3S=bS                        # idem B3/Pupitre
    boule(x4,y4,R,color='blue');b4=b                            # Mise en place de la bille bleue commandant "effet" et "hauteur"
    boule(x5,y5,R,color='dark green');b5=b                      # Mise en place du cercle des commandant "orientation" et "vitesse"
    can.itemconfig(b5,outline='white')                          # couleur blanche pour le contour
    can.itemconfig(curseur4,fill='blue')                        # couleur blanche pour le contour
    can.itemconfig(curseur5,fill='white')                       # couleur blanche pour le contour

    if B1==1:fl(flèche1,o,x1,y1)                                # Mise en place de la flèche liée à B1
    if B3==1:fl(flèche3,o,x3,y3)                                # Mise en place de la flèche liée à B3

def config():
    "Mise en place de la configuration courante"
    Cboule(b1,x1,y1,r);Cboule(b2,x2,y2,r);Cboule(b3,x3,y3,r)    # nouvelles coordonnées de B1,B2,B3
    CbouleS(b1S,x1,y1,RS);CbouleS(b2S,x2,y2,RS);CbouleS(b3S,x3,y3,RS)# idem B1,B2,B3 /Pupitre
    Cboule(b4,x4,y4,R);Cboule(b5,x5,y5,R)                       # idem bille bleue et cercle blanc
    can.itemconfig(b4,fill='blue',outline='blue')
    can.itemconfig(b5,outline='white')
    can.itemconfig(curseur4,fill='blue')
    can.itemconfig(curseur5,fill='white')
    if continuer or passer:
        wo=round(float(o*180/pi),1);can.orientation.config(text='%s'%wo)
        wv=round(float(v),1);can.vitesse.config(text='%s'%wv)
        we=round(float(e*180/pi),1);can.effet.config(text='%s'%we)
        wha=round(float(ha),1);can.hauteur.config(text='%s'%wha)
        can.coords(curseur4,L/8,H+h/2+R4,L/8,H+h/2-R4)
        can.coords(curseur5,7*L/8-R,H+h/2,7*L/8-3*h/16+R/2,H+h/2)
    if rejouer:
        can.coords(curseur4,L/8+R4*cos(o4),H+h/2+R4*sin(o4),L/8-R4*cos(o4),H+h/2-R4*sin(o4))
        can.coords(curseur5,7*L/8+R*cos(o),H+h/2-R*sin(o),x5+R-R*cos(o),y5+R+R*sin(o))
    can.itemconfig(texte1,fill="white")

def effet_choc(dxA,dyA,dxB,dyB):
    "Correction au choc pour tenir compte de l'effet latéral"
    global dxAE,dyAE,dxBE,dyBE,e
    e=(x4-L/8+R)*2/(R4-R)*pi/180
    dxAE=dxA*cos(e)+dyA*sin(e)
    dyAE=-dxA*sin(e)+dyA*cos(e)
    dxBE=dxB*cos(e)+dyB*sin(e)
    dyBE=-dxB*sin(e)+dyB*cos(e)
    we=round(float(e*180/pi),1)
    can.effet.config(text='%s'%we)

def hauteur(dxA,dyA,dxB,dyB):
    "Correction au choc pour tenir compte de la hauteur du coup"
    global dxACR,dyACR,dxBCR,dyBCR,ha
    ha=-2*(y4-H-h/2+R)/(R4-R)
    dxACR=dxA*ha/10
    dyACR=dyA*ha/10
    dxBCR=dxB*abs(ha/10)
    dyBCR=dyB*abs(ha/10)
    wha=round(float(ha),1)
    can.hauteur.config(text='%s'%wha)
    
def rebond_tir(x,y,dx,dy):
    "Rebond sur les bandes pour le premier tir"
    global xR,yR,dxR,dyR,RT,rt
    RT=1;rt+=1
    if rt==1:m=(x4-L/8+R)*2/(R4-R)*(1+(v-1)/20)
    else:m=0
    if x>L-2*r:                                                 # bande droite
        dxs,dys=dx+abs(m),dy+m
        xR,yR,dxR,dyR=L-2*r,y,-dxs*hypot(dx,dy)/hypot(dxs,dys)*n,dys*hypot(dx,dy)/hypot(dxs,dys)*n
    if y>H-2*r:                                                 # bande inférieure
        dxs,dys=dx-m,dy+abs(m)
        xR,yR,dxR,dyR=x,H-2*r,dxs*hypot(dx,dy)/hypot(dxs,dys)*n,-dys*hypot(dx,dy)/hypot(dxs,dys)*n
    if x<0:                                                     # bande gauche
        dxs,dys=dx-abs(m),dy-m
        xR,yR,dxR,dyR=0,y,-dxs*hypot(dx,dy)/hypot(dxs,dys)*n,dys*hypot(dx,dy)/hypot(dxs,dys)*n
    if y<0:                                                     # bande supérieure
        dxs,dys=dx+m,dy+abs(m)
        xR,yR,dxR,dyR=x,0,dxs*hypot(dx,dy)/hypot(dxs,dys)*n,-dys*hypot(dx,dy)/hypot(dxs,dys)*n

def rebond_normal(x,y,dx,dy):    
    "Rebond sur les bandes pour les coups suivants"
    global xR,yR,dxR,dyR,RN
    RN=1
    if x>L-2*r:xR,yR,dxR,dyR=L-2*r,y,-dx*n,dy*n                 # bande droite
    if y>H-2*r:xR,yR,dxR,dyR=x,H-2*r,dx*n,-dy*n                 # bande inférieur
    if x<0:xR,yR,dxR,dyR=0,y,-dx*n,dy*n                         # bande gauche
    if y<0:xR,yR,dxR,dyR=x,0,dx*n,-dy*n                         # bande supérieure

def fl(flèche,a,x,y):
    "calcul de la longueur et mise en place de la flèche de tir"
    global xf,yf,lf                                                  
    SG=pi-atan(float(y+r)/float(x+r))                           # le coin supérieur gauche du billard est vu par la boule sous l'angle SG 
    SD=atan(float(y+r)/float(L-x-r))                            # le coin supérieur droit du billard est vu par la boule sous l'angle SD
    ID=atan(-float(H-y-r)/float(L-x-r))                         # le coin inférieur droit du billard est vu par la boule sous l'angle ID
    IG=-pi-atan(-float(H-y-r)/float(x+r))                       # le coin inférieur gauche du billard est vu par la boule sous l'angle IG
    if a==0:lf=L-x-r                                            # limitation de lf par la bande droite (cas limite)
    if a==pi or a==-pi:lf=x+r                                   # limitation de lf par la bande gauche (cas limite)
    if a<SG and a>SD:lf=(y+r)/sin(a)                            # limitation de lf par la bande supérieure
    if a<=SD and a>ID:lf=(L-x-r)/cos(a)                         # limitation de lf par la bande droite
    if a<=ID and a>IG:lf=-(H-y-r)/sin(a)                        # limitation de lf par la bande inférieure
    if a>=SG and a<pi:lf=-(x+r)/cos(a)                          # limitation de lf par la bande gauche
    if a<IG and a>-pi:lf=-(x+r)/cos(a)
    xf,yf=x+r+lf*cos(a),y+r-lf*sin(a)                           # extrémité de la flèche sur la bande
    can.coords(flèche,x+r,y+r,xf,yf)                            # la flèche accompagne la boule pendant le déplacement
    if B1==1:can.itemconfig(flèche,fill='white')
    if B3==1:can.itemconfig(flèche,fill='grey')

def sm(x,y,dx,dy):
    "Simulation du parcours de la boule tirée"
    global sim
    sim=can.create_line(3*h*x/2/L+L/2-3*h/4+RS,3*h*y/4/H+H+h/8+RS,3*h*(x+dx)/2/L+L/2-3*h/4+RS,3*h*(y+dy)/4/H+H+h/8+RS)

def effacer_simulation():
    "effacer la trajectoire de la boule"
    global b1S,b2S,b3S
    REG66=can.create_rectangle(L/2-3*h/4,H+h/8,L/2+3*h/4,H+7*h/8,width=2,fill='dark green')
    bouleS(x1,y1,RS,color='white');b1S=bS                       # remise en place de B1/Pupitre
    bouleS(x2,y2,RS,color='red');b2S=bS                         # idem B2/Pupitre
    bouleS(x3,y3,RS,color='grey');b3S=bS                        # idem B3/Pupitre

def effacer_texteR():
    "Effacement des textes liés au résultat du tir"
    can3.delete(texte1S);can3.delete(texte1E);can3.delete(texte3S);can3.delete(texte3E)

def boule(x,y,r,color):
    "Coordonnées de la boule"
    global b
    b=can.create_oval(x,y,x+2*r,y+2*r,width=1,fill=color)

def Cboule(b,x,y,r):
    "Coordonnées de la boule"
    can.coords(b,x,y,x+2*r,y+2*r)

def bouleS(x,y,r,color):
    "Coordonnées de la boule"
    global bS
    X,Y=3*h*x/2/L+L/2-3*h/4,3*h*y/4/H+H+h/8                     # changement de système de coordonnées (billard vers simulation)
    bS=can.create_oval(X,Y,X+2*RS,Y+2*RS,width=1,fill=color)

def CbouleS(bS,x,y,r):
    "Coordonnées de la boule"
    X,Y=3*h*x/2/L+L/2-3*h/4,3*h*y/4/H+H+h/8                     # changement de système de coordonnées (billard vers simulation)
    can.coords(bS,X,Y,X+2*RS,Y+2*RS)

def presentation():
    "Fenêtre-message contenant la description sommaire du principe du jeu" 
    msg =Toplevel()
    Message(msg, bg ="dark green", fg ="white", width =450,font ="Arial 8", 
        text =" PRESENTATION\n\n"\
        "Au lancement du jeu, trois boules (BLANCHE, ROUGE et GRISE), apparaissent.La BLANCHE commence.\n\n"\
        "La première étape, si l'on souhaite un jeu différent, consiste à déplacer les boules (abscisses et ordonnées), vers de nouvelles positions.\n\n"\
        "La seconde vise à choisir la direction, la vitesse,l'effet latéral et la hauteur de tir de la boule BLANCHE.\n"\
        "Une flèche de tir apparait, dont la longueur est ajustée aux dimensions du billard.\n\n"\
        "Les boules étant en place, la touche <Tir !> met en mouvement la boule BLANCHE dans la direction choisie.\n"\
        "Aux premiers chocs, les autres boules se mettent à leur tour en mouvement.\n"\
        "La vitesse des boules se réduit à chaque itération, pour simuler le freinage du tapis, ainsi qu'à chaque choc (y/c avec les bandes).\n\n"\
        "A l' arrêt des boules, le score de la boule BLANCHE est comptabilisé.\n"\
        "En cas de succès, la BLANCHE gagne un point.Elle peut alors <Continuer !> ou <Rejouer le coup !> (pour améliorer les positions finales).\n"\
        "En cas d' échec, elle peut aussi rejouer le coup,et, sinon, <Passer la main !> à la GRISE.\n\n"\
        "La touche <Figer !> permet d'interrompre le mouvement à tout moment, et la touche <Relancer !> de le redémarrer.\n\n\n"\
        "ALGORITHME\n\n"\
        "Lorsque la boule BLANCHE approche, par exemple, de la boule GRISE, l'algorithme compare la distance entre leurs centres à la somme de leurs rayons.\n"\
        "Quand l'écart devient négatif, il y a intersection des deux contours.\n\n"\
        "L'algorithme calcule alors le recul  en position des deux boules sur leurs directions initiales et en proportion de leurs vitesses, pour revenir au strict contact.\n\n"\
        "La sortie du choc, en direction et en vitesse, se fait ensuite selon les lois de la mécanique des chocs inélastiques entre solides.\n"\
        "Elle prend en compte (de façon très simplifiée) l'effet latéral et la hauteur de tir imprimés au moment du tir.\n\n"\
        "Un coefficient (n) de réduction de la vitesse intervient à chaque choc, y/c avec les bandes.\n" 
        "Un ralentissement (k) a lieu également à chaque itération.\n").pack(padx =10, pady =10)        

def processus():
    ""
    msg =Toplevel()
    Message(msg, bg ="dark green", fg ="white", width =450,font ="Arial 8", 
        text="PROCESSUS à suivre\n\n"\
        "1.  Cliquer sur <Préparer le jeu !>\n\n"\
        "2.  Régler vitesse et orientation\n"\
             "en déplaçant le cercle blanc\n"\
             "(réglage fin par clics successifs)\n\n"\
        "3.  Régler effet latéral et hauteur\n"\
             "en déplaçant la bille bleue\n\n"\
        "4.  Appuyer sur la touche <Tirer !>\n\n"\
        "5.  En cas de succès, <Continuer !>\n"\
             "ou <Rejouer !> : pour améliorer\n\n"\
        "6.  Si échec, <Passer la main !>\n"\
             "ou <Rejouer !>: pour réessayer\n\n"\
        "7.  Possibilité de <Figer !> le mouvement\n"\
             "puis de <Relancer !>\n\n\n"\
        "Echelle du jeu:\n"\
             "Elle est peut être modifiée en\n"\
             "intervenant dans le menu <Règle du jeu>.\n"\
             "Valeurs possibles: de 0.5 à 1.0\n"\
             "(valeur actuelle=0.5)\n").pack(padx =10, pady =10)

def aPropos():
    "Fenêtre-message indiquant l'auteur" 
    msg =Toplevel()
    Message(msg, width =200, aspect =100, justify =CENTER,
        text ="Billard français \n\nHCD, Octobre 2005.\n"\
        "Python version 2.4.2\nTk version 8.4").pack(padx =10, pady =10)

def scale(E):
    "Changement d'échelle"
    global L,H,h,r,rr,R,R4,R6,RS
    global x1,y1,x2,y2,x3,y3
    global x4o,y4o,x5o,y5o,x4,y4,x5,y5
    E=float(E)
    L,H,h,r,rr,R,R4,R6,RS=E*L,E*H,E*h,E*r,E*rr,E*R,E*R4,E*R6,E*RS                                                    
    x1,y1,x2,y2,x3,y3=E*x1,E*y1,E*x2,E*y2,E*x3,E*y3                                          
    can.config(height=H+h,width=L)
    can2.config(width=h)
    can3.config(height=h/3,width=h)
    x4o,y4o,x5o,y5o=E*x4o,E*y4o,E*x5o,E*y5o
    x4,y4,x5,y5=E*x4,E*y4,E*x5,E*y5
    can.coords(Pupitre,E*4,H,L,H+h)
    can.coords(texte,E*L/2,E*L/3)
    can.coords(t1,L/8,H+h-E*10)
    can.coords(t2,L/2,H+h-E*10)
    can.coords(t3,7*L/8,H+h-E*10)
    can.coords(texte1,L/2,H+E*7.5)
    can.coords(texte2,L/8,H+E*7.5)
    can.coords(texte3,7*L/8,H+E*7.5)
    can.coords(REG4,L/8-R4,H+h/2-R4,L/8+R4,H+h/2+R4)
    can.coords(curseur4,L/8,H+h/2+R4,L/8,H+h/2-R4)
    can.coords(REG6,L/2-3*h/4,H+h/8,L/2+3*h/4,H+7*h/8)
    can.coords(REG5,7*L/8-R6,H+h/2-R6,7*L/8+R6,H+h/2+R6)
    can.coords(REG5B,7*L/8-R,H+h/2-R,7*L/8+R,H+h/2+R)
    can.coords(curseur5,7*L/8-R,H+h/2,7*L/8-3*h/16+R/2,H+h/2)
    can.coords(pA,L/4-rr,H/2-rr,L/4+rr,H/2+rr)
    can.coords(pB,L/2-rr,H/2-rr,L/2+rr,H/2+rr)
    can.coords(pC,3*L/4-rr,H/2-rr,3*L/4+rr,H/2+rr)
    can.coords(pD,3*L/4-rr,H/3-rr,3*L/4+rr,H/3+rr)
    can.coords(pE,3*L/4-rr,2*H/3-rr,3*L/4+rr,2*H/3+rr)
    can3.coords(texte1S,2*h/3,h/4)
    can3.coords(texte1E,2*h/3,h/4)
    can3.coords(texte3S,2*h/3,h/4)
    can3.coords(texte3E,2*h/3,h/4)
    EE=1.0

def echelle():
    "Choix de l'échelle" 
    éch=Toplevel()
    curE=Scale(éch,length=200,label="Echelle du jeu",orient=HORIZONTAL,from_=0.95,to=1.05,resolution=0.05,command=scale)
    curE.set(1.0)                                               # position initiale du curseur 
    curE.pack()

######## Programme principal ############################################

# Création du widget principal :
root = Tk()
root.title('>>>>>>>    BILLARD    <<<<<<<')

# données initiales:                                    
flag=0 	                                                        # compteur		   

# Billard

L=600                                                           # longueur du billard
H=L/2                                                           # largeur du billard
r,rr=10,1                                                       # rayons des boules et des marques du tapis
k,s=0.996,0.5                                                   # coefficient de freinage du tapis, vitesse d'arrêt du mouvement
n=0.8                                                           # coefficient de réduction de la vitesse lors d'un choc
x1,y1,x2,y2,x3,y3=3*L/4-r,H/2-r,L/2-r,H/2-r,L/4-r,H/2-r         # coordonnées courantes des boules
dx1,dy1,dx2,dy2,dx3,dy3=0,0,0,0,0,0                             # composantes courantes des vecteurs vitesse des boules
c12,c23,c31=0,0,0                                               # compteurs de choc
point1,point3,score1,score3=0,0,0,0                             # compteurs de point et de score
R1,R3,T1,T3,PM1,PM3,CT1,CT3=0,0,0,0,0,0,0,0                     # indicateurs (rejouer,tirer,passer la main,continuer)
RN1,RN3,RT1,RT3=0,0,0,0                                         # indicateurs de rebond sur les bandes et après choc
j,j1,j3,t1,t3,r1,r3,rt,t=0,0,0,0,0,0,0,0,0                      # compteurs de lancements de jeux et de tirs
texte1E,texte1S,texte3E,texte3S=0,0,0,0
S1,S3,E1,E3=0,0,0,0                                             # résultats (S=succès,E=échec)
e,ha,o,v,o4=0,0,pi,8,pi/2                                       # réglages initiaux 
figer=0
tA=0

# Pupitre de réglage du tir
h=H/2                                                           # hauteur du pupitre de réglage
R,R4,R6,RS=4,25,3*h/8,3*h*r/4/H                                 # rayons du système de réglage
x4o,y4o,x5o,y5o=L/8-R,H+h/2-R,7*L/8-3*h/16-3*R/2,H+h/2-R
x4,y4,x5,y5=L/8-R,H+h/2-R,7*L/8-3*h/16-3*R/2,H+h/2-R

# création des widgets "dépendants" :
can=Canvas(root,bg='dark green',height=H+h,width=L,highlightbackground='brown')
can.grid(row=1,column=0,rowspan=2)
can2=Canvas(root,bg='brown',highlightbackground='brown')
can2.grid(row=1,column=1,sticky=N)
can3=Canvas(root,bg='white',height=h/3,width=h)
can3.grid(row=2,column=1,padx=0,pady=0,ipadx=10,ipady=10,sticky=N+S)

# Pupitre de commande
Pupitre=can.create_rectangle(4,H,L,H+h,width=2,fill='brown')
t1=can.create_text(L/8,H+h-10,text='Effet et hauteur de tir',fill="white")
t2=can.create_text(L/2,H+h-10,text='Simulation',fill="white")
t3=can.create_text(7*L/8,H+h-10,text='Vitesse et orientation de tir',fill="white")
texte1=can.create_text(L/2,H+7.5,text='Régler le tir, puis "Tirer !"',fill="white")
texte2=can.create_text(L/8,H+7.5,text='Déplacer la bille bleue',fill="white")
texte3=can.create_text(7*L/8,H+7.5,text='Déplacer le cercle blanc',fill="white")
REG4=can.create_oval(L/8-R4,H+h/2-R4,L/8+R4,H+h/2+R4,width=1,fill='white')
curseur4=can.create_line(L/8,H+h/2+R4,L/8,H+h/2-R4,width=1,fill='white')
REG6=can.create_rectangle(L/2-3*h/4,H+h/8,L/2+3*h/4,H+7*h/8,width=2,fill='dark green')
REG5=can.create_oval(7*L/8-R6,H+h/2-R6,7*L/8+R6,H+h/2+R6,width=1,fill='dark green')
REG5B=can.create_oval(7*L/8-R,H+h/2-R,7*L/8+R,H+h/2+R,width=1,fill='white')
curseur5=can.create_line(7*L/8-R,H+h/2,7*L/8-3*h/16+R/2,H+h/2,width=1,fill='dark green',arrow=LAST)

# création des cinq points gris sur le tapis et des deux flèches
pA=can.create_oval(L/4-rr,H/2-rr,L/4+rr,H/2+rr,width=0,fill='light grey')
pB=can.create_oval(L/2-rr,H/2-rr,L/2+rr,H/2+rr,width=0, fill='light grey')
pC=can.create_oval(3*L/4-rr,H/2-rr,3*L/4+rr,H/2+rr,width=0,fill='light grey')
pD=can.create_oval(3*L/4-rr,H/3-rr,3*L/4+rr,H/3+rr,width=0,fill='light grey')
pE=can.create_oval(3*L/4-rr,2*H/3-rr,3*L/4+rr,2*H/3+rr,width=0,fill='light grey')
flèche1=can.create_line(0,0,0,0,arrow=LAST)
flèche3=can.create_line(0,0,0,0,arrow=LAST)

# Message d'introduction
texte=can.create_text(L/2,L/3,text='Cliquer sur le bouton "Règle du jeu"\n\nou\n\ndirectement sur "Préparer le jeu"',fill="white")

# Menu <Règle du jeu> 
RdJ = Menubutton(can2, text ='Règle du jeu',height=2,width=25,relief=GROOVE,bg="dark green",fg="white")
RdJ.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
me1 = Menu(RdJ)
me1.add_command(label='Présentation',underline=0,command=presentation)
me1.add_command(label='Processus',underline=0,command=processus)
me1.add_command(label='Echelle',underline=0,command=echelle)
me1.add_command(label='A propos ...',underline=0,command=aPropos)
RdJ.configure(menu=me1)

# Affichage des réglages et des scores:

  # hauteur:
can.hauteur=Label(can2,text='0',fg='white',bg='brown')
can.hauteur.pack(side=BOTTOM)
Label(can2,text="hauteur",fg='white',bg='brown').pack(side=BOTTOM)
  # effet:
can.effet=Label(can2,text='0',fg='white',bg='brown')
can.effet.pack(side=BOTTOM)
Label(can2,text="effet",fg='white',bg='brown').pack(side=BOTTOM)
  # vitesse:
can.vitesse=Label(can2,text='0',fg='white',bg='brown')
can.vitesse.pack(side=BOTTOM)
Label(can2,text="vitesse",fg='white',bg='brown').pack(side=BOTTOM)
  # orientation:
can.orientation=Label(can2,text='0',fg='white',bg='brown')
can.orientation.pack(side=BOTTOM)
Label(can2,text="orientation",fg='white',bg='brown').pack(side=BOTTOM)
  # Score B3:
can.score3=Label(can2,text='0',fg='white',bg='brown')
can.score3.pack(side=BOTTOM)
Label(can2,text="score GRISE",fg='white',bg='brown').pack(side=BOTTOM)
  # Score B1:
can.score1=Label(can2,text='0',fg='white',bg='brown')
can.score1.pack(side=BOTTOM)
Label(can2,text="score BLANCHE",fg='white',bg='brown').pack(side=BOTTOM)

# Boutons de commande

  # 'Continuer ?':
CT=Button(can2,text='Continuer? ',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=continuer)
CT.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
CT.pack_forget()
  # 'Passer la main ?':
PM=Button(can2,text='Passer la main? ',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=passer)
PM.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
PM.pack_forget()
  # 'Rejouer le coup ?':
RJ=Button(can2,text='Rejouer le coup ?',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=rejouer)
RJ.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
RJ.pack_forget()
  # bouton d'arrêt du mouvement:
FI=Button(can2,text='Figer le mouvement',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=stop)
FI.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
FI.pack_forget()
  # bouton de redémarrage du mouvement:
RL=Button(can2,text='Relancer le mouvement',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=go)
RL.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
RL.pack_forget()
  # 'Tirer !':
T=Button(can2,text='Tirer !',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=tirer)
T.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)
T.pack_forget()
  # 'Préparer le tir !':
J=Button(can2,text='Préparer le jeu !',height=2,width=25,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=jouer)
J.pack(padx=5,pady=5,side=BOTTOM,anchor=SW)

root.mainloop()

Conclusion :


Merci à tous ceux qui ont bien voulu prendre connaissance des premières versions.
Une nouvelle visite du script devrait les intéresser, car de nombreuses fonctionnalités
ont été ajoutées.
Commencer par appuyer sur le bouton "Règle du jeu" qui donne une présentation d'ensemble,
ainsi que le processus à suivre pour jouer.

PS : commentaires et cotations seront les bienvenus !

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.