Author: Ilyas R.

Projets

Un Casse-briques sur ta NumWorks !

Le jeu-vidéo mythique des années 70 ! Casse-briques est un jeu de réflexion qui vous demandera beaucoup d’agilité. Le principe est simple, une balle est lancée, utilisez la plateforme mobile mis à votre disposition pour la faire rebondir et tentez de toucher les briques.

Genèse du projet

Le Casse-briques sur NumWorks est un jeu que j’ai voulu initialement développé il y a de cela un peu moins d’un an, juste après la publication de l’application Colors. J’avais commencé très brièvement la partie graphique du jeu, mais c’était assez rudimentaire et ça ne plaisait pas trop. Ensuite l’été a débuté et suite à des problèmes techniques lors du développement, le projet a peu à peu pris la poussière… Jusqu’à récemment.

Déterrage

Pour le dernier projet de l’année en NSI (le projet libre), j’ai décidé de sortir du placard le jeu, initialement abandonné, pour le redévelopper. Néanmoins, 2 semaines (pas à temps plein malheureusement, au contraire), ça reste peu pour développer un jeu de cette envergure, même pas l’IDE encore ouvert que j’avais énormément de questions qui me traverser l’esprit :

  • Comment faire bouger la balle et la faire rebondir correctement ? (rien que ça)
  • Comment gérer les collisions de la balle avec les briques ? (sans aucun doute la chose la plus complexe)
  • Comment générer des niveaux inédits et cohérents ?

C’est pour ça qu’en réalité, j’ai commencé doucement le développement du projet en avance (mi-mars), pour avoir pleinement le temps de proposer une réalisation finale convenable et fonctionnelle. J’avais énormément d’idées pour la conception du jeu, notamment avec un système de powers-up. Mais tout n’a pas pu voir le jour malheureusement.

La main à la pâte

Maintenant que les idées sont là, il faut retrousser ses manches et plonger dans la phase de développement.

L’ébauche d’un menu

Élaborons d’abord la page le menu du jeu, celui-ci devra afficher notamment, le score, le niveau, ainsi que le nombre de vies restantes. Et c’est le cas :

C’est une partie plutôt simple (on commencer doucement), rien de bien sorcier :

[...]
def menu():
    rec(0, 0, 320, 222, dark_c)
    rec(0, 200, 320, 22, game_c)
    for k in range(len(chars)):  # Display method inspired by @riko_schraf
        for j in range(19):
            if chars[k] >> j & 1 == 1:
                rec(110 + 12 * k + (j % 3) * 3, 40 + (j // 3) * 3, 3, 3, light_c)
    txt("Score: " + str(score), 160-5*(7+len(max(str(score), str(stage)))), 70, bright_c, dark_c)
    txt("Stage: " + str(stage), 160-5*(7+len(max(str(score), str(stage)))), 90, bright_c, dark_c)
    txt("Lives: " + str(pv), 160-5*(7+len(max(str(score), str(stage)))), 110, bright_c, dark_c)
    if pv > 0:
        txt("Press OK to play", 80, 150, bright_c, dark_c)
    else:
        txt("GAME OVER", 115, 150, bright_c, dark_c)
    txt("Gameplay by nsi.xyz/breakout", 20, 202, bright_c, game_c)

Une fonction menu(), assez simpliste dans le fond. L’affichage du nom du jeu « BREAKOUT » est géré entre les lignes 5 et 8. C’est un système d’affichage initié par Eric SCHRAFSTETTER (@riko_schraf), que j’ai modifié pour afficher le texte voulu, il utilise notamment la décomposition d’entiers en binaire pour savoir si une case est « allumée » ou non. Pour les plus déterminés et les plus curieux qui voudrons découvrir comment ça fonctionne, voici quelques ressources : Opération bit à bit — Wikipédia (wikipedia.org) [FR] • Python Bitwise Operators – GeeksforGeeks [EN]

Les fondations

Ensuite, voyons par quoi repose le jeu :

p_size = 40
p_speed = 3
p_x, p_y = 160, 210
p_color = bright_c

b_size = 7
b_dir = [2, -2]
b_pos = [160, 180]

br_width = 30
br_height = 15

game_speed = 0.01
game_state = "IN_MENU"

chars = (121579, 186351, 234191, 188271, 187117, 252783, 252781, 74903)

points = {"red": 100, "orange": 80, "yellow": 60, "green": 20}
stage, score, pv = 1, 0, 3
level = ""
cuboids = []

Ce sont (quasiment) toutes les variables globales utilisés dans le jeu, certaines sont plus intéressantes que d’autres, et nous nous attarderont ici uniquement sur celles que je juge donc intéressantes. Certaines variables respectent une certaine nomenclature (p_*: ce qui concerne la plateforme mobile, b_*: ce qui concerne la balle, br_*: ce qui concerne les briques, etc.).

  • p_size: C’est la taille de la plateforme mobile
  • p_speed: C’est sa vitesse de déplacement (si elle vaut 3, c’est qu’elle va se déplacer de 3 en 3 pixels (vers la gauche ou la droite))
  • p_x: C’est la position x, de la plateforme mobile, plus exactement, c’est la position du pixel au centre de la plateforme mobile. La position y est moins importante, étant donné que celle-ci est constante (c’est pourquoi une list n’a pas été utilisé pour stocker ces deux valeurs).

La position et la taille de la plateforme sont des éléments essentiels pour détecter une collision.
Ici, p_x est est représenté par le pixel rouge :

De la même manière.

  • b_size: C’est le diamètre (même si on parlera ici plutôt de côté) de la balle.
  • b_dir: C’est un vecteur, qui va donc déterminer où la balle va se diriger (si il vaut [1, 1], la balle ira en diagonale vers la droite et vers le bas (l’axe des ordonnées est inversé)). Plus la norme du vecteur est élevé, plus la balle sera rapide (si il vaut [2, 2], il ira aussi vers la droite et vers le bas, mais la balle sera plus rapide).
  • b_pos: C’est la position x, y de la balle.

Ces 3 variables (surtout la 1ère et la 3ème) sont essentiels pour détecter une collision.

Comme vous le voyez, le jeu a été conçu pour très facilement modulable. Pourquoi ? Car déjà c’est préférable lorsqu’on développe n’importe quelle application, et aussi pour intégrer des powers-up facilement. En effet, modifier la valeur de b_size aurait fait un power-up qui change la taille de la balle en plein jeu !

Des collisions

C’est sans aucun doute ce qui m’a mis le plus en difficulté, j’ai voulu faire un système non spécifique au jeu, qui serait donc facile d’intégrer dans un autre jeu, et c’est ce que j’ai fait.

Grosso modo, les collisions fixes (le système n’est pas optimisé pour gérer des entités mouvantes, celles de la plateforme mobile sont gérés autrement et sont spécifiques au jeu) sont stockées dans une list, enfin plutôt dans une list elle-même dans une list, enfin non, plutôt dans une list elle-même dans une list qui est elle-même dans une list. 🤯 😵‍💫
J’ai dit que les collisions étaient stockées, mais qu’est-ce que j’appelle une collision ? En réalité je stocke 2 valeurs entières qui vont former un cuboïde, ce dernier sera interprété comme un masque de collision (hitbox).

Ici le rectangle noir représenté un masque de collision, pour pouvoir le retrouver, nous avons besoin au minimum des coordonnées du point supérieur gauche et du point inférieur droit. Ainsi si la position de la balle se trouve dans la zone, elle doit rebondir. En réalité c’est un peu plus compliqué que ça, car selon le côté du cuboïde où la balle atterrit, la balle ne part pas au même endroit… Il faut donc aussi déterminer le côté du cuboïde où la balle est entré en collision. En bref, ce sont énormément de mathématiques.

[...]
def collision_manager(x, y):
    for i in range(len(cuboids)):
        if cuboids[i] and (i < cuboids[i][0][1]+10 or len(cuboids)-3 <= i <= len(cuboids)):
            h = b_size//2
            x_s, y_s, x_e, y_e = cuboids[i][0][0]-h, cuboids[i][0][1]-h, cuboids[i][1][0]+h, cuboids[i][1][1]+h
            if y_s-abs(b_dir[1]) <= y <= y_s+abs(b_dir[1]) and x_s <= x <= x_e:
                b_dir[1] = -b_dir[1]
                destroy_brick(i)
            elif x_s-abs(b_dir[0]) <= x <= x_s+abs(b_dir[0]) and y_s <= y <= y_e:
                b_dir[0] = -b_dir[0]
                destroy_brick(i)
            elif x_e-abs(b_dir[0]) <= x <= x_e+abs(b_dir[0]) and y_s <= y <= y_e:
                b_dir[0] = -b_dir[0]
                destroy_brick(i)
            elif y_e-abs(b_dir[1]) <= y <= y_e+abs(b_dir[1]) and x_s <= x <= x_e:
                b_dir[1] = -b_dir[1]
                destroy_brick(i)

Voici une démonstration du système (pendante la phase de développement) avec quelques informations de débogage affichés en haut à droite :

Les masques de collisions sont représentés par des rectangles rouges.

Génération de niveaux

Le système de générations de niveaux a été pensé par Vincent ROBERT (@nsi.xyz), ce système utilise également les mathématiques (comme tous les systèmes du jeu en fait).

[...]
def level_generator(stg):
    lvl = "".join([" " for _ in range(10)])+"*"*(stg//11+1)*10
    col = set()
    for i in (2, 3, 5, 7):
        if not stg % i:
            for j in range(i, 10, i):
                col.add(j)
    for i in range(stg // 11 + 2):
        for j in range(10):
            if j in col:
                lvl = lvl[:i * 10 + j] + " " + lvl[i * 10 + j + 1:]
    return lvl

Un niveau est stocké dans un str, à l’aide d’un « motif », le « motif » est composé de « * » et de  » « , lorsqu’il y a un « * », c’est que c’est l’emplacement d’une brique, sinon c’est qu’il y en a pas. Ainsi, générer un niveau consiste à générer une chaine de caractères avec des « * »,  » « . À noter, qu’ici, utiliser une chaine de caractère n’est pas très pertinent, il aurait été préférable d’utiliser un entier, et d’utiliser sa notation binaire (1: emplacement d’une brique, 0: emplacement vide). Peut-être que ce sera optimisé dans une version future du jeu (avec l’intégration des powers-up).

Ensuite, le niveau est construit :

[...]
def level_constructor():
    global level, cuboids
    rec(0, 0, 320, 222, dark_c)
    level = level_generator(stage)
    for i in range(len(level)):
        if level[i] == "*":
            rec(1 + (br_width + 2) * (i % 10), 1 + (br_height + 2) * (i // 10), br_width, br_height, set_color(i // 10))
    cuboids = [[[1+(br_width+2)*(i % 10), 1+(br_height+2)*(i//10)], [1 + (br_width + 2) * (i % 10) + br_width,
                1 + (br_height + 2) * (i // 10) + br_height]] if level[i] == "*" else [] for i in range(len(level))]
    cuboids += (((0, 0), (0, 222)), ((0, 0), (320, 0)), ((320, 0), (320, 222)))

Lorsqu’une brique est dessiné, ses collisions sont directement ajoutés dans la liste des collisions (cuboids).

Fonction mère

Enfin, les interactions du joueur sont directement interprétés dans la fonction engine(), Si on appuie sur la flèches gauche, on déplace la plateforme vers la gauche, et si c’est la flèche droite, on la déplace vers la droite. Lorsqu’il reste 3 collisions, c’est que toutes les briques sont cassés, et donc que le niveau est terminé ! Pourquoi 3 ? Car les collisions des bords de l’écran sont aussi stockés dans cuboids.

def engine():
    global p_x, pv, game_state, stage
    while game_state not in ("L_CLEAR", "G_OVER"):
        if keydown(0) and p_x > 10 + p_size // 2:
            p_x -= p_speed
            pad(p_size, 1)
        if keydown(3) and p_x < 310 - p_size // 2:
            p_x += p_speed
            pad(p_size, -1)
        ball_manager()
        if len(cuboids)-sum([1 if not i else 0 for i in cuboids]) == 3:
            game_state = "L_CLEAR"
            stage += 1
            pv += 1
        debug()
    pv -= 1 if game_state == "G_OVER" else 0
    menu()

Conclusion

Finalement, l’élaboration et le développement de ce projet ont été très enrichissantes. J’étais habitué jusqu’à ce projet à développer des applications « fixes » (avec un seul processus à gérer en même temps), développer des applications « dynamiques » (avec plusieurs processus simultanés (en l’occurrence, les mouvements de la balle, et celles de la plateforme)) est quelque chose d’un peu plus complexe, mais pas moins intéressant, au contraire ! Je suis assez satisfait du jeu final, même si je juge qu’il reste quelques défauts et qu’il manque des fonctionnalités que j’aurai voulu intégrer mais que je n’ai pas pu par manque de temps. Cependant, rien ne m’empêche de continuer le développement du jeu à l’avenir en y ajouter et corrigeant des choses !

À toi de jouer !

Ce jeu est disponible sur https://nsi.xyz/breakout !

NumApps

Breakout en python, NumWorks

Le jeu mythique des années 70 maintenant disponible sur NumWorks ! Sur Casse-briques, vous allez devoir casser toutes les briques de la partie grâce à la balle que vous renvoyez avec la plateforme sous votre contrôle !

Fonctionnalités

  • Plus de 50 niveaux uniques
  • Difficulté croissante au fil des niveaux
  • D’autres fonctionnalités sont prévus par la suite !

Captures d’écran

Commandes

◁ et ▷OK
DéplacerDémarrer

Pour aller plus loin

Si vous souhaitez en savoir davantage sur le fonctionnement et le développement de ce jeu, vous pouvez lire son compte rendu : Un Casse-briques sur ta NumWorks !

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

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.

Tutoriels

Utiliser une IA pour générer, corriger, et modifier un…

Vous avez enfin décidé de débarquer et d’explorer le vaste monde de la programmation ? Dans ce cas, bienvenue chez les fous ! Entre cassage d’écran, arrachage de cheveux, ou encore saut par la fenêtre, vous vous rendrez vite compte comment ce vaste univers peut vous rendre maboule de 1001 manières. Ainsi, si vous souhaitez gagner du temps et être productif en programmation, ce tutoriel est fait pour vous !

En effet, il existe heureusement depuis quelques années des outils, se perfectionnant de mois en mois (voire même de jour en jour), permettant de vous faciliter la vie et surtout de gagner énormément de temps. La chose dont une développeuse ou un développeur a le plus besoin. L’un des outils ayant fait le plus parlé de lui ces dernières semaines est #ChatGPT, une intelligence artificielle capable de dialoguer avec nous en nous apportant des connaissances dans quasiment tous les domaines (mathématiques, langues, physique, etc.). Et donc dans ce tutoriel nous nous intéresseront à l’utilisation de #ChatGPT dans le domaine de la programmation en Python.

Une IA, c’est quoi ?

L’intelligence artificielle (IA) est une technologie qui permet à des machines de simuler l’intelligence humaine. Elle est utilisée dans de nombreux domaines, allant de la reconnaissance de la parole et de l’image à la robotique en passant par la finance et la santé.

Dans le domaine de la programmation, l’IA peut être utilisée pour automatiser certaines tâches de développement, comme la génération de code, la correction de bugs et la modification de scripts existants. Elle peut également être utilisée pour analyser et optimiser des algorithmes, ainsi que pour effectuer des tâches de test et de débogage.

Avantages de l’utilisation d’une IA

L’utilisation de l’intelligence artificielle (IA) dans le domaine de la programmation peut apporter plusieurs avantages incroyables. Voici une ribambelle d’exemples :

  1. L’automatisation de tâches fastidieuses : l’IA peut être utilisée pour automatiser certaines tâches de développement, comme la génération de code, la correction de bugs et la modification de scripts existants. Cela peut aider les développeurs à gagner du temps et à se concentrer sur des tâches plus complexes.
  2. Amélioration de la qualité du code : l’IA peut être utilisée pour analyser et optimiser des algorithmes, ce qui peut aider à améliorer la qualité du code et à réduire les erreurs.
  3. Test et débogage plus efficaces : l’IA peut être utilisée pour effectuer des tests et du débogage de manière plus rapide et plus précise, ce qui peut réduire le temps de développement.

Il est important de noter que l’utilisation de l’IA dans le domaine de la programmation nécessite un minimum de compétence en matière de développement. Une IA n’est pas (encore) parfaite et peut commettre des erreurs, il est donc nécessaire de toujours relire, vérifier, comprendre, et tester un code généré par une IA. Il est également important de veiller à utiliser l’IA de manière éthique et responsable. 😉

Comment accéder à l’IA

Nous utiliserons donc dans ce tutoriel ChatGPT, un chatbot qui utilise l’intelligence artificielle.

Le chat est disponible à tous gratuitement via l’URL suivante : https://chat.openai.com/auth/login. Voici la page dans laquelle vous accèderez :

Comme vous l’avez sans doute deviner, vous aurez besoin d’un compte OpenAI pour utiliser cette IA. Cliquez donc sur le bouton Sign up pour créer un compte, ou sur Log in si vous en avez déjà un.

Quand vous vous serez alors connecté avec succès, vous tomberez normalement nez à nez avec la page suivante, la page principale :

Cette page est assez simple au final, nous retrouvons en haut à gauche un bouton qui nous permet de créer d’autres chat. En effet, l’IA est capable de « retenir », réutiliser, et prendre en compte l’historique des précédents messages ! Donc si on discute avec elle sur des sujets précis, et que nous souhaitons ne pas mélanger des conversations, nous pouvons en ouvrir plusieurs qui seront indépendantes les unes des autres, il suffit donc d’appuyer sur ce bouton, intitulé New Chat.

Et puis en bas, nous retrouvons la barre où nous pourrons écrire et discuter avec l’IA. Tout ce qu’il y a de plus classique.

Utilisation de l’IA pour générer et modifier des scripts Python

L’IA peut être utilisé pour la génération, ou plutôt l’écriture de scripts Python.

Voici donc ci-dessous quelques exemples, assez époustouflants, où nous pouvons utiliser l’IA pour générer des scripts Python.

Mise en situation : Catastrophe, un DS de mathématiques demain et j’avais complètement oublié ! Il me faudrait un script Python qui puisse me calculer le discriminant ainsi que les racines d’une fonction, cela me ferait gagner beaucoup de temps…

Voyons de quoi est capable l’IA :

Aussitôt dit, aussitôt fait, l’IA nous pond une fonction python qui nous permet de calculer le discriminant et les racines d’une fonction du second degré.

Vous noterez qu’il est déjà possible d’obtenir le discriminant et les racines d’une fonction du second degré sans script python en sachant bien utiliser sa calculatrice. 😁

Essayons autre chose, un peu d’art pour voir ! J’aurai besoin d’une fonction me permettant de créer une étoile, à l’aide du module Turtle. Mais ajoutons un peu de piment à tout ça ! La fonction devra prendre en paramètre : La position x et y de l’étoile, la couleur de l’étoile, la taille de l’étoile, ainsi que le nombre de branches de l’étoile. 😈

Et maintenant, la question tant attendu : Est-ce que ça marche cette sorcellerie ? Et bien la meilleur façon de savoir, c’est de tester !

Voici donc le rendu sur Thonny :

Comme vous le voyez c’est très très impressionnant.

En expliquant clairement nos attentes, et nos contraintes. L’IA les respecte (ou en tout cas essaie) et nous offre un code documenté, et la plupart du temps optimisé.

À ce stade, la première question que nous pouvons nous poser est de savoir si le code généré par l’IA sort de ses mains, ou si elles il vient d’Internet. Pour le savoir, je vous laissez deviner, demandons lui simplement !

Ainsi, nous avons vu que l’IA était capable d’écrire du code python selon nos envies. Mais à l’évidence, elle peut aussi être utilisée pour modifier du code python. De la même manière, nous n’avons qu’à lui soumettre notre script à modifier, et lui demander ce qu’elle a à faire. Ensuite, laissez la magie opérer…

Voyons cela, voici le code suivant (qui n’a pas été écrit par l’IA) :

n = input("Choisissez un nombre.\t")
try:
    n = int(n)
except ValueError:
    exit("Ce n'est pas un nombre.")

is_prime = 1*(n > 1)
for i in range(2, n):
    if n % i == 0:
        is_prime = 0
        break
print("Ce nombre est premier." if is_prime else "Ce nombre n'est pas premier.")

Grosso modo, ce script nous dit si un nombre n est premier ou non. Demandons lui de l’optimiser :

Effrayant ou époustouflant ? 🤔

Utilisation de l’IA pour corriger des scripts Python

Bon jusqu’ici, nous n’avons pas été déçu. Voyons de quoi elle vaut dans la correction de scripts python. Voici ci-dessous un exemple concret d’utilisation pour corriger un script Python.

Ci-dessous, un script Python censé afficher la table de 5 :

table = 5
print("Table de" + table + ":")
for i in range(11):
    print("5x" + i + " = " + 5*i)

Malheureusement, je n’arrive à lancer le script et je ne comprends pas l’erreur :
TypeError: can only concatenate str (not "int") to str

Demandons donc à l’IA. Enfin, nous allons seulement lui envoyer notre bout de code tel quel et observer ce qu’il se passe :

Vous avez bien compris, l’IA a deviné par elle même que je voulais qu’elle corrige mon code en python, en remarquant qu’il y avait des erreurs dans celui-ci.

Nonobstant, en cas d’incompréhension d’une erreur, je conseille d’abord de demander à l’IA ce que signifie l’erreur, et d’essayer de la corriger sans son aide. L’IA est capable d’expliquer très clairement un concept, avec des exemples et possiblement des formulations différentes :

Comme vous le voyez, c’est assez spectaculaire, l’IA nous explique explicitement l’erreur, et ajoute des exemples tout au long de son explication. En l’occurrence comme vous l’avez vu, c’était un problème de concaténation.

Conclusion

En conclusion, l’utilisation d’une intelligence artificielle (IA) peut être un outil très utile pour aider les développeurs Python à générer, corriger et modifier leur code. Il existe de nombreux outils et bibliothèques qui permettent d’intégrer l’IA dans le développement de logiciels, comme nous l’avons vu précédemment il y a ChatGPT, mais il existe aussi des alternatives tout aussi époustouflantes, comme GitHub Copilot.

En utilisant ces outils, les développeurs peuvent gagner du temps et de l’efficacité dans leur travail quotidien et se concentrer sur des tâches plus complexes. Cependant, il est important de noter qu’il est toujours important de comprendre et de maîtriser les principes de base du développement de logiciels, même si l’on utilise l’IA pour automatiser certaines tâches.

Vous l’aurez compris, pour ne pas finir à l’hôpital des fous, il est toujours sympa d’avoir un petit assistant sous la main !

Art

Pendules : Une horloge analogique

Une horloge analogique… Qu’est-ce cela peut bien être… Eh bien, c’est tout simplement une horloge avec des aiguilles ! Ainsi, ce programme vous permettra, grâce à Turtle, d’afficher une horloge analogique…

Introduction

La spécificité de cette horloge, est que celle-ci est dynamique. C’est-à-dire que les aiguilles bougent en fonction de l’heure réel. Sinon cela serait trop facile, il suffirait seulement de dessiner un cercle pour le cadran, et des segments pour les aiguilles, et c’est fini. Heureusement, ce n’est pas le cas.

Quelques fonctions

Pour afficher l’heure réel, il faut d’abord récupérer l’heure réel… Pour cela, j’utilise la fonction time.strftime(), à partir du module time, je récupère une donnée de la date actuelle, en l’occurrence j’ai besoin de 3 données : l’heure, la minute, et la seconde. Voici donc une fonction que j’ai codé qui m’a permis de récupérer une de ces 3 données :

def get_time(u):
    match u:
        case "h":
            return int(strftime("%I"))
        case "m":
            return int(strftime("%M"))
        case "s":
            return int(strftime("%S"))

Ici, j’ai utilisé la déclaration match, qui permet, entre autres, de comparer la valeur d’une variable, ici la variable u (pour unité). Par exemple, en appelant la fonction suivante get_time("h"), elle me renvoi la valeur de l’heure actuelle, à l’heure où j’écris cet article, il est exactement 01:44 donc en appelant la fonction maintenant, elle me renverrait la valeur 1.
À savoir qu’utiliser la déclaration match n’est absolument pas obligatoire, j’aurais très bien pu utiliser une structure conditionnelle traditionnelle avec les déclarations if, elif, et else.

Désormais, j’ai besoin d’afficher le cadran de l’horloge, pour cela, rien de plus simple :

def draw_clock(x, y, rayon):
    travel(x, y-rayon)
    pencolor((0,)*3)
    fillcolor((255,)*3)
    begin_fill()
    circle(rayon)
    end_fill()
    travel(x, y-rayon+10)
    fillcolor(7, 24, 31)
    begin_fill()
    circle(rayon-10)
    end_fill()
    travel(x, y-5)
    fillcolor(85, 91, 92)
    begin_fill()
    circle(5)
    end_fill()
    penup()
    goto(x, y)
    setheading(90)
    pencolor((255,)*3)
    for i in range(60):
        forward(rayon-(25 if i%5 == 0 else 15))
        pendown()
        forward(25 if i%5 == 0 else 15)
        penup()
        goto(x, y)
        right(6)

C’est une fonction plutôt simple, bien qu’elle soit assez lourde. On dessine des cercles, et on fait les marquages des minutes et des heures. J’ai dans cette fonction, utilisé une autre fonction qui ne vient pas des modules importés, la fonction travel() (pour voyager). J’aime beaucoup cette fonction car elle est extrêmement simple, et permet d’économiser quelques lignes :

def travel(x, y):
    penup()
    goto(x, y)
    pendown()

En fait, c’est tout simplement l’équivalent d’un goto(), mais sans laisser les traces du pinceau ! Voici donc le cadran, il est fixe :

J’ai également voulu ajouter dans le fond un mur en briques. Pour ne pas laisser le fond vide, la fonction fait seulement 7 lignes :

def wall_brick(x, y, width_bricks, height_bricks):
    for i in range(height_bricks):
        for j in range(width_bricks):
            if i % 2 == 0:
                fill_rect(x+65*j, y+35*i, 60, 30, (161, 84, 68))
            else:
                fill_rect(x+30+65*j, y+35*i, 60, 30, (161, 84, 68))

Les paramètres sont simples, la position du mur (le bas à gauche du mur) via x et y, et le nombre de briques en longueurs, ainsi que le nombre de briques en hauteur. fill_rect() est une fonction maison inspiré de kandinsky.fill_rect(), elle permet de dessiner des rectangles :

def fill_rect(x, y, w, h, c=(0, 0, 0)):
    travel(x, y)
    pencolor(c)
    fillcolor(c)
    begin_fill()
    for i in range(4):
        forward(w if i % 2 == 0 else h)
        right(90)
    end_fill()

Ci-dessous donc, le mur en briques, contruit par la fonction wall_brick(-668, -330, 21, 21) :

Difficultés rencontrées

Il ne reste donc plus qu’à dessiner les aiguilles et les faire pivoter au fur et à mesure. Voici donc la fonction qui occupe cette lourde tâche, et qui m’a bien cassé la tête :

def clock_hands(colors_hands, size_hands):
    s, m, h, k = get_time("s"), get_time("m"), get_time("h"), 0
    update_hours(h, colors_hands[0], size_hands[0])
    update_minutes(m, colors_hands[1], size_hands[1])
    while True:
        update_seconds(s, colors_hands[2], size_hands[2])
        sleep(.6 if k < 90 else .5 if k < 180 else .3 if k < 240 else 0)
        s += 1
        if s == m+2:
            update_minutes(m, colors_hands[1], size_hands[1])
        if s+1 == (h+1)*5:
            update_hours(h, colors_hands[0], size_hands[0])
        if s == 60:
            s, m, h = get_time("s"), get_time("m"), get_time("h")
            update_minutes(m, colors_hands[1], size_hands[1])
            update_hours(h, colors_hands[0], size_hands[0])
            update_seconds(s, colors_hands[2], size_hands[2], 1)
        k += 1

C’est sans doute la fonction la plus compliquée du programme. Les deux paramètres ne sont qu’esthétique, ils permettent de modifier la couleur de chaque aiguille, ainsi que la largeur de chaque aiguille, on ne donc y s’attarder plus que ça.
L’objectif est simple, avoir l’heure la plus précise possible, mais en gardant un mouvement des aiguilles fluide.
J’ai fait le choix d’appeler la fonction get_time() le moins de fois possible, car elle requiert de faire un nombre extrêmement élevé de requêtes plusieurs fois par secondes. Et faire des requêtes inutiles, ça ralentit le programme… La fonction n’est donc appelée que lors de l’initialisation (avant de rentrer dans la boucle infinie), et lorsque la trotteuse à passe à la soixantième seconde. Nous appelons donc la fonction get_time() environ une fois par minute ; et c’est suffisant. Ensuite, on met à jour la position des aiguilles toutes les secondes… Enfin, c’est un peu plus compliqué que ça. En effet, il faut que le mouvement soit fluide, et donc il faut supprimer toutes les actions inutiles. Sauf que 98% du temps, il n’y a que l’aiguille des secondes qui bouge, celle des minutes et des heures ne bougent beaucoup moins souvent. Donc cela ne sert à rien de mettre à jour ces aiguilles à chaque seconde. On le fait donc à des instants précis.
Malgré toutes les optimisations pour fluidifier le rafraichissement, il n’est malheureusement pas parfait.
Également, il s’avère que plus le programme tourne longtemps, plus il est lent… Il faut donc adapter la pause de la fréquence en fonction de quand le programme a démarré. Voici à quoi sert la variable k, et la fonction time.sleep(), nous perdons donc beaucoup de précisions au fil du temps. Mais en contrepartie, j’ai mis en place un système pour rerégler automatiquement les aiguilles.

L’image finale

À toi de « jouer » ?!

Tu ne pourras malheureusement pas vraiment t’amuser avec ce programme. À moins que tu veuilles fixer une horloge. Néanmoins, si tu veux savoir comment elle fonctionne exactement, et la voir bouger, n’hésite pas à télécharger le script !

Télécharger le .py

Tutoriels

Découvrir le module turtle de python

Ce mini tutoriel vous propose quelques exemples de scripts python, simple à comprendre et à modifier, vous permettant de débuter sereinement la réalisation d’une image dessiné avec le module turtle de python.

Cet article a été écrit à deux mains, ainsi vous ne saurez pas qui accabler ci vous y trouvez des coquilles 😅

Tableau des méthodes Turtle en python

Ce tableau présente quelques méthodes turtle, mais il en existe d’autres, il ne faut pas hésiter à lire la documentation officielle : turtle — Turtle graphics

instructionsExplication en françaisExemples d’utilisation
forward(distance)La tortue avance de distance dans la direction précédemment fixéeDessiner des triangles
circle(radius)La tortue trace un cercle de rayon radiusDessiner des cercles
goto(x, y)La tortue se deplace aux point de coordonnées (x,y)Dessiner des triangles
begin_fill()
fillcolor(couleur)
end_fill()
La tortue remplit des formes géométriques de la couleur couleur, c’est comme le seau de peinture dans Paint.Colorier des cercles
il existe de nombreuses autres fonctions dans le module turtle, lisez la documentation officielle : turtle — Turtle graphics et cherchez d’autres tutoriels !

Dessiner des triangles équilatéraux

Le script ci-dessous définit une fonction, capable de construire un triangle équilatéral aux coordonnées (x, y) et de spécifier la longueur du côté du triangle. La couleur par défaut est le noir #000000 ou (1,1,1) mais cette couleur peut-être personnalisé lors de l’appel de la fonction.

from turtle import *
from random import randint

colormode(255)

def triangle(longueur, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x,y)
    pendown()
    pencolor(couleur)
    for i in range(3):
        forward(longueur)
        left(120)

for i in range(42):
    x, y = randint(-600, 400), randint(-300, 200)
    long = randint(10, 200)
    color = (randint(0, 255), randint(0, 255), randint(0, 255))
    triangle(long, x, y, color)

Dessiner des cercles

Le script ci-dessous définit une fonction, capable de construire un cercle aux coordonnées (x, y) et de spécifier la longueur du rayon de ce cercle. La couleur par défaut est le noir #000000 ou (1,1,1) mais cette couleur peut-être personnalisé lors de l’appel de la fonction.

from turtle import *
from random import randint

colormode(255)

def cercle(rayon, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y-rayon)
    pendown()
    pencolor(couleur)
    circle(rayon)
    

for i in range(42):
    x, y = randint(-600, 400), randint(-300, 200)
    radius = randint(10, 200)
    color = (randint(0, 255), randint(0, 255), randint(0, 255))
    cercle(radius, x, y, color)

Dessiner des disques

Le script ci-dessous définit une fonction, capable de construire un disque aux coordonnées (x, y) et de spécifier la longueur du rayon de ce disque. La couleur par défaut est le noir #000000 ou (1,1,1) mais cette couleur peut-être personnalisé lors de l’appel de la fonction.

from turtle import *
from random import randint

colormode(255)

def disque(rayon, x, y, couleur=(1, 1, 1)):
    penup()
    goto(x, y-rayon)
    pendown()
    pencolor(couleur)
    fillcolor(couleur)
    begin_fill()
    circle(rayon)
    end_fill()
    

for i in range(42):
    x, y = randint(-600, 400), randint(-300, 200)
    radius = randint(10, 200)
    color = (randint(0, 255), randint(0, 255), randint(0, 255))
    disque(radius, x, y, color)

Ecrire des fonctions et les réutiliser

Une manière intelligente d’écrire un script est de coder des fonctions, des fonctions paramétrables.

Les fonctions proposées ci-dessus sont paramétrables :

  • coordonnées (x, y),
  • couleur,
  • rayon ou longueur

Nous aurions pu ajouter d’autres paramètres à ces fonctions, comme par exemple la largeur du trait ou une rotation initiale dans le cas des triangles…

Un exemple d’image générée avec une unique fonction, cette fonction est appelée dans une boucle.

Un champ de rose (source), un script de moins de 42 lignes, beaucoup moins !

La fonction elle même contient une boucle.

L’histoire raconte que plusieurs milliers de cercles ont été tracés.

L’image est générée au format .png en exploitant le tutoriel Exporter une image générée par le module turtle sur python

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.

NumApps

Colors en python, NumWorks

Colors est un utilitaire conçu pour votre calculatrice, dont l’objectif est de vous fournir une interface simple, ergonomique, et complète, permettant d’afficher des rendus de couleur via le format RGB de cette couleur ! Vous avez besoin de violet pour effectuer votre plus belle rosace avec Turtle ? Vous ne connaissez pas le format RGB du violet ? Parfait ! Lancez Colors !

Fonctionnalités

  • Affichage du format hexadécimal et RGB de la couleur définie
  • Choix de teintes adaptées proposées automatiquement
  • Support du pavé numérique
  • Système d’impulsion intelligent, pour des déplacements rapides et précis

Captures d’écran

Commandes

△ et ▽◁ et ▷Chiffres
NaviguerModifier / RéglerChoisir / Se déplacer

Pour aller plus loin

Si vous souhaitez en savoir davantage sur le fonctionnement et le développement de cet utilitaire, vous pouvez lire l’article de présentation : Le projet Colors en python sur NumWorks

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.

Projets

Le projet Colors en python sur NumWorks

Colors est un utilitaire développé en python pour la NumWorks. Il permet d’obtenir facilement les formats de couleur RGB et hexadécimal d’une couleur via les valeurs Red, Green, et Blue, qui sont à configurer.

Développement

Étant en train de réfléchir au fonctionnment d’un futur projet (paint.py 🤔🤫). J’avais besoin de fournir un utilitaire facultatif en complément de ce projet pour ne pas l’alourdir. Voici comment est né Colors.

Je commence donc le développement tranquillement, et en un petit week-end, le projet Colors est terminé.

Colors v1

De multiples problèmes m’ont fait perdre beaucoup de temps. Je voulais, à l’origine, afficher une large palette de couleurs, puis grâce à un curseur et des flèches, déplacer le curseur sur un pixel de la palette et nous renvoyer les informations sur la couleur selectionné, c’était palette.py. Problème : j’avais besoin au minimum de 255 pixels en hauteur, et 255 pixels en largeur. Mais l’écran de la NumWorks fait 222 pixels en hauteur. Il m’étais donc impossible d’effectuer la palette précise que je pensais avec une telle taille d’écran.

Parallèlement, en me balandant sur le net afin de comprendre le fonctionnement du format RGB et hexadécimal par rapport aux couleurs, je fus tombé sur un système de sélection de couleur grâce à des barres coulissantes, et j’ai adoré !

Je me suis dit que ce serait parfait pour Palette (qui deviendra Colors). J’ai donc commencé à développer un système reproduisant le fonctionnement d’une barre coulissante. Et comme je suis sympa, je vous mets à disposition la fonction documentée si vous voulez en intégrer vous même dans vos programmes ! 😎

Ayant fini, je demande ce qu’en pense cent20. Grave erreur…

Bon bah mon projet attendra, l’écriture du cahier des charges de Colors v2 est en cours.

La nouvelle fonctionnalité majeure (il n’y en a de nombreuses autres pas forcément visibles) de cette seconde version est l’ajout des teintes :

Colors v2

La v2 plus ou moins terminée, une v3 est en réflexion…

Colors, qui devait être à la base un petit projet abouti en 2 jours, deviendra un gros projet de 2 semaines (pas à temps plein🙄).

Cette troisième et dernière version améliore grandement le rafraichissement des données (en limitant le clignotement), l’interface graphique est également légèrement revu. Cette version supporte également le pavé numérique. Et oui, on fait pas les choses à moitié ! 😏

Colors !

À toi de colorer !

Cet utilitaire est disponible sur https://nsi.xyz/colors

Tutoriels

Développer une application pour la NumWorks sur un vrai…

Vous avez décidé de créer un script python et de porter ce script sur la couche graphique de la calculatrice NumWorks, vous serez vite limité par my.numworks.com qui ne propose qu’une interface très limité de développement.

Si vous codez un script python qui n’exploite que la console, vous pouvez le développer sur n’importe quel outil puis simplement copier / coller votre script dans votre espace de stockage en ligne proposé par NumWorks sur my.numworks.com/python/

Si vous souhaitez d’exploiter des modules propriétaires de la calculatrice NumWorks, tels que ion (gestion du clavier) ou kandinsky (pour gérer l’écran pixel par pixel) alors tout est plus compliqué, enfin tout était plus compliqué !

Il était une fois le workshop …

Le site officiel de NumWorks qui héberge les script s’appelle désormais my.numworks.com mais au départ, il y a fort longtemps, on parlait du workshop, il était accessible en ligne depuis cette adresse workshop.numworks.com et proposait une interface de programmation primitive, qui n’a malheureusement que peu évolué depuis 2017.

Si vous aimez les ascenseurs vous allez adorer cette interface de programmation !

Pas pratique, pas collaborative, bogué, bref on y a codé des jeux et ce fut épique.

La révolution Omega et son IDE en ligne

Le développement de petits jeux sur la NumWorks a été rendu possible par la sortie de la version 13 officielle, et lorsque nous avons commencé à faire des jeux, comme le démineur nous avons très vite suggéré l’idée de rendre possible de développement des projets avec Kandinsky sur PC sans passer par l’interface officielle de NumWorks.

En septembre 2020, l’équipe de Omega (un fork de Epsilon, l’Os de la calculatrice) lançait un IDE en ligne. Il est toujours utilisable à l’adresse https://getomega.dev/ide mais il nécessite un compte GitHub, car les fichiers .py qui y seront crées seront enregistrés sur GitHub.

Pour l’avoir utilisé et exploité avec les élèves en octobre / novembre 2020 pour leurs projets de spécialités NSI, nous pouvons certifier que c’est un superbe outil et une belle amélioration du workshop officiel.

Notre jeu Tetris a été développé sans cet IDE, tout aurait été tellement plus simple avec !

Le talent n’attend pas le nombre des années

Un développeur du nom de ZetaMap , lycéen en terminale STI2D option SIN au lycée Vauban de Brest a codé deux modules pour nous faciliter la vie :

Il est désormais possible d’utiliser un IDE classique (testé uniquement sous Windows) pour développer sur la NumWorks :

Ici le jeu Factors,, il tourne sur un PC alors qu’il exploite des modules spécifiques de la NumWorks.

Comment installer les modules de la NumWorks sur Thonny

Thonny est un IDE léger, simple et excellent pour débuter en python.

Nous allons voir ici comment installer des modules simplement sur Thonny, c’est-à-dire en se passant des traditionnels pip install dans un terminal.

Tout d’abord, il vous faut ouvrir Thonny, cela va de soit, ensuite accéder à l’onglet « Tools » ou « Outils » (selon votre langue), puis à la section « Manage packages… » ou « Gérer les paquets… ». Enfin, il suffit plus que de rechercher les modules « kandinsky » et « ion-Numworks » et de les installer !

Comment installer les modules de la NumWorks sur PyCharm

PyCharm est un IDE un peu lourd, mais très complet. Il peut être notamment utile si vous gérez des projets avec Git.

Nous allons donc voir ici comment installer des modules encore une fois simplement sur PyCharm, c’est-à-dire en se passant des traditionnels pip install dans un terminal.

Tout d’abord, il vous faut évidemment ouvrir PyCharm, ensuite accéder à l’onglet « File », puis aux options « Settings ». À présent, rendez-vous dans la catégorie « Python Interpreter » dans votre Projet « Project: <project_name> ». Désormais, pour installer un module, appuyez sur le bouton « + » :

il suffit plus que de rechercher les modules « kandinsky » et « ion-Numworks » et de les installer !

Comment exploiter les modules ainsi installés

Pour exploiter les modules, rien de plus simple, il faut seulement les importer dans votre code :

from kandinsky import *
from ion import *

Votre code sera donc compatible avec votre IDE et la calculatrice, sans aucune modification requise.

Bugs et limite du portage de la première version stable 1.9

Évidemment, cette technique n’est pas sans limite. Notamment, si vous utilisez Turtle en plus de kandinsky. En installant ces deux modules et en les utilisant, deux émulateurs graphiques s’ouvriront, un exploitant kandinsky, et l’autre Turtle. C’est pour ça qu’il est déconseillé d’utiliser les deux en même temps, utiliser soit l’un soit l’autre.

De plus, est à éviter de changer de fenêtre pendant l’exécution du programme, cela risque de freeze l’émulateur :

Si vous souhaitez discuter de cet émulateur, faire remonter des bugs ou juste dire merci à son concepteur, tiplanet.org a bien voulu nous créer un salon dédié sur le serveur discord.

Lien direct vers ce salon : https://discord.gg/bBM7mgucjF

Utiliser une version 2.4 dev1 ou ultérieur

Le bug du freeze est corrigé, nous utilisons actuellement la version 2.4dev1 et tout fonctionne très bien.

Depuis le gestionnaire de paquet de Thonny, il suffit de cliquer sur les … pour choisir la version à installer.

Il est possible de choisir la version installée simplement, choisissez la pilule verte !
La « version stable » 1.9 est paradoxalement moins stable que la « version de développement » 2.4dev1.

Un exemple de rendu