Author: Robin C.

Tutoriels

Comment installer et utiliser MicroPython sur un ESP32

Dans le premier projet de l’année, nous avons eu la possibilité d’utiliser un ESP32. Cependant, il fallait que cet ESP32 utilise MicroPython pour que le projet soit valable. Nous verrons donc comment installer et utiliser MicroPython sur un ESP32.

Installation de MicroPython

Avant toute chose, il faut télécharger plusieurs fichiers :

  • Le logiciel qui va permettre de flasher ESP32. Le terme flasher correspond à mettre à jour, modifier le micrologiciel d’un appareil
  • Et le micrologiciel qui va être flashé dans l’ESP32
  • Un logiciel pour décompresser les fichiers

Pour cela il vous faut accéder à ce lien qui renvoie vers le site Github et c’est là où vous trouverez le micrologiciel.

Une fois sur cette page, il vous faut cliquer sur le bouton Code puis sur Download ZIP pour télécharger le micrologiciel en format compressé.

Il faut également télécharger le logiciel qui va permettre de flasher l’ESP32. Il vous faut donc accéder à ce lien pour le télécharger.

Pour avoir la bonne version du logiciel qui est compatible avec notre ESP32, il faut trier à gauche par ESP32-S3 :

La page va s’actualiser et vous n’aurez qu’à cliquer sur le bouton Download à côté de Flash Download Tools.

Vous avez donc téléchargé les deux fichiers nécessaires. Cependant il vous faut les décompresser. Pour cela il vous faut un logiciel pour décompresser comme 7zip.

Vous vous retrouverez donc avec deux dossiers comme ceci :

À partir de maintenant il faut que votre ESP32 soit connecté en USB à votre ordinateur.

Nous allons maintenant installer le nouveau micrologiciel dans votre ESP32, pour cela, dans le dossier flash_download_tool_3.9.5 exécutez le fichier flash_download_tool_3.9.5.exe.

Un invite de commande et une petite fenêtre s’ouvre. Nous allons nous intéresser à la petite fenêtre :

Il faut paramétrer la fenêtre selon votre ESP32, dans mon cas il s’agit d’un ESP32-S3 je sélectionne donc ESP32-S3 dans le ChipType et dans LoadMode il faut mettre USB comme l’ESP32est connecté en USB sur l’ordinateur.

Appuyez sur OK.

Dans cette fenêtre il faut cocher la première case et dans la même ligne cliquer sur les 3 points afin de sélectionner le nouveau micrologiciel. Le micrologiciel se trouve dans l’autre dossier st7789s3_mpy-main et dans le sous dossier firmware et il s’agit du fichier firmware.bin . Et à droite du @ il faut mettre un 0. Vous pouvez savoir si vous avez fait une erreur si une des cases est en rouge si par exemple le chemin vers le fichier est mauvais ou si le numéro n’est pas valide. Vous devriez avoir plus ou moins ceci :

Par la suite en bas à droite dans la catégorie COM il faut choisir le port correspondant à celui de votre ESP32. Si vous ne le connaissez pas il vous faut ouvrir le gestionnaire de périphériques, pour ce faire, appuyez sur le bouton Windows + r et écrivez devmgmt.msc pour ouvrir la fenêtre. Et dans la catégorie Port, il faut trouver le Périphérique série USB

Dans mon cas il s’agit du COM5 donc je renseigne le COM5 dans l’application et dans la catégorie BAUD il faut mettre à 9216000.

Vous devriez avoir la même chose sauf peut-être pour le COM et dans ce cas là avant d’appuyer sur START il faut mettre l’ESP32 en mode téléchargement, pour ça il faut appuyer en même temps sur le bouton de reset sur le côté et celui de boot, qui est le plus proche du bouton de reset, en commençant par appuyer sur le bouton reset. Après cela vous pouvez appuyer sur START et le téléchargement commencera.

Utilisation de MicroPython

Après avoir installé votre version de MicroPython vous avez probablement envie de l’utiliser. Et pour cela il vous faut un IDE comme Thonny mais il ne suffit pas de juste le démarrer, il faut configurer votre ESP32 dessus.

Pour configurer votre interpréteur afin d’utiliser l’ESP32 et non l’ordinateur il faut cliquer en bas à droite sur Python 3 local, qui correspond à votre python installé sur votre ordinateur. Cliquez ensuite sur Configurer l’interpréteur.

Dans le type d’interpréteur, il faut choisir MicroPython (ESP32) comme il s’agit d’un ESP32sous MicroPython. un nouveau paramètre apparait, il s’agit du port de l’ESP32 et il s’agit du même que sur le logiciel d’installation du micrologiciel sauf si vous avez changer de port l’ESP32. Vous devriez donc vous retrouver avec ceci :

Cliquez sur OK, la fenêtre va se fermer et retourner sur l’interface normal avec quelques changements sur ce qui est écrit dans la console et aussi en bas à droite dans l’interpréteur.

Vous pouvez taper help() pour avoir quelques informations mais vous y trouverez principalement de la documentation générique qui ne correspond pas forcément à l’ESP32, pour avoir quelque chose de plus détaillé, vous pouvez retourner sur le Github de la version du firmware, c’est en anglais mais vous aurez la documentation qui correspond le mieux à l’ESP32.

Conseils

Je vous conseille d’afficher les fichiers de l’ESP32, pour ça en haut dans affichage il faut cliquer sur Fichiers.

Cette fenêtre permet d’accéder aux fichiers présents sur l’ESP32, de les supprimer, de les télécharger ou de téléverser des fichiers de votre ordinateur vers l’ESP32.

Vous remarquerez deux fichiers importants : boot.py et main.py. Ces fichiers sont importants car ce sont des fichiers qui vont être exécutés automatiquement par l’ESP32 dès qu’il sera alimenté, dans ce cas, boot.py sera exécuté puis main.py dès que l’exécution de boot.py est terminée. Dans l’image ci-dessus les fichiers correspondent aux fichiers de ma Station Météo sur ESP32, le fichier boot est vide mais tout le code est contenu dans main.py, sachant que les autres fichiers sont soit des modules supplémentaires ou des images qui sont utilisés dans le code principal.

Également dans le dossier contenant le firmware soit st7789s3_mpy-main il y a un dossier exemples qui contient quelques exemples qui peuvent être utiles pour comprendre le fonctionnement des différentes fonctions et méthodes. Cela passe de l’affichage de l’heure, à de l’écriture, ou un jeu. Il y a aussi deux fichiers importants qui sont tft_buttons.py et tft_config.py qui sont les deux modules permettant de s’occuper de l’écran et des boutons et qui doivent donc être en tout temps dans l’ESP32.

Conclusion

Pour conclure, j’espère que ce tutoriel vous sera utile afin de comprendre le fonctionnement de MicroPython sur ESP32 et vous donnera envie d’en savoir plus sur ces derniers.

Projets

Une station météo sur un ESP32

Cette année nous avons eu la possibilité de réaliser un projet sur esp32 (microcontrôleurs), j’ai donc réalisé une mini station météo sans aucun capteur qui utilise internet pour récupérer la météo d’Avignon et l’afficher.

L’idée

Trouver quoi faire sur un esp32 n’a pas été tâche facile, en effet l’objectif final était d’utiliser au moins une API afin d’obtenir des données et par la suite de les traiter. J’ai donc fait en premier lieu des recherches afin de trouver des API utilisables en python et surtout gratuites. J’ai donc au final utilisé l’API du site openweather qui permet de recueillir la météo aux coordonnées renseignées et j’ai également utilisé un protocole permettant d’avoir l’heure.

Préparation de l’esp32

Pour pouvoir faire du Python sur mon eps32 j’ai dû le préparer, puisque à la base, l’esp32 se code avec du C j’ai donc installé MicroPython sur la carte, MicroPython est une version de Python qui est adapté aux microcontrôleurs et qui permet de passer du C au Python. Vous retrouverez probablement dans un tutoriel arrivant pendant les vacances de Noël comment faire.

Le script

Avant de présenter le script il faut savoir qu’un esp32 lorsqu’il est alimenté exécute automatiquement deux fichiers : le boot.py et le main.py. J’ai donc réalisé mon script dans le main.py et j’ai laissé le boot.py vierge.

########## IMPORTATIONS ##########
import tft_config    #Module natif qui prend en charge la configuration de l'écran
import tft_buttons    #Module natif qui prend en charge l'utilisation des boutons
import st7789    #Module natif qui prend en charge une partie de l'affichage sur l'écran
import vga2_8x16    #Police d'écriture native
import vga2_8x8    #Police d'écriture native
import vga2_bold_16x32    #Police d'écriture native
import network    #Module natif qui prend en charge la connexion à un point d'accès wifi
from time import sleep    #Le module time
import ntptime    #Module natif qui permet d'utiliser le protocole ntp (Network Time Protocol)
from machine import RTC    #Module natif qui permet de faire énormément de choses mais qui dans ce cas va régler l'horloge interne
import json    #Module natif qui permet de convertir le résultat des requêtes en un format utilisable en python
import urequests    #Module natif qui permet de faire des requêtes sur internet
from login_wifi import SSID, PASSWORD    #Fichier login_wifi.py qui permet de modifier les logins wifi de l'esp32
                                         #(fait dans un autre fichiers pour réduire les risques d'erreurs)

On a au début du fichier les importations des modules et du fichier login_wifi.py.

########## ECRAN ##########
ecran_initialise = False #Variable global pour savoir si l'écran est initialisé ou pas
tft = tft_config.config(1) #Variable globale qui représente l'écran et qui en utilisant des méthodes permet de faire des choses avec
def afficher_texte(txt, x=0, y=0, alaligne=False, police=vga2_8x16): #Fonction qui permet d'afficher du texte à l'écran
    global ecran_initialise
    if not ecran_initialise:
        initialisation()
    if not alaligne:
        tft.fill(st7789.BLACK)
    tft.text(police, txt, x, y, st7789.WHITE, st7789.BLACK)

def deinit(): #Fonction qui déinitialise l'écran
    global ecran_initialise
    if ecran_initialise:
        tft.deinit()
        ecran_initialise = False

def initialisation(): #Fonction qui initialise l'écran
    global ecran_initialise 
    tft.init()
    ecran_initialise = True

On a ici les 3 fonctions qui s’occupent de l’écran : l’affichage du texte avec comme paramètres le texte à écrire, les coordonnées qui si elles ne sont pas fournies seront en 0, 0, si le texte est à la ligne et la police d’écriture. Sachant que le alaligne définit si lorsque le texte s’affiche l’écran sera remplit en noir avant l’affichage du texte. Il y a également les fonctions initialisation() et deinit() qui initialise et déinitialise l’écran.

########## INTERNET ##########
wlan = network.WLAN(network.STA_IF) #Variable global qui permet de s'occuper d'accéder à internet
wlan.active(True) #Activation du point d'accès
def do_connect(ssid=SSID, password=PASSWORD): #Fonction qui permet de se connecter à un accès internet
    wlan.connect(ssid, password) #Lancement en arrière plan de la connexion
    essai = 0
    while wlan.isconnected() == False: #Tant que la connexion n'est pas faite...
        essai += 1
        afficher_texte('Connecting.  ', 0, 0, True) #Affichage de message
        sleep(0.5)
        afficher_texte('Connecting.. ', 0, 0, True)
        sleep(0.5)
        afficher_texte('Connecting...', 0, 0, True)
        sleep(0.5)
        if essai == 10: #Si il y a 10 essais de connexions qui n'aboutissent pas...
            break #Sortie de la boucle while
    if essai == 10: #Si il y a 10 essais de connexions qui n'aboutissent pas... 
        afficher_texte('Connexion impossible...', 0, 0) 
        afficher_texte('Tentez de modifier login_wifi.py', 0, 20, True) #Affichage du message d'erreur...
        while True: #Puis une boucle infini pour ne pas exécuter le reste du code qui ne fonctionnera pas
            pass
    afficher_texte('Connected to :', 0, 0) #Sinon message qui indique que l'esp32 est connecté
    afficher_texte(ssid, 0, 20, True)
    sleep(1)
    tft.fill(st7789.BLACK) #Remplit l'écran en noir

def disconnect(): #Fonction qui permet de se déconnecter d'un réseau
    wlan.disconnect()
    
    
    
##### Script du login_wifi.py #####

SSID='Le ssid de votre réseau (son nom)'
PASSWORD='Et son mot de passe'

Ici ce sont les fonctions qui s’occupent du réseau de l’esp32 en se connectant au réseau définit dans le fichier login_wifi.py et qui tant que le réseau n’est pas connecté va afficher le texte ‘Connecting…’ et après 10 essais un message d’erreur s’affiche en expliquant comment changer les logins du réseau, sinon on affiche que la connexion s’est faite. Il y a aussi la fonction disconnect qui permet de se déconnecter du réseau.

########## HEURE ##########
ntptime.host = 'ntp.unice.fr' #Définition du site où la demande de l'heure est faite
def set_heure(): #Fonction qui règle l'heure de l'horloge interne en demandant au serveur
    if not wlan.isconnected(): #Si pas connecté, se connecte au réseau
        do_connect()
    ntptime.settime() #Règle l'horloge interne de l'esp32 selon l'heure donnée par le serveur

def get_heure(): #Fonction qui renvoie une liste du format : 
                 #[année, mois, jour, jour de la semaine, heure, minute, seconde, milliseconde] 
    heure = list(RTC().datetime()) #Récupération de l'heure interne
    heure[4] += 1 #Décalage horaire
    if heure[4] == 24:
        heure[4] = 0
    if len(str(heure[6]))==1: #Ajout d'un 0 si l'heure, la minute ou la seconde ne comporte qu'un chiffre
        heure[6] = '0' + str(heure[6])
    if len(str(heure[4]))==1:
        heure[4] = '0' + str(heure[4])
    if len(str(heure[5]))==1:
        heure[5] = '0' + str(heure[5])
    return heure  
    
def afficher_heure(): #Fonction qui permet d'afficher en gros au milieu de l'écran l'heure avec les secondes
    temps = get_heure() #Définition de l'heure actuelle
    afficher_texte("Menu ->", 264, 0, True) #Affichage permettant de savoir sur quel bouton appuyer pour retourner au menu
    while bouton_droite.value() == 1: #Tant que le bouton de droite n'est pas appuyé
        if get_heure()[6] != temps[6]: #Si l'heure actuelle diffère de celle définie en dehors de la boucle
            afficher_texte(f"{temps[4]} : {temps[5]} : {temps[6]}", 64, 69, True, vga2_bold_16x32) #Affichage de l'heure
            temps = get_heure() #Définition de l'heure pour recommencer la vérification du if
    menu() #Affichage du menu
    while bouton_droite.value() == 0: #Tant que le bouton de droite est appuyé, ne rien faire
        pass

Par la suite on a les fonctions qui s’occupent de l’heure utilisant le protocole ntp qui permet de récupérer l’heure en temps réel avec set_heure() qui règle l’horloge interne de l’esp32 et get_heure() qui récupère l’heure et qui va la retourner dans ce format :

[année, mois, jour, jour de la semaine, heure, minute, seconde, milliseconde]

Et afficher_heure() va afficher l’heure en s’actualisant chaque seconde et qui s’arrête lorsque l’on appuie sur le bouton de droite pour retourner au menu.

########## METEO ##########
adresse_meteo = "https://api.openweathermap.org/data/2.5/weather?lat=43.95&lon=4.8167&appid=6dc180325a613e8fe2292078d342022a&lang=fr"
#Variable globale qui définit le lien de l'API de météo
def meteo(): #Fonction qui fait une requête au serveur de météo et qui renvoie une liste de certaines infos
    if not wlan.isconnected(): #Si pas connecté, se connecte au réseau
        do_connect()
    data = json.loads(urequests.get(adresse_meteo).text) #Fait la requête et la convertie pour être utilisable en python
    temperature = str(round(data.get("main").get("temp") - 273.15, 1)) #Température en kelvin puis en celsius
    if len(temperature) == 3: #Si la temp est de ce format : 1.2 fait de la temp ce format : 01.2
        temperature = '0' + temperature
    humidite = str(data.get("main").get("humidity")) # Humidité
    if len(humidite) == 1: #Comme pour la temperature
        humidite = '0' + humidite
    vent_vitesse = data.get("wind").get("speed") * 3.6 # vitesse du vent en m/s puis en Km/h
    vent_orientation = data.get("wind").get("deg")  # origine du vent en degré
    image = 'image/' + data.get('weather')[0].get('icon') + '.png' #Récupération du code de l'image puis mise dans le bon format
    description = data.get('weather')[0].get('description') #Description de la météo
    nom_ville = f"{data.get('name')}, {data.get('sys').get('country')}" #Récupération du nom de la ville
    return [temperature, humidite, str(round(vent_vitesse)), str(vent_orientation), image, description, nom_ville]

Pour la météo il y a deux fonctions et voici la première : meteo() va faire une demande API vers le site openweather afin de récupérer la météo d’Avignon.

def station_meteo(): #Fonction qui s'occupe d'afficher les données de la météo et qui s'actualise toutes les minutes
    actualisation = True #Variables qui dit si oui ou non la météo doit s'actualiser chaque minutes
    while actualisation: #Boucle while tant que actualisation == True
        data = meteo() #Récupère les infos de la météo
        heure = get_heure() #Récupère l'heure
        afficher_texte("Menu ->", 264, 0, False) #Affiche le texte pour le retour au menu
        
        longueur = int((320-(len(data[6]))*8)/2) #Affiche le nom de la ville 
        for i in range(len(data[6])):            #Lettre par lettre pour éviter les problèmes dû aux accents
            if data[6][i] == '\xe9':
                lettre = 130
            elif data[6][i] == '\xe8':
                lettre = 138
            elif data[6][i] == '\xe0':
                lettre = 133
            elif data[6][i] == '\xe7':
                lettre = 135
            else:
                lettre = data[6][i]
            afficher_texte(lettre, longueur, 5, True)
            longueur += 8

        tft.fill_rect(0, 44, 320, 1, st7789.WHITE) #Fait une barre blanche pour séparer le nom de la ville du reste
        
        #Affichage de la température
        temp = [data[0][0]+data[0][1], data[0][2]+data[0][3]]
        
        afficher_texte('Temp', 28, 50, True)
        afficher_texte(temp[0], 20, 67, True, vga2_bold_16x32)
        afficher_texte(temp[1], 52, 81, True, vga2_8x16)
        afficher_texte('o', 52, 67, True, vga2_8x8)
        afficher_texte('C', 60, 67, True, vga2_8x16)
        
        #Affichage de l'heure et des minutes
        afficher_texte(f"{heure[4]} : {heure[5]}", 132, 25, True)
        
        #Affichage de l'humidité
        x = 20
        if len(data[1]) == 3:
            x -= 8
        afficher_texte("Humid", 24, 105, True)
        afficher_texte(data[1] + '%', x, 122, True, vga2_bold_16x32)
        
        #Affichage de l'image selon le code donné
        tft.png(data[4], 110, 30, True)    
        
        #Affichage du vent avec sa vitesse et sa direction
        y = 67
        if len(data[2]) == 1:
            x = 252
        elif len(data[2]) == 2:
            x = 244
        else:
            x = 236
        afficher_texte('Vent', 260, 50, True)
        afficher_texte(data[2], x, y, True, vga2_bold_16x32)
        afficher_texte('Km/h', x + len(data[2])*16, y+14, True) 
        
        y = 122
        if len(data[3]) == 1:
            x = 264
        elif len(data[3]) == 2:
            x = 256
        else:
            x = 248
        afficher_texte("Direc", 256, 105, True)
        afficher_texte(data[3], x, y, True, vga2_bold_16x32)
        afficher_texte('o', x + len(data[3])*16, y, True, vga2_8x8)
        
        #Affichage de la description qui correspond à l'image
        longueur = int((320-(len(data[5]))*8)/2)
        for i in range(len(data[5])):
            if data[5][i] == '\xe9':
                lettre = 130
            elif data[5][i] == '\xe8':
                lettre = 138
            elif data[5][i] == '\xe0':
                lettre = 133
            elif data[5][i] == '\xe7':
                lettre = 135
            else:
                lettre = data[5][i]
            afficher_texte(lettre, longueur, 108, True)
            longueur += 8
        
        #Attente pour actualisation avec retour possible au menu
        while True:
            if get_heure()[5] != heure[5]: #Si la minute change on refait un tour de la boucle principale
                break
            if bouton_droite.value() == 0: #Si on appuie sur le bouton de droite on sort des boucles pour retourner au menu
                actualisation = False
                break
    menu() #Affichage du menu
    while bouton_droite.value() == 0: #Ne rien faire tant que le bouton de droite est appuyé
        pass

Et en deuxième fonction il y a station météo qui est une boucle while qui s’actualise chaque minute et qui affiche toutes les informations soit : le nom de la ville, l’heure, la température, le pourcentage d’humidité, la vitesse du vent et sa direction et pour finir une image représentant le temps et une description juste en dessous

########## BOUTONS ##########    
boutons = tft_buttons.Buttons() #Récupération des boutons de l'esp32   
bouton_gauche = boutons.left  #Définition des boutons dans des variables
bouton_droite = boutons.right
#bouton_gauche.value() == 0 si appuie, 1 si relâché
#bouton_droite.value() == 0 si appuie, 1 si relâché

def wait(): #Fonction qui permet de récupérer l'appui d'un bouton et
    while True: #qui attend son relâchement avant de renvoyer le bouton appuyé
        if bouton_gauche.value() == 0:
            while bouton_gauche.value() == 0:
                pass
            return 'gauche'
        if bouton_droite.value() == 0:
            while bouton_droite.value() == 0:
                pass
            return 'droite'

Il y a également la fonction qui s’occupe de récupérer l’appuie des boutons et les variables globales qui représentent les deux boutons

########## MENU ##########
def menu(): #Fonction qui affiche le menu
    tft.fill(st7789.BLACK) #Remplit l'écran en noir
    longueur = 256 #Et affiche ce que font les boutons
    mot = "Météo ->"
    for i in range(len(mot)):
        if mot[i] == '\xe9':
            lettre = 130
        else:
            lettre = mot[i]
        afficher_texte(lettre, longueur, 0, True)
        longueur += 8
    afficher_texte("Heure ->", 256, 154, True)
    
    longueur = 56
    mot = "Station Météo"
    for i in range(len(mot)):
        if mot[i] == '\xe9':
            lettre = 130
        else:
            lettre = mot[i]
        afficher_texte(lettre, longueur, 69, True, vga2_bold_16x32)
        longueur += 16
    afficher_texte("By Robin.C", 0, 154, True)

La fonction menu() affiche météo -> et heure -> au niveau des boutons pour indiquer à quoi ils servent, écrit en gros au milieu ‘Station Météo’ et en bas à gauche le nom du génie qui a écrit ce code ;).

########## MAIN ##########
def main(): #Fonction principale qui s'occupe de tout
    if not wlan.isconnected(): #Si pas connecté, se connecte au réseau
        do_connect()
    set_heure()
    menu()
    
    while True: #Boucle infini qui appelle les fonctions
        bouton = wait()
        if bouton == 'gauche':
            tft.fill(st7789.BLACK)
            afficher_heure()
        if bouton == 'droite':
            tft.fill(st7789.BLACK)
            station_meteo()

Pour finir les fonctions il y a la fonction principale qui va connecter l’esp32 à internet, afficher le menu puis faire une boucle infini afin de choisir si on veut la météo ou l’heure et permettre de changer pendant l’exécution de ce qu’on a choisi.

########## DÉMARRAGE ##########
#Essaie d'exécuter main() et si ça plante ou si l'exécution est interrompu
#Déinitialise l'écran pour éviter des problèmes lors d'autres exécutions
#Et désactive aussi le point d'accès pour encore une fois éviter des problèmes
try:
    main()
finally:
    deinit()
    wlan.active(False)

Et enfin le code qui permet d’appeler la fonction main() d’une certaine manière: main() est dans un try afin qu’il soit exécuté et le finally fait que lorsque l’exécution sort du try le code dans le finally et ce code qui est la déinitialisation de l’écran et la désactivation du point d’accès internet, cela permet de redémarrer l’exécution en évitant des possibles plantages.

Fichiers

Pour avoir cette magnifique station météo chez vous il vous faut simplement un esp32 avec micropython (sachant que pour ma part je possède un esp32 Lilygo T-display S3) et de télécharger les fichiers que vous pouvez retrouver en cliquant sur le bouton :

Conclusion

Pour conclure ce projet m’a permis d’apprendre énormément de choses sur le micropython et je suis assez content du résultat car lorsque j’ai commencé le projet le fait qu’il y ai peu de documentation et personne pour aider était dur, cependant en cherchant un peu partout et en essayant des choses j’ai réussi à comprendre plein de choses.

Rendu final

Projets

Un Puissance 4 sur ta NumWorks !

Le Puissance 4 est probablement un jeu de votre enfance, c’est pourquoi je vous propose de jouer à ma version du Puissance 4 directement sur votre NumWorks avec un ami.

L’idée

L’idée du Puissance 4 a été longuement réfléchi car le projet de ces vacances était libre et j’avais eu l’idée de faire un jeu sur la NumWorks mais je ne savais pas lequel faire. Après avoir parcouru le site à la recherche de jeu original qui n’avait pas été déjà fait, l’idée du Puissance 4 m’est venu à l’esprit.

La réalisation

Pour réaliser le jeu j’ai avant tout fait en sorte qu’il soit jouable sur la console avant de faire la partie graphique de la calculatrice (même si certains ajouts ont été fait pendant le développement de la partie graphique).

Le Script

Je vais maintenant vous présenter le script et vous faire une brève explication de chacune des fonctions :

from kandinsky import fill_rect as rect, draw_string as txt
from time import sleep
from ion import keydown

Au tout début du script nous avons évidemment les appels des différents modules :

  • Kandinsky : c’est un des modules propriétaire de NumWorks qui est utilisé afin de dessiner des rectangles ou même écrire du texte sur l’écran de la calculatrice
  • Time : permet avec la fonction sleep que j’appel de mettre des pause dans le script
  • Ion : c’est le second module propriétaire de NumWorks qui permet de prendre en compte l’appui des touches pendant le script.

Nous avons ensuite la définition des variables globales :

# 1 = rouge // 2 = jaune
# Variables globales
player = 1 #Définit le joueur qui doit jouer
grille_preview = [0, 0, 0, 0, 0, 0, 0] #Est utilisé pour déterminer les positions possible du preview
grille = [[0 for i in range(7)] for i in range(6)] #La matrice qui représente la grille de jeu

#Les couleurs utilisées
rouge = (182, 2, 5)
jaune = (255, 181, 49)
gris = (191, 189, 193)

pos = 3 #Donne la position du preview

#Les points des joueurs
points_rouge = 0
points_jaune = 0

Ensuite une des fonctions majeures du jeu : la fonction de vérification. Cette fonction va après chaque coup vérifier toutes les positions afin de voir si il y a un gagnant ou pas.

def verifie(): #Gagnant ?
  for i in range(6): #lignes
    for j in range(4):
      if grille[i][j] == player and grille[i][j+1] == player and grille[i][j+2] == player and grille[i][j+3] == player:
        gagnant(player)
  for i in range(3): #colonnes
    for j in range(7):
      if grille[i][j] == player and grille[i+1][j] == player and grille[i+2][j] == player and grille[i+3][j] == player:
        gagnant(player)
  for i in range(3): #diagonales
    for j in range(4):
      if grille[i][j] == player and grille[i+1][j+1] == player and grille[i+2][j+2] == player and grille[i+3][j+3] == player:
        gagnant(player)
  for i in range(3, 6):
    for j in range(4):
      if grille[i][j] == player and grille[i-1][j+1] == player and grille[i-2][j+2] == player and grille[i-3][j+3] == player:
        gagnant(player)

Cependant je n’ai pas tout dit à propos de cette fonction car celle ci n’est pas tout à fait de moi : en effet avant d’avoir cette fonction là j’avais codé une fonction vérification mais celle ci faisait environ 70 lignes et n’était pas du tout optimisé ce qui amenait à des ralentissement, ChatGPT m’a donc aidé à réduire la fonction afin d’avoir la version ci-dessus. Et donc voici en exclusivité ma fonction de vérification originelle :

def verifie():
    #horizontalement
    for i in range(6):
        for j in range(4):
            rouge = 0
            jaune = 0
            for k in range(4):
                if grille[i][j+k] == 1 :
                    rouge += 1
                elif grille[i][j+k] == 2 :
                    jaune += 1
            if rouge == 4 :
                player_won(1)
            elif jaune == 4 :
                player_won(2)
    
    #verticalement
    for i in range(3):
        for j in range(7):
            rouge = 0
            jaune = 0
            for k in range(4):
                if grille[i+k][j] == 1 :
                    rouge += 1
                elif grille[i+k][j] == 2 :
                    jaune += 1
            if rouge == 4 :
                player_won(1)
            elif jaune == 4 :
                player_won(2)

    #diagonalement
    """(ij)
    /
    2 diag de 4 : 03 12 21 30 // 26 35 44 53
    2 diag de 5 : 04 13 22 31 40 // 16 25 34 43 52
    2 diag de 6 : 05 14 23 32 41 50 // 06 15 24 33 42 51
    
    \
    2 diag de 4 : 03 14 25 36 // 20 31 42 53
    2 diag de 5 : 02 13 24 35 46 // 10 21 32 43 54
    2 diag de 6 : 00 11 22 33 44 55 // 01 12 23 34 45 56
    """

    #création des listes pour vérifier
    diag4 = [[[0, 3], [1, 2], [2, 1], [3, 0]], [[2, 6], [3, 5], [4, 4], [5, 3]], [[0,3], [1,4], [2,5], [3,6]], [[2,0], [3,1], [4,2], [5,3]]]
    diag5 = [[[0, 4], [1, 3], [2, 2], [3, 1], [4, 0]], [[1, 6], [2, 5], [3, 4], [4, 3], [5, 2]], [[0, 2], [1, 3], [2, 4], [3, 5], [4, 6]], [[1, 0], [2, 1], [3, 2], [4, 3], [5, 4]]]
    diag6 = [[[0, 5], [1, 4], [2, 3], [3, 2], [4, 1], [5,0]],[[0, 6], [1, 5], [2, 4], [3, 3], [4, 2], [5,1]],[[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5,5]],[[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5,6]]]

    #vérification des diagonales de 4
    for i in range(4):
        if grille[diag4[i][0][0]][diag4[i][0][1]] == grille[diag4[i][1][0]][diag4[i][1][1]] == grille[diag4[i][2][0]][diag4[i][2][1]] == grille[diag4[i][3][0]][diag4[i][3][1]] :
            if grille[diag4[i][0][0]][diag4[i][0][1]] == 1 :
                player_won(1)
            elif grille[diag4[i][0][0]][diag4[i][0][1]] == 2 :
                player_won(2)

    #vérification des diagonales de 5
    for i in range(4):
        for j in range(2):
            if grille[diag5[i][0+j][0]][diag5[i][0+j][1]] == grille[diag5[i][1+j][0]][diag5[i][1+j][1]] == grille[diag5[i][2+j][0]][diag5[i][2+j][1]] == grille[diag5[i][3+j][0]][diag5[i][3+j][1]] :
                if grille[diag5[i][0+j][0]][diag5[i][0+j][1]] == 1 :
                    player_won(1)
                elif grille[diag5[i][0+j][0]][diag5[i][0+j][1]] == 2 :
                    player_won(2)

    #vérification des diagonales de 6               
    for i in range(4):
        for j in range(3):
            if grille[diag6[i][0+j][0]][diag6[i][0+j][1]] == grille[diag6[i][1+j][0]][diag6[i][1+j][1]] == grille[diag6[i][2+j][0]][diag6[i][2+j][1]] == grille[diag6[i][3+j][0]][diag6[i][3+j][1]] :
                if grille[diag6[i][0+j][0]][diag6[i][0+j][1]] == 1 :
                    player_won(1)
                elif grille[diag6[i][0+j][0]][diag6[i][0+j][1]] == 2 :
                    player_won(2)

Ma fonction donc vérifiait chaque possibilité afin de gagner au jeu mais le plus long était surtout les diagonales qui n’étaient pas automatisées avec des boucles sans variables définies mais plutôt avec des listes interminables des positions possibles des diagonales et cela ralentissait considérablement le script. C’est pour cela que j’ai préféré utilisé la fonction de ChatGPT.

Par la suite nous avons une autre fonction majeure qui est celle qui est appelée dans le cas d’une fin de partie donc soit ci un joueur a gagné ou si il y a égalité et c’est cette même fonction qui redémarre une partie donc rénitialise la grille et actualise les scores :

def gagnant(winner): #Met fin à une partie gagnant ou pas
  global points_jaune, points_rouge, player, nb_partie, grille, partie
  affichage_grille()
  if winner == 1 : #Vérifie la variable winner pour savoir qui a gagné et agir en conséquence
    points_rouge += 1
    player = 2
    txt("Rouge a gagné !", 88, 20)
  elif winner == 2 :
    points_jaune += 1
    player = 1
    txt("Jaune a gagné !", 88,20)
  else :
    txt("C'est une égalité", 76, 20)
  wait() #La fonction attend qu'il y ai une touche de pressé (permet de voir la grille avant sa rénitialisation
  actu_src() #Actualisation des score
  grille = [[0 for i in range(7)] for i in range(6)] #Rénitialise la grille
  affichage_grille() #Affiche la grille

Ensuite nous avons la fonction selection qui est je dirai la dernière fonction majeure du script, elle permet au joueur de selectionner la colonne ou il souhaite ajouter son jeton et appel la fonction jouer que nous allons voir juste après :

def selection():
  global pos
  affichage_grille()
  preview(pos) #Affiche le preview du jeton (par défaut la 4ème colonne)
  add = 1
  while True : # Boucle qui permet de prendre en compte l'appui des touches et agit en conséquence.
    preview(pos)
    while colonne_pleine(pos):
      pos = (pos+add)%7
      preview(pos)
    key_pressed = wait() #Récupère la touche appuyé
    if key_pressed == 0: #Flèche de gauche // décale le preview vers la gauche
      add = -1
    if key_pressed == 3:#Flèche de droite // décale le preview vers la droite
      add = +1
    if key_pressed==0 or key_pressed==3: #Calcul la position du preview 
      pos = (pos+add)%7
      preview(pos)
    if key_pressed == 4 or key_pressed == 52 : #OK ou EXE (permet de jouer le coup)
      rect(75, 17, 170, 20, (255,255,255))
      jouer(pos)

Suite à cela il y a la fonction jouer qui est appelé dans la fonction selection et qui permet de jouer le coup dans la colonne sélectionné dans selection() :

def jouer(colonne): # Ajoute un jeton dans la colonne donnée
  global player
  if player == 1 : #Si c'est au tour de rouge
    animation(colonne) # Fais l'animation de chute du jeton
    grille[gravite(colonne)][colonne] = 1 #Modifie la matrice 
    verifie() #Vérifie si il y a un gagant
    player = 2 #Change de joueur
  elif player == 2 : #La même chose que pour le rouge mais pour le jaune
    animation(colonne)
    grille[gravite(colonne)][colonne] = 2
    verifie()
    player = 1
  grille_pleine() #Vérifie si la grille est pleine dans le cas d'un nul

Après il y a la fonction qui anime la chute du jeton dans la grille :

def animation(colonne): #Animation chute
  if player == 1 : #Définit la couleur du jeton
    color = rouge
  else :
    color = jaune
  for i in range(0, gravite(colonne)+1): #Dessine des jeton à la suite jursqu'à la dernière ligne
      ligne = (i-1)*(i!=0)
      rect(75+(25*colonne),42+(ligne*25),20,20,gris)
      sleep(0.05)
      draw_cercle(75 + (25*colonne) + 7, 42 + (i*25) + 2, color)
      sleep(0.05)

Ensuite la fonction qui dessine les cercles mais qui n’est pas de moi mais de M.Robert :

def draw_cercle(x,y,color): #Fait des cercles (Par VR)
  for d in range(6):
    rect(x-d+(d==0),y+d+(d==5),6+2*d-2*(d==0),16-2*d-2*(d==5), color)

Par la suite il y a ma fonction colonne_pleine qui dit si la colonne donnée en paramètre est pleine ou pas :

def colonne_pleine(colonne):
  if (grille[0][colonne] == 1) or (grille[0][colonne] == 2):
    return True
  return False

Une fonction similaire à la précedente : fonction grille_pleine utilise colonne_pleine afin de déterminer si la grille est pleine ou pas donc si il y a égalité :

def grille_pleine(): #Vérifie si il y a une égalité soit si la grille est pleine
  colonne_pleines = 0
  for i in range(7): #Passe en revue les premières lignes de chaque colonne
    if colonne_pleine(i):
      colonne_pleines += 1
  if colonne_pleines == 7: #Si toutes les colonnes sont pleine
    gagnant(0) #Appelle la fonction gagnant pour mettre fin à la partie

Il y a aussi la fonction gravité qui va retourner la ligne la plus basse où le jeton peut aller :

def gravite(colonne): #Détermine la ligne où le jeton peut se placer
  ligne = 5
  while grille[ligne][colonne] != 0: #parcours la colonne de haut en bas jusqu'à trouver la ligne vide la plus basse 
    if ligne == 0 :
      return ligne
    ligne -= 1
  return ligne

Une autre des fonctions principales : affichage_grille permet comme son nom l’indique d’afficher la grille entière :

def affichage_grille():
  rect(75, 42, 175, 150, (255,255,255))
  pos_x, pos_y_base, marge = 50, 42, 25
  for i in range(7):
    pos_x += marge
    pos_y = pos_y_base
    for y in range(6):
      cote = 20
      if grille[y][i] == 1:
        color = rouge
      elif grille[y][i] == 2:
        color = jaune
      else :
        color = gris
      if grille[y][i] == 1 or grille[y][i] == 2:
        rect(pos_x, pos_y, cote, cote, gris)
        draw_cercle(pos_x + 7, pos_y + 2,color)
      else:
        rect(pos_x, pos_y, cote, cote, gris)
      pos_y += marge

La fonction efface une possible grille avec un rectange blanc de la même taille que la grille puis réaffiche la grille

Voici ce que produit la fonction :

Ensuite il y a affichage_preview qui va afficher le preview du jeton au bon endroit :

def affichage_preview(col_preview):
   rect(75, 17, 170, 20, (255,255,255)) #Efface l'ancien preview
   if player == 1 : #Choisi la bonne couleur
      color = rouge
   else :
      color = jaune
   draw_cercle(75 + (col_preview * 25) + 7, 17+2, color) #Fait le preview au bon endroit

Il y a la fonction wait qui retourne la touche sur laquelle vous appuyez :

def wait(buttons=(0,1,2,3,4,52)): #Retourne la touche appuyée
   while True:
      for i in buttons:
         if keydown(i):
            while keydown(i): True
            return i

Et enfin les deux dernières fonctions qui sont liées : les fonctions du score, il y a la fonction affichage_src qui est appelé au début du jeu afin d’afficher le score :

#Score par Thomas S. mais code par Robin C.
def affichage_src():
   txt("J-1", 22, 42)
   draw_cercle(35,70,rouge)
   txt("Score", 12, 117)
   if points_rouge < 10 :
      largeur = 32
   else :
      largeur = 27
   txt(str(points_rouge),32,142)
   txt("J-2", 267, 42)
   draw_cercle(280,70,jaune)
   txt("Score", 257, 117)
   if points_jaune < 10 :
      largeur = 277
   else :
      largeur = 272
   txt(str(points_jaune),277,142)

def actu_src(): #Actualise le score
   txt(str(points_rouge),37-5*len(str(points_rouge)),142)
   txt(str(points_jaune),282-5*len(str(points_jaune)),142)

Et il y a aussi la fonction actu_src qui va à chaque fin de partie, actualiser le score en écrasant l’ancien score.

Voici ce que produit la fonction affichage_src() seule :

Et pour finir il y a le lancement du jeu à la fin avec juste avant le code qui permet d’afficher le lien en bas de l’écran :

#Lien article
rect(0,200,320,22,(148,113,222))
txt("Code by nsi.xyz/puissance4",33,202,(242,)*3,(148,113,222))

#lancement du jeu
affichage_src()
selection()

Images du jeu

Conclusion

Pour conclure le tout, ce projet a été très plaisant à faire et très instructif. Il faut savoir que faire un jeu sur la NumWorks comme le mien demande du travail notamment sur la prise en main de la partie graphique qui n’a pas été sans problèmes pour moi mais avec les entrainements de M.Robert sur twitter la prise en main fut plus simple.

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

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é.

Tutoriels

Comment héberger un serveur Minecraft sur son ordinateur ?

N’avez vous jamais rêvé de pouvoir avoir un serveur Minecraft pour pouvoir jouer avec vos amis autre que sur un serveur public ? Avec ce tutoriel vous apprendrez comment en héberger un sur votre ordinateur et comment le configurer. 

Prérequis

Tableau des exigences pour un serveur minecraft (pris sur le fandom)

Télécharger le serveur

La première chose à faire est de télécharger le fichier .jar du serveur. Pour cela rendez-vous sur le site : https://mcversions.net/

Puis téléchargez le fichier de la version que vous voulez pour votre serveur.

Prenons par exemple un serveur en 1.16.2 :

Je recherche la version 1.16.2 dans la barre de recherche et deux choix s’offrent à moi : soit télécharger une version stable ou une version snapshot (instable). Je vais choisir la version stable pour éviter de futurs problèmes de crash, bugs ou autres.

Une fois que j’ai cliqué sur Download j’arrive sur cette page : 

Sur la page je vais cliquer sur “Download Server Jar” pour récupérer le .jar du serveur.

Après l’avoir téléchargé faites un dossier sur votre ordinateur (peu importe sa localisation) puis placez le .jar dedans. Exécutez le, des fichiers vont apparaître.

Avant :

Après :

Vous allez maintenant ouvrir le eula.txt et changer le eula=false en eula=true. Accepter le EULA fait que vous acceptez le Contrat de Licence Utilisateur Final de Minecraft.

Après ça sauvegardez le fichier et relancez le server.jar

Le serveur va désormais se lancer quand vous exécuterez le fichier.

Pour éteindre le serveur tapez stop dans la console et après quelques instants il devrait se fermer.

De nouveaux fichiers sont apparus !

Passons en revue ces derniers : 

  • logs : vous trouverez des fichiers .txt avec la totalité de la console écrite dedans. Un nouveau fichier se crée lors de la fermeture du serveur.
  • world : la map de votre serveur, vous pouvez glisser un de vos monde à l’intérieur pour charger celui-ci.
  • banned-ips/players des fichiers texte ou vous pourrez retrouver la liste des personnes bannis sur le serveur.
  • ops : un fichier texte ou vous trouverez toutes les personnes avec les permissions administrateur.
  • usercache : un fichier texte qui liste toutes les personnes qui se sont connecté au moins une fois sur le serveur.
  • whitelist : un fichier texte ou vous pouvez mettre des gens en liste blanche si vous voulez que le serveur ne soit accessible que pour les personnes répertorié dans le fichier.
  • server.properties : le fichier texte de configuration du serveur.

Configuration

Pour configurer le serveur vous devez ouvrir le server.properties

Voici une liste des paramètres de base qui peuvent être intéressant : 

spawn-protection=16 	// Définit une zone de protection autour du point d'apparition.
force-gamemode=false 	// Force le changement de mode de jeu pour celui par défaut pour les joueurs qui se connectent.
allow-nether=true 	// Active ou non le Nether
gamemode=survival 	// Définit le mode de jeu par défaut
difficulty=easy 	// Définit la difficulté par défaut
spawn-monsters=true 	// Active ou non l'apparition des monstres
pvp=true 	// Active ou non les dégâts entre joueurs
level-type=default 	// Définit le type de monde pour la génération
hardcore=false 	// Active ou non le mode Hardcore
enable-command-block=false 	// Active ou non les blocs de commandes
max-players=20 	// Définit la limite de joueurs sur le serveurs
spawn-npcs=true 	// Active ou non l'apparition des Villageois
allow-flight=false 	// Autorise ou pas le vol (ne permet pas aux joueurs de voler mais autorise seulement le vol)
level-name=world 	// Définit le nom du dossier ou le monde se trouve (permet notamment de changer de monde)
view-distance=10 	// Définit la distance de rendu maximal sur le serveur
resource-pack= 	// Active un pack de ressource pour le serveur (les joueurs auront le choix ou pas de l'activer)
spawn-animals=true 	// Active ou non l'apparition des animaux
white-list=false 	// Active ou non une liste blanche pour le serveur (mettre le nom des joueurs dans le whitelist.json)
generate-structures=true 	// Active la génération des structures ou pas lors de la génération du monde
max-build-height=256 	// Définit la hauteur de construction maximale
online-mode=true 	// IMPORTANT : Si activé les joueurs n'ayant pas acheté le jeu (minecraft cracké) ne pourront pas rejoindre et seuls les joueurs ayant acheté le jeu le pourront, cependant si désactivé les skins des joueurs sera le skin d'Alex soit le skin de base
level-seed= 	// Définit la seed du monde pour la génération
motd=A Minecraft Server 	// Définit ce que le serveur affichera dans le menu multijoueurs (Utilisez ce site pour le modifier plus facilement https://minecraft.tools/fr/motd.php)

Si vous voulez en savoir plus sur le server.properties n’hésitez pas à visiter ce site : https://minecraft.fandom.com/wiki/Server.properties

Après avoir configuré votre serveur il va vous falloir faire une dernière chose afin de pouvoir utiliser le serveur correctement :

Vous allez maintenant ouvrir un fichier texte et à l’intérieur vous allez placer cette ligne de code :

java -Xmx4096M -Xms4096M -jar forge-1.16.2-33.0.61.jar

Après cela, enregistrez le fichier en .bat avec le nom que vous souhaitez.

Le code est assez simple : -Xmx4096M -Xms4096M désigne la quantité de RAM que vous allouerez au serveur (veuillez si vous voulez changer de valeur mettre une puissance de 2 : 1024, 2048, 3072, 4096 …)

ensuite le forge-1.16.2-33.0.61.jar désigne le fichier du serveur qui va s’ouvrir, il est donc à modifier afin de correspondre au nom du fichier de votre serveur

Vous pouvez aussi rajouter le paramètre nogui à la fin de la ligne si vous ne souhaitez pas cet interface et que vous ne voulez avoir qu’un interpréteur de commande :

Une fois ceci fait votre serveur est configuré et est prêt à être utilisé pour jouer à minecraft dans sa version vanilla (sans mods) et à partir de maintenant il va falloir permettre à vos amis de vous rejoindre, et une des solutions est d’utiliser le logiciel Hamachi. Voici une vidéo permettant d’utiliser Hamachi pour vous connecter vous et vos amis sur le serveur :

https://youtu.be/f3L_g2phhyI

Important

Pour se connecter au serveur si il est hébergé sur votre ordinateur l’ip du serveur sera : localhost alors que vos amis devront regarder sur Hamachi l’adresse ip de l’odinateur qui héberge le serveur et la copier afin d’avoir ce modèle : xx.xx.xx.xx:port sachant que le port par défaut est 25565 si il n’est pas modifié dans le server.properties

adresse ip de mon ordinateur sur Hamachi. l’adresse Ip de mon serveur serait 25.47.160.63:25565

Et la première fois que vous vous connecterez n’oubliez pas dans la console du serveur de faire la commande : op 'Votre pseudo' afin de vous ajouter les permissions administrateur

Maintenant vous avez un serveur que vous pouvez utiliser avec vos amis mais le tutoriel ne s’arrête pas là sauf si votre serveur vous plaît comme il est et que vous ne voulez pas ajouter de mods

Avoir un serveur avec des mods

Après vous avoir montré comment héberger un serveur dit Vanilla je vais vous montrer comment avoir un serveur avec des mods grâce à Forge. Forge est une API qui permet à Minecraft de rajouter du contenu (mods).

Le serveur Forge est fortement similaire au serveur Vanilla (à par les mods) donc vous devrez probablement retourner sur les explications du serveur Vanilla pour le serveur Forge

Télécharger le serveur

Pour télécharger le serveur rendez vous sur ce site : https://files.minecraftforge.net/net/minecraftforge/forge/

Dans la partie de gauche, choisissez la version que vous voulez pour votre serveur (et accessoirement votre jeu) :

Enfin télécharger la dernière version du serveur en cliquant sur Installer :

Lorsque vous cliquerez sur Installer vous arriverez sur un site où vous aurez une pub, après quelques secondes cliquez sur SKIP en haut à droite et après cela le téléchargement se lancera.

Une fois que vous aurez téléchargé le .jar lancez le :

Enfin cliquez sur Install server et choisissez le dossier où vous souhaitez votre serveur enfin faites OK et patientez pendant que le serveur se télécharge

Après cela n’oubliez pas d’installer la version forge sur votre jeu : Install client, OK

Après que tout soit installé votre dossier de serveur devrait ressembler à cela :

comme pour le serveur Vanilla il vous faut exécuter le forge-1.xx.x.jar (et non pas le minecraft_server.1.xx.x.jar)

Ensuite acceptez le eula comme pour le serveur Vanilla et relancer le .jar

Le serveur est maintenant prêt il ne manque plus qu’à mettre les mods dans le dossier mods et pour la configuration, le serveur se configure comme la version Vanilla la seule différence peut venir du level-type si vous utilisez un mod qui modifie la génération du monde (Biomes O’ Plenty par exemple où il faut mettre biomesoplenty à la place de default dans le server.properties)

Et pour rejoindre le serveur n’oubliez pas de mettre aussi les mods présent sur le serveur dans votre jeu car sinon vous ne pourrez pas le rejoindre ce qui est dommage…

Le serveur est finalement fini et vous pouvez jouer avec vos amis dessus.

Art

Perspective : un paysage Synthwave

Dans le cadre de la 5ème édition du dispositif “Regards de Géomètre”, nous avons décidé dans le thème « Perspective » de produire un paysage synthwave.

Origines de la Synthwave

Avant tout la synthwave est un genre musical électronique ayant émergé dans les années 2000/2010 qui s’inspire d’éléments des années 80. Et c’est justement dans les clips de ces musiques que l’on va retrouver ce type d’image.

Pour en savoir plus nous vous conseillons cette vidéo qui explique brièvement les origines.

Le Projet

Revenons donc au projet. Pour ce projet nous avons donc décidé de produire une image d’un paysage synthwave. Pour cela nous utilisons le module turtle ainsi que le module random, le module turtle est utilisé pour produire l’image est le module random est utilisé pour les étoiles de l’image où la taille et leur position est générée aléatoirement nous avons également utilisé le script permettant d’exporter une image générée par turtle en .png que vous pouvez retrouver ici.

Structure du script

Pour la structure du script nous avons décidé de découper chaque partie de l’image (pavage, fond, étoiles, soleil, ville et montagnes) afin de créer des fonctions et à la fin nous les avons toutes appelées dans un ordre précis pour que les différentes parties de l’image soit dans leur plan respectif.

Analyse du script

Nous allons donc analyser le script.

Commençons par l’appel des modules et la mise en place de turtle.

from turtle import *
from random import randint
# vérification des modules importés
try:
    from PIL import Image
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False
titre = "Perspective - Un paysage Synthwave"
title(titre+" | Au lycée, la meilleure spécialité, c'est la spé NSI")
setup(1280, 720) # définit la taille de la fenêtre
colormode(255) # permet l'utilisation de couleurs rgb
speed(0) #Remplaçable par tracer(2) (10x plus rapide) mais si il est utilisé des lignes du pavage peuvent manquer
hideturtle() #dissimule la tortue

On appelle les fonctions turtle, random avec pour random uniquement randint et on utilise une partie du script « exporter une image générée par turtle » pour vérifier que l’utilisateur a bien installé le module PIL et dans le cas contraire un message d’erreur s’affichera et lui donnera un lien pour installer le module et tout ça sans que le script ne s’arrête. Après la vérification on met en place le titre de la fenêtre qui va affiché le rendu ainsi que sa taille. Enfin on définit le type de couleurs utilisées (R,G,B), la vitesse de la tortue, et on dissimule la tortue (c’est plus joli).

Commençons par la première fonction : le fond

def fond():
    penup()
    rciel = 0
    gciel = 0 
    bciel = 0
    hauteur = -360
    goto(-642,-358)
    pendown()
    while hauteur != 360:
        pencolor(round(239 + rciel), round(41 + gciel), round(209 + bciel))
        forward(1280)
        hauteur += 1
        goto(-640, hauteur)
        rciel += (-29/180)
        gciel += (2/45)
        bciel += (7/720)

Pour le fond on aurait pu utiliser une fonction qui crée un rectangle et qui le remplit avec fill_rect, cependant la couleur dans ce cas est uni ce qui ne nous intéresse pas. Nous avons donc produit un script qui fait un fond dégradé qui fait avancer la tortue sur une ligne d’un pixel de large et à la fin de cette ligne la tortue est envoyé grâce à un goto à la ligne d’après et qui ajoute la différence de chaque couleur (rouge,vert et bleu) entre la couleur de début du dégradé et la couleur de fin. Tout ceci est arrondi car turtle n’est pas compatible avec des arguments à virgule (pour la fonction pencolor en tout cas).

Par la suite la fonction qui produit les étoiles a été codée :

def etoile():
    for i in range(90):
        penup()
        goto(randint(-720,720), randint(0,360))
        pendown()
        pencolor(255, 255, 255)
        lcercle = randint(1,3)
        fillcolor('white')
        begin_fill()
        circle(lcercle)
        end_fill()

Pour les étoiles on définit aléatoirement leur position sur la moitié haute de l’image, on les met en blanc, on définit aussi aléatoirement la taille de l’étoile et on créer l’étoile avec sa position, et sa taille en aléatoire puis on refait ce processus 90 fois pour avoir 90 étoiles.

Ensuite nous avons le soleil (Le script affiché n’est qu’une petite partie du script total du soleil car il est très long et qu’il se répète, il est donc inutile de commenter la suite) :

def soleil():
    penup()
    liste1 = [10,7,5,4,3,3,3,3,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,1,2,1,1,1]
    liste2 = [1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0]
    pliste1 = 0
    pliste2 = 0
    rsoleil = 0
    gsoleil = 0
    bsoleil = 0
    lsoleil = 8
    hauteur = 30
    goto(0,30)
    pendown()
    for i in range(15):
        pencolor(round(255 + rsoleil), round(23 + gsoleil), round(226 + bsoleil))
        forward(lsoleil)
        backward(2*lsoleil)
        hauteur += 1
        lsoleil += liste1[pliste1]
        pliste1 += 1
        goto(0, hauteur)
        rsoleil += (0)
        gsoleil += (114/101)
        bsoleil += (-203/202)
    forward(lsoleil)
    backward(2*lsoleil)
    penup()

Pour le soleil nous réutilisons le script du dégradé, cependant nous l’avons modifier pour que le point d’origine de la tortue soit au centre du rectangle et que la longueur de chaque ligne soit défini par une liste (ce qui permet de faire un cercle en prenant les longueur d’un cercle utilisable en pixel art). Pour ce qui est des endroits ou le soleil est coupé la tortue fait le tracé mais on a utilisé la fonction penup() qui fait que la tortue ne dessine plus.

Après nous avons la fonctions des montagnes (Encore une fois le script n’est pas complet car il se répète encore 3 fois après.) :

def montagne():
    #montagne derrière la première
    penup()
    goto(-480,0)
    fillcolor(110, 27, 188)
    begin_fill()
    for i in range(3):
        forward(250)
        left(120)
    end_fill()
    
    goto(-480,0)
    pencolor(51, 210, 246)
    ymontagne = 10
    for i in range(11):
        pendown()
        goto(-355,ymontagne)
        goto(-230,0)
        penup()
        goto(-480,0)
        ymontagne += 20

Pour le script des montagnes nous avons utilisé le script pour faire des triangles équilatéraux retrouvable ici. Et ensuite nous avons utilisé un ‘for i in range’ pour faire des goto enchaîné pour faire les lignes des montagnes.

Ensuite nous avons les fonctions de la ville. Pourquoi les fonctions car il y a une fonction pour chaque bâtiment différent ainsi qu’une fonction finale qui définit l’ordre des bâtiments.

def bat1():
    penup()
    rbat = 0
    gbat = 0 
    bbat = 0
    hauteur = 0
    pendown()
    xturtle, yturtle = pos()
    while hauteur != 72:
        pencolor(round(125 + rbat), round(35 + gbat), round(216 + bbat))
        forward(42)
        hauteur += 1
        goto(xturtle, hauteur)
        rbat += (-5/3)
        gbat += (-7/15)
        bbat += (-72/25)
    forward(42)
    penup()
    right(90)
    forward(72)
    left(90)

Pour le bâtiment 1 il y a un dégradé (toujours le même script) puis on fait que la tortue finisse le bâtiment en bas à droite de ce dernier pour pouvoir enchainer les bâtiments.

fun fact : Au début nous n’avions pas prévu les quelques lignes à la fin pour que la tortue puisse enchaîner les bâtiments sans que les fonctions des bâtiments aient besoin d’être modifiés ce qui nous a amené à avoir tous les bâtiments qui se chevauchaient.

def ville():
    penup()
    goto(-320,0)
    bat3(), bat2(), bat1(), bat4() ,bat3(), bat4(), bat3(), bat2(), bat1(), bat2(), bat1(), bat3(), bat1(), bat4(), bat2(), bat1(), bat3(), bat1(), bat4(), bat3()

Et à la fin on a fait la fonction ville qui appelle dans l’ordre choisi les bâtiments. Le goto permet de définir où commence le premier bâtiment, les autres se mettent juste après le premier sans se chevaucher ni laisser un espace.

Par la suite nous avons le pavage (très long aussi, il sera donc coupé) :

def pavage():
    colormode(255)
    pensize(5)
    speed(0)
    rciel = 0
    gciel = 0 
    bciel = 0
    hauteur = -360
    penup()
    goto(-640,-360)
    pendown()
    while hauteur != 0:
        pencolor(round(15 + rciel), round(4 + gciel), round(76 + bciel))
        forward(1280)
        hauteur += 1
        goto(-640, hauteur)
        rciel += (91/180)
        gciel += (1/36)
        bciel += (7/18)

Pour le début du pavage on retrouve encore le script du dégradé mais avec les couleurs modifiées.

pencolor(229, 123, 240)
    #Lignes au dessus du pavage
    pensize(4),penup(),goto(-640,0),pendown(),goto(640,0),pensize(2),penup(),goto(-640, 0),pendown()
    #lignes gauche
    penup(),goto(-20.00,0),pendown(),goto(-60.00,-360.00),penup(),goto(-60.00,0),pendown(),goto(-180.00,-360.00),penup(),goto(-100.00,0),pendown(),goto(-300.00,-360.00),penup(),goto(-140.00,0),pendown(),goto(-420.00,-360.00),penup(),goto(-180.00,0),pendown(),goto(-540.00,-360.00),penup(),goto(-220.00,0),pendown(),goto(-660.00,-360.00),penup(),goto(-260.00,0),pendown(),goto(-780.00,-360.00),penup(),goto(-300.00,0),pendown(),goto(-900.00,-360.00),penup(),goto(-340.00,0),pendown(),goto(-1020.00,-360.00),penup(),goto(-380.00,0),pendown(),goto(-1140.00,-360.00),penup(),goto(-420.00,0),pendown(),goto(-1260.00,-360.00),penup(),goto(-460.00,0),pendown(),goto(-1380.00,-360.00),penup(),goto(-500.00,0),pendown(),goto(-1500.00,-360.00),penup(),goto(-540.00,0),pendown(),goto(-1620.00,-360.00),penup(),goto(-580.00,0),pendown(),goto(-1740.00,-360.00),penup(),goto(-620.00,0),pendown(),goto(-1760.00,-360.00)
    #lignes droites
    penup(),goto(20,0),pendown(),goto(60.00,-360.00),penup(),goto(60.00,0),pendown(),goto(180.00,-360.00),penup(),goto(100.00,0),pendown(),goto(300.00,-360.00),penup(),goto(140.00,0),pendown(),goto(420.00,-360.00),penup(),goto(180.00,0),pendown(),goto(540.00,-360.00),penup(),goto(220.00,0),pendown(),goto(660.00,-360.00),penup(),goto(260.00,0),pendown(),goto(780.00,-360.00),penup(),goto(300.00,0),pendown(),goto(900.00,-360.00),penup(),goto(340.00,0),pendown(),goto(1020.00,-360.00),penup(),goto(380.00,0),pendown(),goto(1140.00,-360.00),penup(),goto(420.00,0),pendown(),goto(1260.00,-360.00),penup(),goto(460.00,0),pendown(),goto(1380.00,-360.00),penup(),goto(500.00,0),pendown(),goto(1500.00,-360.00),penup(),goto(540.00,0),pendown(),goto(1620.00,-360.00),penup(),goto(580.00,0),pendown(),goto(1740.00,-360.00),penup(),goto(620.00,0),pendown(),goto(1760.00,-360.00)
    #Lignes horizontales
    penup(),goto(-640, -300),pendown(),goto(640, -300),penup(),goto(-640, -240),pendown(),goto(640, -240),penup(),goto(-640, -190),pendown(),goto(640, -190),penup(),goto(-640, -140),pendown(),goto(640, -140),penup(),goto(-640, -100),pendown(),goto(640, -100),penup(),goto(-640, -70),pendown(),goto(640, -70),penup(),goto(-640, -40),pendown(),goto(640, -40),penup(),goto(-640, -15),pendown(),goto(640, -15),

On a par la suite énormément de goto afin de faire le quadrillage du pavage.

Pour produire l’image finale nous avons les appels des différents fonctions à la fin :

#appel de toutes les fonctions
fond(), etoile(), soleil(), montagne(), ville(), pavage()

Et pour exporter l’image finale en .png il y a la suite du script pour exporter une image générée par turtle en .png utilisé au début du script.

#enregistrement de l'image finale avec vérification des modules importés
image = getcanvas()
nom_du_fichier_sans_extension=titre+"_"+hex(randint(2**30+2**25,2**30+2**25+2**24-1))[2:]
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')
try:
    psimage = Image.open(nom_du_fichier_sans_extension+".ps")
    psimage.load(scale=2)
    psimage_resized = psimage.resize((1280, 720))
    psimage.save(nom_du_fichier_sans_extension+".png")
    print(nom_du_fichier_sans_extension+".png", psimage.size, "sauvegardé dans le dossier")    
except:
    if not pillow_installed:
        print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
        print("https://nsi.xyz/py2png")
    else:
        print("Oops! - 'ghostscript' not installed- RTFM :")
        print("https://nsi.xyz/py2png")
exitonclick()

Le script va donc générer une image en .ps et la convertir en .png avec un nom généré aléatoirement pour éviter que à chaque fois que vous générez une image l’image soit écrasée

Télécharger le .py

L’image finale