Author: Corentin K.

Projets

Le jeu du morpion en python

Nous avons réalisé ce projet dans le cadre d’un travail demandé par nos professeurs de NSI, nous avions le choix entre différents projets mais avons choisi le morpion car nous voulions réaliser un jeu pour finir l’année en beauté.

Introduction

Cet article vous présentera le dernier projet de la spécialité NSI que nous avons réalisé avec Bilal L., Corentin K. et Joseph A.

Pour ce dernier projet nous avons décidé de faire le jeu du Morpion sur la calculatrice Numworks en python. Pour cela nous avons dû utiliser différents modules et faire des recherches variées pour savoir comment coder ce jeu. C’est ce que nous allons vous expliquer dans ce compte rendu.

Bonne lecture !

Quelques fonctions basiques mais importantes

Premièrement, nous avons regardé quelques vidéos et sites web pour savoir par où commencer, nous sommes finalement tombés sur la vidéo de « Schraf : Maths-info” (une chaîne YouTube spécialisée en informatique) : “Python – Jeu du type Morpion avec la tortue (Part 1)”.

Dans cette vidéo, il commence par faire la grille donc nous l’avons suivi et avons récupéré le script de celle-ci après l’avoir compris :

H, L =320 , 222
def grille(nb):
      case = min(H,L) // nb
      H_m, L_m = -nb * case / 2, -nb * case /2
      for i in range(1,nb):
        trait(L_m, H_m + case * i, 0, nb * case)
        trait(L_m + case * i, H_m, 90, nb * case)
      return case, H_m, L_m
    grille(3)

La 1ère ligne permet, en fonction de la taille de l’écran de faire les différentes cases demandées par le joueur.

La seconde ligne, elle, permet de tracer les différentes cases avec le bon écart.


Ensuite, grâce à la fonction trait : la grille est tracée.

Une fois la fonction achevée, on l’appelle avec le nombre de case que l’on veut, ici, il est de 3.

Puis, il nous faut le cercle et la croix pour les placer sur la grille, on a donc fait une fonction pour chaque :

def cercle():
        pensize(2)
        setheading(0)
        pendown()
        penup()
        forward(30)
        pendown()
        left(90)
        circle(25)
        setheading(0)
        penup()
        backward(25)
        left(90)
 def croix():
        pensize(2)
        right(45)
        pendown()
        for i in range(4):
            right(90)
            forward(40)
            penup()
            backward(40)
            pendown()
        setheading(90)
        penup()

Au début, nous avons eu un problème avec le cercle car il ne se crée pas directement au centre mais sur les côtés, on a donc dû rajouter à la fonction un forward pour que le centre du cercle se place au centre de la colonne.


Pour que les joueurs puissent comprendre les règles du jeu pour ce qu’il ne connaissent pas ou ne savent pas appuyer sur quelles touches de la calculatrice pour jouer, nous avons décidé de faire deux pages d’accueil pour tout expliquer.

draw_string("Appuyez sur EXE pour continuer",20,4
0,(255,128,0))draw_string("Règles du jeu : \n\n  ·Chaque joueur possèdent un \n  symbole différent.\n  ·Le premier avec 3 symboles\n  sur une ligne gagne.",0,75,(0, 0,0))
while not (keydown(KEY_EXE)):True

Nous avons fait la première page d’accueil qui indique les règles du jeu en utilisant la fonction draw_string qui permet d’écrire du texte de la façon demandée. Ensuite avec le module Ion, il nous suffit d’appuyer sur la touche “EXE” de la calculatrice pour passer à la seconde page d’explication.


Le cœur du morpion

Le déplacement entre les cases

Cela fait, il nous fallait pouvoir déplacer naviguer entre les différentes cases.

On a donc utilisé le module Ion qui est sur la calculatrice Numworks, il permet d’utiliser les touches de la calculatrice pour y relier des actions, ici : se déplacer ou dessiner les croix et cercles.

Nous avons eu beaucoup de mal à utiliser ce module au début mais c’est en regardant un autre code de “Schraf : Maths-info” sur le jeu de la vie que nous avons compris comment il fonctionnait.

Dans un premier temps, il faut utiliser keydown puis mettre la touche que l’on veut utiliser en paramètre.

Par exemple:

if keydown(KEY_ONE):
      goto(-75,-74)
      croix()

Ici, quand la touche 1 de la calculatrice est enfoncée, la tortue se déplace aux coordonnées demandées et se met à dessiner la croix.

Dans l’ordre des choses, il suffit donc que chaque case soit assignée à une touche.

Et à partir de là, on peut dessiner toutes les croix ou des cercles à n’importe quelle case.

Cela nous a permis de voir le début d’un jeu fonctionnel et nous a largement motivés quant à la suite de ce projet !


La vérification de victoire

La vérification permet de vérifier si un des joueurs a gagné, et si oui, lequel. C’est sûrement ce qu’il nous a le plus posé de problème au cours du projet.

D’abord, nous avons dû apprendre à savoir ce qu’est une matrice (une liste de liste) : 

matrice = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]

On peut la percevoir comme une grille en 3×3, comme celle du morpion pour faciliter l’explication : 

matrice = [[' ', ' ', ' '],
           [' ', ' ', ' '],
           [' ', ' ', ' ']]

C’est une liste de listes où chaque liste contient 3 éléments, ici vides.

Comme dit plus haut, on doit percevoir cette matrice comme la grille du morpion, pour en quelque sorte les “relier” : 

if j == 1:
      if keydown(KEY_ONE) and matrice[0][0] == " ":
            indice = 0
            goto(-75,-74)
            croix()
            matrice[0][0] = j
            j = 2
            verification_croix()

Dans la ligne « matrice[0][0] = j », on associe le 1er emplacement de la 1ère liste de la matrice à j, donc à 1 (ce même nombre renvoie à l’utilisation de la croix).

On associe alors indirectement la case en haut à gauche à la croix, puis on lance la fonction verification_croix.

def verification_croix():
    for i in range (3):
        if matrice[i][0] == matrice[i][1] == matrice[i][2] == 1:
            victoire_croix()
    for i in range(3):
        if matrice[0][i] == matrice[1][i] == matrice[2][i] == 1:
            victoire_croix()
    if matrice[0][0] == matrice[1][1] == matrice[2][2] == 1:
        victoire_croix()
    if matrice[0][2] == matrice[1][1] == matrice[2][0] == 1:
        victoire_croix()

Dans cette fonction qui vérifie si le joueur croix a gagné, on vérifie pour les 8 combinaisons gagnantes du morpion si les emplacements de listes de la matrice correspondants (donc de la grille) sont égaux à 1 (donc à la croix).

Si c’est effectivement le cas, on lance la fonction victoire_croix qui affiche un écran de victoire bien mérité pour le joueur croix.


Difficultés rencontrées

Lors de la conception du jeu, nous sommes tombés sur plusieurs pépins.

Bien sûr, nous avons dû les surmonter, cependant, il y a eu de l’apprentissage mais également du bricolage.

On peut prendre l’exemple de la 1ère tentative de vérification qui était quelque peu fatiguée : 

def verification_cercle():
    if get_pixel(-75,-74) and get_pixel(0,-74) and get_pixel(75,-74) = (254, 254, 254):
        victoire_cercle()
    elif get_pixel(-75,-0) and get_pixel(0,0) and get_pixel(75,0) = (254, 254, 254):
        victoire_cercle()
    elif get_pixel(-75,-74) and get_pixel(0,74) and get_pixel(75,74) = (254, 254, 254):
        victoire_cercle()

On utilise la couleur du milieu de la croix et du cercle pour vérifier si la combinaison est gagnante.

La croix possède un pixel noir en son milieu et le cercle fait quelques aller-retours en son milieu en une couleur quasi-blanche (en RBG) afin de la différencier du fond blanc.

À l’aide de la méthode get_pixel(), on récupère la couleur d’un pixel à une coordonnée précise et on la compare à une valeur fixe, ici (254,254,254), du quasi-blanc. 

Si 3 pixels de la même et de cases à combinaison gagnante sont alignés, l’écran de victoire du joueur concerné s’affiche.
Cependant, rien ne se passait quand une combinaison gagnante apparaissait, on a donc décidé de changer d’approche malgré la tristesse de ne pas pouvoir exploiter cette méthode de pure ingéniosité…

Ensuite l’un des autres problèmes majeurs était de lier la matrice à la grille.


En effet, lors de la conception du jeu, nous voulions au début faire déplacement libre sur la matrice avant d’appuyer sur un 2ème bouton pour valider la case dans laquelle on veut jouer.


Nous avons donc décidé d’enlever le déplacement libre, c’est-à-dire que dès que l’on clique sur un chiffre, une croix ou un rond se met directement à l’emplacement voulu, cela nous a permis de pouvoir relier la matrice à la grille plus facilement:

if keydown(KEY_ONE) and matrice[0][0] == " ":
  goto(-75,-74)
  croix()
  matrice[0][0] = j
  j = 2
  verification_croix()
if keydown(KEY_TWO) and matrice[0][1] == " ":
  goto(0,-74)
  croix()
  matrice[0][1] = j
  j = 2
  verification_croix()

Comme on peut le voir sur le script ci-dessus, la matrice et la grille sont constamment liées.

Conclusion

Ainsi, nous avons l’immense fierté d’avoir conçu ce jeu vidéo sur la calculatrice, nous avons pu en apprendre plus sur le python et les différents modules qu’il possède qui nous étaient alors inconnus jusqu’à présent.

Au début de la conception de ce jeu vidéo, le travail nous paraissait immense et nous n’avions aucune idée d’où commencer ni de quoi faire, des problèmes ont été rencontrés mais nous avons pu trouver des solutions en codant, testant, en faisant des erreurs, nous corrigeant et en s’améliorant.


Le jeu

Avec relancement de partie (cliquer sur EXE à la fin d’une partie) mais avec un bug : lamhassni-bilal/morpion.py — Python — NumWorks

Sans relancement : corentin-katic84/morpion.py — Python — NumWorks

Sources

Chaîne YouTube de Schraf : Maths-info : https://youtube.com/@Schraf

Article Calculatrice Numworks : https://nsi.xyz/tutoriels/programmer-en-python-et-porter-son-code-sur-la-numworks/

Aide à la programmation : https://nsi.xyz/projets/un-menu-en-python-pour-les-jeux-sur-la-numworks/ ; https://www.codingame.com/playgrounds/48392etteur

Art

Astronomie : Le Seigneur des Anneaux … d’Uranus

Dans cet article, nous allons vous présenter notre projet sur Uranus, la septième planète du système solaire, que nous avons réalisée grâce au module turtle de python. Pour rédiger le script, nous avons notamment utilisé des fonctions, boucles et instructions conditionnelles.

Présentation et début du projet

La réalisation de ce projet s’inscrit dans le cadre de la 5ème édition du dispositif « Regard de géomètre ». Nos enseignants nous ont donc demandé de réaliser un script en python sur un thème parmi plusieurs qu’ils avaient sélectionnés. Nous avons choisi de faire notre projet sur le thème « Astronomie » car celui-ci nous a aussitôt inspirés. De plus, nous avons une passion commune pour l’astronomie. L’idée nous est donc venue de dessiner la planète Uranus ainsi que ses anneaux (eh oui Uranus possède des anneaux même si ceux-ci sont beaucoup moins imposants que ceux de Saturne 😁) grâce au module turtle de python.

  • Mais avant de coder Uranus, nous avons voulu rajouter un fond noir (le fond d’écran est blanc par défaut) pour symboliser l’espace. Afin de réaliser le fond noir, nous avons augmenté la taille du stylo grace à la fonction pensize() donc en le mettant à une taille égale à 10000. Ensuite nous avons choisi les coordonnées (x, y) du point vers lequel doit se déplacer la tortue (stylo = tortue) : on utilise donc goto(x, y). De plus, il est inutile de préciser la couleur puisque dans ce cas la couleur par défaut est le noir soit exactement la couleur que nous souhaitons obtenir. Enfin, une fois le fond noir réalisé, on réduit la taille du stylo pour que celui-ci ne soit pas trop grand lors de l’exécutions des fonctions suivantes.
  • Ce qui donne le code suivant :
pensize(10000)
goto(100,1)
pensize(5)
  • Ensuite, une fois que le fond noir est codé, il faut passer à la planète Uranus.
  • Dans un premier temps, nous décidons de la dessiner à l’aide de disques dont on augmentera le rayon successivement avec la boucle for i in range(). Mais, chaque fois qu’un disque apparaîtra, il effacera le précédent. Cela n’aurait guère d’importance si nous n’avions pas eu l’idée de faire des disques de couleurs différentes. Ainsi, la couleur finale d’Uranus est celle du dernier disque formé et la planète n’en est pas vraiment une.
  • Nous nous sommes donc rabattus sur les cercles afin de réaliser Uranus. Puis, à force de réfléchir sur le problème et avec un peu de recul, nous avons décidé de partir d’un cercle de rayon 0 puis d’augmenter ce rayon de la valeur de la variable t. En répétant cette action un grand nombre de fois (ici 220 fois) et en faisant en même temps un dégradé de couleur (en réduisant ou en augmentant les valeurs du Rouge et/ou du Vert et/ou du Bleu du code RVB de la couleur initiale par t) on obtient Uranus !
  • Le code python ci-dessous est l’application de ce que nous venons de dire précédemment. Nous allons cependant préciser quelques éléments :
    • Pour définir la fonction uranus, on doit rentrer la valeur du rayon, des coordonnées x et y du point de départ ainsi que définir la couleur du cercle.
    • Ces éléments sont définis après dans la boucle for t in range(220), ainsi que la valeur de la variable t, avant de les définir. Cela permet à la fonction de tracer des cercles de rayons de plus en plus grands.
    • Les éléments présents en dessous de def uranus permettent de positionner la tortue de façon à tracer un cercle.
    • Si vous souhaitez changer la couleur de la planète, vous avez la possibilité de faire un dégradé bleu en remplaçant les valeurs de color = (254 - t, 12 + t, 1 + t) par : color = (40, 221-t, 255).
def uranus(rayon, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y)
    pendown()
    pencolor(couleur)
    circle(rayon)

t = 1
for t in range(220):
    x, y = 50,-200
    radius = 0 + t
    color = (254 - t, 12 + t, 1 + t)
    uranus(radius, x, y, color)

Et voici le résultat (fond noir + fonction uranus) :

Améliorations du projet

Une fois la planète obtenue sur fond noir nous contemplons de manière enthousiaste notre travail. Mais il nous reste toujours les anneaux à faire ! Et c’est là que ça se complique.

  • Pour les anneaux, nous pensions tout d’abord à les faire avec des cercles. Après plusieurs tentatives, il faut se rendre à l’évidence : les anneaux avec les cercles ça ne marche pas ! C’est à ce moment-là que l’un d’entre nous se souvient de la présence, sur la calculatrice NumWorks, dans la rubrique « Fonction », d’une fonction appelée « conique » qui trace des ellipses. La forme de l’ellipse nous semblant similaire à celle des anneaux, nous décidons donc de tracer des ellipses pour les anneaux. Comme nous ne savons pas comment coder une ellipse avec le module turtle de python, nous faisons une recherche sur Internet. Nous tombons alors sur une fonction très simple permettant de tracer une ellipse. Nous l’ajoutons alors au script.
  • Puis, nous remanions le script trouvé sur Internet en reprenant les mêmes principes utilisés précédemment dans la fonction uranus c’est-à-dire en augmentant progressivement la taille du rayon et en faisant un dégradé de couleurs. Pour définir la couleur, nous avons utilisé pencolor() afin que la couleur des anneaux soit autre que noir. Puis nous avons aussi rajouté pensize(5) avant for j in range(30) pour augmenter la taille du tracé. Enfin nous avons changé la valeur de seth() afin que les anneaux soient inclinés vers le bas droit de l’image, ainsi que la valeur de la fonction anneaux() dans le but que ceux-ci soient assez grands pour que la planète se situe bien au milieu.
  • Et comme d’habitude ne pas oublier de définir la variable avant son utilisation (sinon ça ne marche pas😉). Ci-dessous le code python des anneaux ainsi qu’Uranus + ses anneaux.
# Source: https://stacklima.com/dessiner-une-ellipse-a-laide-de-turtle-en-python/
j = 1
def anneaux(rad):
  pensize(5)
  for j in range(30):
    pencolor(255- 5*j, 255- 5*j, 255- 5*j)
    circle(rad + j,90)
    circle(rad//2 + j,90)

seth(-80) 
anneaux(290)

Arrivés à ce stade-là, on aurait pu arrêter. On avait la planète Uranus, ses anneaux et le fond noir. Mais l’image semblait un peu vide, on a donc eu l’idée de rajouter quelque chose en fond d’écran : des étoiles.

  • Pour dessiner les étoiles, c’est assez facile. Si on veut dessiner une étoile à 5 branches on peut dessiner une partie de l’étoile et répéter cette action cinq fois. Tout ceci en utilisant (encore) une fonction, dans laquelle on doit définir la longueur d’un trait, les coordonnées (x, y) du point de départ ainsi que la couleur de l’étoile. Avant de coder la fonction on met la taille du stylo à 5 avec pensize(5) pour remplir l’interieur de l’étoile (il est inutile de le préciser avant puisqu’on l’a déjà mis dans le code du fond noir qui est situé avant la fonction etoiles). On choisit aussi de préférence une longueur assez basse (mais pas trop quand même). 4 est un bon chiffre par exemple.
  • La boucle for i in range(5) permet de dessiner 5 fois deux demi-branches d’étoiles pour obtenir une étoile (il faut faire attention aux mesures d’angles et si l’on tourne à droite ou à gauche). La boucle for i range(210) permet de dessiner 210 étoiles. Dans cette boucle, on définit les coordonnées (x, y) ainsi que la couleur de l’étoile grâce au module randint. Celui-ci permet, pour chaque étoile, de définir des coordonnées aléatoires comprises entre -800 et 800 pour x et -400 et 400 pour y. C’est le même principe pour les couleurs à part que l’on a réduit l’écart entre les nombres pour obtenir comme couleur (toujours de manière aléatoire) du jaune, de l’orange ou des couleurs s’en rapprochant de très près.

Le code python des étoiles :

def etoiles(longueur, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y)
    pendown()
    pencolor(couleur)
    for i in range(5):
        forward(longueur)
        left(74.2)
        forward(longueur)
        right(146)

for i in range(210):
    x, y = randint(-800, 800), randint(-400, 400)
    longueur = 4
    color = (randint(247, 255), randint(127, 255), randint(0, 12)) 
    etoiles(longueur, x, y, color)

Et le résultat :

Finalisation du projet

Enfin, nous avons pensé à rajouter le plus grand satellite naturel d’Uranus, Titania (à ne pas confondre avec Titan qui lui est le plus grand satellite naturel de Saturne).

  • On s’est dit que rajouter un petit détail supplémentaire pourrait être intéressant. On a tout de suite pensé à rajouter son plus grand satellite naturel. Pour le faire, on a utilisé le même procédé que pour Uranus, à savoir des cercles qui augmentent progressivement de rayon et un dégradé de couleurs. Mais cette fois-ci, pour plus d’originalité, nous avons utilisé des instructions conditionnelles pour faire le dégradé de couleur en fonction de la taille des cercles.
  • On utilise une boucle while cette fois-ci (on aurait pu aussi utiliser une boucle for comme dans les autres fonctions et vice versa). Ici on prend radius (le rayon du cercle) comme variable puis on l’augmente de 1 (avec +=) jusqu’à ce que radius soit égal à 30 : le principe reste donc le même qu’avec une boucle for.
  • Puis, dans la boucle while, on utilise des instructions conditionnelles afin que les cercles soit d’une certaine couleur en fonction de leur taille. Ainsi, la première condition (if) s’applique aux cercles dont le rayon est inférieur ou égal à 28 et dont la couleur sera du marron très clair. La deuxième condition (elif) concerne les cercles ayant un rayon compris entre 28 (non inclus) et 29 (inclus) : la couleur du cercle sera marron clair. A noter que l’on aurait pu remplacer elif radius > 28 and radius <= 29: par elif radius == 29: . Enfin, la troisième et dernière condition (else) concerne tous les cercles dont le rayon va jusqu’à 30 (mais ne concerne pas ceux des précédentes conditions), qui auront donc un couleur marron.
  • Le code et l’image obtenus :
def titania(rayon, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y)
    pendown()
    pencolor(couleur)
    circle(rayon)
    
x, y = -200, 100
radius = 0
while radius <= 30:
    radius += 1
    if radius <= 28:
        color = (167-2, 103-2, 38-1)
    elif radius > 28 and radius <= 29:
        color = (136-2, 66-2, 29)   
    else :
        color = (91-2, 60-2, 17)
    titania(radius, x, y, color)

Au cours de la rédaction de notre script python, nous avons rencontré différents problèmes que nous avons essayé de résoudre.

  • Le problème majeur que nous avons rencontré est le décalage des anneaux et de la planète sur l’image quand d’autres fonctions étaient exécutées avant. De plus, comme la même variable i était utilisée pour chaque fonction cela créait des confusions de même que le positionnement des objets sur l’image. Nous avons donc changé le nom des variables afin que chacune est un nom différent (i, j, t et radius) ce qui explique le nom des variables précédentes. Nous avons aussi ajouté les lignes suivantes entre chaque fonction afin d’éviter toutes confusions et pour remettre les fonctions à jour :
penup()
goto(0,0)
pendown()
  • Nous avons aussi eu des problèmes avec la fonction titania, notamment avec le cercle marron qui se décalait trop vers le haut. Nous avons donc réduit l’intervalle dans lequel il était appliqué pour supprimer ce décalage. Nous avons rencontré d’autres problèmes minimes et sans importance notamment sur les valeurs et la précision de celles-ci, qui peuvent faire varier le rendu.

L’image finale

Voici donc le résultat final ci-dessous une fois que tous les éléments ont été assemblés et les problèmes réglés :

Télécharger le .py

Si vous désirez le télécharger (il en format compressé il faudra donc le décompresser😉). Le script contient un code python permettant de générer une image ainsi que les fonctions permettant de dessiner le fond noir, les étoiles, les anneaux, Uranus et Titania (dans l’ordre) :