Étiquette : Jeux

Projets

Un Pac-Man sur ta Numworks, en python

Rond comme un ballon, et plus jaune qu’un citron, c’est lui Pac-Man ! Pac-Man débarque sur ta NumWorks ! Aide le à manger toutes les pac-gommes en évitant les fantômes et accumule le plus grand score.

Bon jeu !

URL courte de cet article : nsi.xyz/pacman

Pac-Man débarque sur ta NumWorks !

Aide le à manger toutes les pac-gommes en évitant les fantômes et accumule le plus grand score. Pour cela, tu peux :

  • Manger des pac-gommes (10)
  • Manger des super pac-gommes (50)
  • Manger les fantômes (200, 400, 800, puis 1600)

Si tu manges tout les fantômes a chaque super pac-gommes, tu obtiendras même un bonus de 12000 points !

Bon jeu !

Pac-Man, un sacré projet…

Comme toujours, sur nsi.xyz, on aime raconter l’histoire derrière nos projets.

Celle là commence dans une cuisine. Je commençais à faire à manger quand le patron  m’a envoyé un message concernant un bug sur un de ses projets « un jeu en une soirée » : Treasure

L’idée me plaisait, j’avais envie de faire pareil. Mais quel jeu choisir ?

🤓  Je vais tenter de faire un jeu en une nuit aussi vous m’avez motivé. Une idée ?
🧐 Pacman, tout autre jeu serait trop facile pour toi 😅

Pac-Man, ca paraissait infaisable… Des IA différentes pour chaque fantômes, des collisions, des tableaux et encore des tableaux, tout ça dans 32Ko max !

On est parti !

Première étape : Comment stocker la carte ?

Déjà, posons nous la question suivante : C’est quoi la carte dans Pac-Man ?

La carte, c’est soit :

  • Les chemins : On peut se balader dessus, les fantômes aussi.
  • Les murs : Comme on peut le penser, c’est l’inverse des chemins, on ne peut pas les traverser.

Du coup, on est sauvé ! Le tableau contiendra uniquement des 1 (pour les murs) ou des 0 (pour les chemins). Ainsi, on passe d’un tableau 2D de Booléen à un tableau 1D d’entier.
L’astuce est simple mais efficace : Une ligne de 1 et de 0 peut être convertie en entier grâce au fait que l’ordinateur code les entier en binaire.
En plus de réduire la complexité en espace, ça réduit aussi celle en temps, ce qui est pratique quand on parle d’un jeu tel que Pac-Man sur une calculatrice.

Deuxième étape : Construire la carte

Je commence déjà par choisir une taille pour les cases : 10px.
Sachant que la NumWorks dispose d’un écran de 222 px de haut, je sais que ma carte fera 22 cases de haut.Retour ligne automatique
A l’aide d’un logiciel professionnel et d’une image sur Internet, je crée une image blanche de 18x22px (Pourquoi 18 ? Je ne sais pas) que je commence à colorier de façon à obtenir ceci :

Et cette carte se traduit par le tableau suivant :

bits = 18  # Le nombre de bits pour chaque entiers, ce sera utile plus tard
terrain = (262143,131841,187245,187245,131073,186285,135969,252783,249903,251823,1152,251823,249903,251823,131841,187245,147465,219051,135969,195453,131073,262143)

Encore une fois, on utilise des tuple pour gagner en mémoire.

Maintenant, il ne reste plus qu’a rendre la carte à l’écran.
Et pour ca, on utilise une belle fonction :

def render():
    global terrain
    for l in range(len(terrain)):
        for c in range(bits):
            fill_rect(c*10+140,l*10+1,10,10,colors[0])
            if terrain[l]>>(bits-1-c) & 1:
                for d in ((1,0),(0,1),(-1,0),(0,-1)):
                    if 0 <= l + d[0] <= len(terrain) - 1 and 0 <= c + d[1] <= bits - 1 and not terrain[l + d[0]]>>(bits-1-(c+d[1])) & 1:
                        fill_rect(c*10+140+9*(d[1]==1),l*10+1+9*(d[0]==1),1+9*(d[1]==0),1+9*(d[0]==0),colors[1])

Pour faire simple, cette fonction lit les entier comme s’ils était des tableaux de 1 et de 0. Pour chaque case, elle dessine un carré noir. Si la case est un 1, elle regarde dans les 4 direction possibles, si un 0 s’y trouve, alors elle dessine un mur de ce côté.

En rajoutant une petite fonction pour du pré-rendu que je ne détaillerai pas, on obtiens déjà ceci :

Troisième étape : Ajouter de « la classe » au jeu

Outre l’aspect douteux de ce jeu de mot, il est temps de créer une classe pour Pac-Man et ses amis.
Ainsi née la classe Entity qui contiendra, à elle seule, toutes les méthodes pour le jeu. En effet, si on ne considère pas l’entrée des directions, les fantômes et Pac-Man se déplacent de la même manière sur la carte.

Pour faire court, cette classe contient 4 méthodes :

  • Le constructeur : initialise tout les attributs de l’entité.
  • espace : C’est la méthode qui teste les collision, grâce à elle, les fantômes et Pac-Man ne tournent que sur les intersections sans déborder sur les murs. Et évidemment, ils ne traversent pas non plus les murs.
  • move : C’est la méthode qui fait se déplacer les entités, qui réactualise les pac-gommes et super pac-gommes (j’y viendrai plus tard) et qui gère les collisions Pac-Man / Fantômes et Pac-Man / pac-gommes.
  • ia : Détermine la direction des fantômes.

Attardons-nous sur la méthode ia :

def ia(self,x,y):
    if self.f:
        while True:
            d = randint(0,3)
            dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[d]
            if d != (3,2,1,0)[self.d] and self.espace(dx,dy):
                self.d = d
                break
    else:
        distances = [9999 for _ in range(4)]
        for i in range(4):
            if i != (3,2,1,0)[self.d]:
                dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[i]
                if self.espace(dx,dy):
                    distances[i] = sqrt((self.y + dy - y)**2 + (self.x + dx - x)**2)
        self.d = distances.index(min(distances))

On voit qu’elle comporte deux parties, c’est un des éléments clés de Pac-Man : quand les fantômes sont dans l’état effrayé, ils choisissent une direction au hasard, sinon, ils choisissent la direction qui leur permet d’avoir la plus courte distance euclidienne à un point. Rajoutons que les fantômes n’ont pas le droit de faire demi-tour.

Mais une question se pose : Quel est donc ce point que visent les fantômes ?

Quatrième étape : Les fantômes débarquent !

Dans Pac-Man, il y a quatre fantômes. Mais il n’y a pas que leur couleur qui change, leur comportement aussi !

Petit listing exhaustif :

Blinky poursuit Pac-Man, ainsi le point qu’il cherche à atteindre et le point au centre de Pac-Man.

Pinky est la plus intelligente, ainsi le point qu’elle cherche à atteindre est deux cases devant Pac-Man dans la direction qu’il regarde.

Inky est un peu plus farceur, pour savoir ou il vise, il faut prendre en compte la position de Blinky et de Pac-Man. Posons v le vecteur Blinky-Point deux cases devant Pac-Man dans la direction qu’il regarde, Inky visera le point situé à 2v, ce qui le rend imprévisible.

Clyde quant à lui est un froussard, il agit comme Blinky quand à lui, est à 4 « mètres » de Pac-Man, mais vise un angle de la carte quand il est proche de Pac-Man.

Cinquième étape : Déplacer Pac-Man

Les fantômes maintenant capables de changer de direction, il n’en n’est rien pour Pac-Man…Retour ligne automatique
Comment se déplace Pac-Man ? Pac-Man se déplace jusqu’au bout d’un chemin avant de s’arrêter. Si on lui donne une direction, il la prendra dès qui le pourra sauf si la direction en question est la direction opposée à celle qu’il emprunte déjà.

Du coup, avec nos méthodes et un peu d’astuce, c’est tout simple !

for i in range(4):
    if keydown(i):
        if i == (3,2,1,0)[pacman.d]:
            pacman.d = i
        pacman.nd = i
if pacman.espace():
    pacman.d = pacman.nd

Sixième étape : Gestion des pac-gommes

Vous l’aurez peut-être remarqué mais le jeu est relativement fluide et rapide.Retour ligne automatique
Tout ça repose sur une grosse optimisation sur les pac-gommes, sans cette optimisation, le jeu tourne à 4 IPS.

Une question importante se pose : Comment réussir à afficher et rafraichir les pac-gommes ?

Une première approche innocente serait de stocker les pac-gommes sous forme de tableau de 0, 1 et 2 et le parcourir en affichant ou non les précieuses gommes. Sauf que là, bonjour le temps pour chaque image…

Une deuxième approche est de créer deux listes d’entier et d’utiliser les opérateurs binaires et les soustractions pour stocker les pac-gommes.Retour ligne automatique
Enfin, et c’est la l’énorme gain de temps. Pour chaque entités, après un mouvement, on regarde dans les deux listes si une (super) pac-gomme se trouve sur la case précédente, que ce soit un mur ou non, et on dessine une pac-gomme si besoin. Et c’est comme ça qu’on atteint une fluidité de jeu.

En plus, au lieu d’un parcours de liste, on a juste a implémenter les cinq lignes suivantes :

px, py = int(self.x - 5.5*dx), int(self.y - 5.5*dy)
if pacgommes[py]>>(bits-1-px) & 1:
    fill_rect(px*10+144,py*10+5,2,2,(250, 207, 173))
if superpacgommes[py]>>(bits-1-px) & 1:
    fill_rect(px*10+143,py*10+4,4,4,(250, 207, 173))

Septième étape : Gestion du score et des états des fantômes

Pour le score, on a juste à implémenter les règles citées en début d’article.

Pour la gestion des états des fantômes, c’est un peu plus compliqué qu’une simple variable.
Il faut rajouter un attribut afin que lorsqu’on mange un fantôme, il ne soit plus effrayé sans pour autant que les autres ne le soient plus.
Enfin, il faut rajouter une clock qui nous permettra de rendre leur état normal au fantômes après un certain temps en le rendant blanc juste avant pour alerter le joueur.

Conclusion

Evidemment, je ne me suis pas attardé sur tous les détails car se serait beaucoup trop long. Mais ce sont les éléments principaux pour un Pac-Man.
Je n’ai pas rajouté les changement de phases entre Chase et Scatter, de peur d’impacter les performances ou de dépasser la place disponible en mémoire.
Le Pac-Man fini rend bien :

Code jouable disponible ici :

Projets

Un Sokoban pour la NumWorks

Le Sokoban est un jeu de puzzle inventé au Japon en 1982. Le principe est simple : vous êtes un gardien d’entrepôt et vous devez amener les caisses aux bons emplacement. Pour cela, vous pouvez uniquement pousser les caisses, et une seule à la fois.*

Une partie des informations de cette page sont extraites de la page Wikipédia Sokoban.
Si vous souhaitez creuser le sujet, n’hésitez pas à la consulter.

L’objectif du jeu est de résoudre les niveaux en un nombre minimum de déplacements. Par ailleurs, il existe des milliers de niveaux, pour la majorité créés par la communauté. En fonction de la taille et de la complexité de la grille, ainsi que de l’exigence de l’utilisateur pour réduire son nombre de coups, tous les niveaux de difficultés sont imaginables. Ces éléments donnent une grande durabilité au jeu.

En raison de son apparition rapide dans le monde du jeu vidéo, de sa simplicité apparente et de son intérêt toujours renouvelé, le jeu a connu un grand succès et a été développé sous de nombreuses versions et plateformes. A mon tour de le porter sur la calculatrice NumWorks !

Fonctionnalités

  • Aide (règles, touches, signification des couleurs) affichée au lancement et accessible à tout moment.
  • Ajustement de l’affichage à la taille de la grille.
  • Possibilité d’annuler les mouvements précédents.
  • Déplacement rapide.
  • Détection de la victoire.
  • Ajout et choix de niveaux facile (voir la section correspondante).

Lancer le jeu

Le moteur de jeu et le contenu (les niveaux) sont stockés dans des scripts séparés :

Pour jouer, il faut télécharger ces scripts et exécuter sokoban.py. Il vous est demandé de préciser la cartouche (liste de niveaux) à utiliser ainsi que le niveau auquel vous souhaitez commencer. Il n’est pas nécessaire de remplir ces champs, le jeu commencera par défaut au niveau 1 de la cartouche soko.

Ajouter des niveaux

Si vous venez à bout des 50 niveaux inclus dans la cartouche de base, vous pouvez en télécharger une autre sur le Workshop. Ils sont nommés soko1soko2, … Pour les utiliser, il suffit de rentrer le nom de la cartouche au lancement du jeu.

Une présentation détaillée de ce projet à été publiée sur tiplanet.org

Projets

Un Tetris en python pour la NumWorks

Tout le monde sait qu’une calculatrice programmable sert à jouer à des jeux. Or quand on parle de jeux, un des premiers à nous venir en tête est Tetris, alors pourquoi pas un Tetris pour NumWorks ?

Ainsi, après un démineur, un snake, on vous propose cette fois ci le jeu mythique de la Gameboy adapté en flat design et codé en python pour la NumWorks.

Donne moi 32 ko, je te ferai un Tetris !

Jusqu’à la version 13.2 d’Epsilon, l’OS officiel de la NumWorks, faire un jeu en python sur la couche graphique de la NumWorks était impossible et les scripts commençaient à planter dès qu’ils pesaient 3-4 ko.

Avec un tas python heap de 32 ko, on peut enfin commencer à s’amuser et monter des projets dont le code source pèse près de 8ko.

Le récit de la création d’un jeu en image

Tout commença le 19 avril 2020 dans une nuit confinée.

🧐 Bon on vient de finir nos trois projets en python, on fait quoi maintenant ?
🤓 Oh on peut acheter des contrefaçon de Gameboy sur Aliexpress pour 10€ ! Et il y a 400 jeux dedans.
🧐 La nuit porte conseil, on en parle demain, enfin dans quelques heures !

Le lendemain :

🧐 Bon on fait un Tetris. Je viens de lire l’article sur Wikipédia, c’est passionnant. Par contre par un mot à Arthur, il va nous piquer notre idée sinon. Et cette fois, on ne stocke pas coordonnées des cases occupées dans des matrices, et on va utiliser la fonction get_pixel() de la NumWorks pour déterminer si une case est occupée ou non.

et c’est ainsi qu’un premier Tétrominos apparaît à l’écran.

🤓 J’ai peut être fini la collision
🧐 Hein ?! Mais j’ai pas commencé moi…
🧐 Bon je partage l’écran. Qu’est ce qu’on a dit déjà ? 120 pixels pour le menu, 120 pixels pour le score et 42 pixels pour le jeu en lui même c’est bien ça ?


🧐 Bon je viens de finir la maquette d’une ébauche de menu, reste à le coder !
🤓 et moi je viens de finir les collisions …
🧐 Pour les couleurs ça sera délicat et subtil. On va s’écarter du code couleur original.

🧐 Et du coup on va faire un rappel dans le menu
🤓 On fixe les points d’ancrage dans les pièces, et ainsi elles peuvent tourner

Rendu final souhaité (et obtenu)

🤓 Astuce : Si tu joues sur Omega, alors l’interface du jeu n’est plus orange mais rouge, elle s’adapte au fork de Epsilon.

🧐 D’ailleurs faudra que je relance l’équipe de développement de Omega, ils m’ont promis un tas python de 120 ko et j’ai l’impression que ce projet n’avance pas !
🧐 Bon ils sont encore sur répondeur, ils ont du bloquer mon numéro !
🤓 On fait une pause, j’ai envie de jouer un peu :

🧐 Bon je vais spoiler Critor de tiplanet.org mais toujours aucune info à Arthur !
🤓 Score codé, avec des fonctions polynôme et les points marqués dépendent du niveau du jeu …
🧐 Niveau max : 99, Score max : 999 999
🤓 Vitesse du jeux codé, avec une fonction logarithme népérien car un polynôme n’était pas adapté …

🧐 Bon maintenant s’agit de faire un gros score, histoire d’être certain que Critor ne nous batte pas !

Commencé un dimanche, fini le mercredi suivant, c’était presque trop facile !

🧐 Je reviens, je vais conseiller à Critor de commencer level 42, ça plantera son score !

Meilleurs scores

🧐 Il a commencé sa partie directement au niveau 42, du coup son score est à peine acceptable !🤓 En plus, il a triché ! Il a utilisé une vision prédictive triple et la grille d’aide !

Bien évidemment, il a nié avoir triché 🙄

Je ne les ai même pas utilisés, c’était pour mettre à l’écran le maximum d’éléments visuels, car j’étais parti pour l’utiliser également comme photo d’illustration d’article

Xavier Andréani, alias Critor du tiplanet.org

Mais nous ne sommes pas dupes non plus !

Epsilon 20…

31 Janvier 2023, une nouvelle version du système d’exploitation de la NumWorks est publié : Epsilon 20 ! 😍
NumWorks décide alors d’enfin implémenter le tableau périodique (qui était déjà d’ailleurs disponible sur Omega depuis un bon moment) dans son système, mais décide également de mettre à jour le fonctionnement du codage des couleurs dans l’application Python, ce qui a pour effet de mettre le Tetris, sortie 3 ans plus tôt, complètement KO et dysfonctionnel…

2,5 ans plus tard

🧐 Bon, ça fait plus de 2 ans que le Tetris est planté, il va falloir faire quelque chose…
🧐 Demande de l’aide à Kevin
🤓 Un peu sous l’eau en ce moment, ça bosse ici (il faut bien payer les impôts pour augmenter les parlementaires et les professeurs)
😎 J’aurai un peu de temps après mes partiels, je peux y jeter un œil et en profiter pour mettre à jour l’interface, j’aurai juste à reprendre le code de l’UI/UX du Mastermind, il est flexible et facilement implémentable
😎 Bon ça m’a pris toute la journée mais c’est censé être bon, j’en ai aussi profiter pour rajouter un système de pause, je me souviens qu’à l’époque c’était frustrant de perdre sa partie quand le prof passait derrière…
🧐 Tsss

Réagir ou commenter ce jeu

N’hésitez à réagir à cet article sur le forum tiplanet.org associé :
Tetris en Python pour NumWorks 13.2.0+

Vous pouvez également réagir à l’annonce de ce Tetris posté sur twitter :

https://twitter.com/nsi_xyz/status/1259418545173209089

Projets

Un Snake codé en python pour la NumWorks

Le snake est un jeu très ancien, apparu en 1976 dans Blockade. Le principe est simple : le joueur dirige un serpent qui grandit, et doit éviter le plus longtemps possible de se mordre la queue ou les bordures. Il existe cependant de très nombreuses variantes. Nous vous proposons ici un snake codé en python pour la NumWorks.

Un classique des portables Nokia sous Symbian OS

Sa simplicité apparente et son caractère addictif l’ont mené à être porté sur de très nombreuses plateformes, provoquant un succès planétaire. Nokia y a grandement participé en l’intégrant sur ses téléphones dès 1998, y compris l’iconique 3310.

Cet article présente donc une énième version de ce jeu, cette fois pour la calculatrice NumWorks.

Evolution et défis

La première version ne propose pas d’option et stocke le corps du serpent dans une liste constituée des coordonnées, en divisant l’écran en une grille de 32 par 20 carreaux :

snake = [[3, 3], [4, 3], [5, 3], [6, 3]] # Corps du serpent, de la queue à la tête

Fonctionnel, très pratique pour le faire avancer, par les méthodes pop()et append(), et pour savoir comment dessiner les bordures. Cependant un problème apparaît rapidement : l’occupation de la mémoire. En effet, les listes, et tout particulièrement les listes de listes, sont très coûteuses.

La première évolution a donc consisté à changer le système de stockage du serpent : désormais, seules les coordonnées de la queue et la tête sont enregistrées, diminuant considérablement la place occupée en mémoire, et ce quelle que soit la longueur du serpent.

snake = [3, 3, 6, 3] # Coordonnées de la queue et de la tête

Cela demande en revanche de modifier l’ensemble des procédures graphiques, et d’utiliser une méthode de détection de couleur de pixel, kandinsky.get_pixel(). Les fonctions get_pixel() et set_pixel()n’étant pas bijectives, il a fallu s’assurer de la bijectivité des couleurs utilisées. (Ceci fera l’objet prochainement d’un article dédié)

Pour éviter des conversions fréquentes entre les coordonnées de la grille et celles des pixels, l’ensemble des coordonnées est ensuite modifié pour ne présenter uniquement des coordonnées en pixels.

snake = [30, 52, 60, 52] # Coordonnées de la queue et de la tête

La gestion de la direction a également suscité réflexion, car il faut pouvoir saisir à tout moment les instructions de changement de direction, et il faut également éviter au joueur de mourir lors des demi-tours. Après quelques améliorations, on arrive à ce code :

# Gestion du temps et de la direction
direction = di
while monotonic() < time + speed: # Attente du prochain rafraîchissement du serpent
    for k in range(4): # Changement de direction
        if keydown(k) and direction+k != 3: di = k
    if keydown(6): start() # Retour au menu
time = monotonic()

A la demande de Robert Vincent, j’adapte le code pour pouvoir implémenter un mode où la téléportation d’un bord à l’autre de l’écran est possible, par un jeu de modulos. Le script perd en optimisation, mais gagne en polyvalence.

Les étapes suivantes sont moins lourdes de conséquences :

  • ajout de différents niveaux de difficulté, modifiant la vitesse du serpent et l’intensité de son agrandissement à chaque pomme mangée. 
  • ajout du mode « Dingue », où le serpent grandi et accélère tout seul, progressivement, au lieu de manger des pommes. 

Les dernières étapes ont été de brancher le jeu sur un menu polyvalent graphique en python codé en parallèle, et de finaliser les options d’accessibilité : 

  • choix définitif des touches utilisées
  • amélioration du dessin de la pomme 
  • ajustement du système de points

L’ajout d’autres modes n’est pas exclu.

Images

MenuInterface de jeu
TéléportationPartie perdue

Réagir à cet article

Vous pouvez réagir à cet article sur le topic tiplanet.org de cette news : 
Snake, calcul mental, et interface lancement jeux Python

Projets

Un démineur en python sur la NumWorks

Le démineur, jeu de réflexion dont le but est de localiser des mines cachées dans une grille représentant un champ de mines virtuel, avec pour seule indication le nombre de mines dans les zones adjacentes est désormais porté en python sur ta NumWorks !

Souvenir de jeunesse

J’ai découvert la programmation sur une désormais antique Casio 9960GT, propulsé par son CPU 8 bits à 4,3 MHz. Lycéen et autodidacte, j’ai alors entrepris de créer un démineur sur ma calculatrice et cela fut pour moi une découverte de différents concepts en algorithmique.

Le processeur de cette génération de calculatrice était tellement lent et l’accès à la mémoire encore plus lent que chaque test logique prenait un temps considérable. Le démineur codé et parfaitement fonctionnel se montrait ainsi d’une lenteur remarquable au premier lancement lors de l’affichage de la couche graphique, il fallait près de 42 secondes pour initialiser la couche graphique.

Quelques caractéristiques de ce démineur :

  • 5 niveau de difficulté
  • un plateau de jeu de 14 cases par 7, soit 98 cases en tout.
  • L’initialisation de la couche graphique prenait à elle seule plus de 30 secondes
  • La navigation était fluide, mais la fonction découverte pouvait prendre une bonne dizaine de seconde.

Sur une NumWorks, en Python

20 ans plus tard, l’idée est donc de coder un nouveau démineur, en python, et de le faire tourner sur la calculatrice à la mode du moment : une NumWorks, propulsé par un CPU ARMv7 à 100 / 200 mhz, donc probablement plus de 50 fois plus rapide que celui de mon antique Casio.

Mes élèves de seconde étant tous équipés de cette calculatrice, ils pourront s’amuser en silence quand les cours sont trop ennuyeux, et cela montrera aux futurs élèves de la spécialité NSI les possibilités de la calculatrice et la souplesse du langage python.

L’objectif est de sortir ce démineur pour la sortie officielle de la version 13 de Epsilon, le logiciel sous licence CC BY-NC-SA de la calculatrice NumWorks. La version 13 de Epsilon introduit beaucoup d’améliorations et de nouveautés à l’application Python, celle qui nous intéresse particulièrement et le getkey, la capacité de la calculatrice dans un script python à détecter si une touche est pressée.

Le site tiplanet.org ayant déjà bien documenté cette fonctionnalité, on dispose donc déjà des codes associés aux touches :

Créer une interface graphique

Pour faire un démineur, on a besoin d’un tableau de n lignes et p colonnes dans lequel on va afficher une mine, un drapeau ou un chiffre.

Il faut donc s’adapter à la résolution de l’écran, et la principale contrainte à prendre en compte est la taille d’affichage des chiffres.

Puis on les met en couleur

et enfin on code les mines et le drapeau.

#codage des couleurs
co=color
f,h,n = 255,127,0
c=[co(f,f,f), co(45,125,210), co(151,204,4), co(238,185,2), co(244,93,1), co(215,65,167), co(n,n,n), co(n,n,n), co(n,n,n),
   co(h,h,h),co(192,192,192),co(96,96,96),co(253,236,185)]
def terrain(): #Affiche la grille
  fill_rect(8,21,300,200,c[9])
  for y in range(21,243,20):
    for x in range(8,309):
      set_pixel(x,y,c[10])
  for x in range(8,320,20):
    for y in range(21,222):
      set_pixel(x,y,c[10])
def cl(x,y):
  fill_rect(9+20*x,22+20*y,19,19,c[0])
 
def drapeau(x,y):
  fill_rect(17+20*x,26+20*y,3,9,c[12])
  fill_rect(17+20*x,36+20*y,3,3,c[12])
 
def mine(x,y): # Ce code ne sera pas retenu pour la version finale.
  cl(x,y);
  fill_rect(12+20*x,36+20*y,13,4,c[9])
  fill_rect(14+20*x,34+20*y,9,2,c[11])
  fill_rect(17+20*x,32+20*y,3,2,c[5])

Une matrice pour stocker les mines

Le plus simple est de stocker les mines dans une matrice 10×15.
Pour cela on défini une matrice en python et on la rempli de 0.

m = [[0 for y in range(10)] for x in range(15)]

puis un script génère des bombes, codées 999 dans la matrice.

def gvt(): #fonction renommée depuis ...
  b = 20
  while b>0:
    x, y = randint(0,14),randint(0,9)
    if m[x][y]!=999:
      m[x][y]=999
      b-=1
      for p in range(max(0,x-1),min(15,x+2)):
        for q in range(max(0,y-1),min(10,y+2)):
          if m[p][q]!=999:m[p][q]+=100  

Le script ci-dessus a deux fonctions, il place les 20 mines puis « calcule » pour chaque case le nombre de bombes dans les 8 cases autour.

Ainsi, la matrice contient à ce stade uniquement des entiers :
soit des 0 (0 bombes dans les 8 cases environnantes), soit des multiples de 100, soit des 999.

Pourquoi 100 et pas 1 pour indiquer qu’il y a une bombe ?

Car on va utiliser cette matrice pour y stocker une information importante, à savoir si la case a été découverte ou pas.

m[i][j]La case (i,j) contientDans les 8 cases autour de la case (i,j) il y acase découverte
9991 mineon ne sait pasnon sinon (…)
3420 mine3 minesoui
3000 mine3 minesnon
420 mine0 mineoui
10 mine0 mineen cours de découverte
00 mine0 minenon

Calcul du nombre de mines de chaque case

En parcourant l’article Wikipédia sur le démineur lors de la rédaction de ce compte rendu, j’y ai découvert que j’ai construit intuitivement l’un des deux algorithmes classiques pour le calcul des nombres des différentes cases du démineur.

Sur mon antique Casio 9960GT, j’utilisais l’algorithme par case :

explorer tableau par case
si case est vide
nombre := compter_mines(case.voisinage)
case.valeur = nombre<

et sur la NumWorks, j’ai construit une version légèrement adapté de l’algorithme par mine :

// Placement de la mine
explorer mine.voisinage par case
si case est vide
incrémenter case.valeur

Dans une première version en python, j’utilisais la partie décimale pour y stocker le 42 ou le 0.01, ainsi cela donnait 1,42 si il y a une mine dans le voisinage et que la case est déjà découverte, mais ceci posa un problème, le test :

if 1.42-round(1.42)==42

ne marchait pas, car python ne dispose pas d’un moteur de calcul exact sur les réels.
Le résultat du calcul 1.42-1 en python est 0.419999999999999

Par ailleurs, stocker en réel et non un entier est susceptible de consommer plus de mémoire, donc le stockage de l’information sous la forme d’entier est préférable pour les tests conditionnels.

Une fonction pour les découvrir toutes !

A partir de la matrice, on peut exécuter un script qui révèle les positions et vérifie que tout fonctionne correctement :

def verif():
  for x in range(15):
    for y in range(10):
      if m[x][y]==666:
        mine(x,y)
      elif m[x][y]>0:
        chiffre(x,y)

La fonction chiffre elle a pour objectif d’afficher le nombre de bombes autour :

def chiffre(x,y):
  cl(x,y)
  nb[1]+=(m[x][y]%100!=42)
  i=m[x][y]-m[x][y]%100
  m[x][y]=i+42
  if i>=100:v=int(i/100);draw_string(str(v),13+20*(x),23+20*(y),c[v],c[0])
  deminage()

pas à pas de la fonction chiffre :

  • cl(x,y) éclairci la case
  • nb[1] s’incrémente de 1 si la case n’a pas déjà été découverte, c’est le compteur de la victoire
  • Si la valeur m[x][y] contenait Z00 elle sera désormais de Z42, Z étant un entier entre 0 et 8.
  • On affiche la valeur de Z, avec une couleur adaptée.
  • On exécute le script deminage(), qui calcule le score et vérifie si l’on a gagné.

Le plus difficile à coder est la fonction de découverte de proche en proche, si on dévoile une case ne contenant aucune mine, et pas de mine non plus dans les 8 cases alentours, il faut faire un « script récursif » de traitement et de découverte de ces cases.

La fonction decouvre() est la suivante :

def decouvre(x,y):
  i=1
  while i>0:
    chiffre(x,y)
    for p in range(max(0,x-1),min(15,x+2)):
      for q in range(max(0,y-1),min(10,y+2)):
        if m[p][q]>=100:chiffre(p,q)
        elif m[p][q]==0:m[p][q]+=1
    i=0
    for p in range(15):
      for q in range(10):
        if m[p][q]%100==1:i=1;x=p;y=q;p=14;q=9

Elle est appelée par la fonction marche() :

def marche(x,y):
  if m[x][y]>=999:explose()
  elif m[x][y]>=100:chiffre(x,y)
  else:decouvre(x,y)

Ainsi la fonction découverte n’est appelée que si on « marche » sur une case sans mine, et que les 8 cases aux alentours ne contiennent également aucune mine.

Elle teste les 8 cases du voisinage, affiche le chiffre de celles qui doivent afficher un chiffre. Si les cases contiennent un 0, il devient 1 et devra être traité par le script ultérieurement.

Ce fonctionnement n’est pas optimal en temps de calcul, mais il ne crée aucun nouvel objet en mémoire. Il n’est pas vraiment non plus récursif au sens python pour éviter là encore de consommer trop de mémoire.

Une précédente version ajoutait les coordonnées des cases à traiter dans une liste, mais cette liste grossissait et pouvait saturer la mémoire.

Piloter un drone sur un terrain miné

Il ne reste plus qu’à coder la navigation dans l’écran, c’est à dire l’interaction entre l’affichage et le clavier de la calculatrice.

a,r,t,l,k,s=set_pixel,fill_rect,draw_string,range,keydown,sleep
p=[[6,5],[7,5]]
 
def survol():
  x, y = p[1][0], p[1][1]
  v = m[x][y]
  gps(x,y)
  # t(str(int(v/100)),20,2)
  x, y = p[0][0], p[0][1]
  v = m[x][y]
  if p[1][0]!=x or p[1][1]!=y:
    if (v-42)%100==0:gps(x,y,0)
    else:gps(x,y,9)
  del p[0]
 
def gps(x,y,i=6):
  r(9+20*x,22+20*y,19,2,c[i])
  r(26+20*x,22+20*y,2,19,c[i])
  r(9+20*x,22+20*y,2,19,c[i])
  r(9+20*x,39+20*y,19,2,c[i])
 
def drone():
  while not k(6):
    if k(0):p.append([max(p[0][0]-1,0),p[0][1]])
    if k(3):p.append([min(p[0][0]+1,14),p[0][1]])
    if k(1):p.append([p[0][0],max(p[0][1]-1,0)])
    if k(2):p.append([p[0][0],min(p[0][1]+1,9)])
    if k(4):marche(p[0][0],p[0][1])
    if k(17) or k(16):drapeau(p[0][0],p[0][1])
    if k(52):nb[2]=0;nb[0]=22;minage(nb[0]);s(1)
    if k(45):nb[0]=min(nb[0]+5,90);minage(nb[0]);s(1)
    if k(46):nb[0]=max(nb[0]-5,10);minage(nb[0]);s(1)
    if len(p)>1:survol();s(0.120)

La fonction survol() déplace le curseur, en rétablissant l’affichage de la case précédemment survolée à l’aide de la fonction gps().

la fonction drone() attend que l’utilisateur appuie sur une touche du clavier.

ToucheCode toucheAction
Flèche haut1se déplacer
Flèche bas2se déplacer
Flèche gauche0se déplacer
Flèche droite3se déplacer
OK4marcher sur la case
Clear ou Tolbox17Afficher un drapeau
Maison6quitter la boucle
+45ajouter 3 mines et recommencer la partie
46enlever 3 mines et recommencer la partie
EXE52recommencer la partie avec les paramètres par défaut

Le nombre de mine par défaut est fixé à 19 pour un plateau de 150 cases, soit un ratio de 12.7%.

Le démineur de Microsoft sur Windows 10, appelé Minesweeper propose des ratios de 11,1%, 15%, et 20% respectivement pour les modes faciles, moyen et difficile.

Le nombre de mine qu’il est possible de placer est un nombre dans la liste :
13, 16, 19, 22, 25, …, 40 mais une petite astuce mathématiques permet de choisir n’importe quel entier entre 11 et 42 pour désigner le nombre de mine.

Oui n’importe quel nombre entier entre 11 et 42 alors que l’interface et l’utilisation des touches [+] et [-] ne permet que d’ajouter ou d’enlever 3 mines…

Epsilon version 13.2

La sortie de la version 13.2.0 d’Espilon, le logiciel qui anime la NumWorks permet de faire tourner sans problème ce démineur. Si dans la version 13.0.0 et 13.1.0 le démineur ne fonctionnait pas à cause d’une affectation insuffisante de mémoire pour l’exécution des scripts python, la version 13.2.0 résout ce problème qui n’est plus qu’un mauvais souvenir. Ainsi, la NumWorks est l’une des calculatrice la plus rapide lors de l’exécution des scripts Python, et que pour un prix d’achat de 79€ elle n’a clairement aucune concurrence dans cette gamme de prix.

Omega, un firmware alternatif pour votre NumWorks

Le logiciel de la NumWorks est libre et ouvert, ce qui est très appréciable et on peut même dire admirable dans l’univers très fermé des calculatrices.

Ainsi, tout le monde peut accéder au code source, le lire, le modifier et le redistribuer en respectant les conditions imposées par la licence CC BY-NC-SA.

Une équipe de passionné a donc décidé de développer un firmware alternatif, et il l’ont appelé Omega. Ils intègrent différentes amélioration, et poussent les developpeur du firmware stock à s’améliorer sans cesse.

Pour tester ce démineur, tu dois :

  • Soit mettre à jour ta calculatrice en version 13.2.0 au minimum.
  • Soit installer le firmware Omega.
InstallerLien hypertexte
1. Installer Le firmware Omegaen quelques clics
1. Ou mettre à jour Epsilonen quelques clics
2. Installer le démineurworshop

Le démineur en version 1.20

Une nouvelle version de ce démineur est sortie le 18 avril 2020, elle intègre un menu graphique python développé en interne pour cette occasion.

Le script du démineur pèse désormais 8.42ko, et le démineur peut désormais être paramétré par un menu graphique, interactif et intuitif.

De nouvelles options ont été ajoutées :

  1. Le choix du graphisme : Flat vs Classique
  2. Un mode triche à tester avec la touche MAJ de l’ordinateur ou Shift sur la calculatrice.

Quelques dernières remarques

Si tu souhaites réagir à cet article, tu es invité à le faire sur ce forum :
[tiplanet.org] Un démineur en python pour la NumWorks, tu y trouvera des discussions pointues sur l’utilisation de la mémoire lors de l’éxécution d’un script Python.

Entre le script initial et le script proposé en ligne à cet instant, de nombreuses réécritures portions du code ont été réécrites, mais les explications de cet article restent valables.

Tutoriels

Jouer à des jeux Wii sur son PC

Vous voulez jouer aux jeux de votre enfance sur votre pc ? Grace a l’émulateur Dolphin, c’est possible !


Remarque des enseignants :

Le sujet de la légalité des émulateurs est un sujet complexe qui nécessiterait un traitement spécifique et de solides compétences en droit. Le tutoriel est publié mais tous les liens externes ont été supprimés. Si vous avez « surement cassée, ou vendue, votre console de jeu préférée… » ce tutoriel ne vous concerne pas car vous seriez dans l’illégalité.


Aah, la Wii. Cette console a marqué de nombreuses générations, mais surtout la notre…

Malheuresement, ce n’est désormais plus possible d’y jouer sur un écran de télévision récent, la faute a des entrées vidéos qui n’existent plus. De plus, vous avez surement cassée, ou vendue, votre console de jeu préférée…

Mais pas de panique ! Si vous avez soudainement envie de replonger dans vos jeux favoris, il existe un moyen d’y jouer directement sur votre PC, grâce à ll’émulateur Dolphin !

Ceci est tiré du site Internet de Dolphin, que je vous donnerai juste après :
« Dolphin est un émulateur pour deux récentes consoles de jeu de Nintendo : la GameCube et la Wii. Il permet aux joueurs sur PC d’apprécier les jeux réalisés pour ces deux consoles en full HD (1080p) avec différentes améliorations : compatibilité avec tous les contrôleurs de PC, vitesse turbo, jeu en ligne en réseau, et bien plus encore ! »

Configuration minimale

Tout d’abord, vérifions si votre PC peut faire tourner des jeux Wii :

Pour la carte graphique, n’importe quelle carte de l’actuel milieu de gamme sera suffisante pour jouer correctement à Dolphin en HD. Par contre si votre carte graphique a 6 ans ou plus, le jeu risque d’être ralenti.

Pour le processeur, tous les Ryzen sont parfaits, ou alors au minimum un intel i5.

Bon, vous ne pouvez pas jouer à Pokémon sur un ordi portable de 2005, vous l’avez compris, mais si vous avez un ordinateur dans la limite du correct, vous pouvez bien vous amusez !

Installation du logiciel

Dans un premier temps il faut télécharger l’émulateur.

Normalement, un fichier rar (icone avec les livres) va se télécharger.

Vous croyez que c’était fini ? Et non, car maintenant, il faut le décompresser

Pour décompresser un fichier, il faut télécharger un logiciel permettant de le faire, comme par exemple WinRar, dont le lien pour le télécharger est juste 

Installez le logiciel, puis revenez sur votre fichier RAR : Clic droit dessus, appuyer sur « WinRar », puis sur « Extraire vers…. »

Vous allez maintenant vous retrouvez avec un dossier « dolphin ».
Ouvrez le, et vous tomberez sur l’application « dolphin.exe »
Double cliquez dessus, et l’installation de Dolphin pourra commencer.
Une fois l’installation terminée, lancez Dolphin.

Si vous avez suivi toutes les étapes, une fenetre comme celle là va s’afficher :

De toute évidence, pour jouer a un jeu, il faut d’abord le télécharger. Toute les manipulations expliquées ici sont faites sur Mario Kart Wii. Pour télécharger le jeu, c’est sur lien supprimé.

Pour lancer le jeu, cliquez sur « Open », et choisissez le fichier se nommant « MKW ».
Mais ne lancez pas le jeu maintenant ! Car, une question se pose : Comment controler le jeu ?

Manettes

Les manettes de la Wii, les Wiimotes, peuvent être connectées sur votre PC, mais c’est assez compliqué.

Je vous laisse sur ce tutoriel, pour ce qui veulent essayer

On peut aussi jouer avec votre clavier, mais c’est vraiment pas amusant, et vraiment pas pratique

Le plus simple serait de connecter une manette style Playstation ou Xbox, qui sont très courante et disponible a petit prix ( Vous en avez pour 5€ en Chine ).

Moi j’utilise une manette Xbox, mais vous pouvez utiliser une autre manette, c’est exactement pareil.

Pour configurer vos manettes, cliquez sur le bouton « Controllers », toujours sur Dolphin. Cette fenêtre là devrait apparaitre :

Allez dans la parties « Wiimotes », choisissez « Emulated Wiimote » dans le menu déroulant, puis cliquez sur le bouton configurer.

Cette page devrait apparaître :

Ici, vous pouvez configurer tout les boutons de votre manette.
Si vous voulez remplacer le bouton A par votre bouton B, cliquez sur Button A, et appuyer sur le bouton B de votre manette.
Je sais pas si c’est très clair, mais vous comprendrez tout seul, c’est très intuitif.
Il y a deux façon pour tout configurer correctement :
Vous pouvez soit essayer des combinaisons, pour avoir une configuration que vous aimez, soit aller voir sur Internet, ou il y a des configurations toutes faites. Moi, j’ai pris ma configuration Mario Kart sur Internet, parce que c’est quand super long de tout chercher a la main. Pour ceux qui sont intéressée par jouer à Mario Kart, ma config est sur le screen si dessus.

Quand votre manette est configurée, vous pouvez enfin commencer a vous amusez.

Voila une petite vidéo « gameplay », pour vous montrez a quoi le jeu ressemble sur PC. Le jeu tourne parfaitement bien et tout est très fluide.

Légalité

Attention : Tout ceci est il bien légal ?

(note des enseignants : nous vous invitons à relire le message introductif.)

Voila un extrait de la loi française :

-Il est entièrement légal de posséder, créer et télécharger un émulateur du fait que ce dernier tend à reproduire une console sans la copier pour autant.

-En revanche, le BIOS nécessaire à beaucoup d’émulateur ne peut pas être téléchargé et est soumis à la même loi que les ROMS et ISOS.

-Les sauvegardes de données sont autorisées dans un cadre personnel, vous devez posséder le jeu et ne pouvez extraire les données que de votre jeu. Il est illégal de télécharger des ISOS et/ou ROMS dans la mesure où il ne s’agit pas de vos propres données (que vous ayez ou non le jeu en question).

Et bien, oui, c’est légal, à condition que vous possédez une WII, et que vous possédez le jeu auquel vous jouez. Sinon, vous risquez une amende de 10000 €.

Mais rassurez vous, c’est assez rare que en Europe, les éditeurs de jeux vidéos (Nintendo dans ce cas), sanctionne ceux qui utilisent des émulateurs. 

(note des enseignants : ce n’est pas parce que la justice sanctionne peu que cela rend un usage illégal licite !)

Par contre, en Chine, c’est formellement interdit d’émuler, et la sanction peut aller jusqu’à la prison…

PS : Tout ce tutoriel marche aussi avec les jeux Gamecube, tout est pareil !

Projets

Le jeu du pendu en Python

Le Pendu est un jeu consistant à trouver un mot en devinant quelles sont les lettres qui le composent. Dans cet article, nous allons analyser ensemble un jeu du Pendu, fait en Python. Contrairement aux apparences, ce programme peut être très dur a réaliser, surtout pour les non-initiés.

L’article comme le code produit ci-dessous sont largement améliorable. Ce jeu inclus quelques petits bugs non résolus.

Introduction

Tout d’abord, qu’est ce qu’on attend d’un jeu du Pendu, en Python :

  • L’ordinateur choisit un mot au hasard dans une liste, un mot de huit lettres maximum. – Le joueur tente de trouver les lettres composant le mot.
  • À chaque coup, il saisit une lettre.
  • Si la lettre figure dans le mot, l’ordinateur affiche le mot avec les lettres déjà trouvées. -Celles qui ne le sont pas encore sont remplacées par des barres (_).
  • Le joueur a 6 chances. Au delà, il a perdu.

Le début du code

 print("Bienvenue dans le jeu du Pendu")
play=int(input("Tape 1 si tu veux jouer ! \n "))
if play == 1 :  
    prénom=input("Quel est ton nom ?")
    print("\n")
    print("Salut", prénom)
    import random
    liste_mots=["laitue", "hareng", "jambon", "pharynx", "phoque", "langue",
                "stylo","agent","fromage","whisky","billet","boyaux",
                "laser","joystick","crane","joyeux","cahier","camping","argent",
                "rivage","physique",]

Dans cette première partie de code, il ne faut SURTOUT pas oublier le import random, car on utilise beaucoup d’aléatoire dans ce petit jeu.
Sinon, par grand chose d’important a relever, a part la liste de mot que le jeu va utiliser pour le jeu.

Initialisations

 score = 0
    print("Tu as 6 vies")
    print("\n")
    vie = 6

La non plus, c’est pas super intéressant…
On définit juste les variables des vies et du score

while play == 1 :
        vie = 6
        mot=(liste_mots[random.randint(0,21)])
        longueur=len(mot)
        barre=["_ "]
        barre=barre*longueur
        grandeur=longueur

Attention : Ça se corse…
Avec la ligne « mot=(liste_mots[random.randint(0,21)]) », le programme choisi un mot au hasard dans la liste de mots du début.

Dans le jeu du Pendu, les lettres qui n’ont pas été trouvés sont remplacées par des barres (_), et c’est ce qu’on règle cette portion :
longueur=len(mot) calcule le nombre de lettres du mot choisi, puis barre=barre*longueur affiche le nombre de « _ » en fonction du nombre de lettres.
On l’affiche sous forme de liste, car ce sera beaucoup plus facile pour la suite du programme.
La variable « grandeur » est la même que « longueur », sauf que « grandeur » va (aussi) servir pour la suite du programme

Le jeu

  while vie!=0 and grandeur!=0 :
            lettre_choisi = input("Choisi une lettre  ")
            print("\n")
            if lettre_choisi in mot :
                print("Bravo!")
                if lettre_choisi in barre:
                    print ("Tu l'as déja dit !")
                    resultat = ' '.join(barre)
                    print(resultat)
                else:
                    position=int(mot.index(lettre_choisi))
                    barre.pop(position)
                    barre.insert(position,lettre_choisi)
                    resultat = ' '.join(barre)
                    print(resultat)
                    grandeur=grandeur-1

Tiens tiens, on retrouve « grandeur » !
En fait, grandeur sert a dire au programme quand le joueur a trouvé toute les lettres, car si le mot fait par exemple 7 lettres, on pourra logiquement trouver la bonne lettre 7 fois.
Ensuite, le programme nous demande de choisir une lettre (désolé pour la faute d’orthographe « lettre_choisi » ), et si la lettre est dans le mot, alors le programme continue.

Voila la principale difficulté du programme :L’affichage du mot
En effet, il faut afficher le mot avec toutes les lettres qu’on a trouvées.
Tout d’abord, on repére la position de la lettre trouvée avec « position=int(mot.index(lettre_choisi)) », puis on insère la lettre choisie dans la liste « barre » (_ _ _ _ _ _ _) a la bonne position. On supprime aussi une (_) pour que le mot reste a la bonne longueur avec « barre.pop(position) ».
Enfin, on transforme la liste en chaîne de caractère pour que l’affichage soit plus clair, pour finir dans la variable « résultat »

Avant de continuer, je voudrai vous montrer ce qui se passe si on met une lettre qu’on a déjà mise :
C’est exactement comme auparavant, sauf que grandeur ne diminue pas, et qu’il n’y a pas besoin d’afficher une lettre de plus, car elle a déjà été affichée

Trouver toutes ces formules m’ont causé BEAUCOUP de problèmes, donc j’espère que ce sera clair pour vous.

Sinon, si la lettre choisie n’est pas dans le mot, alors :

            else:
                print("Raté")
                if grandeur==longueur :
                    print(longueur*"_ ")
                else:
                    print (resultat)
                vie=vie-1
                print("Il te reste",vie,"vies")
                print("\n")

Sur cette partie, si le joueur se trompe des la première lettre, le programme ne peut pas afficher la variable « résultat », car elle n’existe pas encore. Le programme affiche donc seulement le mot sous forme de barre (_). Dans tout les cas, une vie est retirée.

La fin du jeu

   if vie==0 :
            print("Tu as perdu")
        elif grandeur==0 :
            print("Bravo ! Tu as trouvé le mot !")
            score=score+5
            print("Tu a gagné 5 points !")
        replay=int(input("Tape 1 pour rejouer, et sur 2 si tu veux quitter le jeu   "))
        if replay != 1 :
            break
    print(prénom,"vous avez un score de ",score)

Evidemment, si vos vies tombent à 0, vous perdez.
Si vous gagnez, vous gagnez 5 points. A 50 points vous avez une surprise….
Vous pouvez rejouer en tapant 1, et vous quittez le programme en tapant 2

Voila, j’espère vous avoir aidés a comprendre ce petit programme !
N’hésitez pas a le personnaliser, avec vos propres fonctions, ou avec quelques easters eggs sympathiques !

Le code entier est téléchargeable en bas de la page !