PYTHON en MIASHS L1 & L2

Dominique Gonzalez

Université Lille3-Charles de Gaulle

Ce document est soumis à la licence GNU FDL. Permission vous est donnée de distribuer, modifier des copies de ces pages tant que cette note apparaît clairement.

Mars 2005


Table des matières

Introduction
I. Le cours
1. Échauffement : un peu d'algorithmique
2. Un peu plus d'algorithmique
3. Introduction à ncurses
Qu'est ce que Ncurses ?
Le minimum pour utiliser ncurses
Positionnement à l'écran
Premier exemple
Exercices
4. Ncurses et le clavier
La méthode getch()
D'autres méthodes
Les codes de retour de getch()
Un exemple
Exercices
5. Tout se complique...
6. Pour qui sont ces serpents ?
Première version simpliste
Attention aux bords
Un vrai serpent
Grandir
Enfin…
Améliorations éventuelles
II. Correction des exercices
7. Un peu d'algorithmique, correction
8. Un peu plus d'algorithmique, correction
9. Introduction à Ncurses, correction
10. Ncurses et le clavier, correction
11. Tout se complique, correction
12. Pour qui sont ces serpents, correction
Première version simpliste
Attention aux bords
Un vrai serpent
Grandir
Enfin…
En prime
III. Annexes
A. Où trouver ce document ?
B. C'est quoi Python ?
C. D'autres sources d'information

Introduction

Vous avez devant les yeux le support du cours donné aux étudiants de 1ère et 2ème années de licence MIASHS, au 2ème semestre de l'année 2004-2005, à l'Université Lille3-Charles de Gaulle.

Ces étudiants ont au préalable suivi un cours d'initiation à la programmation, également en Python, basé sur l'environnement du Robot.

Le but de ce cours est d'abord de montrer qu'on peut programmer sans l'environnement du Robot. On essaiera d'arriver à des notions les complexes possible. Pour ne pas « effaroucher » le public on se placera dans des cas d'exercices ludiques qui, tout en utilisant des notions simples, amèneront à se poser des problèmes non triviaux.

Le cours

Chapitre 1. Échauffement : un peu d'algorithmique

Quelques exercices pour se remettre en route… Les corrigés se trouvent au Chapitre 7, Un peu d'algorithmique, correction.

  1. Afficher tous les entiers de 21 à 145.

  2. Afficher 250 étoiles (« * »).

  3. Écrire un programme qui écrit 500 fois « Je dois faire des sauvegardes régulières de mes fichiers. »

  4. Calculer la somme de tous les entiers de 21 à 145.

  5. Calculer 35! (factorielle).

  6. Afficher un triangle rectangle composé d'étoiles (« * ») de 20 de côté :

    	  *
    	  **
    	  ***
    	  ****
    	  *****
    	  ******
    	  *******
    	  ********
    	  *********
    	  **********
    	  ***********
    	  ************
    	  *************
    	  **************
    	  ***************
    	

  7. Écrire un programme qui affiche la table de multiplication par 13.

  8. Écrire un programme qui affiche la table de multiplication totale de {1,…,10} par {1,…,10}.

  9. Sachant que le prix d'une vache est de 9000 F et que celui d'un mouton est de 5000 F, écrire un programme qui demande le nombre de moutons et de vaches et qui affiche le prix du troupeau ainsi constitué.

  10. Saisir deux horaires (heures/minutes/secondes), les afficher dans l'ordre croissant.

  11. Saisir deux horaires en heures minutes, secondes ; calculer le temps écoulé entre les deux (en secondes).

  12. Saisir un entier N, afficher la somme des N premiers entiers.

  13. Je suis ligoté sur les rails en garre d'Arras. Il vous faut écrire un programme qui affiche un tableau me permettant de connaître l'heure à laquelle je serai déchiqueté par le train parti de la gare du Nord à 9h (il y a 170 km entre la gare du Nord et Arras). Le tableau présentera les différentes heures possibles pour toutes les vitesses de 100 km/h à 300km/h, par pas de 10km/h, les résultats étant arrondis à la minute inférieure. On y trouvera par exemple:

    	160 km/h 10h03
    	170 km/h 10h00
    	180 km/h 9h56
          
    1. Écrire une procédure tchacatchac(v) qui reçoit en paramètre la vitesse du train et qui affiche l'heure à laquelle je serai écrasé.

    2. Écrire le programme utilisant cette procédure et qui affiche le tableau demandé.

  14. Un permis de chasse à points remplace désormais le permis de chasse traditionnel. Chaque chasseur possède au départ un capital de 100 points. S'il tue une poule il perd 1 point, 3 points pour un chien, 5 points pour une vache et 10 points pour son meilleur ami. Le permis coûte 200 euros.

    Écrire une fonction permisSup qui admet en paramètres les nombres de victimes du chasseur et qui renvoie la somme qu'il aura à débourser pour ses permis supplémentaires.

    Puis utiliser cette fonction dans un programme qui demande le nombre de victimes et qui affiche la somme qu'aura à débourser le chasseur.

  15. Un gardien de phare va aux toilettes 5 fois par jour. Les WC sont au rez-de-chaussée. Écrire une procédure hauteurParcourue(nbMarches, hauteurMarche) qui reçoit en paramètres le nombre de marches du phare et la hauteur de chaque marche (exprimée en centimètres) et qui affiche :

    	Pour x marches de y cm, il parcourt z mètres en une semaine.
          

    On n'oubliera pas :

    • qu'une semaine comporte 7 jours,

    • qu'une fois en bas, le gardien doit remonter,

    • la hauteur des marches est exprimée en cm et le résultat doit l'être en mètres.

Chapitre 2. Un peu plus d'algorithmique

Les corrigés se trouvent au Chapitre 8, Un peu plus d'algorithmique, correction.

  1. Saisir une suite de nombres terminée par -1, qui ne fait pas partie de la liste à traiter ; afficher le double de chacun des nombres à mesure qu'ils sont saisis, afficher FIN quand c'est fini.

  2. Saisir une suite de nombres terminée par -1, qui ne fait pas partie de la liste à traiter ; afficher leur somme quand c'est fini.

  3. Saisir une suite de nombres terminée par -1, qui ne fait pas partie de la liste à traiter ; afficher le plus grand quand c'est fini.

  4. Coefficients du binôme :

    1. Écrire une fonction qui renvoie n!.

    2. Utiliser la fonction précédente pour écrire une fonction qui renvoie

      C_n^p
    3. Utiliser les fonctions précédentes dans un programme qui affiche les coefficients du binôme pour toutes les valeurs de n dans {0,1,..,20} :

      C^0_n C^1_n C^2_n \dots C^{n-2}_n C^{n-1}_n C^n_n
  5. La suite de Fibonnacci est définie par les relations suivantes :

    • F0=0,

    • F1=1,

    • Fn=Fn-1 + Fn-2 pour tout n>1.

    Donc :

    • F0=0,

    • F1=1,

    • F2=0+1=1,

    • F3=1+1=2,

    • F4=2+1=3,

    • F5=3+2=5,

    • F6=5+3=8,

    • F7=8+5=13,

    • etc…

    Écrire un programme qui affiche les 50 premières valeurs de Fn.

  6. Fibonnacci, le retour : afficher les rapports

    \frac{f_n}{f_{n-1}}

    ainsi que leurs différences avec le nombre

    nombre d'or (\frac{\sqrt{5}-1}{2})

Chapitre 3. Introduction à ncurses

Résumé

Ncurses est une bibliothèque qui fournit des fonctions de définition de touches du clavier, de couleurs d'écran et permet l'affichage de plusieurs fenêtres sans recouvrement sur un terminal texte.

Qu'est ce que Ncurses ?

(Ce paragraphe est extrait de http://linuxfocus.unixtech.be/Francais/March2002/article233.shtml.)

Voulez vous que vos programmes disposent d'une interface couleur sur un terminal texte ? Ncurses est une bibliothèque qui offre ce type de fonctionnalité pour les terminaux en mode texte. Ncurses peut :

  • utiliser tout l'écran si vous le souhaitez,

  • créer et gérer des fenêtres,

  • utiliser 8 couleurs différentes,

  • permettre le contrôle de votre programme par la souris,

  • utiliser les touches de fonction du clavier.

Tout système UNIX conforme à la norme ANSI/POSIX supporte la bibliothèque Ncurses. De ce fait, elle est capable de détecter les propriétés du terminal à partir de la base de données du système, et donc de fournir une interface indépendante du terminal. Ainsi, ncurses est utilisable et fiable pour des travaux devant fonctionner sur différentes plates-formes et différents types de terminaux.

Ncurses est disponible à cette adresse : http://www.gnu.org/software/ncurses/.

Le minimum pour utiliser ncurses

Commençons par expliquer le minimum à mettre dans vos programmes pour qu'ils puissent utiliser ncurses (qui s'appelle d'ailleurs curses, sans « n », dans Python, mais ça ne change rien).

Voici le programme minimum :

      import curses            # Importation de la bibliothèque curses
      ecran = curses.initscr() # Créer une structure ecran

         ........              # Votre programme se place là
      
      curses.endwin()          # Terminer proprement
    

Remarque

Le nom ecran choisi ci-dessus est totalement arbitraire. Vous pouvez choisir ce que vous voulez. Ce n'est qu'un nom de variable. Mais il faudra adapter dans la suite des exemples.

On verra au fur et à mesure de l'avancée dans le cours d'autres commandes nécessaires au bon fonctionnement de curses.

Remarque

L'utilisation de curses modifie énormément le fonctionnement du terminal dans lequel vous exécutez le programme. La dernière ligne permet de laisser le terminal dans un état propre au sortir du programme.

Mais encore faut-il arriver à cette dernière ligne… Si votre programme plante il n'atteindra pas cette ligne et vous vous retrouverez à utiliser un terminal en très mauvais état…

Si cela vous arrive la solution est simple : tapez en aveugle la commande reset (suivie d'un appui sur la touche Entrée).

Positionnement à l'écran

Selon ce que vous voulez placer à l'écran vous utiliserez les procédures suivantes :

addstr() :

Pour positionner une chaîne de caractères .

Ses paramètres peuvent être :

str ou ch :

Affiche la chaîne str ou le caractère ch à la position actuelle du curseur.

str ou ch, attr :

Affiche la chaîne str ou le caractère ch à la position actuelle du curseur, en utilisant l'attribut attr.

y, x, str ou ch :

Affiche la chaîne str ou le caractère ch à la yème ligne et à la xème colonne.

y, x, str ou ch, attr :

Affiche la chaîne str ou le caractère ch à la yème ligne et à la xème colonne, en utilisant l'attribut attr.

Les attributs (attr) permettent de contrôler l'apparence de ce qui est affiché (vidéo inversée, gras, souligné, etc.).

Quand il est mentionné « position actuelle du curseur », il s'agit de la dernière position où on a fait un affichage. Cette position peut cependant être modifiée par la méthode move(y,x).

addch() :

Se comporte exactement comme addstr, mais sert à positionner un caratère uniquement, et non une chaîne.

Ce caractère peut-être soit une chaîne Python de longueur 1, ou un entier, qui représente le code ASCII du caractère à afficher.

Premier exemple

Le programme suivant va placer la phrase « ça marche! » à la position (x,y)=(12,5).

      # -*- coding: utf-8 -*-
      import curses            # Importation de la bibliothèque curses
      ecran = curses.initscr() # Créer une structure ecran
      ecran.addstr(5,12,"ça marche")
      ecran.getch()            # Attente frappe clavier
      curses.endwin()          # Terminer proprement
    

Exercices

Les corrigés se trouvent au Chapitre 9, Introduction à Ncurses, correction.

  1. La position en haut à gauche est (0,0). Placez-y une étoile (« * »).

  2. La taille de l'écran s'obtient par la méthode getmaxyx() qui renvoie un couple de valeurs. Placez une étoile dans chaque coin de l'écran.

  3. Placez une ligne horizontale de 20 étoiles au centre de l'écran.

  4. Placez un rectangle composé d'étoiles, 20 colonnes sur 10 lignes, au centre de l'écran.

  5. Même exercice que le précédent, mais le rectangle est creux (on ne voit que les bords).

  6. Dessinez un disque composé d'étoiles, de rayon 10, au centre de l'écran.

    Rappels :

    • Un disque de rayon R est l'ensemble des points du plan dont la distance au centre du cercle est inférieure ou égale à R.

    • La distance entre deux points de coordonnées (a,b) et (c,d) est la racine carrée de (a-c)2+(b-d)2.

  7. Amélioration : tracez seulement le cercle (pas l'intérieur du disque).

  8. Variation : tracez le plus grand cercle qu'il soit possible d'inscrire dans l'écran.

Chapitre 4. Ncurses et le clavier

La méthode getch()

La façon la plus simple de permettre à un programme utilisant Ncurses de tenir compte des frappes au clavier est d'utiliser la méthode getch() : elle renvoie comme résultat le code ASCII (entre 0 et 255) du caractère correspondant à la touche frappée.

Les codes supérieurs à 255 correspondent aux touches spéciales (touches de fonctions, flèches, etc.). Voir la section intitulée «  Les codes de retour de getch()  » pour les principaux codes correspondant à ces touches.

D'autres méthodes

D'autres méthodes peuvent être utiles :

nodelay() :

Si on n'utilise pas cette fonction, le programme s'arrête quand il rencontre getch() et attend la frappe au clavier pour reprendre.

On l'utilise avec un paramètre :

1 ou True :

Pour supprimer l'attente : le programme se contente de scruter le clavier et il continue, qu'une touche soit enfoncée ou non (la valeur renvoyée quand il n'y a pas de touche enfoncée est ERR, c'est-à-dire -1).

0 ou False :

Pour activer l'attente.

C'est l'état par défaut.

clear() :

Efface l'écran.

echo() et noecho() :

La fonction echo() (resp. noecho()) active (resp. inhibe) l'écho à l'écran de la touche frappée.

keypad() :

Permet de transformer l'appui sur une touche de fonction en un seul code de caractère.

On l'utilise avec un paramètre :

1 ou True :

L'utilisation d'une touche de fonction correspond à un seul code caractère. Voir la section intitulée «  Les codes de retour de getch()  » pour les principaux codes correspondant à ces touches.

0 ou False :

L'utilisation d'une touche de fonction correspond à l'envoi d'une suite de plusieurs codes caractères, beaucoup plus difficile à interpréter.

C'est l'état par défaut.

Les codes de retour de getch()

Les codes renvoyés par getch() lors de l'appui d'une touche sont des entiers. il est assez difficile de se souvenir de tous ces nombres. C'est pour cela qu'on utilise des constantes dont les noms sont choisis pour être facilement mémorisables.

Pour les utiliser dans un programme on écrira par exemple « curses.KEY_F2 » pour désigner la touche F2.

En voici quelques exemples. Pour une liste plus complète voir, entre autres, Curses, constants . (D'autres liens se trouvent Annexe C, D'autres sources d'information.)

CodeSignification
KEY_DOWNFlèche bas
KEY_UPFlèche haut
KEY_LEFTFlèche gauche
KEY_RIGHTFlèche droite
KEY_FnTouche de fonction Fn (valeurs possibles de n : de 1 à 64)

Un exemple

      while 1:
          c = ecran.getch()
          if c == ord('p'): ecran.addstr(10,10,"Touche P")
          elif c == ord('q'): break  # Sort de la boucle
          elif c == curses.KEY_HOME: ecran.clear()
    

Ce que fait cet extrait de programme :

Il s'agit d'une boucle (potentiellement) sans fin (while 1).

Si on appuie sur la touche p, il s'écrit « Touche P » à l'écran.

Si on appuie sur la touche q, on sort de la boucle.

Si on appuie sur la touche Home, on efface l'écran.

Tout autre touche est sans effet.

Exercices

Les corrigés se trouvent au Chapitre 10, Ncurses et le clavier, correction.

  1. Écrire un programme qui commence par afficher une étoile en haut à gauche de l'écran, puis la déplace vers le bas à droite (direction Sud Est) à chaque fois qu'on appuie sur une touche.

    On sortira du programme en appuyant sur la touche « q ».

    On ne prendra pas la peine de contrôler la sortie de l'écran, on s'occupera de cela plus tard : si l'affichage sort, le programme plante ; ce n'est pas important pour l'instant. Mais dans ce cas n'oubliez de taper « reset » à l'aveugle pour retouver un terminal en bon état.

  2. Écrire un programme qui affiche en alternance au même endroit sur l'écran l'une des deux phrases « Il marche » et « Il ne marche plus ». Le passage de l'un à l'autre se fera par l'appui sur la touche d'espacement, la sortie du programme se fera proprement par l'appui sur la touche « q ».

  3. Écrire un programme qui déplace le curseur à l'écran en utilisant les flèches du clavier.

    On ne prendra pas la peine de contrôler la sortie de l'écran dans cette première version.

  4. Améliorez le programme précédent en contrôlant la sortie de l'écran : si l'utilisateur essaie de faire sortir le curseur, rien ne se passe.

  5. Écrire un programme qui permet de contrôler au clavier un compteur. On sortira du programme par l'appui sur la touche « q ».

    Les flèches haut et bas feront varier le compteur de 1 (en plus ou en moins), tandis que les touches page suivante et page précédente feront varier le compteur de 100.

Chapitre 5. Tout se complique...

Nous allons dans ce chapitre devoir affronter des exercices plus conséquents...

Les corrigés se trouvent au Chapitre 11, Tout se complique, correction.

  1. Dessinez à l'écran à l'aide du clavier :

    • On se déplace à l'écran avec les flèches.

    • À tout moment l'appui de la touche d'espacement change l'état de la case sur laquelle on est : si elle est occupée, elle devient vide ; si elle est vide, elle devient occupée (par exemple par une étoile, mais vous pouvez choisir autre chose).

    • On sort du programme par l'appui sur la touche « q ».

  2. Utilisation de la méthode de Monte-Carlo pour une évaluation de la valeur de π. Ou comment utiliser le hasard pour calculer π.

    On répête un tirage aléatoire des coordonnées d'un point dans un carré de 1 de côté (voir image ci-dessous). On compte à la fois le nombre de tirages faits, et le nombre de fois où le point obtenu se touve à l'intérieur du quart de disque de rayon 1 centré en (0,0), représenté en gris dans l'image ci-dessous.

    C_n^p

    Quel rapport avec π direz-vous.

    Il est immédiat. L'aire du disque de rayon 1 est πR2. Donc celle du quart de disque est de π/4. Tandis que l'aire du carré est de 1.

    Si les tirages sont uniformément répartis[1], le rapport entre le nombre de points dans le quart de disque et le nombre de tirages doit être π/4.

    Il suffit de multiplier ce rapport par 4 pour obtenir une approximation de π. Aussi simple que ça.

    Eh bien maintenant, au boulot ! Il faudra que votre affichage ressemble à ceci :

    	728 tirages
    	pi=3.18131868132
    	ecart=0.0397260277289
          

    À chaque nouveau tirage, l'affichage est mis à jour.

    Rappels (?) :

    • Pour avoir la « vraie » valeur de π, il faut utiliser math.pi, en ayant d'abord importé la bibliothèque math.

    • Pour avoir une valeur aléatoire entre 0 et 1, il faut utiliser random.random(), en ayant d'abord importé la bibliothèque random.

    • La distance du point de coordonnées (x,y) au points de coordonnées (0,0) est la racine carrée de x2+y2. Donc pour être dans le disque il suffit que x2+y2<1



[1] Ce qui, reconnaissons le, est loin d'être garanti. Mais on fera semblant d'y croire…

Chapitre 6. Pour qui sont ces serpents ?[2]

Les corrigés se trouvent au Chapitre 12, Pour qui sont ces serpents, correction.

Nous allons dans ce chapitre réaliser un jeu simple, mais qui fait partie de l'histoire des jeux vidéo… (Certains diront peut-être préhistoire !). Il s'agit de centipede.

Vous pourrez trouver sur le web beaucoup de détails sur ce jeu. Mais en voici quelques mots en attendant : il s'agit du déplacement d'une sorte de serpent[3]. Ce serpent se déplace dans un espace clos. La tête en se déplaçant ne doit jamais recouper le reste du corps. Tout serait très simple si la longueur du serpent n'augmentait pas continuellement, rendant les manœuvres de plus en plus difficiles…

Première version simpliste

Dans cette première version le serpent n'aura pas de corps. On déplacera donc seulement sa tête.

Le mode de déplacement de la tête est simple :

  • la tête est en déplacement continu, droit devant elle ;

  • l'utilisateur ne peut que la faire tourner à droite ou à gauche ;

Les changements de direction se feront par appui sur les flèches du clavier.

Dans cette première version on ne cherchera pas à vérifier que la tête ne touche pas les bords de l'écran. Si cela arrive le programme plante, tant pis !

Attention aux bords

Dans cette deuxième version on fera attention à ne pas percuter les bords.

Pour cela on commencera par matérialiser les bords autour de l'écran.

On fera en sorte de faire afficher un message annonçant à l'utilisateur qu'il a perdu en cas de contact avec les bords (et le jeu s'arrêtera proprement bien entendu).

Un vrai serpent

Nous allons commencer à donner son apparence définitive au serpent : il ne faut plus se contenter d'une tête ; il faut lui donner un corps.

Grandir

Étape suivante : le serpent grandit avec le temps qui passe, par exemple d'une cellule tous les 5 tours (ou plus, ou moins, à vous de choisir…).

Enfin…

Enfin la dernière modification.

Votre programme doit vérifier que le serpent ne se mord pas la queue. La tête ne doit jamais recouper le corps.

Si cela arrivait, perdu !

Améliorations éventuelles

Vous pouvez prévoir de décompter le temps, d'ajouter des obstacles, d'inclure de la nourriture pour votre serpent, etc.

Ces modifications ne seront pas corrigées.



[2] « Pour qui sont ces serpents qui sifflent sur vos têtes ? » Racine, Andromaque, V, 8

[3] En fait centipede est le mot anglais qui signifie mille pattes.

Correction des exercices

Chapitre 7. Un peu d'algorithmique, correction

Correction des exercices du Chapitre 1, Échauffement : un peu d'algorithmique.

  1.         for i in range (21,146):
                print i,
          
  2.         for i in range (1,251):
                print '*',
          

    On aurait aussi pu écrire :

            print 250*'*'
          
  3.         for i in range (1,501):
                print i,'Je dois faire des sauvegardes régulières de mes fichiers'
          
  4.         s = 0
            for i in range (21,146):
                s += i
            print 'somme =',s
          
  5.         f = 1
            for i in range (1,36):
                f *= i
            print '36! =',f
          
  6.         for i in range (1,21):
                for j in range (1,i+1):
                    print '*',
                print
          

    On aurait aussi pu écrire :

            for i in range (1,21):
                print i*'*'
          
  7.         for i in range (1,14):
                print i,'x 13 =',i*13
          
  8.         for i in range (1,11):
                for j in range (1,11):
                    print i*j,'\t',
                print
          

    ou, mieux :

            print '\t  ',
            for j in range (1,11):
                print j,'\t  ',
            print
            print '\t',
            for j in range (1,11):
                print '-----\t',
            print
            for i in range (1,11):
                print '  ',i,'\t|',
                for j in range (1,11):
                    print i*j,'\t  ',
                print
          
  9.         nbMoutons = input(" Entrez le nombre de moutons : ")
            nbVaches = input(" Entrez le nombre de vaches : ")
            valeur = nbVaches * 9000 + nbMoutons * 5000
            print "La valeur du troupeau est de ", valeur
          
  10.         def saisie (texte):
                h = input("Horaire n%s.   heures : " %texte)
                m = input("             minutes : ")
                s = input("            secondes : ")
                return h,m,s
            
            def convertir(h,m,s):
                return h*3600+m*60+s
            
            h1,m1,s1 = saisie (1)
            h2,m2,s2 = saisie (2)
            horaire1 = convertir(h1,m1,s1)
            horaire2 = convertir(h2,m2,s2)
            if horaire1 < horaire2:
                print h1,'h',m1,'mn',s1,'s  est avant ', h2,'h',m2,'mn',s2,'s'
            else:
                print h2,'h',m2,'mn',s2,'s  est avant ', h1,'h',m1,'mn',s1,'s'
          
  11.         # -*- coding: utf-8
            def saisie (texte):
                h = input("Horaire n%s.   heures : " %texte)
                m = input("             minutes : ")
                s = input("            secondes : ")
                return h,m,s
            
            def convertir(h,m,s):
                return h*3600+m*60+s
            
            h1,m1,s1 = saisie (1)
            h2,m2,s2 = saisie (2)
            horaire1 = convertir(h1,m1,s1)
            horaire2 = convertir(h2,m2,s2)
            print 'durée écoulée : '
            if horaire1 < horaire2:
                print horaire2-horaire1
            else:
                print horaire1-horaire2
          

    ou mieux :

            # -*- coding: utf-8 
            def saisie (texte):
                h = input("Horaire n%s.   heures : " %texte)
                m = input("             minutes : ")
                s = input("            secondes : ")
                return h,m,s
            
            def convertir(h,m,s):
                return h*3600+m*60+s
            
            h1,m1,s1 = saisie (1)
            h2,m2,s2 = saisie (2)
            print 'durée écoulée : ',abs( convertir(h1,m1,s1) - convertir(h2,m2,s2) )
          
  12.         n = input("un entier SVP : ")
            s = 0
            for i in range (1,n+1):
                s += i
            print 'La somme est',s
          

    ou :

            n = input("un entier SVP : ")
            print 'La somme est',n*(n+1)/2
          
  13.         # -*- coding: utf-8 
            def tchacatchac(v):
                heure = 9 + int(170 / v)
                minutes =  (60 * 170 / v) % 60
                print " À", v, "km/h, je me fais déchiqueter à ", heure, "h", minutes, "mn."
            
            i = 100
            while i <= 300:
                tchacatchac(i)
                i += 10
          
  14.         # -*- coding: utf-8 
            def PermisSup(p,c,v,a):
                pointsperdus = p+3*c+5*v+10*a
                nbrepermis = pointsperdus/100
                return 200*nbrepermis
            
            poules = input("Combien de poules ? ")
            chiens = input("Combien de chiens ? ")
            vaches = input("Combien de vaches ? ")
            amis   = input("   Combien d'amis ? ")
            print 'À payer : ',
            payer = PermisSup(poules,chiens,vaches,amis)
            if payer == 0:
                print "rien à payer"
            else:
                print payer,'euros'
          
  15.         # -*- coding: utf-8 
            def hauteurParcourue (nb,h):
                print "Pour",nb,"marches de",h,"cm, il parcourt",
                print nb*h*2*5*7/100,"mètres en une semaine"
            
            nbmarches = input("Combien de marches ? ")
            hauteurmarche = input("Hauteur (en centimètres) d'une marche ? ")
            hauteurParcourue (nbmarches,hauteurmarche)
          

Chapitre 8. Un peu plus d'algorithmique, correction

Correction des exercices du Chapitre 2, Un peu plus d'algorithmique.

  1.         i = input ("Entrez votre entier : ")
            
            while i <> -1:
                print "2 * ", i, " = ", 2*i 
                i = input ("Entrez un autre entier : ")
                
            print "FIN."
          
  2.         i = input ("Entrez votre entier : ")
            s = 0
            
            while i <> -1:
                s += i
                i = input ("Entrez un autre entier : ")
                
            print "La somme est :",s
          
  3.         n = input( "Entrez un entier :")
            max = n
            while n <> -1:
                if n > max:
                    max = n
                n = input ( "Entrez un autre entier : ")
            
            print "L'entier le plus grand de la suite est :",max
          
  4.         def fact(n):
                f = 1
                for i in range(1,n+1):
                    f *= i
                return f
            
            def cnp(n,p):
                return fact(n)/(fact(p)*fact(n-p))
            
            def coefficients(n):
                for i in range(0,n+1):
                    print cnp(n,i),
                print
            
            for i in range(0,21):
                coefficients(i)
          
  5.         def fib(n):
                if n == 0: return 0
                if n == 1: return 1
                moins2 = 0
                moins1 = 1
                for i in range(2,n+1):
                    f = moins1+moins2
                    moins2 = moins1
                    moins1 = f
                return f
            
            for i in range(0,50):
                print i,'\t',fib(i)
          
  6.         from math import sqrt
            
            def fib(n):
                if n == 0: return 0
                if n == 1: return 1
                moins2 = 0
                moins1 = 1
                for i in range(2,n+1):
                    f = moins1+moins2
                    moins2 = moins1
                    moins1 = f
                return f
            
            nbor = (sqrt(5)+1)/2
            print "nombre d'or :",nbor
            print
            for i in range(2,50):
                x = 1.0*fib(i)/fib(i-1)
                print i,'\t',x,'\t',x-nbor
          

Chapitre 9. Introduction à Ncurses, correction

Correction des exercices du Chapitre 3, Introduction à ncurses.

  1.         import curses
            ecran = curses.initscr()
            ecran.addch(0,0,"*")
            ecran.getch()      
            curses.endwin()
         
  2. Quand vous écrivez « y, x = ecran.getmaxyx() » n'oubliez pas que x et y représentent les dimensions de l'écran. Comme le système de coordonnées commmence à (0,0), on ne pourra donc atteindre que x-1 et y-1.

            import curses
            ecran = curses.initscr()
            y, x = ecran.getmaxyx()
            ecran.addch(0,0,"*")
            ecran.addch(y-1,0,"*")
            ecran.addch(0,x-1,"*")
            ecran.addch(y-1,x-2,"*")
            ecran.addch(y-2,x-1,"*")
            ecran.getch()      
            curses.endwin()
          

    Vous avez sans doute remarqué que la position en bas à droite ne peut pas être atteinte. C'est parce qu'après y avoir écrit le curseur sortirait de l'écran.

  3.         import curses
            ecran = curses.initscr()
            y, x = ecran.getmaxyx()
            ecran.move(y/2,x/2-10)
            for i in range (0,20):
                ecran.addch("*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          

    ou, mieux :

            import curses
            ecran = curses.initscr()
            y, x = ecran.getmaxyx()
            ecran.addstr(y/2,x/2-10,20*"*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          
  4.         import curses
            ecran = curses.initscr()
            y, x = ecran.getmaxyx()
            for i in range (0,10):
                ecran.addstr(y/2-5+i,x/2-10,20*"*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          
  5. En trichant :

            import curses
            ecran = curses.initscr()
            y, x = ecran.getmaxyx()
            for i in range (0,10):
                ecran.addstr(y/2-5+i,x/2-10,20*"*")
            for i in range (0,8):
                ecran.addstr(y/2-4+i,x/2-9,18*" ")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          

    Mieux :

            import curses
            ecran = curses.initscr()
            y, x = ecran.getmaxyx()
            ecran.addstr(y/2-5,x/2-10,20*"*")
            for i in range (1,9):
                ecran.addch(y/2-5+i,x/2-10,"*")
                ecran.addch(y/2-5+i,x/2+9,"*")
            ecran.addstr(y/2+4,x/2-10,20*"*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          
  6.         import curses
            ecran = curses.initscr()
            ymax, xmax = ecran.getmaxyx()
            x0,y0 = xmax/2,ymax/2
            for x in range (0,xmax):
                for y in range(0,ymax):
                    d = (x0-x)*(x0-x)+(y0-y)*(y0-y)
                    if d < 100:
                        ecran.addch(y,x,"*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          
  7.         import curses
            ecran = curses.initscr()
            ymax, xmax = ecran.getmaxyx()
            x0,y0 = xmax/2,ymax/2
            for x in range (0,xmax):
                for y in range(0,ymax):
                    d = (x0-x)*(x0-x)+(y0-y)*(y0-y)
                    if d <= 108 and d >= 92 :
                        ecran.addch(y,x,"*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          
  8.         import curses
            ecran = curses.initscr()
            ymax, xmax = ecran.getmaxyx()
            rayon=min(xmax,ymax)/2-1
            x0,y0 = xmax/2,ymax/2
            inf=rayon*rayon-8
            sup=rayon*rayon+8
            for x in range (0,xmax):
                for y in range(0,ymax):
                    d = (x0-x)*(x0-x)+(y0-y)*(y0-y)
                    if d <= sup and d >= inf :
                        ecran.addch(y,x,"*")
            ecran.move(0,0)    
            ecran.getch()      
            curses.endwin()
          

Chapitre 10. Ncurses et le clavier, correction

Correction des exercices du Chapitre 4, Ncurses et le clavier.

  1.         import curses
            
            ecran = curses.initscr()
            curses.noecho()
            c = ''
            pos = 0;
            
            while c<>ord('q') :
                ecran.clear()
                ecran.addch(pos,pos,'*')
                c = ecran.getch()
                pos += 1
            
            curses.echo()
            curses.endwin()
         
  2.         import curses
            
            ecran = curses.initscr()
            curses.noecho()
            c = ''
            etat = True
            
            while c<>ord('q') :
                ecran.clear()
                if etat :
                    ecran.addstr(10,10,'il marche')
                else :
                    ecran.addstr(10,10,'il ne marche plus')
                c = ecran.getch()
                if c == ord(' ') :
                    etat = not etat
            
            curses.echo()
            curses.endwin()
         
  3.         import curses
            
            ecran  = curses.initscr()
            curses.noecho()
            ecran.keypad(1)
            
            ecran.clear()
            x = 20
            y = 20
            while 1 :
                ecran.clear()
                ecran.move(y,x)
                c = ecran.getch()
                if c == curses.KEY_DOWN : y += 1
                elif c == curses.KEY_UP : y -= 1
                elif c == curses.KEY_LEFT : x -= 1
                elif c == curses.KEY_RIGHT : x += 1
                elif c == ord('q') : break 
            
            curses.echo()
            curses.endwin()
          
  4.         import curses
            
            ecran = curses.initscr()
            curses.noecho()
            ecran.keypad(1)
            
            ecran.clear()
            ymax,xmax = ecran.getmaxyx()
            x = 20
            y = 20
            while 1 :
                ecran.clear()
                if x > xmax-1 : x = xmax-1
                if x < 0 : x = 0
                if y > ymax-1 : y = ymax-1
                if y < 0 : y = 0
                ecran.move(y,x)
                c = ecran.getch()
                if c == curses.KEY_DOWN : y = y+1
                elif c == curses.KEY_UP : y = y-1
                elif c == curses.KEY_LEFT : x = x-1
                elif c == curses.KEY_RIGHT : x = x+1
                elif c == ord('q') : break 
            
            curses.echo()
            curses.endwin()
         
  5.         import curses
            
            ecran = curses.initscr()
            curses.noecho()
            ecran.keypad(1)
            
            ecran.clear()
            compteur=0
            while 1:
                ecran.clear()
                ecran.addstr(10,10,str(compteur))
                c = ecran.getch()
                if c == curses.KEY_DOWN: compteur -= 1
                elif c == curses.KEY_UP: compteur += 1
                elif c == curses.KEY_NPAGE: compteur -= 100
                elif c == curses.KEY_PPAGE: compteur += 100
                elif c == ord('q'): break 
            
            curses.echo()
            curses.endwin()
          

Chapitre 11. Tout se complique, correction

Correction des exercices du Chapitre 5, Tout se complique....

  1.         import curses
            
            def deplacement(touche,x,y):                  1
                if touche == curses.KEY_DOWN : y += 1
                elif touche == curses.KEY_UP : y -= 1
                elif touche == curses.KEY_LEFT : x -= 1
                elif touche == curses.KEY_RIGHT : x += 1
                return x,y
            
            def controle(x,y):                            2
                if x > xmax-1 : x = xmax-1
                if x < 0 : x=0
                if y > ymax-1 : y = ymax-1
                if y < 0 : y=0
                return x,y
            
            def dessine (x,y):                            3
                if ecran.inch(y,x) == ord(' '):
                    ecran.addch (y,x,'*')
                else:
                    ecran.addch (y,x,' ')
                ecran.move (y,x)
            
            ecran = curses.initscr()
            curses.noecho()
            ecran.keypad(1)
            
            ecran.clear()                                 4
            ymax,xmax = ecran.getmaxyx()
            x = 20
            y = 20
            while 1:
                x,y = controle (x,y) 
                ecran.move(y,x)
                c = ecran.getch()
                if c == ord('q') : break 
                elif c == ord(' ') : dessine (x,y)
                else : x,y = deplacement (c,x,y)
                
            curses.echo()
            curses.endwin()
          

    On remarquera dans ce programme l'utilisation de procédures et fonctions :

    1

    pour le calcul de la nouvelle position du curseur après un éventuel appui sur une des flèches de déplacement ;

    2

    pour le contrôle de la position du curseur ; on l'empêche de sortir des limites imposées ;

    3

    pour changer le dessin de ce qu'il y a sous le curseur : étoile si rien, rien si étoile ;

    4

    corps du programme.

    Elles ne sont pas là pour éviter de répêter le même code à plusieurs endroits du programme (chaque fonction ou procédure n'est utilisée qu'une fois), ce qui est une des principales raisons possibles de leur utilisation.

    Ici elles sont utilisées pour rendre le code plus clair : le corps 4 du programme est plus facile à lire et à comprendre, car il est plus court.

    Vous pouvez comparer avec la version suivante, sans fonction ni procédure, mono-bloc et plus difficile à analyser :

            import curses
            
            ecran = curses.initscr()
            curses.noecho()
            ecran.keypad(1)
            
            ecran.clear()
            ymax,xmax = ecran.getmaxyx()
            x = 20
            y = 20
            while 1:
                if x > xmax-1 : x = xmax-1
                if x < 0 : x=0
                if y > ymax-1 : y = ymax-1
                if y < 0 : y=0
                ecran.move(y,x)
                c = ecran.getch()
                if c == ord('q') : break 
                elif c == ord(' ') :
                    if ecran.inch(y,x) == ord(' '):
                        ecran.addch (y,x,'*')
                    else:
                        ecran.addch (y,x,' ')
                        ecran.move (y,x)
                else :
                    if c == curses.KEY_DOWN : y += 1
                    elif c == curses.KEY_UP : y -= 1
                    elif c == curses.KEY_LEFT : x -= 1
                    elif c == curses.KEY_RIGHT : x += 1
                
            curses.echo()
            curses.endwin()
          

    Cette version reste encore presque facile à comprendre (ce programme n'est pas très long), mais on commence à avoir du mal à y trouver une décomposition logique, alors que c'était immédiat dans la première version.

  2.         import random
            import curses
            import math
            
            ecran = curses.initscr()
            curses.noecho()
            ecran.nodelay(1)
            
            tirages=0
            disque=0
            
            while 1:
                if ecran.getch() == ord('q') : break 
                x = random.random()
                y = random.random()
                tirages += 1
                if x*x+y*y <= 1:
                    disque += 1
                monpi = 4.0*disque/tirages
                ecran.clear()
                ecran.addstr(5,5,str(tirages)+" tirages")
                ecran.addstr(6,5,"pi="+str(monpi))
                ecran.addstr(7,5,"ecart="+str(abs(monpi-math.pi)))
            
            curses.echo()
            curses.endwin()
         

Chapitre 12. Pour qui sont ces serpents, correction

Correction des exercices du Chapitre 6, Pour qui sont ces serpents ?.

Première version simpliste

# -*- coding: utf-8 -*-
import curses  # pour le dessin
import time    # pour la mesure du temps d'attente

# renvoie la nouvelle direction après appui sur la touche "DROITE"
def droite() :
    if direction == DROITE : return BAS
    elif direction == BAS : return GAUCHE
    elif direction == GAUCHE : return HAUT
    else : return DROITE

# renvoie la nouvelle direction après appui sur la touche "GAUCHE"
def gauche() :
    if direction == DROITE : return HAUT
    elif direction == HAUT : return GAUCHE
    elif direction == GAUCHE : return BAS
    else : return DROITE

# calcule la nouvelle position et place le serpent
def deplace() :
    l = ligne + direction[LGN]
    c = colonne + direction[COL]
    ecran.addch(l,c,'O')
    return l,c
   
ecran = curses.initscr() # intialisation de l'écran
ecran.nodelay(True)      # pas d'attente sur getch()
ecran.keypad(True)       # UN code sur les touches de fonctions
curses.curs_set(False)   # cacher le curseur

# définitions des directions de déplacement
DROITE = (0,1)
GAUCHE = (0,-1)
HAUT = (-1,0)
BAS = (1,0)

# indices pour les accès aux éléments d'un couple de  coordonénes
LGN = 0
COL = 1

# initialisations du positionnement du serpent
hauteur, largeur = ecran.getmaxyx()  # taille de l'écran
ligne,colonne = hauteur/3,largeur/3  # position (de départ : arbitraire!)
direction = DROITE                   # directon (de départ : arbitraire!)

c=''                    # pour que c existe à la ligne suivante
while c <> ord('q'):    # tant qu'on n'a pas appuyé sur la touche 'q'
    ecran.clear()       # effacer l'écran
    ligne,colonne = deplace()
                        # calcule la nouvelle position et place le serpent
    time.sleep(0.1)     # attente pour ralentir (arbitraire!)
    c=ecran.getch()     # saisie de l'appui sur une touche du clavier
    if c == curses.KEY_LEFT: direction = gauche()
                        # on tourne à gauche
    elif c == curses.KEY_RIGHT: direction = droite()
                        # on tourne à droite

# remettre l'écran en "bon" état
curses.endwin()               # sortie du mode "curses"
print "\n\n\tF I N I !\n\n"   # message de fin!
  

Attention aux bords

# -*- coding: utf-8 -*-
import curses  # pour le dessin
import time    # pour la mesure du temps d'attente

# renvoie la nouvelle direction après appui sur la touche "DROITE"
def droite() :
    if direction == DROITE : return BAS
    elif direction == BAS : return GAUCHE
    elif direction == GAUCHE : return HAUT
    else : return DROITE

# renvoie la nouvelle direction après appui sur la touche "GAUCHE"
def gauche() :
    if direction == DROITE : return HAUT
    elif direction == HAUT : return GAUCHE
    elif direction == GAUCHE : return BAS
    else : return DROITE

# calcule la nouvelle position et place le serpent
def deplace() :
    l = ligne + direction[LGN]
    c = colonne + direction[COL]
    ecran.addch(l,c,'O')
    return l,c

# ===================================
#              NOUVEAU
# ===================================
# teste si on a touché les bords
def touche() :
    return (ligne <= 0) or (colonne <= 0) or (ligne >= hauteur-1) or (colonne >= largeur-1)

# affiche un message (s) au centre de l'écran
def message(s) :
    vide = (largeur-len(s))/2                   # longueur libre de chaque côté du message
    debut = hauteur/2 - 2                       # hauteur d'affichage du message
    ecran.addstr(debut  ,0,largeur*"=")         # un trait au-dessus
    ecran.addstr(debut+1,0,vide*" "+s+vide*" ") # message au centre de la ligne
    ecran.addstr(debut+2,0,largeur*"=")         # un trait au-dessous
    ecran.nodelay(False)                        # attente sur les getch()
    ecran.getch()                               # pour attendre...
    
# dessine le bord du terrain
def bord():
    ecran.addstr(0,0,largeur*"+")          # bord du haut
    ecran.addstr(hauteur-1,0,largeur*"+")  # bord du bas
    for lg in range(1,hauteur-1):          # les côtés :
        ecran.addstr(lg,0,"+")             #    à gauche   
        ecran.addstr(lg,largeur-1,"+")     #    à droite
# ===================================

    
ecran = curses.initscr() # intialisation de l'écran
ecran.nodelay(True)      # pas d'attente sur getch()
ecran.keypad(True)       # UN code sur les touches de fonctions
curses.curs_set(False)   # cacher le curseur

# définitions des directions de déplacement
DROITE = (0,1)
GAUCHE = (0,-1)
HAUT = (-1,0)
BAS = (1,0)

# indices pour les accès aux éléments d'un couple de  coordonénes
LGN = 0
COL = 1

# initialisations du positionnement du serpent
hauteur, largeur = ecran.getmaxyx() # taille de l'écran
ligne,colonne = hauteur/3,largeur/3 # position (de départ : arbitraire!)
direction = DROITE                  # directon (de départ : arbitraire!)
largeur -= 1                        # NOUVEAU : pour ne pas accéder à la dernière colonne
                                    # (bord : pb d'affichage dans coin en bas à droite)


c=''                          # pour que c existe à la ligne suivante
while c <> ord('q'):          # tant qu'on n'a pas appuyé sur la touche 'q'
    ecran.clear()             # effacer l'écran
    bord()                    # dessin du bord du terrain
    ligne,colonne = deplace() # calcule la nouvelle position et place le serpent
    if touche() :             # si on touche le bord ...
        message ('Perdu !')   #   ... affichage du message "Perdu!"
        break                 #   ... sortie de la boucle while (donc fin du programme)
    time.sleep(0.1)           # attente pour ralentir (arbitraire!)
    c=ecran.getch()           # saisie de l'appui sur une touche du clavier
    if c == curses.KEY_LEFT: direction = gauche()
                              # on tourne à gauche
    elif c == curses.KEY_RIGHT: direction = droite()
                              # on tourne à droite

# remettre l'écran en "bon" état
curses.endwin()               # sortie du mode "curses"
print "\n\n\tF I N I !\n\n"   # message de fin!
  

Un vrai serpent

# -*- coding: utf-8 -*-
import curses  # pour le dessin
import time    # pour la mesure du temps d'attente

# renvoie la nouvelle direction après appui sur la touche "DROITE"
def droite() :
    if direction == DROITE : return BAS
    elif direction == BAS : return GAUCHE
    elif direction == GAUCHE : return HAUT
    else : return DROITE

# renvoie la nouvelle direction après appui sur la touche "GAUCHE"
def gauche() :
    if direction == DROITE : return HAUT
    elif direction == HAUT : return GAUCHE
    elif direction == GAUCHE : return BAS
    else : return DROITE

# calcule la nouvelle position et place le serpent
def deplace() :
    serpent.append((ligne,colonne))    # NOUVEAU : ajoute la position actuelle de 
                                       # la tête en fin de la liste des positions
    l = ligne + direction[LGN]
    c = colonne + direction[COL]
    for z in serpent:                  # NOUVEAU : pour toutes les cases du serpent ...
        ecran.addch(z[LGN],z[COL],'*') #   ... afficher la case
    ecran.addch(l,c,'O')
    return l,c

# teste si on a touché les bords
def touche() :
    return (ligne <= 0) or (colonne <= 0) or (ligne >= hauteur-1) or (colonne >= largeur-1)

# affiche un message (s) au centre de l'écran
def message(s) :
    vide = (largeur-len(s))/2                   # longueur libre de chaque côté du message
    debut = hauteur/2 - 2                       # hauteur d'affichage du message
    ecran.addstr(debut  ,0,largeur*"=")         # un trait au-dessus
    ecran.addstr(debut+1,0,vide*" "+s+vide*" ") # message au centre de la ligne
    ecran.addstr(debut+2,0,largeur*"=")         # un trait au-dessous
    ecran.nodelay(False)                        # attente sur les getch()
    ecran.getch()                               # pour attendre...
    
# dessine le bord du terrain
def bord():
    ecran.addstr(0,0,largeur*"+")          # bord du haut
    ecran.addstr(hauteur-1,0,largeur*"+")  # bord du bas
    for lg in range(1,hauteur-1):          # les côtés ...
        ecran.addstr(lg,0,"+")             #   ... à gauche   
        ecran.addstr(lg,largeur-1,"+")     #   ... à droite

    
ecran = curses.initscr() # intialisation de l'écran
ecran.nodelay(True)      # pas d'attente sur getch()
ecran.keypad(True)       # UN code sur les touches de fonctions
curses.curs_set(False)   # cacher le curseur
serpent = []             # création du serpent (liste) vide
longueur = 15            # taille du serpent

# définitions des directions de déplacement
DROITE = (0,1)
GAUCHE = (0,-1)
HAUT = (-1,0)
BAS = (1,0)

# indices pour les accès aux éléments d'un couple de  coordonénes
LGN = 0
COL = 1

# initialisations du positionnement du serpent
hauteur, largeur = ecran.getmaxyx() # taille de l'écran
ligne,colonne = hauteur/3,largeur/3 # position (de départ : arbitraire!)
direction = DROITE                  # directon (de départ : arbitraire!)
largeur -= 1                        # pour ne pas accéder à la dernière colonne
                                    # (bord : pb d'affichage dans coin en bas à droite)


c=''                              # pour que c existe à la ligne suivante
while c <> ord('q'):              # tant qu'on n'a pas appuyé sur la touche 'q'
    ecran.clear()                 # effacer l'écran
    bord()                        # dessin du bord du terrain
    ligne,colonne = deplace()     # calcule la nouvelle position et place le serpent
    serpent = serpent[-longueur:] # NOUVEAU : ne garde que la fin du serpent (taille invariable)
    if touche() :                 # si on touche le bord ...
        message ('Perdu !')       #   ... affichage du message "Perdu!"
        break                     #   ... sortie de la boucle while (donc fin du programme)
    time.sleep(0.1)               # attente pour ralentir (arbitraire!)
    c=ecran.getch()               # saisie de l'appui sur une touche du clavier
    if c == curses.KEY_LEFT: direction = gauche()
                                  # on tourne à gauche
    elif c == curses.KEY_RIGHT: direction = droite()
                                  # on tourne à droite

# remettre l'écran en "bon" état
curses.endwin()                   # sortie du mode "curses"
print "\n\n\tF I N I !\n\n"       # message de fin!
  

Grandir

Peu de changement entre ces deux versions. Vous trouverez ci-dessous une partie du programme, celle qui contient les changements (les lignes sont indiquées par un commentaire).

# initialisations du positionnement du serpent
hauteur, largeur = ecran.getmaxyx() # taille de l'écran
ligne,colonne = hauteur/3,largeur/3 # position (de départ : arbitraire!)
direction = DROITE                  # directon (de départ : arbitraire!)
largeur -= 1                        # pour ne pas accéder à la dernière colonne
                                    # (bord : pb d'affichage dans coin en bas à droite)

compteur=0                          # NOUVEAU : compteur  entre 2 croissance du serpent

c=''                              # pour que c existe à la ligne suivante
while c <> ord('q'):              # tant qu'on n'a pas appuyé sur la touche 'q'
    ecran.clear()                 # effacer l'écran
    bord()                        # dessin du bord du terrain
    ligne,colonne = deplace()     # calcule la nouvelle position et place le serpent
    serpent = serpent[-longueur:] # ne garde que la fin du serpent (taille invariable)
    compteur += 1                 # NOUVEAU : un tour de plus
    if compteur > 5:              # NOUVEAU : si on passe 5 tours ...
        compteur = 0              # NOUVEAU :   ... remise du compteur à zéro
        longueur += 1             # NOUVEAU :   ... fait grandir le serpent
    if touche() :                 # si on touche le bord ...
        message ('Perdu !')       #   ... affichage du message "Perdu!"
        break                     #   ... sortie de la boucle while (donc fin du programme)
  

Enfin…

Il suffit de modifier légérement la fonction toucher().

# NOUVEAU : la fonction est réécrite entièrement
# teste si on a touché les bords ou le corps
def touche() :
    # si la tête du serpent touche le bord 
    if (ligne <= 0) or (colonne <= 0) or (ligne >= hauteur-1) or (colonne >= largeur-1) :
        return True    # renvoyer VRAI (= on a perdu)
    # pour toutes les cases du serpent
    for z in serpent:
        if z == (ligne,colonne) : # si la case est au même endroit que la tête ...
            return True           #   ... renvoyer VRAI (= on a perdu)
    # si on arrive ici, c'est qu'il n'y a pas eu de problème
    return False       # renvoyer FAUX (= pas de problème)
  

En prime

En cadeau une version complète, légérement modifiée, qui utilise différemment les flèches de direction. L'appui sur une des quatre flèches de direction fait tourner le serpent dans cette direction de façon absolue : par exemple, si on appuie sur la flèche HAUT le serpent part vers le haut.

# -*- coding: utf-8 -*-
import curses  # pour le dessin
import time    # pour la mesure du temps d'attente

# renvoie la nouvelle direction après appui sur la touche ""
def bas() :
    if (direction == DROITE) or (direction==GAUCHE) :
        return BAS
    return direction

# renvoie la nouvelle direction après appui sur la touche "haut"
def haut() :
    if (direction == DROITE) or (direction==GAUCHE) :
        return HAUT
    return direction

# renvoie la nouvelle direction après appui sur la touche "droite"
def droite() :
    if (direction == HAUT) or (direction==BAS) :
        return DROITE
    return direction

# renvoie la nouvelle direction après appui sur la touche "gauche"
def gauche() :
    if (direction == HAUT) or (direction==BAS) :
        return GAUCHE
    return direction

# calcule la nouvelle position et place le serpent
def deplace() :
    serpent.append((ligne,colonne))    # ajoute la position actuelle de la tête
                                       # en fin de la liste des positions
    l = ligne + direction[LGN]
    c = colonne + direction[COL]
    for z in serpent:                  # pour toutes les cases du serpent ...
        ecran.addch(z[LGN],z[COL],'*') #   ... afficher la case
    ecran.addch(l,c,'O')
    return l,c

# NOUVEAU : la fonction est réécrite entièrement
# teste si on a touché les bords ou le corps
def touche() :
    # si la tête du serpent touche le bord 
    if (ligne <= 0) or (colonne <= 0) or (ligne >= hauteur-1) or (colonne >= largeur-1) :
        return True    # renvoyer VRAI (= on a perdu)
    # pour toutes les cases du serpent
    for z in serpent:
        if z == (ligne,colonne) : # si la case est au même endroit que la tête ...
            return True           #   ... renvoyer VRAI (= on a perdu)
    # si on arrive ici, c'est qu'il n'y a pas eu de problème
    return False       # renvoyer FAUX (= pas de problème)

# affiche un message (s) au centre de l'écran
def message(s) :
    vide = (largeur-len(s))/2                   # longueur libre de chaque côté du message
    debut = hauteur/2 - 2                       # hauteur d'affichage du message
    ecran.addstr(debut  ,0,largeur*"=")         # un trait au-dessus
    ecran.addstr(debut+1,0,vide*" "+s+vide*" ") # message au centre de la ligne
    ecran.addstr(debut+2,0,largeur*"=")         # un trait au-dessous
    ecran.nodelay(False)                        # attente sur les getch()
    ecran.getch()                               # pour attendre...
    
# dessine le bord du terrain
def bord():
    ecran.addstr(0,0,largeur*"+")          # bord du haut
    ecran.addstr(hauteur-1,0,largeur*"+")  # bord du bas
    for lg in range(1,hauteur-1):          # les côtés ...
        ecran.addstr(lg,0,"+")             #   ... à gauche   
        ecran.addstr(lg,largeur-1,"+")     #   ... à droite

    
ecran = curses.initscr() # intialisation de l'écran
ecran.nodelay(True)      # pas d'attente sur getch()
ecran.keypad(True)       # UN code sur les touches de fonctions
curses.curs_set(False)   # cacher le curseur
serpent = []             # création du serpent (liste) vide
longueur = 15            # taille du serpent

# définitions des directions de déplacement
DROITE = (0,1)
GAUCHE = (0,-1)
HAUT = (-1,0)
BAS = (1,0)

# indices pour les accès aux éléments d'un couple de  coordonénes
LGN = 0
COL = 1

# initialisations du positionnement du serpent
hauteur, largeur = ecran.getmaxyx() # taille de l'écran
ligne,colonne = hauteur/3,largeur/3 # position (de départ : arbitraire!)
direction = DROITE                  # directon (de départ : arbitraire!)
largeur -= 1                        # pour ne pas accéder à la dernière colonne
                                    # (bord : pb d'affichage dans coin en bas à droite)

compteur=0                          # compteur  entre 2 croissance du serpent

c=''                              # pour que c existe à la ligne suivante
while c <> ord('q'):              # tant qu'on n'a pas appuyé sur la touche 'q'
    ecran.clear()                 # effacer l'écran
    bord()                        # dessin du bord du terrain
    ligne,colonne = deplace()     # calcule la nouvelle position et place le serpent
    serpent = serpent[-longueur:] # ne garde que la fin du serpent (taille invariable)
    compteur += 1                 # un tour de plus
    if compteur > 5:              # si on passe 5 tours ...
        compteur = 0              #   ... remise du compteur à zéro
        longueur += 1             #   ... fait grandir le serpent
    if touche() :                 # si on touche le bord ...
        message ('Perdu !')       #   ... affichage du message "Perdu!"
        break                     #   ... sortie de la boucle while (donc fin du programme)
    time.sleep(0.1)               # attente pour ralentir (arbitraire!)
    c=ecran.getch()               # saisie de l'appui sur une touche du clavier
    if c == curses.KEY_LEFT: direction = gauche()
                                  # on tourne à gauche
    elif c == curses.KEY_RIGHT: direction = droite()
                                  # on tourne à droite
    elif c == curses.KEY_UP: direction = haut()
                                  # on tourne à gauche
    elif c == curses.KEY_DOWN: direction = bas()
                                  # on tourne à droite

# remettre l'écran en "bon" état
curses.endwin()                   # sortie du mode "curses"
print "\n\n\tF I N I !\n\n"       # message de fin!
  

Annexes

Annexe A. Où trouver ce document ?

Ce document est disponible sous plusieurs formats sur le web:

Annexe B. C'est quoi Python ?

Python est un langage portable, dynamique, extensible, gratuit, qui permet (sans l'imposer) une approche modulaire et orientée objet de la programmation. Python est developpé depuis 1989 par Guido van Rossum et de nombreux contributeurs bénévoles.

Détaillons un peu les principales caractéristiques de Python, plus précisément, du langage et de ses deux implantations actuelles :

  • Python est portable, non seulement sur les différentes variantes d'UNiX, mais aussi sur les OS propriétaires: MacOS, BeOS, NeXTStep, M$-DOS et les différentes variantes de Window$. Un nouveau compilateur, baptisé JPython, est écrit en Java et génère du bytecode Java.

  • Python est gratuit, mais on peut l'utiliser sans restriction dans des projets commerciaux.

  • Python convient aussi bien à des scripts d'une dizaine de lignes qu'à des projets complexes de plusieurs dizaines de milliers de lignes.

  • La syntaxe de Python est très simple et, combinée à des types de données évolués (listes, dictionnaires…), conduit à des programmes à la fois très compacts et très lisibles. A fonctionnalités égales, un programme Python (abondament commenté et présenté selon les canons standards) est souvent de 3 à 5 fois plus court qu'un programme C ou C++ (ou même Java) équivalent, ce qui représente en général un temps de développement de 5 à 10 fois plus court et une facilité de maintenance largement accrue.

  • Python gère ses ressources (mémoire, descripteurs de fichiers…) sans intervention du programmeur, par un mécanisme de comptage de références (proche, mais différent, d'un garbage collector).

  • Il n'y a pas de pointeurs explicites en Python.

  • Python est (optionnellement) multi-threadé.

  • Python est orienté-objet. Il supporte l'héritage multiple et la surcharge des opérateurs. Dans son modèle objets, et en reprenant la terminologie de C++, toutes les méthodes sont virtuelle.

  • Python intègre, comme Java ou les versions récentes de C++, un système d'exceptions, qui permettent de simplifier considérablement la gestion des erreurs.

  • Python est dynamique (l'interpréteur peut évaluer des chaînes de caractères représentant des expressions ou des instructions Python), orthogonal (un petit nombre de concepts suffit à engendrer des constructions très riches), reflectif (il supporte la métaprogrammation, par exemple la capacité pour un objet de se rajouter ou de s'enlever des attributs ou des méthodes, ou même de changer de classe en cours d'exécution) et introspectif (un grand nombre d'outils de développement, comme le debugger ou le profiler, sont implantés en Python lui-même).

  • Comme Scheme ou SmallTalk, Python est dynamiquement typé. Tout objet manipulable par le programmeur possède un type bien définit à l'exécution, qui n'a pas besoin d'être déclaré à l'avance.

  • Python possède actuellement deux implémentations. L'une, interprétée, dans laquelle les programmes Python sont compilés en instructions portables, puis exécutés par une machine virtuelle (comme pour Java, avec une différence importante: Java étant statiquement typé, il est beaucoup plus facile d'accélérer l'exécution d'un programme Java que d'un programme Python). L'autre, génère directement du bytecode Java.

  • Python est extensible: comme Tcl ou Guile, on peut facilement l'interfacer avec des librairies C existantes. On peut aussi s'en servir comme d'un langage d'extension pour des systèmes logiciels complexes.

  • La librairie standard de Python, et les paquetages contribués, donnent accès à une grande variété de services: chaînes de caractères et expressions régulières, services UNIX standard (fichiers, pipes, signaux, sockets, threads…), protocoles Internet (Web, News, FTP, CGI, HTML…), persistence et bases de données, interfaces graphiques.

  • Python est un langage qui continue à évoluer, soutenu par une communauté d'utilisateurs enthousisates et responsables, dont la plupart sont des supporters du logiciel libre. Parallèlement à l'interpréteur principal, écrit en C et maintenu par le créateur du langage, un deuxième interpréteur, écrit en Java, est en cours de développement.

Les domaines d'application naturels de Python incluent entre autres :

  • L'apprentissage de la programmation objet.

  • Les scripts d'administration système ou d'analyse de fichiers textuels.

  • Tous les développement liés à l'Internet et en particulier au Web: scripts CGI, navigateurs Web, moteurs de recherche, agents intelligents, objets distribués…

  • L'accès aux bases de données (relationnelles).

  • La réalisation d'interfaces graphiques utilisateurs.

  • Le calcul scientifique et l'imagerie. Python ne sert alors pas à écrire les algorithmes, mais à combiner et mettre en oeuvre rapidement des librairies de calcul écrites en langage compilé (C, C++, Fortran, Ada…).

  • Le prototypage rapide d'applications. L'idée générale est de commencer par écrire une application en Python, de la tester (ou de la faire tester par le client pour d'éventuelles modifications du cahier des charges). Trois cas peuvent alors se présenter :

    • Les performances sont satisfaisantes, après optimisation éventuelle du code Python. On livre alors le produit tel quel au client.

    • Les performances ne sont pas satifaisantes, mais l'analyse de l'exécution du programme (à l'aide du profiler de Python) montre que l'essentiel du temps d'exécution se passe dans une petite partie du programme. Les fonctions, ou les types de données, correspondants sont alors réécrits en C ou en C++, sans modification du reste du programme.

    • Sinon, il est toujours possible de réécrire tout le programme, en utilisant la version Python comme un brouillon.

    Même dans le pire des trois cas, il est très vraissemblable que le temps de développement aura été sensiblement plus court que si le programme avait été développé directement en C ou en C++.

Le site officiel de Python est http://www.python.org. On y trouvera la distribution officielle, de nombreux paquetages contribués, les compte-rendus des six conférences Python qui se sont déjà tenue à ce jour.

Annexe C. D'autres sources d'information

Python HOWTO Documents

Les HowTos de Python ; c'est là que vous trouverez, entre autres, Curses Programming with Python et Python Advocacy HOWTO ci-dessous.

Curses Programming with Python

Clair, concis ; tout ce qu'il faut savoir pour utiliser curses avec Python.

Python Advocacy HOWTO

Pourquoi utiliser Python ?

X/Open Curses, Issue 4 Version 2 Reference Pages

La liste de toutes les procédures utilisables dans curses.

Curses, constants , Manipulating Characters with Curses , Curses, constants

Quelques pages qui dressent la liste des constantes utilisées dans curses.