Un serpent python ... pas comme les autres !


Description

Ce script est présenté sous la forme d'un JEU.
Il fait intervenir un SERPENT orientable en direction et "armé" :
- d'une FLECHE rétractable
- d'un MISSILE à autoguidage terminal
Le but du jeu consiste pour le serpent à détruire le plus rapidement possible plusieurs cibles.
Cinq niveaux de jeu sont proposés.

Source / Exemple :


#! /usr/bin/env python 
# -*- coding: Latin-1 -*- 
# Python version 2.4.2 
# Tk version 8.4 
# IDLE version 1.1.2 
   
# <<< Serpent PYTHON >>> 
  
############# PRESENTATION ################# 
   
# Ce script est présenté sous la forme d'un JEU. 
  
# Il fait intervenir un SERPENT orientable en direction et "armé" : 
# - d'une FLECHE rétractable 
# (commmande de tir par la touche < Espace > du clavier 
# - d'un MISSILE à autoguidage terminal 
# (commmande de tir par la touche de la lettre < X > du clavier 
# 
# La "FLECHE" est représentée par un cercle blanc ancré au nez du serpent, auquel elle est reliée 
# par un "fil" extensible losqu'elle est projetée. Elle change de couleur, en bleu, lorsqu'elle est 
# "désactivée". 
# Le missile est représenté aussi par un cercle blanc, plus petit.Il est situé à l'extrémité de la queue 
# du serpent et s'en détache quand il est mis en oeuvre. Il revient à sa place après un tir suivi d'une collision. 
# 
# Le but du jeu consiste pour le serpent à détruire le plus rapidement possible plusieurs CIBLES. 
# 
# Les CIBLES, de couleur marron, sont immobiles au départ. 
# Elles peuvent se mettre en mouvement dans le courant de la partie. 
# Elles deviennent alors DANGEREUSES pour le serpent qui risque la DESTRUCTION en cas de collision. 
# Lorsque le serpent tire avec sa flèche rétractable, la cible est normalement détruite immédiatement. 
# Mais cette flèche peut se désactiver; dans ce cas, la cible n'est pas détruite et, surtout, elle devient MOBILE. 
# La flèche se réactive au tir suivant ... si le serpent a survécu. 
# Qand elle est désactivée, un message d'avertissement apparait dans le panneau rouge situé à droite 
# du plan de jeu. 
# Les cibles sont également entourées d'un cercle de détection pour les tirs de missile. 
# Dès que le missile entre en contact avec l'un de ces cercles, il change d'orientation et 
# se dirige automatiquement vers le centre de la cible détectée. 
# La cible se met en mouvement, puis elle est rapidement détruite au contact du missile. 
# Lorsqu'il y a une détection multiple, les autres cibles deviennent aussi mobiles, et donc DANGEREUSES. 
# 
# Dix OBSTACLES fixes sont placés sur le jeu. Ils sont alignés et de couleur bleue. Indestructibles, ils 
# permettent au serpent de rebondir et ainsi de multiplier les changements de trajectoire. 
# Les cibles peuvent s'y cacher. 
# 
# La partie n'est donc pas gagnée d'avance !!! 
# 
# La fenêtre de jeu comprend: 
# - un plan carré à l'intérieur duquel évoluent le serpent et les cibles 
# - un champ de saisie du niveau de jeu choisi (1, 2, 3, 4) 
# - quatre boutons de commande : 
# . < Mise en place ! > 
# . < Lancer le jeu ! > 
# . < Stop ! > 
# . < Quitter le jeu ! > 
# - un bouton donnant la : 
# . < Règle du jeu ! > 
# - un compteur visualisant le temps écoulé 
# - un compteur visualisant le nombre de cibles détruites 
# - un panneau rouge pour le message d'avertissement 
# 
# 
# Commencer -comme il est d'usage- par prendre connaissance de la < Règle du jeu ! >. 
# and "enjoy it !!!" 
  
  
############# COMMENTAIRE ################# 
   
# Le programme fait intervenir l'interface graphique Tkinter, avec quatre modules : 
# math, random, tkMessageBox, time. 
# L'utilisation de LISTES (pour les coordonnées et les diamètres des cercles) et d'une BIBLIOTHEQUE 
# pour leur identification, permet de réduire considérablement le nombre de lignes de code. 
# C'est l'intérêt de ce script. 
# L'algorithme de collision des cercles est celui que j'avais présenté dans les codes intitulés: 
# "BILLARD Français", et "COLLISIONS multiples". 
  
###################################################################################### 
  
from Tkinter import* 
from math import hypot,sqrt,floor,log 
from random import randrange 
from tkMessageBox import askokcancel 
from time import time 
import tkFont 

###################################################################################### 

nS=40# nombre de cercles constituant le serpent 
nC=39# nombre de cibles 
nO=10# nombre d'obstacles 
  
###################################################################################### 

def niveau_jeu(): 
    " choix du niveau de jeu " 
    global V,TD,nS 
    if activation_cases==1: 
        N=choix_niveau.get()# entrée du niveau de jeu choisi (de 1 à 5) 
        # V est un paramètre de réglage de la vitesse du serpent 
        # TD est un paramètre de réglage du nombre total de cibles détruites, pour lequel un tir de flèche 
        # devient dangereux pour le serpent : les cibles détectées deviennent en effet mobiles ...et susceptibles 
        # de le détruire en cas de collision par le travers : 
        if N==1: V=2.0; TD=20;nS=40# correspond à la 20ème cible détruite 
        if N==2: V=2.0; TD= 5;nS=40# correspond aux destructions de rang multiple de 5 : 5ème, 10ème, 15ème,...,35ème 
        if N==3: V=2.0; TD= 4;nS=45# rang multiple de 4 : 4ème, 8ème, 12ème,..., 36ème cible détruite 
        if N==4: V=2.5; TD= 3;nS=40# rang multiple de 3 : 3ème, 6ème, 9ème,..., 39ème cible détruite 
        if N==5: V=3.0; TD= 2;nS=32# rang multiple de 2 : 2ème, 4ème, 6ème,..., 38ème cible détruite 

def mise_en_place(): 
    " commande de mise en place du jeu " 
    global flag,top_start,score_time 
    flag=1 
    ST.configure(state=NORMAL)# activation du bouton de commande < Stop !> 
    LJ.configure(state=NORMAL)# id <Lancer le jeu !> 
    MP.configure(state=DISABLED)# désactivation du bouton de commande < Mise en place !> 
    niveau_jeu()# prise en compte du niveau de jeu choisi, en provenance du module ci-dessus 
    score_time=0# mise à zéro du compteur de temps écoulé 
    can.score.config(text='%s'%score_time)# visualisation de ce compteur 
    top_start=time()# top départ 
    can.delete(ALL)# nettoyage" du plan de jeu 
    serpent()# activation du module (ci-dessous) 
    cibles()# id 
    obstacles()# id 
   
def lancer_le_jeu(): 
    " commandes de lancement des mouvements et de gestion des collisions " 
    global activation_cases 
    LJ.configure(state=DISABLED)# désactivation du bouton de commande <Lancer le jeu !> 
    activation_cases 
    if flag==1: 
        mouvements_serpent()# activation du module (ci-dessous) 
        mouvements_fleche()# id 
        mouvements_cibles()# id 
        collisions_serpent_obstacles()# id 
        collisions_serpent_cibles()# id 
        collisions_missile_cibles()# id 
        if total_destruction == nC:# arrêt lorsque toutes les cibles sont détruites 
            top_end=time()# top fin de partie 
            score_time=int(round(top_end-top_start,0))# calcul de la durée de la partie 
            for i in range(0,nO):can.delete(O[i])# effacement des obstacles fixes 
            for i in range(1,150):sapin();can.delete(ALL)# activation en boucle du module animé "sapin+neige" 
            for i in range(1,150):sapin() 
            can.create_text(L/2,H/3-50,text='Joyeuses fêtes !!!',fill="white",font="Arial 12") 
            root.after(10000,stop)# l'animation finale dure 10 secondes 
        if hypot(x[nS-2]-x[1],y[nS-2]-y[1])>200*V-80:# critère de désintégration du serpent 
            destruction()# destruction du serpent 
        score_time=int(round(time()-top_start,0))# calcul de la durée de la partie 
        can.score.config(text='%s'%score_time)# visualisation du temps écoulé 
        can.total_destruction.config(text='%s'%total_destruction)# visualisation du nombre de cibles détruites 
        activation_cases=0 
        root.after(10,lancer_le_jeu) 
  
###################################################################################### 
  
def serpent(): 
    " création du serpent PYTHON (S) et de sa flèche (F) " 
    global S,LF,x,y,dx,dy,DS 
    x=[L-(i+2)*V*n for i in range(0,nS)]# liste des abscisses des 40 cercles constituant le serpent 
    y=[H-(i+2)*2*V*n for i in range(0,nS)]# liste des ordonnées 
    dx=[-V*n for i in range(0,nS)]# liste des vitesses horizontales 
    dy=[-2*V*n for i in range(0,nS)]# liste des vitesses verticales 
    DS=[7] 
    for i in range(0,nS-1):DS.append(1+d*(1+i)/nS) # liste des diamètres 
    S={}# bibliothèque des cercles 
    LF=can.create_line(x[nS-1],y[nS-1],x[nS-2],y[nS-2],fill="orange")# ligne reliant la flèche à la tête du serpent 
    for i in range(0,nS): 
        S[i]=can.create_oval(x[i]-DS[i]/2,y[i]-DS[i]/2,x[i]+DS[i]/2,y[i]+DS[i]/2,fill="orange",outline="orange") 
    can.itemconfig(S[nS-4],width=9);can.itemconfig(S[nS-3],width=6);can.itemconfig(S[nS-2],width=3)# dessin de la tête du serpent 
    can.itemconfig(S[0],fill="white")# couleur blanche pour le missile (en queue) 
    can.itemconfig(S[nS-1],fill="white",width=1,outline="blue")# dessin de la flèche rétractable (extrémité en forme de cercle) 
     
def cibles(): 
    " création des cibles (C) " 
    global C,Q,xC,yC,dxC,dyC,DC,DQ 
    xC=[randrange(10,780) for i in range(0,25)]# liste des abscisses des 39 cibles (distribution aléatoire) 
    for i in range(25,39):xC.append(randrange(10,520))# pour éviter au départ de placer une cible sur le serpent 
    yC=[10+i*19 for i in range(0,nC)]# liste des ordonnées (régulièrement espacées) 
    dxC=[0 for i in range(0,nC)]# les cibles sont immobiles 
    dyC=[0 for i in range(0,nC)]# id 
    DC=[randrange(10,20) for i in range(0,nC)]# diamètres compris entre 10 et 20 
    DQ=[100 for i in range(0,nC)]# diamètres des cercles de détection des cibles 
    C={}# bibliothèque des cibles 
    Q={}# bibliothèque des cercles de détection 
    for i in range(0,nC): 
        Q[i]=can.create_oval(xC[i]-DQ[i]/2,yC[i]-DQ[i]/2,xC[i]+DQ[i]/2,yC[i]+DQ[i]/2,outline='#007000') 
        C[i]=can.create_oval(xC[i]-DC[i]/2,yC[i]-DC[i]/2,xC[i]+DC[i]/2,yC[i]+DC[i]/2,fill="brown",outline="yellow") 
  
def obstacles(): 
    " création des obstacles fixes (C) " 
    global O,xO,yO,dxO,dyO,DO 
    xO=[L/4+i*50 for i in range(0,nO)]# liste des abscisses des 10 obstacles (distribution régulière) 
    yO=[H/4+i*50 for i in range(0,nO)]# liste des ordonnées (id) 
    dxO=[0 for i in range(0,nO)]# les cibles sont immobiles 
    dyO=[0 for i in range(0,nO)]# id 
    DO=[40 for i in range(0,nO)]# diamètre des obstacles 
    O={}# bibliothèque des obstacles 
    for i in range(0,nO): 
        O[i]=can.create_oval(xO[i]-DO[i]/2,yO[i]-DO[i]/2,xO[i]+DO[i]/2,yO[i]+DO[i]/2,fill="blue",outline="yellow") 
  
###################################################################################### 
  
def mouvements_serpent(): 
    " mouvements du serpent (S) " 
    global x,y,dx,dy,DS 
    global vF,tirF,GC,DR,HT,BS,iG,iD,iH,iB 
    if tirF==1:desactiver_touches()# concerne les flèches du clavier 
    if tirF==0:activer_touches()# id 
    for i in range(0,nS): 
        # commandes de direction 
        if GC==1:# clavier : activation de la flèche gauche 
            desactiver_touches();X,Y=x[i]-u,y[i]-v;Z=hypot(X,Y) 
            if Z==0 :iG+=1;dx[i],dy[i]=-hypot(du,dv),0 
            if iG==nS-1:activer_touches();GC=0;iG=0 
        if DR==1:# clavier : activation de la flèche droite 
            desactiver_touches();X,Y=x[i]-u,y[i]-v;Z=hypot(X,Y) 
            if Z==0 :iD+=1;dx[i],dy[i]=hypot(du,dv),0 
            if iD==nS-1:activer_touches();DR=0;iD=0 
        if HT==1:# clavier : activation de la flèche haute 
            desactiver_touches();X,Y=x[i]-u,y[i]-v;Z=hypot(X,Y) 
            if Z==0 :iH+=1;dx[i],dy[i]=0,-hypot(du,dv) 
            if iH==nS-1:activer_touches();HT=0;iH=0 
        if BS==1:# clavier : activation de la flèche basse 
            desactiver_touches();X,Y=x[i]-u,y[i]-v;Z=hypot(X,Y) 
            if Z==0 :iB+=1;dx[i],dy[i]=0,hypot(du,dv) 
            if iB==nS-1:activer_touches();BS=0;iB=0 
        # mouvements 
        x[i],y[i]=x[i]+(1+collisionF/nC)*dx[i],y[i]+(1+collisionF/nC)*dy[i]# le mouvement du serpent s'accélère avec la destruction des cibles 
        if tirM==0: 
            x[0],y[0]=x[1]-dx[1],y[1]-dy[1]# le missile est maintenu en queue 
        can.coords(S[i],x[i]-DS[i]/2,y[i]-DS[i]/2,x[i]+DS[i]/2,y[i]+DS[i]/2) 
    # collisions avec les bordures 
    for i in range(0,nS-1):# maintien dans les limites du jeu par rebond sur les bords 
        collision_bordures(x[i],y[i],dx[i],dy[i],DS[nS-1]) 
        x[i],y[i],dx[i],dy[i]=pp,qq,rr,ss 
  
def mouvements_fleche(): 
    " mouvements de la flèche (F) " 
    global x,y,dx,dy 
    global vF,tirF,activationF,total_destruction 
    can.coords(LF,x[nS-1],y[nS-1],x[nS-2],y[nS-2]) 
    if tirF==1:# la flèche du serpent est lancée dans l'axe à une vitesse supérieure 
        dx[nS-1],dy[nS-1]=vF*dx[nS-1]/hypot(dx[nS-1],dy[nS-1]),vF*dy[nS-1]/hypot(dx[nS-1],dy[nS-1]) 
        x[nS-1],y[nS-1]=x[nS-1]+dx[nS-1],y[nS-1]+dy[nS-1] 
    if tirF==1 and x[nS-1]>L-d/2 or x[nS-1]<d/2 or y[nS-1]>H-d/2 or y[nS-1]<d/2:# maintien dans les limites du jeu 
        x[nS-1],y[nS-1],dx[nS-1],dy[nS-1]=x[nS-2],y[nS-2],dx[nS-2],dy[nS-2] 
        vF,tirF=0,0 
    if tirF==0 and x[nS-1]>L-d/2 or x[nS-1]<d/2 or y[nS-1]>H-d/2 or y[nS-1]<d/2:# maintien dans les limites du jeu 
        x[nS-1],y[nS-1],dx[nS-1],dy[nS-1]=x[nS-2],y[nS-2],dx[nS-2],dy[nS-2] 
    total_destruction = collisionF + collisionM 
    for i in range(1,13): 
        if total_destruction==TD*i:# si le nombre de cibles détruites est un multiple de TD (voir le module niveau_jeu ci-dessus) 
            activationF=1 
            can.itemconfig(S[nS-1],fill="blue")# la flèche du serpent change de couleur 
            avertissement.config(bg="yellow",fg="red")# un message d'avertissement apparait dans la fenêtre 
        if total_destruction==TD*i+1:# pour les valeurs suivantes 
            activationF=0 
            can.itemconfig(S[nS-1],fill="white")# la flèche reprend sa couleur 
            avertissement.config(bg="brown",fg="brown")# le message disparait 
             
def mouvements_cibles(): 
    " petits mouvements des cibles lors des collisions(O) " 
    global xC,yC,dxC,dyC 
    for i in range(0,nC): 
        # mouvement 
        xC[i],yC[i]=xC[i]+dxC[i],yC[i]+dyC[i] 
        dxC[J],dyC[J]=0.9995*dxC[J],0.9995*dyC[J] 
        can.coords(C[i],xC[i]-DC[i]/2,yC[i]-DC[i]/2,xC[i]+DC[i]/2,yC[i]+DC[i]/2) 
        can.coords(Q[i],xC[i]-DQ[i]/2,yC[i]-DQ[i]/2,xC[i]+DQ[i]/2,yC[i]+DQ[i]/2) 
        # collisions avec les bordures 
        collision_bordures(xC[i],yC[i],dxC[i],dyC[i],DC[i]) 
        xC[i],yC[i],dxC[i],dyC[i]=pp,qq,rr,ss 
  
###################################################################################### 
  
def collisions_serpent_obstacles(): 
    " gestion des collisions Serpent / Obstacles " 
    global x,y,dx,dy,DS 
    global xO,yO 
    for j in range(0,nO): 
        if tirF==1:# après un tir 
            X,Y=xO[j]-x[nS-1],yO[j]-y[nS-1] 
            Z=hypot(X,Y) 
            if Z<=DS[nS-1]/2+DO[j]/2:# s'il y a collision Flèche / Obstacle 
                x[nS-1],y[nS-1],dx[nS-1],dy[nS-1]=x[nS-2],y[nS-2],dx[nS-2],dy[nS-2]# la flèche revient en place 
        for i in range(0,nS):# collisions Serpent / Obstacles 
            collisions(x[i],y[i],xO[j],yO[j],dx[i],dy[i],dxO[j],dyO[j],DS[nS-1],DO[j]) 
            x[i],y[i],xO[j],yO[j]=rxa,rya,rxb,ryb 
            dx[i],dy[i]=rdxa,rdya 
  
def collisions_serpent_cibles(): 
    " gestion des collisions Serpent / Cibles " 
    global x,y,dx,dy 
    global xC,yC 
    global collisionF,tirF,vF,activationF,J 
    for j in range(0,nC): 
        if tirF==1:# après un tir 
            X,Y=xC[j]-x[nS-1],yC[j]-y[nS-1] 
            Z=hypot(X,Y) 
            if Z<=DS[nS-1]/2+(1+collisionF/3)*DC[j]/2:# s'il y a collision Serpent / Cibles 
                J=j# rang (dans la liste) des cibles détectées 
                if activationF==1:# si la flèche est activée (couleur bleue) 
                    xC[J],yC[J]=xC[j],yC[j] 
                    dxC[J],dyC[J]=4*dy[nS-1]/hypot(dx[nS-1],dy[nS-1])/Z,-4*dx[nS-1]/hypot(dx[nS-1],dy[nS-1]/Z)# les cibles touchées se mettent en mouvement 
                    can.itemconfig(C[J],fill="green")# et changent de couleur 
                    collisionF==-1# le tir n'est pas compté 
                else: 
                    xC[j],yC[j],DC[j],DQ[j]=0,0,0,0# la cible est détruite 
                    x[nS-1],y[nS-1],dx[nS-1],dy[nS-1]=x[nS-2],y[nS-2],dx[nS-2],dy[nS-2]# la flèche revient en place 
                    collisionF+=1# le tir est comptabilisé 
                    vF,tirF=0,0 
                    can.create_rectangle(L*(collisionF-1)/nC,H-10,L*collisionF/nC,H,fill="pink",outline="blue") 
                    can.create_text(50,H-15,text='flèche',fill="white") 
                    if collisionM!=0:# s'il y a eu, en outre, destruction d'une cible, une nouvelle cible apparait 
                        dxC[j],dyC[j]=0,0# immobilisation de celle-ci 
        for i in range(0,nS):# collisions Serpent / Cibles 
            collisions(x[i],y[i],xC[j],yC[j],dx[i],dy[i],dxC[j],dyC[j],DS[nS-1],DC[j]) 
            x[i],y[i],xC[j],yC[j]=rxa,rya,rxb,ryb 
            dx[i],dy[i]=rdxa,rdya 
  
def collisions_missile_cibles(): 
    " gestion des collisions Missile de queue / Cibles / Cercle de détection " 
    global x,y,dx,dy,DS 
    global tirM,collisionM 
    global xC,yC,dxC,dyC,DC 
    global DQ 
    if tirM==1:# lorsque le missile est tiré 
        for j in range(0,nC): 
            DQ[j]=DQ[j]+0.05# le diamètre des cercles de détection augmente à chaque tir 
            X,Y=x[0]-xC[j],y[0]-yC[j] 
            Z=hypot(X,Y) 
            if Z<=DQ[j]/2+DS[0]/2:# s'il y a contact du missile avec le cercle de détection 
                dx[0],dy[0]=-X/Z*hypot(dx[0],dy[0]),-Y/Z*hypot(dx[0],dy[0])# le missile s'oriente en direction de la cible 
                DQ[j]=0.97*DQ[j]# le cercle de détection rétrécit lentement 
                can.itemconfig(C[j],fill='#00FF00')# la couleur des cibles détectées passe au vert 
                dxC[j],dyC[j]=0.25*randrange(1,2),0.25*randrange(1,2)# les cibles détectées se mettent en mouvement 
  
                " ATTENTION !!! A partir de ce moment, les cibles mises en mouvement sont DANGEREUSES. " 
                " Le serpent est détruit s'il entre à leur contact : il lui faut donc les détruire en priorité " 
                " et, sinon, les éviter " 
                 
                if DQ[j]<0.97*nC:# collision du missile avec la cible visée 
                    collisionM+=1 
                    tirM=0 
                    xC[j],yC[j],dxC[j],dyC[j],DC[j],DQ[j]=0,0,0,0,0,0# destruction de la cible 
                    x[0],y[0]=x[1]-dx[1],y[1]-dy[1]# le missile revient à sa place (en queue du serpent) 
                    can.create_rectangle(L-L*collisionM/nC,H-10,L-L*(collisionM-1)/nC,H,fill="grey",outline="blue") 
                    can.create_text(L-50,H-15,text='missile',fill="white") 
    if tirM==0 : 
        x[0],y[0]=x[0],y[0] 
                    
###################################################################################### 
  
def collision_bordures(p,q,r,s,D): 
    " algorithme commun de collision avec les bordures " 
    global pp,qq,rr,ss 
    if p>L-D/2:pp,qq,rr,ss=L-D/2,q,-r,s 
    if p<D/2:pp,qq,rr,ss=D/2,q,-r,s 
    if q>H-D/2:pp,qq,rr,ss=p,H-D/2,r,-s 
    if q<D/2:pp,qq,rr,ss=p,D/2,r,-s 
    if p<=L-D/2 and p>=D/2 and q<=H-D/2 and q>=D/2: 
        pp,qq,rr,ss=p,q,r,s 
  
def collisions(xa,ya,xb,yb,dxa,dya,dxb,dyb,da,db): 
    " algorithme commun de collisions entre cercles " 
    global rxa,rya,rxb,ryb 
    global rdxa,rdya 
    X,Y=xb-xa,yb-ya 
    Z=hypot(X,Y) 
    if Z<=(da+db)/2: 
        dX,dY=dxb-dxa,dyb-dya 
        dZ=hypot(dX,dY) 
        if dZ==0:m=0 
        else: 
            k,K=dZ*dZ,X*dX+Y*dY 
            m=(K+sqrt(K*K-k*(Z*Z-(da+db)/2*(da+db)/2)))/k 
        rxa,rya,rxb,ryb=xa-m*dxa,ya-m*dya,xb-m*dxb,yb-m*dyb 
        X,Y=rxb-rxa,ryb-rya 
        Z=hypot(X,Y) 
        if Z<=(da+db)/2: 
            dxb,dyb=0,0 
            dX,dY=dxb-dxa,dyb-dya 
            dZ=hypot(dX,dY) 
            if dZ==0:m=0 
            else: 
                k,K=dZ*dZ,X*dX+Y*dY 
                m=(K+sqrt(K*K-k*(Z*Z-(da+db)/2*(da+db)/2)))/k 
            rxa,rya,rxb,ryb=xa-m*dxa,ya-m*dya,xb-m*dxb,yb-m*dyb 
        xx,xy,yy=X*X/Z/Z,X*Y/Z/Z,Y*Y/Z/Z 
        rdxa,rdya=(yy-xx)*dxa-2*xy*dya,-2*xy*dxa-(yy-xx)*dya 
    else: 
        rxa,rya,rxb,ryb=xa,ya,xb,yb 
        rdxa,rdya=dxa,dya 
  
###################################################################################### 
 
    # Fonctions de déplacement des touches : 
  
def left(event): 
    " S à gauche " 
    global u,v,du,dv,GC 
    GC+=1 
    u,v,du,dv=x[nS-1],y[nS-1],dx[nS-1],dy[nS-1] 
  
def right(event): 
    " S à droite " 
    global u,v,du,dv,DR 
    DR+=1 
    u,v,du,dv=x[nS-1],y[nS-1],dx[nS-1],dy[nS-1] 
  
def up(event): 
    " S vers le haut " 
    global u,v,du,dv,HT 
    HT+=1 
    u,v,du,dv=x[nS-1],y[nS-1],dx[nS-1],dy[nS-1] 
  
def down(event): 
    " S vers le bas " 
    global u,v,du,dv,BS 
    BS+=1 
    u,v,du,dv=x[nS-1],y[nS-1],dx[nS-1],dy[nS-1] 
  
def tirer_fleche(event): 
    " commande touche < Espace > " 
    global vF,tirF 
    tirF=1 
    vF=10 
  
def tirer_missile(event): 
    " commande touche < x > ou < X > " 
    global tirM 
    tirM=1 
  
def activer_touches(): 
    "" 
    root.bind("<Left>",left) 
    root.bind("<Right>",right) 
    root.bind("<Up>",up) 
    root.bind("<Down>",down) 
  
def desactiver_touches(): 
    "" 
    root.unbind("<Left>") 
    root.unbind("<Right>") 
    root.unbind("<Up>") 
    root.unbind("<Down>") 
  
###################################################################################### 
  
def destruction(): 
    " message explicatif " 
    stop() 
    can.delete(texte) 
    texte2=can.create_text(L/2,420,text='''Le serpent a été détruit après collision avec une cible mobile (de couleur verte) !\n\n 
    Lorsque le serpent tire un missile, il peut arriver que plusieurs cibles fixes soient détectées. 
    Dans ce cas, les cibles détectées changent de couleur : elles passent du marron au vert. 
    et, surtout, elles entrent en mouvement. 
    Le missile se dirige vers la première détectée et la détruit. 
    Mais les autres cibles, devenues mobiles, sont dangereuses pour le serpent. 
    Elles doivent être détruites le plus rapidement possible et, surtout, absolument évitées.\n\n 
    Vous pouvez continuer en appuyant sur : Mise en place ! 
    et, sinon, sur : Quitter le jeu !\n''',fill="white",font="Arial 12") 
  
###################################################################################### 
 
def stop(): 
    " arrêt des mouvements et mise à zéro de tous les compteurs " 
    global flag,tirF,collisionF,tirM,collisionM,activationF,GC,DR,HT,BS,texte,arrivée,activation_cases 
  
    ST.configure(state=DISABLED) 
    LJ.configure(state=DISABLED) 
    MP.configure(state=NORMAL) 
    avertissement.configure(bg="brown",fg="brown") 
    flag=0 
    tirF,collisionF=0,0 
    tirM,collisionM=0,0 
    root.bind("<Left>",left) 
    root.bind("<Right>",right) 
    root.bind("<Up>",up) 
    root.bind("<Down>",down) 
    GC,DR,HT,BS=0,0,0,0 
    for i in range(0,nO):can.delete(O[i]) 
    for i in range(0,nC):can.delete(C[i]),can.delete(Q[i]) 
    texte=can.create_text(L/3,100,text='''MERCI\net\nn' hésitez pas à commenter (et à coter...) ce script !''',fill="white",font="Arial 12") 
    top_end=time() 
    activationF=0 
    activation_cases=1 
  
def quitter(): 
    " façon classique de quitter l'application " 
    ans=askokcancel('Serpent PYTHON',"Voulez-vous réellement quitter ?") 
    if ans:root.quit() 
  
def sapin(): 
    " petit dessin en guise de récompense !!!" 
    can.create_polygon(400,400 ,385,455 ,415,455,fill="green") 
    can.create_polygon(400,440 ,375,495 ,425,495,fill="green") 
    can.create_polygon(400,480 ,365,530 ,435,530,fill="green") 
    can.create_polygon(400,510 ,355,570 ,445,570,fill="green") 
    can.create_polygon(400,540 ,345,610 ,455,610,fill="green") 
    can.create_rectangle(392,610 ,408,625 ,fill="green") 
    a,b,c=randrange(0,800),randrange(0,800),randrange(2,8) 
    can.create_oval(a,b,a+c,b+c,fill="white") 
  
###################################################################################### 
  
def presentation(): 
    "Fenêtre-message contenant la description sommaire du principe du jeu" 
    msg =Toplevel() 
    Message(msg, bg ="dark green", fg ="white", width =810,font ="Arial 10", 
        text =''' PRESENTATION\n 
        Au lancement du jeu, le bouton < Mise en place > fait apparaître:\n 
           - le serpent PYTHON constitué de 40 cercles de diamètres décroissants, de la tête vers la queue 
           - la FLECHE (rétractable) du serpent : cercle blanc placé au niveau du nez 
           - le MISSILE (à autoguidage terminal) : petit cercle blanc situé en queue 
           - 10 obstacles fixes indestructibles de couleur bleue et de même diamètre 
             (sur lesquels rebondissent serpent, flèche et missile) 
           - 39 cibles à détruire, de couleur marron et de diamètre variable 
             (leurs abscisses ont des valeurs aléatoires, leurs ordonnées sont échelonnées régulièrement) 
             (ces cibles sont toute entourées d'un cercle de détection dont le diamètre est constant au départ, puis variable ensuite)\n\n 
        Le jeu consiste pour le serpent à détruire les 39 cibles, le plus rapidement possible, sans être lui-même détruit. 
        (la durée de la partie et le nombre de cibles détruites sont affichés à droite de la fenêtre de jeu)\n 
        Les moyens dont le serpent dispose sont: 
           - sa FLECHE, qui peut être tirée par pression rapide ou continue sur la touche < Espace > du clavier 
           - le MISSILE, qui est tirable par pression rapide sur la touche < X > du clavier 
              (la progression des destructions est visualisée en bas du plan de jeu)\n\n 
        Utilisation de la FLECHE\n 
        Au début, les tirs de la flèche doivent être précis pour pouvoir détruire les cibles. 
        Ils deviennent plus faciles ensuite, car la distance d'interception augmente lentement avec le nombre de succès. 
        Mais il ne sont pas sans risques pour le serpent, car à une certaine occurence, fonction du niveau de jeu choisi, cette flèche s'active. 
        Ceci a pour effet, lors du tir suivant, de neutraliser sa capacité de destruction, et surtout de mettre en mouvement les cibles détectées. 
        Il y a alors DANGER de DESTRUCTION pour le serpent, en cas de collision avec celles-ci. 
        Une fois tirée, la flèche se désactive, et les tirs sans risques redeviennent possibles...jusqu'à l'activation suivante.\n 
        Utilisation du MISSILE\n 
        Après tir par le serpent dans la zone de détection d'une cible, le missile se réoriente pour se diriger vers le centre de la cible. 
        La cible entre en mouvement, sa couleur passe au vert, son cercle de détection se rétracte, puis elle disparait. 
        Lorsqu'une ou plusieurs autres cibles sont aussi détectées, elles deviennent également mobiles; leur couleur passe aussi au vert. 
        Le missile reprend sa place, en queue du serpent, et peut à nouveau être tiré. 
        Mais il y a alors DANGER pour le serpent, puisque ces cibles sont mobiles.\n 
        En fin de partie, l'utilisation des flèches (haut,bas,gauche,droite) du clavier pourra permettre de gagner du temps. 
        Elles modifient la direction de marche du serpent.\n 
         
        Pour réussir une partie, une combinaison des deux types de tir sera nécessaire pour éviter la destruction du serpent. 
        Avec un peu d'entrainement, on parviendra au niveau 5, le plus intéressant.\n\n''').pack(padx =10, pady =10) 
  
def processus(): 
    "Fenêtre-message contenant la description du processus de jeu" 
    msg =Toplevel() 
    Message(msg, bg ="dark green", fg ="white", width =450,font ="Arial 10", 
        text='''PROCESSUS à suivre\n\n 
        1. Choisir le niveau de jeu 
             (cinq valeurs possibles : 1, 2, 3, 4, 5)\n 
        2. Cliquer sur le bouton < Mise en place ! >\n 
        3. puis sur le bouton < Lancer le jeu ! >\n 
        4. Tir avec la flèche rétractable : 
             appuyer sur la touche < Espace > du clavier\n 
        5. Tir d'un missile : 
             appuyer sur la touche de la lettre < X > du clavier\n 
        6. Changement de direction du serpent : 
             utiliser les quatre flèches du clavier 
             (haut, bas, gauche, droite)\n 
        7. Bouton < Stop !> 
             pour arrêter les mouvements\n 
        8. Bouton < Quitter le jeu !> 
             pour abandonner la partie\n''').pack(padx =10, pady =10) 
  
def aPropos(): 
    "Fenêtre-message indiquant les versions utilisées" 
    msg =Toplevel() 
    Message(msg, width =200, aspect =100, justify =CENTER, 
        text ='''Serpent PYTHON 
        HCD, Décembre 2005 
        Python version 2.4.2 
        Tk version 8.4''').pack(padx =10, pady =10) 
  
######## Programme principal ############################################ 
   
root = Tk() 
root.title('>>>>>>> Serpent PYTHON <<<<<<<') 
f=1  
L,H=800*f,800*f 
flag=0 
d=10 
n=1.5 
V=0 
tirF,collisionF=0,0 
tirM,collisionM=0,0 
activationF=0 
u,v=0,0 
GC,DR,HT,BS=0,0,0,0 
iG,iD,iH,iB=0,0,0,0 
score_time=0 
top_end=0 
J=0 
activation_cases=1 
# mise en place des deux canevas 
can=Canvas(root,bg='dark green',height=H,width=L) 
can.grid(row=1,column=0,rowspan=2) 
can2=Canvas(root,bg='brown',highlightbackground='brown') 
can2.grid(row=1,column=1,sticky=N) 
  
# commandes à partir du clavier 
root.bind("<Left>", left) 
root.bind("<Right>", right) 
root.bind("<Up>", up) 
root.bind("<Down>", down) 
root.bind('<space>', tirer_fleche) 
root.bind('x', tirer_missile) 
root.bind('X', tirer_missile) 
  
#Avertissement 
can.total_destruction=Label(can2,text='0',fg='white',bg='brown') 
can.total_destruction.pack(side=BOTTOM) 
Label(can2,text="Nombre de cibles détruites",fg='white',bg='brown').pack(side=BOTTOM) 
  
texte3='''ATTENTION !!! 
  
La flèche du serpent 
est passée au BLEU 
  
Le prochain tir est risqué ! 
  
CONSEIL 
  
Tirer le missile ! 
(La flèche repasse au BLANC 
ensuite)''' 
  
avertissement=Label(can2,text=texte3,height=13,width=24,relief=GROOVE,font='BOLD',bg="brown",fg="brown",command=None) 
avertissement.pack(padx=5,pady=5,side=BOTTOM,anchor=SW) 
  
# Score (durée de la partie) 
can.score=Label(can2,text='0',fg='white',bg='brown') 
can.score.pack(side=BOTTOM) 
Label(can2,text="Temps écoulé",fg='white',bg='brown').pack(side=BOTTOM) 
  
# Menu <Règle du jeu> 
RdJ = Menubutton(can2, text ='Règle du jeu',height=2,width=35,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='A propos ...',underline=0,command=aPropos) 
RdJ.configure(menu=me1) 
  
# Boutons de commande à partir de la fenêtre Tkinter 
Button(can2,text='Quitter le jeu !',height=2,width=35,relief=GROOVE,bg="white",command=quitter).pack(padx=5,pady=5,side=BOTTOM,anchor=SW) 
ST=Button(can2,text='Stop !',height=2,width=35,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=stop) 
ST.pack(padx=5,pady=5,side=BOTTOM,anchor=SW) 
LJ=Button(can2,text='Lancer le jeu !',height=2,width=35,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=lancer_le_jeu) 
LJ.pack(padx=5,pady=5,side=BOTTOM,anchor=SW) 
MP=Button(can2,text='Mise en place !',height=2,width=35,relief=GROOVE,bg="white",activebackground="dark green",activeforeground="white",command=mise_en_place) 
MP.pack(padx=5,pady=5,side=BOTTOM,anchor=SW) 

# Niveau de jeu 
Label(can2,text='''Choisir le niveau de jeu''',fg='white',bg='brown').pack(padx=5,pady=5,side=BOTTOM) 
niveau=["1","2","3","4","5"] 
choix_niveau=IntVar() 
choix_niveau.set(niveau[0]) 
for i in range(0,5): 
    rad=Radiobutton(can2,text=str(i+1),variable=choix_niveau,value=niveau[i],command=niveau_jeu) 
    rad.pack(padx=6,pady=5,side=LEFT) 

#root.resizable(0,0) 
root.config(bg="brown") 
root.mainloop() 
root.destroy()

Conclusion :


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.