Étiquette : NumWorks

Projets

Faites une partie de chasse au trésor sur la…

Partez à l’aventure et déterrez le trésor caché tout en prenant garde aux pièges dissimulés aux alentours ! Personnalisez également votre expérience grâce à trois seuils de difficulté et des cartes générées aléatoirement. Aurez-vous l’audace de vous mesurer à ce jeu de logique inspiré du démineur ?

Cliquez sur ce lien afin d’accéder aux règles du jeu.

Un projet sous couche graphique

Bien avant que le projet libre soit annoncé, j’avais créé un jeu de chasse au trésor sur Python, sous couche textuelle. La console de mon IDE affichait les différentes cases grâce à un print() et l’utilisateur devait entrer les coordonnées de la case à retourner.

Cette façon de procéder, bien qu’utile afin de tester un concept de jeu, demeure tout de même lente et peu accessible. Un jeu comme celui du démineur doit fournir un moyen rapide d’interagir avec lui. C’est la raison pour laquelle j’ai décidé de rénover ce jeu sous couche graphique, c’est-à-dire avec des sprites au lieu de chaînes de caractères.

Les bibliothèques Python

Tout au long du code sont exploitées deux bibliothèques Python : kandinsky et ion.

La bibliothèque kandisky propose deux fonctions utiles afin d’afficher des sprites et du texte : fill_rect(), qui trace un rectangle, et draw_string(), qui affiche du texte.

La bibliothèque ion, quant à elle, permet de détecter les touches poussées sur la calculatrice.

Le bout de code suivant importe les fonctions utiles de ces bibliothèques et crée des diminutifs afin de les appeler, en l’occurrence « fr » pour « fill_rect » et « ds » pour « draw_string » :

from kandinsky import fill_rect as fr
from kandinsky import draw_string as ds
from ion import keydown

Afin d’avoir un aperçu de mon code sur la calculatrice, j’ai installé sur mon IDE les plugins ion-numworks et kandinsky grâce à ce tutoriel.

La logique du jeu

Afin de bien coder ce jeu, il fallait que je comprenne la logique derrière celui-ci.

En effet, le jeu est divisé en deux plateaux : le plateau visible et le plateau caché.

En effet, le plateau visible est une grille de dimensions 5 x 5 remplie de chiffres. Le plateau caché, grille de mêmes dimensions, contient quant à lui le trésor, les piques et les couleurs. Les deux plateaux interagissent entre eux.

Le plateau caché serait initialisé en premier. Il recevrait le trésor et on placerait les couleurs en fonction de l’emplacement du trésor. Enfin, on positionnerait les piques et on changerait trois couleurs afin de brouiller les pistes.

Ensuite, on initialiserait le plateau visible et placerait les nombres en fonction de l’emplacement des piques. De ce fait, à chaque fois que le joueur retournera une case, une couleur, un pique ou le trésor sera affiché.

L’initialisation des plateaux

La réalisation du programme a commencé par la fonction init_plateau() qui, comme son nom l’indique, initialise les plateaux. Le plateau caché, dans le script, est représenté par la variable plateau_inf qui est une liste composée de cinq listes elles-mêmes composées de cinq éléments :

plateau_inf = [["V", "V", "V", "V", "V"],
               ["V", "V", "V", "V", "V"],
               ["V", "V", "V", "V", "V"],
               ["V", "V", "V", "V", "V"],
               ["V", "V", "V", "V", "V"]]

Ici, les listes sont remplies par la lettre « V » afin de ne pas avoir à placer la couleur verte. En effet, après avoir placé le trésor, les piques, les cases rouges et les cases orange, il ne reste plus que les cases vertes qui n’ont pas été touchées.

Ensuite, on place le trésor. Pour cela, j’utilise le module random et en particulier sa fonction randint() afin de choisir aléatoirement les coordonnées de la case :

def init_plateau():
    # -------------------- plateau inférieur
    # placement du trésor
    tresor_x, tresor_y = randint(0, 4), randint(0, 4)    
    plateau_inf[tresor_x][tresor_y] = "X"

Puis, on positionne les couleurs. Puisque la variable plateau_inf est remplie de « V », il nous suffit de placer des « R » et des « O » (pour rouge et orange).

Le bout de code suivant peut être plutôt hermétique. Ce dernier vérifie si les huit cases autour du trésor sont bien à l’intérieur de la grille, et si c’est le cas, on y place un « R ». Le même processus est adopté pour les seize cases autour des cases rouges afin d’y ajouter un « O » :

def init_plateau():
    # -------------------- plateau inférieur
    # placement du trésor
    # [...]
    # placement des cases rouges et orange
    for i in range(tresor_x - 2, tresor_x + 3):
        for j in range(tresor_y - 2, tresor_y + 3):
            if 0 <= i <= 4 and 0 <= j <= 4 and (i, j) != (tresor_x, tresor_y):
                if abs(tresor_x - i) <= 1 and abs(tresor_y - j) <= 1:
                    plateau_inf[i][j] = "R"
                elif abs(tresor_x - i) <= 2 and abs(tresor_y - j) <= 2:
                    plateau_inf[i][j] = "O"

Ensuite, on place les piques en prêtant bien attention à ce qu’un pique ne tombe pas sur le trésor et que deux piques ne soient pas sur la même case. On utilise la variable global nb_piques qui pourra être modifiée en fonction de la difficulté choisie :

nb_piques = [11]

def init_plateau():
    # -------------------- plateau inférieur
    # placement du trésor
    # [...]
    # placement des cases rouges et orange
    # [...]
    # placement des piques
    for _ in range(nb_piques[0]):
        while True:
            pique_x = randint(0, 4)
            pique_y = randint(0, 4)
            if plateau_inf[pique_x][pique_y] != "S" and plateau_inf[pique_x][pique_y] != "X":
                plateau_inf[pique_x][pique_y] = "S"
                break

Enfin, on met en place les fausses couleurs. Ce bout de code s’appuie sur beaucoup de conditions : il faut que la case choisie au hasard soit une case de couleur, et non un pique ou le trésor, et que cette case ne soit pas déjà une case colorée par une fausse couleur. Si ces conditions sont remplies, on peut changer la couleur de la case choisie :

def init_plateau():
    # -------------------- plateau inférieur
    # placement du trésor
    # [...]
    # placement des cases rouges et orange
    # [...]
    # placement des piques
    # [...]
    # placement des fausses couleurs
    for _ in range(nb_fausses[0]):
        fausses_originales = []
        while True:
            fausse_x, fausse_y = randint(0, 4), randint(0, 4)
            if plateau_inf[fausse_x][fausse_y] != "S" and plateau_inf[fausse_x][fausse_y] != "X":
                if (fausse_x, fausse_y) not in fausses_originales:
                    fausses_originales.append((fausse_x, fausse_y))
                    while True:
                        espace_nouv = randint(0, 2)
                        if espace_nouv == 0 and plateau_inf[fausse_x][fausse_y] != "V":
                            plateau_inf[fausse_x][fausse_y] = "V"
                            break
                        elif espace_nouv == 1 and plateau_inf[fausse_x][fausse_y] != "O":
                            plateau_inf[fausse_x][fausse_y] = "O"
                            break
                        elif espace_nouv == 2 and plateau_inf[fausse_x][fausse_y] != "R":
                            plateau_inf[fausse_x][fausse_y] = "R"
                            break
                    break

Et voilà ! Nous avons fini d’initialiser le plateau caché. Il ne reste plus que le plateau visible. Pas d’inquiétudes : celui-ci prend moins de temps à programmer. Il suffit de parcourir le plateau caché, et, lorsque l’on rencontre un pique, on ajoute 1 aux cases adjacentes du plateau visible (si elles se trouvent dans la grille) :

plateau_sup = [[0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0]]

def init_plateau():
    # -------------------- plateau inférieur
    # placement du trésor
    # [...]
    # placement des cases rouges et orange
    # [...]
    # placement des piques
    # [...]
    # placement des fausses couleurs
    # [...]
	# -------------------- plateau supérieur
    for ligne in range(0, 5):
        for colonne in range(0, 5):
            if plateau_inf[ligne][colonne] == "S":
                if ligne < 4: plateau_sup[ligne + 1][colonne] += 1
                if colonne < 4: plateau_sup[ligne][colonne + 1] += 1
                if ligne > 0: plateau_sup[ligne - 1][colonne] += 1
                if colonne > 0: plateau_sup[ligne][colonne - 1] += 1

La programmation du gameplay

À présent que les plateaux sont programmés, il faut permettre au jouer d’interagir avec eux. Cela se fait grâce à la fonction jeu().

Cette fonction peut être décomposée en deux parties : l’initialisation et la boucle.

Commençons par l’initialisation. Celle-ci permet de tracer la grille et afficher les chiffres et couleurs à l’écran :

def jeu():
    x, y = 0, 0
    reset()
    init_plateau()
    fr(0, 0, 320, 240, 'white')
    grille()

La fonction jeu() commence par instancier les coordonnées du curseur déplaçable. Ensuite, elle réinitialise les plateaux et autres variables grâce à la fonction reset(), appelle la fonction init_plateau() vue précédemment et trace une grille grâce à la fonction grille() (tirée de ce script).

def reset():
    for x in range(5):
        for y in range(5):
            plateau_sup[x][y] = 0
            plateau_inf[x][y] = 'G'
    cases_retournees.clear()
    cases_marquees.clear()
    vies[0] = 3

def grille():
    for i in range(6):
        if i < 6:
            fr(70, 31 + 35 * i, 176, 1, 'black')
        fr(70 + 35 * i, 31, 1, 176, 'black')

Souvenez-vous : « fr » est le diminutif de la fonction fill_rect() du module kandinsky. En vérité, on trace une multitude de rectangles très fins qui se croisent afin de créer la grille.

Les variables cases_retournees, cases_marquees et vies présentes dans la fonction reset() seront expliquées juste après.

Nous voilà à présent dans la boucle de la fonction. Cette boucle non-bornée se répète continuellement grâce aux mots-clefs « while True » :

vies = [3]

def jeu():
    # [...]
    while True:
        fr(100, 10, 130, 15, 'white')
        ds("<3\t" * vies[0], 88, 8, 'black')
        affiche()

Ici, le nombre de vies est affiché grâce à la variable globale vies. Entre autres, la fonction affiche() est appelée :

rouge = (249, 65, 68)
orange = (243, 114, 44)
vert = (144, 190, 109)

cases_retournees = []
cases_marquees = []

def affiche():
    for coord1 in cases_marquees:
        fr(71 + 35 * coord1[0], 32 + 35 * coord1[1], 35, 2, rouge)
        fr(71 + 35 * coord1[0], 32 + 35 * coord1[1], 2, 35, rouge)
        fr(71 + 35 * coord1[0], 64 + 35 * coord1[1], 35, 2, rouge)
        fr(103 + 35 * coord1[0], 32 + 35 * coord1[1], 2, 35, rouge)
    for coord2 in cases_retournees:
        if plateau_inf[coord2[0]][coord2[1]] == 'G':
            couleur = vert
        elif plateau_inf[coord2[0]][coord2[1]] == 'O':
            couleur = orange
        elif plateau_inf[coord2[0]][coord2[1]] == 'R':
            couleur = rouge
        elif plateau_inf[coord2[0]][coord2[1]] == 'S':
            couleur = 'grey'
        fr(71 + 35 * coord2[0], 32 + 35 * coord2[1], 34, 34, couleur)

Cette fonction, en premier lieu, parcourt la liste cases_marquees et ajoute un contour rouge à chaque case dont les coordonnées sont présentes dedans. Ce contour est composé de quatre rectangles fins qui forment un carré.

affiche() parcourt également la liste cases_retournees qui retient les cases dont il faut afficher la couleur si elles ne cachent pas un pique. En fonction du contenu du plateau caché, la fonction attribue une couleur différente à chaque case.

Cette première partie de la boucle se termine par l’affichage des nombres sur la grille. Cependant, la tâche n’est pas aussi aisée qu’en apparence. En effet, lorsqu’une ligne de texte est affichée grâce à la fonction draw_string() (représentée ici par « ds »), son fond est blanc par défaut.

Seulement, si une case est retournée et est donc colorée, afficher un chiffre sur fond blanc dessus est peu esthétique :

Il faut donc changer le fond de la ligne de texte affichée en fonction de la case sur laquelle elle se trouve :

def jeu():
    # [...]	
		for a in range(0, 5):
            for b in range(0, 5):
                couleur = 'white'
                if (a, b) in cases_retournees:
                    if plateau_inf[a][b] == 'G':
                        couleur = vert
                    elif plateau_inf[a][b] == 'O':
                        couleur = orange
                    elif plateau_inf[a][b] == 'R':
                        couleur = rouge
                    elif plateau_inf[a][b] == 'S':
                        couleur = 'grey'
                ds(str(plateau_sup[a][b]), 83 + 35 * a, 41 + 35 * b, 'black', couleur)

Les boucles bornées imbriquées l’une dans l’autre génèrent toutes les coordonnées possibles sur une grille de dimensions 5 x 5. Ensuite, on observe lesquelles de ces coordonnées correspondent à des cases retournées, et on colorie le fond du texte en fonction de la case.

Enfin, passons au cœur de la boucle. Il y a beaucoup d’éléments à analyser.

def jeu():
    # [...]	
		while True:
            curseur(x, y, jaune)
            x_bis, y_bis = x, y
            touche = attente([0, 1, 2, 3, 4, 52, 17])

La boucle infinie commence par afficher le curseur du joueur aux bonnes cordonnées grâce à la fonction curseur() :

def curseur(x, y, c):
    fr(70 + 35 * x, 31 + 35 * y, 35, 1, c)
    fr(70 + 35 * x, 31 + 35 * y, 1, 35, c)
    fr(70 + 35 * x, 66 + 35 * y, 35, 1, c)
    fr(105 + 35 * x, 31 + 35 * y, 1, 35, c)

Ce curseur est formé de quatre rectangles fins.

Ensuite, la boucle utilise la fonction attente() (tirée de ce script) afin d’enregistrer les touches poussées par le joueur :

def attente(touches):
    while True:
        for nb in touches:
            if keydown(nb):
                while keydown(nb):
                    True
                return nb

Pourquoi ne pas simplement utiliser la fonction keydown() fournie par le module ion ? C’est tout bonnement simple : on veut détecter la touche poussée par l’utilisateur lorsque celui-ci relâche la touche. En effet, en utilisant keydown(), si le joueur reste appuyé sur une touche, alors, avec la boucle non-bornée, le mouvement serait répété plusieurs fois. Cela rendrait l’expérience de jeu ingérable puisque le curseur se déplacerait sur la longueur ou largeur de la grille au moindre appui de touche. Il nous faut donc attendre que l’utilisateur relâche la touche, et c’est ce que nous permet la fonction attente().

En ce qui concerne le reste de la fonction jeu(), selon la touche poussée, on effectue différentes actions.

def jeu():
    # [...]	
			if touche == 4 or touche == 52:
                if (x, y) not in cases_retournees:
                    cases_retournees.append((x, y))
                    verif(x, y)
                    break

Dans le cas ou le joueur appuie sur la touche [OK], on s’assure que la case sur laquelle il se trouve n’est pas déjà retournée, et, dans ce cas, on la retourne. Enfin, on appelle la fonction verif() qui vérifie l’état de la partie, c’est-à-dire si le joueur a gagné, perdu, ou s’il continue de jouer :

def verif(x, y):
    if plateau_inf[x][y] == 'X':
        fr(80, 60, 160, 100, 'black')
        ds("Bien joué !", 110, 100, 'white', 'black')
        attente([4, 52, 17])
        menu()
    elif plateau_inf[x][y] == 'S':
        vies[0] -= 1
        if vies[0] < 1:
            fr(90, 10, 130, 15, 'white')
            fr(80, 60, 160, 100, 'black')
            ds("Perdu !", 130, 100, 'white', 'black')
            attente([4, 52, 17])
            menu()

Si le joueur a retourné la case qui cachait le trésor, on le ramène au menu ; si, en revanche, celui-ci tombe sur un pique, on lui retire une vie, puis, on jette un coup d’œil au nombre de vies qu’il lui reste, et s’il est à zéro, on ramène le joueur au menu. La fonction menu() sera présentée plus tard.

De retour à la boucle. Si le joueur appuie sur la touche [DEL], on s’assure que la case sur laquelle il se trouve n’est pas déjà marquée, et, dans ce cas, on la marque.

def jeu():
    # [...]	
		elif touche == 17:
            if (x, y) not in cases_marquees:
                cases_marquees.append((x, y))
            break

Les deux bouts de code de la boucle présentés contiennent tous deux le mot-clef « break ». Ce dernier permet de sortir de la boucle secondaire afin de revenir dans la boucle principale de sorte que l’écran puisse être « rafraîchi », c’est-à-dire que les sprites soient mis à jour.

Enfin, en fonction de la flèche de navigation poussée par le joueur, on change les coordonnées de son curseur si celui-ci ne s’évade pas de la grille, puis on colore en noir l’emplacement précédent du curseur.

def jeu():
    # [...]	
		elif x > 0 and touche == 0:
            x -= 1
        elif x < 4 and touche == 3:
            x += 1
        elif y > 0 and touche == 1:
            y -= 1
        elif y < 4 and touche == 2:
            y += 1
        curseur(x_bis, y_bis, 'black')

Et nous voilà à la fin de la fonction jeu(). Le plus dur est dernière nous !

La mise en place du menu principal

La fonction menu() permet d’afficher les différents seuils de difficulté et d’accéder au tutoriel. Cette fonction est la première a être appelée.

Tout comme la fonction jeu(), menu() est composée d’une initialisation et d’une boucle.

violet = (148, 113, 222)
choix = 0

def menu():
    global choix
    fr(0, 0, 320, 240, 'white')
    fr(0, 200, 320, 40, jaune)
    ds('Chasse au trésor', 80, 20, 'black')
    ds('-----------------', 75, 40, 'black')
    ds("Code by nsi.xyz/chasse-au-tresor", 0, 202,'white', violet)

Durant l’initialisation, on affiche tout simplement quelques lignes de texte telles que le titre du jeu. Rien de bien compliqué. Il serait cependant intéressant de faire remarquer que la variable choix est placée hors de la fonction avant d’être rendue globale grâce au mot-clef « global ».

Pourquoi donc ? Eh bien, puisque la variable est hors de la fonction, sa valeur n’est pas écrasée lorsque l’on appelle à nouveau la fonction. Ainsi, le jeu peut se souvenir du choix du joueur lorsque celui-ci navigue parmi les options.

Passons à présent à la boucle de la fonction :

choix_couleurs = {0: ('Facile', 128, 70, vert),
                  1: ('Moyen', 133, 100, orange),
                  2: ('Difficile', 115, 130, rouge),
                  3: ('Tutoriel', 118, 160, jaune)}

def menu():
    # [...]
	while True:
        for i in range(4):
            texte, x, y, couleur = choix_couleurs[i]
            ds(texte, x, y, couleur if i == choix else 'black')

Cette partie de la boucle n’est pas aussi compliquée qu’il n’y paraît. En effet, selon le choix du joueur, elle affiche les quatre options disponibles dans différentes couleurs en exploitant un dictionnaire. Cela permet donc à l’utilisateur de visualiser ce qu’il s’apprête à faire.

La seconde partie de la boucle est également simple. Si le joueur appuie sur la flèche du haut ou du bas, la variable choix est mise à jour. Les conditions supplémentaires aux lignes 4 et 6 permettent de ne pas sortir des quatre options possibles :

def menu():
    # [...]
        touche = attente([1, 2, 4, 52])   
            if touche == 1 and choix > 0:
                choix -= 1
            elif touche == 2 and choix < 3:
                choix += 1
            elif touche == 4 or touche == 52:
                if choix == 0:
                    nb_piques[0] = 11
                    jeu()
                if choix == 1:
                    nb_piques[0] = 13
                    jeu()
                if choix == 2:
                    nb_piques[0] = 15
                    jeu()
                if choix == 3:
                    tutoriel()

En revanche, si l’utilisateur pousse la touche [OK], on modifie le nombre de piques selon la difficulté choisie, puis on se rend au jeu ou tutoriel. Et voilà !

Mot de fin

Créer ce projet sous couche graphique sur la calculatrice a été une expérience enrichissante. Non seulement ai-je été obligé de bien réfléchir à la logique de mon jeu en accord avec celle de mon programme, mais j’ai également dû coder avec les contraintes de mémoire de la calculatrice.

Mon script, j’en suis certain, pourrait être bien davantage optimisé ; mais je pense avoir fait du mieux que j’ai pu dans le temps imparti.

Je tiens également à faire remarquer que je n’ai pas abordé la fonction tutoriel() étant donné que celle-ci n’est composée que de lignes de texte affichées par la fonction draw_string(), et n’est par conséquent pas des plus captivantes à analyser.

Merci d’avoir lu jusqu’ici !

Lien

Voici le lien du jeu qui amène à mon workshop :

NumApps

Une chasse au trésor sur la NumWorks

Explorez un plateau et déterrez le trésor caché tout en évitant les pièges dans ce jeu inspiré du démineur !

Règles

Le jeu se déroule sur une grille de dimensions 5 x 5 remplie de chiffres. Celle-ci cache des pics et un trésor à découvrir afin de gagner.

Chaque chiffre représente le nombre de cases adjacentes qui cachent un pique. Cela signifie que chaque chiffre peut s’étendre de 0 à 4 (les cases en diagonale ne sont pas comptées comme adjacentes, contrairement au démineur).

Vous pouvez choisir de retourner la case que vous désirez. Si la case retournée abrite le trésor, vous gagnez la partie. En revanche, si elle cache un pique (représenté par la couleur grise), vous perdez une vie. Lorsque vos trois vies sont épuisées, vous avez échoué.

Dans le cas où la case est vide, celle-ci affiche une couleur qui vous renseigne sur sa distance par rapport au trésor :

  • Une case verte est éloignée du trésor ;
  • Une case orange est à deux à quatre cases du trésor ;
  • Une case rouge est à une à deux cases du trésor.

Voyez le schéma ci-dessous :

Toutefois, ne vous laissez pas berner ! Parmi les cases colorées, trois sont fausses et indiquent la mauvaise couleur. La disposition des couleurs peut davantage ressembler à cela :

Ainsi, votre but est de découvrir suffisamment de cases colorées afin de pouvoir estimer la position du trésor. Il se peut tout à fait que vous retourniez la case gagnante du premier coup, donc n’hésitez pas à faire plusieurs parties !

Commandes

◁ ▷OKDEL
Naviguer dans les menus
Déplacer le curseur
Sélectionner une option
Retourner une case
Marquer une case*
Retour

* Marquer une case, contrairement au démineur, n’empêche pas de la retourner. Cette fonctionnalité sert simplement d’aide visuelle au joueur. Également, une fois qu’une case est marquée, on ne peut plus retirer la marque.

Captures d’écran

Pour plus d’informations

Jetez un coup d’œil à cet article si vous souhaitez en apprendre plus sur le développement du jeu !

Lien

Voici le lien vers le jeu depuis mon workshop :

Projets

Comment a été créé Pong

Dans cet article, nous allons nous interresser à la programmation et à l’architecture de la version 2 de Pong. Le jeu est écrit en Python pour numworks sous Oméga, car cette version offre plus de tas (100ko contre 32ko dans la version officielle).

Pourquoi une seconde version ?

La première version avait une architecture assez peu soignée car je ne l’avais pas pensé de manière globale. De plus, elle avais des manques de compression assez flagrants car elle fesait plus de 12ko. Pour le petite anecdote, j’avais du la compresser énormément pour pouvoir la faire tourner car elle souffrait d’un dépassement de mémoire. Paradoxalement, c’est un de mes jeux qui a le mieux marché, sans doute car il était très complet. Mais je ne voulais pas laisser à un programmeur qui tombe sur le jeu l’image que c’est un travail que je trouve a posteriori presque bâclé. J’ai donc entrepris de le réécrire.

Première étape : programmer les menus.

Moteur de menu

Pour une fois, j’ai voulu commencer par programmer le menu (j’ai appelé le moteur menulib). Je pourrais alors adapter l’architecture du moteur de jeu à l’architecture globale. Pour programmer ce moteur de menu, je suis parti d’une syntaxe de création de menu un peu alambiquée, qui rassemble touts les éléments iterractifs (le moteur ne s’occupe que des éléments interractifs, les titres ou autre doivent être dessinés à part): l’unique fonction prend en parametre un liste de tupple qui contiennent eux même le type, le titre et si besoin les valeurs de l’élément :

menu ( position x, position y, elements list , [color] , [background color] )

Les éléments on chacun un identifiant :

TypeIDSyntaxeInformation
Button« btn »["btn","name"]Simple button. Not intended to be used as a text
List« lst »["lst","name",("element1","element2"),current element index]Element that allow the user making a choice beetwen the different elements purposed
Slider« sld »["sld","name",(min val,max val),current value]Element that allow the user chosing a value bettwen min val and max val
(Extrait de la documentation, que j’avais écrite en anglais)

La fonction retourne alors le nom du bouton sélectionné et l’indexe de chaque liste ou slider.

De cette manière, j’ai pu créer facilement des menus et récupérer le résultat de l’interaction avec l’utilisateur. Exemple ci dessous avec le menu principal

def mainMenu(conf=base_conf):
  transition(conf) #petite animation

  drawSprite(LOGO,10,10,12,conf[Col2]) #dessiner le logo
  drawRect(10,80,300,142,conf[Col3]) #dessiner le fond
  
  act_el=[["btn","Play"],["lst","Mode",("Solo","2 Player","Vs Comp."),conf[Mode]],["btn","Game Options"],["btn","Graphics Options"]]
  #défini les éléments
  get_act=menu(20,100,act_el,conf[Col1],conf[Col3]) # appelle la fonction et affecte le résulat de l'action à get_act
  
  conf[Mode]=get_act[1]["Mode"] #défini la configuration du mode de jeu à la sélection.
  if get_act[0]=="Game Options":gameMenu(conf) #analyse quel bouton a été selectionné
  elif get_act[0]=="Graphics Options":graphMenu(conf)
  else:gameEngine(conf)

La configuration et la persistance des options

Vous avez peut-être remarqué que les fonctions mainMenu, graphMenu, gameMenu et transition prennent toutes en paramètre la variable conf. C’est une liste, en quelque sorte un fichier de configuration, qui contient toutes le informations et paramètres du programme. Cette liste est enregistrée dans un fichier sous forme de chaine grâce à la fonction str() elle est décompressé à partir de ce fichier de manière totalement transparente grâce à la fonction eval() :

def saveConf(conf):
  try : #fonction try utilisée pour éviter des arrêts suite à une potentielle erreur.
    with open("pong.conf","w") as f:
      f.truncate(0) #on vide le fichier ...
      f.write(str(conf)) #... et on réécrit la chaine
  except: print("Saving configuration failed.") #message d'erreur en cas d'échec, mais cela ne stop pas le programme

def loadConf():
  try: #fonction try utilisée pour éviter des arrêts suite à une potentielle erreur ou si la configuration n'a pas encore été enregistrée
    with open("pong.conf","r") as f:return eval(f.readline()) #comme la chaine correspond à une liste, elle est évaluée comme une liste par eval()
  except: #si le fichier n'existe pas
    print("Loading configuration failed.")
    return base_conf #on retourne la configuration par défaut

Pour plus de lisibilité du code, chaque élément de la configuration (qui rappelons-le, est une liste) n’est non pas atteint grace à un index mais grace à une constante qui a pour valeur l’index correspondant :

Mode,Diff,MaxPts,BallSpd,BallDetails,PadDetails,Col1,Col2,Col3,BgCol,Theme,Best=0,1,2,3,4,5,6,7,8,9,10,11

Gestion des thème

Pour gérer les theme, j’ai simplement utilisé la configuration. Les couleurs et le numéro du thème y sont contenus et mis à jour quand cela est nécessaire. Pour mettre à jour ces couleurs, une fonction récupère le numéro du thème dans la configuration et renvoie cette configuration avec les bonnes couleurs :

def setTheme(conf,nb):
  conf[Theme]=nb #définit le numéro du thème dans la configuration
  a,b,c,d=(255,255,255),(255,200,0),(100,100,100),(60,60,60) #défini les 4 couleurs du thème de base
  if nb==1:a,b,c,d=d,(200,150,60),(200,200,200),a #thème light : réarrange et modifie les couleurs
  elif nb==2:b=(220,50,50) #theme omega
  elif nb==3:b=(200,100,200) #theme nsiOs
  conf[Col1:BgCol+1]=a,b,c,d #on defini les couleurs dans la config
  return conf #et on renvoie la config

Le moteur de jeu

Architecture et déplacement

J’ai longtemps hésité sur le choix de l’architecture. J’ai bien évidemment directement pensé à une classe en orienté objet qui pourrait s’occuper à la fois des pads et de la balle. Mais cette option est assez très gourmande en mémoire et j’ai aussi pensé à une fonction « constructrice » qui retournerais une liste avec toutes les information de l’objet. Finallement, j’ai tout de même opté pour une classe Entity() qui contient différentes fonctions très utiles :

class Entity():
    def __init__(it,x,y,w,h,col,bg_col):
      #Fonction d'initialisation des attributs
      #Défini la position, la taille, la couleur, etc...
      it.x,it.y,it.w,it.h,it.col,it.bg_col=x,y,w,h,col,bg_col
      it.spd_x,it.spd_y=0,0
      it.last_draw=(int(it.x-it.w//2),int(it.y-it.h//2),int(it.w),int(it.h),it.bg_col)
    def hitBox(it,it2):
      #Fonction qui renvoie vrai si it (objet 1) touche it2 (objet 2)
      if it.x-it.w//2<it2.x+it2.w//2 and it.x+it.w//2>it2.x-it2.w//2 and it.y-it.h//2<it2.y+it2.h//2 and it.x+it.w>it2.x and it.y<it2.y+it2.h//2 and it.y+it.h//2>it2.y-it2.h//2:return True
      else: return False
    def applyVec(it):
      #Fonction qui applique la vitesse de l'objet à sa position
      it.x+=it.spd_x
      it.y+=it.spd_y
    
    def hideObj(it):
      #Fonction qui dessine un rectangle de la couleur du fond pour cacher le dernier affichage de l'objet
      drawRect(*it.last_draw)
    
    def drawObj(it,detail=0):
      #Fonction qui dessine un rectangle à la place de l'objet
      it.last_draw=[int(it.x-it.w//2),int(it.y-it.h//2),int(it.w),int(it.h),it.bg_col]
      if detail:
        for x2,y2 in zip((2,0),(0,2)):drawRect(int(it.x-it.w//2)+x2,int(it.y-it.h//2)+y2,int(it.w)-x2*2,int(it.h)-y2*2,it.col)
      else: drawRect(*it.last_draw[0:4]+[it.col])

Ensuite, j’ai amélioré le déplacement de la balle. Je lui ai ajouté un attribut ball.a qui représente son angle, et à chaque collision sa vitesse latérale et horizontale est redéfinie grace à une fonction :

def vec(s,a): #la fonction prend en parametre la vitesse s et l'angle a
      a=radians(a)#les fonctions trigo prennent l'angle en radians
      x=s*cos(a)
      y=s*sin(a)
      return x,y #retour du vecteur vitesse en 2D

Pour les collisions : c’est encore plus simple. J’utilise une fonction qui prend en paramètre l’angle de l’objet et de la surface, qui calcule l’angle d’incidence et qui retourne l’opposé :

def collide(a1,a2):
  return a2-simp(a1-a2)
#la fonctio simp() simplifie un angle pour qu'il soit compris entre 0° et 360°.

Apres, je « n’avais qu’a » associer des touches aux pad, gérer leur collision avec le haut et la bas et détecter quand la balle est en dehors.

Afficher le score

Il ne restait plus qu’a afficher le score (bon, en fait il restait pas mal de petits truc à faire mais je vais pas les détailler ici). Pour cela, j’ai utilisé la technique de Schraf : Math-infos que j’ai adapté. J’ai d’abord créé une fonction drawSprite() un peu compliquée, qui grosso modo lit un chiffre pour le décompresser en binaire; et lit chaque bit. Si le bit est de 1, un carré est dessiné. Si le bit est de 0, on laisse la case vide. La fonction drawNumber permet d’automatiser l’affichage d’un sprite pour chaque chiffre du nombre désiré.

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

Projets

Un ensemble de Julia sur la calculatrice et avec…

Pour ce dernier projet de cette année, j’ai décidé de faire, le tout sur la calculatrice Numworks, un ensemble de Julia en utilisant des palettes de couleurs, cela donnera une image bien plus belle que le script Mandelbrot de la calculatrice par exemple.

D’où vient l’idée ?

J’ai déjà réalisé des projets sur les fractales, mais surtout sur les ensemble de Mandelbrot. Cette fois, j’ai voulu faire un ensemble de Julia, la « mère » des ensemble de Mandelbrot. Mais je voulais pousser la technique un peu plus loin.

Pour rendre mes fractales encore plus belles, j’ai voulu créer une palette de couleur ce qui rendra les dégradés de couleurs bien plus beaux.

Qu’est-ce qu’un ensemble de Julia ?

Avant d’expliquer comment faire cette palette, qu’est-ce qu’un ensemble de Julia ?

Un ensemble de Julia, c’est une fractale qui peut avoir plusieurs formes, ici, j’utilise l’ensemble ayant pour valeur du nombre complexe c : 0,36 + 0,36i ce qui nous donne cela quand on met 100 itérations maximum :

Comment ajouter et rendre les couleurs de ma fractale plus belle ainsi que les problèmes rencontrés ?

Avant de commencer à regarder les modifications, il faut comprendre la différence entre les couleurs de mon premier script et les couleurs de la palette. Dans le premier script, ma couleur prenait le nombre d’itérations pour se définir (voir si elle est rouge, bleu, vert…). La palette, elle, regarde à quel point le pixel est loin ou près de la fractale pour choisir la teinte et la couleur du pixel.

Pour créer une fractale de l’ensemble de Julia en utilisant des palettes de couleurs, j’ai procédé comme cela :

  • Tout d’abord à partir du script Mandelbrot de la Numworks et grâce à des recherches, je crée un script Julia qui fabrique un ensemble de Julia avec c = 0,36 + 0,36J. Cela donne une image comme ci-dessus. les lignes :
z = complex(0,0)
c = complex(xmin+*x/319-2.5, -2.5*y/221+1.25)

Deviennent :

z = complex(xmin+(xmax-xmin)*x/320+(ymax-(ymax-ymin)*y/222)*1J)
c = complex(0.36,0.36)
  • Ensuite, je crée dans mon script deux boucles for j in range qui vont me fabriquer une liste « palette » de 255 listes ayant 3 valeurs chacune qui représente les valeurs RGB :
palette = []
r = 255
g = 255
b = 255
for j in range(0,128):
  b = 255 -2 * j
  palette.append([r,g,b])
for j in range(128,256):
  r = 255 -2 * (j-128)
  g = 255 -2 * (j-128)
  palette.append([r,g,b])
  • Explication : Pour modifier la couleur, je crée au début de ma fonction JULIA 3 variables, R pour le rouge, G pour le vert et B pour le bleu qui ont chacune comme valeur initiale 255. Pour réaliser la fractale, je rentre dans le range() les valeurs 0 et 128 pour faire une coupure entre quand je transforme le blanc en bleu et quand je transforme le bleu en noir. Je modifie les variables R et G en faisant 255 – 2 * j ce qui va diminuer les deux valeurs, puis je les rajoute dans une liste de ma palette qui contient mes valeurs R, G et B à chaque nouvelles valeurs de j dans une autre boucle for qui va de 128 à 255 je modifie la valeur du bleu avec l’équation 255 – 2 * (j – 128) et je rajoute à nouveau une liste avec les valeurs r g et b.
  • Je modifie la définition de ma variable « col » pour qu’elle récupère les trois valeurs contenues dans l’une des listes de ma palette et je rajoute une variable couleur pour rendre plus simple le code :
couleur = palette[int(255*i/N_iteration)]
col = color(couleur[0],couleur[1],couleur[2])
  • Et voilà, quand je lance mon programme avec 100 itérations, cela me donne cette fractale :

Code complet :

from kandinsky import*
def Julia(N_iteration):
    palette = []
    xmax = 2
    xmin = -2
    ymax = 1.3875
    ymin = -1.387
    r = 255
    g = 255
    b = 255
    for j in range(0,128):
        b = 255 -2 * j
        palette.append([r,g,b])
    for j in range(128,256):
        r = 255 -2 * (j-128)
        g = 255 -2 * (j-128)
        palette.append([r,g,b])
    for x in range(320):
        for y in range(222):
            i = 0
            z = complex(xmin+(xmax-xmin)*x/320+(ymax-(ymax-ymin)*y/222)*1J)
            c = complex(0.36,0.36)
            while i < N_iteration and abs(z) < 2:
                i = i + 1
                z = z*z+c
            couleur = palette[int(255*i/N_iteration)]
            col = color(couleur[0],couleur[1],couleur[2])
            set_pixel(x,y,col)

Télécharger le .py

Autres modifications possibles :

  • Tout d’abord la couleur, pour l’instant, je n’ai seulement réussi à faire les 6 couleurs des synthèses additive et soustractive (rouge, vert, bleu, magenta, cyan et jaune). Je m’y suis pris trop tard pour faire les autres couleurs, car il faut modifier les équations des lignes qui modifie les variables R, G et B. Pour faire cela il faut modifier le code comme ceci :
for j in range(0,128):
        b = 255 -2 * j
        palette.append([r,g,b])
    for j in range(128,256):
        r = 255 -2 * (j-128)
        g = 255 -2 * (j-128)
        palette.append([r,g,b])
  • On peut modifier le point de vue en modifiant les valeurs de ces lignes :
xmax = 2
xmin = -2
ymax = 1.3875
ymin = -1.387

Il faut faire attention aux proportions sinon l’image sera distordue, par exemple si on met ces valeurs, on obtient :

xmax = 0.5
xmin = -0.5
ymax = 0.346875
ymin = -0.346875

Ici, j’ai seulement zoomé au milieu de l’image

  • Et on peut aussi faire comme sur l’image ci-dessous en passant par plusieurs couleurs en faisant plusieurs boucles for j in range :

Ici, on passe du gris, au jaune, au rouge, puis au noir.

En conclusion :

Même si ce travail ne m’a pas permis d’apprendre de nouvelles choses en python, il m’a fait travailler mon raisonnement pour programmer de futurs codes. Je me suis bien amusé à le faire, car je l’ai fait avec mon père qui avait déjà fait la même chose, mais dans d’autres langages de programmation comme RUST, et vu qu’il n’est pas très fort en python, même si il a la réflexion pour le code, nous avons pu croiser nos compétences pour finir le programme. Le seul bémol, la résolution de la calculatrice est tellement faible que cela nous donne une image très pixelisée.

Sources :

NumApps

Sudoku en python, NumWorks

Le sudoku est un jeu de logique qui se joue sur une grille de 9×9 cases, divisée en 9 blocs de 3×3 cases. Le but est de remplir chaque case avec un chiffre de 1 à 9, en respectant certaines règles : chaque chiffre ne doit apparaître qu’une seule fois par ligne, colonne et bloc. Le jeu est terminé lorsque toutes les cases sont remplies ou lorsque toutes les vies ont été usées.

Fonctionnalités

  • 4 difficultés
  • Système de vies
  • Génération aléatoire des grilles à chaque nouvelle partie
  • Menu d’aide

Captures d’écran

Commandes

◁ △ ▷ et ▽OKPavé numérique
Se déplacer / ParamétrerValiderPlacer un nombre

En savoir plus

Si vous souhaitez en apprendre plus sur ce projet, cet article pourrait vous intéresser.

Télécharger

Voici le lien pour récupérer le code du jeu et le télécharger sur votre calculatrice.

NumApps

Reversi en python, NumWorks

Le Reversi est un jeu de société pour deux joueurs qui se joue sur un plateau de 64 cases. Les joueurs placent tour à tour des pions noirs ou blancs sur le plateau en capturant les pions adverses et en retournant les pions de l’adversaire pour gagner le contrôle du plateau. Le joueur avec le plus de pions de sa couleur à la fin de la partie est déclaré vainqueur.

Fonctionnalités

  • Mode multijoueur et contre IA
  • 2 niveaux de difficultés pour l’IA
  • Système de score
  • Easter egg (trichez pas en regardant le code)

Captures d’écran

Commandes

◁ △ ▷ et ▽OK
Se déplacer / ParamétrerValider

En savoir plus

Si vous souhaitez en apprendre plus sur ce projet, cet article pourrait vous intéresser.

Télécharger

Voici différents liens pour récupérer le code du jeu et le télécharger sur votre calculatrice.

NumApps

Puissance 4 en python, NumWorks

Le Puissance 4 est un jeu de société qui se joue à deux. Le principe est d’aligner 4 jetons que ça soit horizontalement, verticalement ou en diagonale.

Captures d’écran

Commandes

◁ et ▷OK ou Exe
Choisir la colonneValider le coup

À propos

Vous pouvez en savoir plus sur le jeu en lisant l’article qui détaille le programme en lui même, un projet réalisé dans le cadre de la spécialité NSI.

Lien du jeu

Il existe deux liens pour le jeu, sachez que seul le premier garanti de disposer de la dernière version et que le deuxième est un lien alternatif qui peut être dépassé.

NumApps

Mastermind en python, NumWorks

Un jeu qui remettra vos compétences mentales et déductives à l’épreuve ! Sur Mastermind, vous allez devoir trouver la bonne combinaison de couleurs. Mais attention, hâtez vous et réfléchissez bien car vous n’avez que 10 tentatives, sinon c’est perdu !

Fonctionnalités

  • Configuration du nombre de couleurs possibles, jusqu’à 8 couleurs possibles
  • Configuration de la taille de la grille, jusqu’à 6 cases
  • Paramétrage de la possibilité d’avoir plusieurs mêmes couleurs dans la combinaison
  • Possibilité d’afficher une aide (Legit) qui donne la proportion de grille aléatoires conforme.

Captures d’écran

Commandes

△ et ▽◁ et ▷OK
ChoisirSe déplacer / ParamétrerValider

Télécharger

Nous vous proposons 2 liens distincts, le premier est le lien vers la source du créateur de l’application, le deuxième est un lien alternatif en cas de problème. Seul le premier lien garanti de disposer de la dernière version de l’application.

NumApps

Convert en python, NumWorks

Convert est un utilitaire conçu pour votre calculatrice, son objectif est de vous fournir une interface afin d’effectuer des conversions binaires, hexadécimales, et décimales. Convert est un outil ultra rapide qui pourra vous aider à effectuer des conversions facilement et rapidement.

Fonctionnalités

  • Conversions possibles du binaire vers décimal et hexadécimal, hexadécimal vers binaire et décimal, ainsi que décimal vers binaire et hexadécimal
  • Support des nombres négatifs en complément à deux. 0 à 255 en mode Integer, et -128 à 127 en mode Two’s complement

Captures d’écran

Commandes

△ et ▽◁ et ▷OK
Augmenter / DiminuerSe déplacerChanger de mode

Télécharger

Nous vous proposons 2 liens distincts, le premier est le lien vers la source du créateur de l’application, le deuxième est un lien alternatif en cas de problème. Seul le premier lien garanti de disposer de la dernière version de l’application.