Qui n’a jamais rêvé d’avoir un robot en LEGO qui lui annonce la météo ? Et bien même si ce n’est pas le cas, nous l’avons fait pour vous ! Venez découvrir un robot capable de comprendre son environnement pour vous donner la météo.
Fonctionnalités du robot
Notre robot au doux nom de ROBKEY est capable de :
déplacer ses bras et son bassin ;
reconnaitre l’endroit où il se trouve pour donner précisément la météo ;
afficher l’heure, la météo et la température ambiante ;
d’avoir quelques réactions faciales ;
et bien plus.
La conception du robot
Pour concevoir notre robot, nous avons dû prendre en compte plusieurs choses. Tout d’abord, les moteurs n’ont pas été particulièrement dur à installer car il ne font que la moitié de la taille de l’esp 32. Il nous a donc paru évident de mettre les moteurs des bras dans le torse du robot et le moteur du bassin dans le châssis de ses chenilles.
Ensuite il a fallu installer planche de test qui a eu plusieurs positions possibles. Premièrement dans son dos car cela aurait permis de pouvoir faire tourner le bassin du robot à 360°, mais les câbles auraient difficilement bien tenu à l’esp 32 et au robot lui-même. Nous nous sommes finalement entendu à l’idée de mettre la planche sur la batterie des chenilles bien que cela empêche le robot de faire des tours sur lui même.
Maintenant, le plus important : le placement de l’ESP32. Nous avions deux idées, l’une le plaçait dans le torse du robot, l’autre faisait en sorte qu’il soit la tête légèrement rentrée dans les épaules. Nous avons opté pour la seconde option car celle-ci nous permet d’afficher un petit visage sur l’écran du microcontrôleur.
Pour conceptualiser l’apparence de notre robot, nous avons donc réalisé un schéma en 3D très simple sur Paint 3D en regroupant nos idées pour imaginer ce à quoi il ressemblera.
Construction de la structure du robot
Pour construire le robot, j’ai (Sylvain) commencé par regarder les constructions Lego que j’avais précédemment réalisées et qui pouvaient être détruite pour commencer à récupérer leurs pièces. Avec toutes ses matières premières je me suis laissé porter par mon imagination pour construire le LEGO le plus ressemblant à un robot en essayant de faire en sorte qu’il soit plutôt résistant (bon d’accord j’ai peut-être failli à cette tâche).
C’est grâce aux joies de la maladie que j’ai pu entamer la construction. Pour commencer, j’ai récupéré le châssis d’une autochenille radiocommandée en la modifiant pour la rendre plus compacte et en laissant un espace pour le moteur du bassin.
Entouré en blanc : Le moteur du bassin
Ensuite j’ai commencé à faire ledit bassin, que j’élargis à la fin de la construction car je l’avais fait trop fin. Suivi du torse, le plus simple car il devait être creux pour laisser la places aux câbles et aux moteurs. Il fallait aussi préparer les trous pour les axes des bras.
Après j’ai fait les bras qui sont vraiment très simplistes mais à ce moment je commençais sérieusement à manquer de pièces plus techniques et même des plus simples pour faire quelque chose de plus sympathique. Mais quoi qu’il en est l’un des deux bras représente un soleil pour quand il fait beau, tandis que l’autre représente un ciel nuageux.
Pour finir j’ai réalisé la cage de la planche de test et solidifié les éléments les moins solides et résolu les quelques problèmes mécaniques rencontrés lors des premiers tests des moteurs.
L’électronique du robot
En parallèle de la construction du LEGO, je (Thomas) me suis lancé dans la conception électronique de celui-ci.
Pour cela, j’ai d’abord listé tout ce que je voulais intégrer à l’électronique :
2 boutons,
1 capteur de température,
3 moteurs.
Pour m’aider dans la réalisation électronique, je me suis aidé de tous ces sites :
Les câbles blancs (entourés en rouge) récupèrent l’information de la pression du bouton et les câbles noirs (entourés en vert) s’occupe de fermer le circuit (de l’électricité passent dans le bouton, et il faut que l’électricité puisse faire une boucle). Si vous voulez, les boutons reçoivent de l’énergie par les câbles blancs et l’expulsent par les câbles noirs.
Concernant le branchement du capteur :
A droite à quoi ressemble le capteur de température de face et à gauche son branchement. La câble de gauche, en blanc récupère les mesures du capteur, celui du milieu en marron, alimente positivement le capteur (le capteur reçoit de l’énergie par ce câble) et le câble noir à droite du câble marron lui ferme la boucle (le capteur expulse l’énergie par ce câble.)
Et pour finir, les moteurs ont 3 câbles :
Le câble orange sert à contrôler le moteur, le rouge à l’alimenter et le marron à fermer la boucle. On refait ça 3 fois puisqu’on a 3 moteurs.
On branche tout ceci à la planche de test puis à l’ESP32, et voilà montage terminé :
Programmation du robot
Maintenant que le robot est construit et le montage électronique assemblé, il faut passer à la programmation de l’ESP32. Veuillez noter que depuis l’écriture de ce qui suit, le code a bénéficié de quelques mises à jours. Vous retrouverez l’archive en fin d’article avec les fichiers à jours.
Tout d’abord, il faut coder les briques de bases, qui vont nous permettre de manipuler différents outils, capteurs, actionneurs, api, etc.
C’est pour cela que nous créons un fichier buttons.py qui contient une classe Button dans laquelle nous initialisons 4 boutons : les 2 du microcontrôleur et les deux boutons ajoutés. Dans cette classe, vous verrez que chaque boutons a des caractéristiques supplémentaires. En effet il est possible de rendre un bouton poussoir équivalent à un levier. Nous créons différentes fonctions pour récupérer ou modifier des informations en rapport avec les boutons.
from machine import Pin
from time import sleep
# Importer les modules est inutile car ils sont déjà importés dans boot.py
class Buttons():
def __init__(self):
#self.name = "t-display-s3"
# NomDuBouton : [ PinUtilisé , mode , value ]
# 1 : [ Pin 3 en mode INPUT avec une résistance Pull-Down , levier , 0 ]
# 1 : [ Pin(3, mode=Pin.IN, pull=Pin.PULL_DOWN), 1, 0 ]
# Note : le chiffre de value dans la liste n'est pas pris en compte si le bouton est en mode "poussoir" -> On ne peut pas mettre None car si on décide de changer le mode du bouton, la valeur devient importante.
# Pour une raison inconnue, "left" et "right" ne fonctionne plus.
self.list_button = {"left" : [Pin(0, Pin.IN, Pin.PULL_UP),0,1],
"right" : [Pin(14, Pin.IN, Pin.PULL_UP),0,1],
1 : [Pin(2, mode=Pin.IN, pull=Pin.PULL_UP),0,1],
2 : [Pin(3, Pin.IN, Pin.PULL_UP),0,1]
}
def getButton(self, button):
return self.list_button[button]
def setButton(self, button, pin, mode, value=None): # Le set fait également add.
self.list_button[button] = [pin,mode,value]
def setMode(self, button, val:bool): # Ce mode change le mode du bouton -> soit "levier" soit "poussoir"
if isinstance(val, bool) or val in (0,1):
self.list_button[button][1] = val
if not(val):
self.list_button[button][2] = 1
return print("Mode définie sur : " + str(self.list_button[button][1]))
else:
raise ValueError("val must be a boolean or a integer included between 0 and 1.")
def setValue(self, button, value):
if self.list_button[button][1]:
self.list_button[button][2] = value
return print("Valeur définie sur : " + str(self.list_button[button][2]))
else:
print("Button must be a toogle button, not a push button.")
return False
def getValue(self, button): # Renvoie la valeur du bouton
if not(self.list_button[button][1]): # Si le statut de mode_toogle est égal à 0 <=> Si le bouton est en mode "poussoir" et non "levier"
return self.list_button[button][0].value()
else:
if not(self.list_button[button][0].value()):
self.list_button[button][2] = 0 if self.list_button[button][2] else 1
sleep(0.25)
return self.list_button[button][2]
def isButtonPressed(self,button): # Renvoie True si le bouton est pressé (donc si value = 0) et False sinon
return not(self.getValue(button))
Continuons avec le fichier temperature.py dans lequel nous avons une simple fonction qui nous renvoie les données récupérées par le capteur de température et d’humidité.
from time import sleep #Inutile car importé dans boot.py
def getTemperatureAndHumidity(capteur): # Capteur doit etre sous cette forme : capteur = dht.DHT11(Pin(17)) -> Ne pas oublier d'importer le module dht
try:
capteur.measure() # Met à jour les données de températures et d'humidités
sleep(1)
return (capteur.temperature(),capteur.humidity())
except:
print("Valeurs non récupérées")
return False
Egalement, à l’aide des fichiers motor.py et servo.py (trouvé sur internet), nous créons des fonctions qui réaliseront des déplacements particuliers :
from servo import Servo
import time
motor=Servo(pin=12) # A changer selon la broche utilisée
motorbd=Servo(pin=10)
motorbg=Servo(pin=11)
def droit(t=1):
motor.move(285)
time.sleep(t)
def bassin(t=2):
motor.move(265)
time.sleep(t)
motor.move(305)
time.sleep(t)
def baissebg(t=0.5):
motorbg.move(335)
time.sleep(t)
def levebg(t=0.5):
motorbg.move(270)
time.sleep(t)
def baissebd(t=0.5):
motorbd.move(335)
time.sleep(t)
def levebd(t=0.5):
motorbd.move(270)
time.sleep(t)
droit()
Pour finir, à l’aide du fichier connection.py, nous créons plusieurs fonctions en rapport avec la partie sans-fil de l’ESP32.
import network
import st7789 #En théorie inutile car déjà importé dans boot.py
from time import sleep
def connect_to_wifi(display, font, ssid, mdp):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
display.init()
display.draw(font,"Connexion a un point",0,111)
display.draw(font,"d'acces en cours",15,126)
wlan.connect(ssid,mdp)
while not wlan.isconnected():
pass
display.fill_rect(0,107,170,25,st7789.BLACK)
display.draw(font,'Connecte !',40,111)
sleep(0.8)
display.fill_rect(0,107,170,10,st7789.BLACK)
display.deinit()
def search_wlan():
station = network.WLAN(network.STA_IF)
station.active(True)
return station.scan()
Passons aux modules qui vont utiliser des apis :
En premier lieu, nous avons le fichier geoloc.py dans lequel nous utilisons l’api de google qui à l’aide des réseaux wifi aux alentours est capable de situer l’ESP32. Dans ce fichier est présent une fonction qui récupère les coordonnées géographiques de l’ESP32.
from modules.connection import search_wlan
import ustruct as struct
import urequests as requests
import ujson as json
import modules.api_txt as api_txt
def getLocation(api_key): # Avec Google maps API + sécurité sur le nombre de requête.
if int(api_txt.get_api_counter()[0]) > 24000:
print("Quota de demande dépassé !!! Vous ne pouvez pas faire de requête...")
return False
list_wlan = search_wlan()
data = {
"considerIp": False,
"wifiAccessPoints": []
}
for wifi in list_wlan:
entry = {
"macAddress": "%02x:%02x:%02x:%02x:%02x:%02x" % struct.unpack("BBBBBB", wifi[1]),
"signalStrength": wifi[3],
"channel": wifi[2]
}
data["wifiAccessPoints"].append(entry)
headers = {"Content-Type": "application/json"}
url = "https://www.googleapis.com/geolocation/v1/geolocate?key=" + api_key
response = requests.post(url, headers=headers, data=json.dumps(data))
api_txt.add_api_counter(0)
return json.loads(response.content)["location"]
En second et dernier lieu, il reste le fichier meteo.py qui va être capable d’utiliser une api de météorologie pour récupérer, analyser et mettre en forme des données en rapport avec la météo.
import urequests
import modules.api_txt as api_txt
def get_meteo(latitude, longitude): # Sur la prochaine heure
if int(api_txt.get_api_counter()[1]) > 10000:
print("Quota de demande dépassé !!! Vous ne pouvez pas faire de requête...")
return False
url = "https://api.open-meteo.com/v1/forecast?latitude={0}&longitude={1}¤t=temperature_2m,precipitation&daily=temperature_2m_max,temperature_2m_min,precipitation_sum&timezone=auto&forecast_days=3".format(latitude,longitude)
json_meteo = urequests.get(url).json()
api_txt.add_api_counter(1)
return {"current":
{"temperature":
[
json_meteo["current"]["temperature_2m"],
json_meteo["current_units"]["temperature_2m"]
],
"precipitation":
[
json_meteo["current"]["precipitation"],
json_meteo["current_units"]["precipitation"]
]
},
"demain":
{"temperature": [round((json_meteo["daily"]["temperature_2m_min"][1] + json_meteo["daily"]["temperature_2m_max"][1]) / 2, 1), json_meteo["daily_units"]["temperature_2m_min"]],
"precipitation":
[
json_meteo["daily"]["precipitation_sum"][1],
json_meteo["daily_units"]["precipitation_sum"]
]
},
"apres-demain":
{"temperature": [round((json_meteo["daily"]["temperature_2m_min"][2] + json_meteo["daily"]["temperature_2m_max"][2]) / 2, 1), json_meteo["daily_units"]["temperature_2m_min"]],
"precipitation":[
json_meteo["daily"]["precipitation_sum"][2],
json_meteo["daily_units"]["precipitation_sum"]
]
}
}
Passons à la gestion graphique, nous avons récupérer le module tft_config.py fournit par les développeurs du pilotes graphiques pour l’esp32. Celui-ci permet de créer facilement une gestion de l’écran avec les bonnes caractéristiques. Nous n’afficherons pas le code ici, car ce n’est pas nous qui l’avons codé. Vous pouvez le retrouver dans l’archive à la fin de cet article ou ici.
Nous avons créé le fichier affichage.py qui contient un certain nombre de fonctions pour réaliser le rendu graphique pour l’ESP32. Il contient également le code qui récupère des images pour les animations du visage et des icones. Ces dessins et animations ont été réalisé par Ilana, ancienne première NSI.
import st7789
from time import sleep, localtime
import modules.motor as motor
last_temps = -1
def display_time(display,font,x,y):
global last_temps
temps = localtime()
if last_temps < temps[5]:
display.png("/images/icones/horloge.png",x,y,True)
display.fill_rect(x+30,y+5,60+6,10,st7789.BLACK)
display.draw(font, str(temps[3]) + ":" + str(temps[4]) + ":" + str(temps[5]),x+30,y+10)
last_temps = temps[5]
def display_meteo(display,font,infos_meteo, x, y):
display.draw(font,str(infos_meteo["temperature"][0]),x,y)
display.draw(font, font.LAST,x + display.draw_len(font,str(infos_meteo["temperature"][0])), y)
display.draw(font, " C",x + display.draw_len(font,str(infos_meteo["temperature"][0])), y)
display.draw(font,str(infos_meteo["precipitation"][0]),x+80,y)
display.draw(font,str(infos_meteo["precipitation"][1]),x + 80 + display.draw_len(font,str(infos_meteo["precipitation"][0])),y)
def display_temperature(display,font,infos_temperature,x,y):
display.fill_rect(x,y,70,20,st7789.BLACK)
display.png("/images/icones/temperature.png",x,y,True)
display.draw(font,str(infos_temperature) + " C",x+30,y+9)
display.draw(font,font.LAST,x+30+display.draw_len(font,str(infos_temperature)), + y+9)
def meteo_icon(display,icone,x,y):
display.fill_rect(x,y,50,50,st7789.BLACK)
if icone == "pluie":
display.png("/images/icones/mauvais_temps.png",x,y,True) #Nuage pluie gris pas content
else:
display.png("/images/icones/beau_temps.png",x,y,True) # Soleil content
def meteo_widget(display,font,infos_meteo,x,y):
display.fill_rect(x,y,170,200,st7789.BLACK)
display.draw(font, "Auj.",x,y)
display_meteo(display,font,infos_meteo["current"],x, y+20) #x,y+20)
display.draw(font, "Dem.", x, y+40) #x+50, y)
display_meteo(display,font,infos_meteo["demain"], x, y+60) #x+50,y+20)
display.draw(font, "Apr-Dem.", x, y+80) #x+90, y)
display_meteo(display,font,infos_meteo["apres-demain"], x, y+100) #x+90,y+20)
def main_menu(display,font,infos_temp,infos_meteo):
display_time(display,font, 10, 111) # x = 52 pour un semblant de centrage
display_temperature(display,font,infos_temp,10,151)
if infos_meteo["current"]["precipitation"][0] > 0:
icone = "pluie"
motor.baissebg()
motor.levebd()
else:
icone = "soleil"
motor.baissebd()
motor.levebg()
meteo_widget(display,font,infos_meteo,10,191)
meteo_icon(display,icone,110,111)
def animation(display,name:str,iteration:int,i_fixe:int): # list_anim : {"content":(2,1),"endormi":(4,1),"somnole":(3,1),"observe":(0,0),"monocle":(2,1)}
display.fill_rect(0,0,170,105,st7789.BLACK)
for i in range(2,iteration+1):
display.png("/images/" + name + "/" + name + str(i) + ".png",0,0)
if name == "endormi":
sleep(0.3)
for i in range(iteration,0,-1):
display.png("/images/" + name + "/" + name + str(i) + ".png",0,0)
if name == "endormi":
sleep(0.3)
display.png("/images/" + name + "/" + name + str(i_fixe) + ".png",0,0)
Pour finir nous avons le fichier api_txt.py qui s’occupe de gérer des fichiers de textes importants pour le fonctionnement du programme, présents dans le dossier data.
"""
api[0] => Google Maps API
api[1] => Météo API
"""
def get_api_counter():
fichier_r = open("data/counter-api.txt",'r')
liste = fichier_r.read().split(",")
fichier_r.close()
return liste
def add_api_counter(indice_api): # ne fonctionne qu'avec deux apis
fichier_w = open("data/counter-api.txt",'w')
val = get_api_counter()
val[indice_api] = int(val[indice_api]) + 1
fichier_w.write(str(val[0]) + "," + str(val[1]))
fichier_w.close()
def set_api_counter(indice_api,value): # ne fonctionne qu'avec deux apis
fichier_w = open("data/counter-api.txt",'w')
api_value = get_api_counter()
api_value[indice_api] = str(value)
fichier_w.write(api_value[0] + "," + api_value[1])
fichier_w.close()
Maintenant que toutes nos briques sont construites, il ne reste qu’à écrire le fichier python principal qui va organiser tout le programme tel un chef d’orchestre.
Assemblage final
Maintenant, il faut simplement assembler notre montage électronique à notre construction LEGO. Puis à brancher notre ESP32 à un câble, lui injecter le code, et tout en le gardant brancher, le lancer, et la magie opère !
ROBKEY est assemblé !ROBKEY est vivant !
Fichiers
Veuillez noter que vous devrez ajouter votre propre clé api Google Maps API pour que la position soit mise à jour (dans boot.py à la ligne 21). Sinon vous pouvez changer manuellement la position dans le fichier old-location.txt se trouvant dans le dossier data. Veuillez respecter l’ordre suivant et ne surtout rien ajouter d’autre dans le fichier, pas même un espace :
latitude,longitude
Vous devrez également ajouter vos propres informations pour un point d’accès dans le fichier boot.py au niveau de la ligne 19 : wlan_info = ("SSID","MDP") Le projet ne fonctionne pas sans une connexion à internet !!
Un jeu de société existant sous l’ombre des dames et des échecs, il est temps de rendre justice ! Le Reversi est un jeu de société pour deux joueurs qui se joue sur un plateau de 64 cases. Les joueurs placent tour à tour des pions noirs ou blancs sur le plateau, en capturant les pions adverses et en retournant les pions de l’adversaire pour gagner le contrôle du plateau.
À la découverte d’un nouveau jeu !
« – Alors qu’avons-nous là… Un Tetris ? Déjà fait et étonnement, pas le niveau. Un 2048 ? 😤 Une bataille navale ? Oula, pas certain de ce coup. Bon alors, quel jeu faire sur la NumWorks qui n’est pas trop dur à réaliser, mais reste néanmoins intéressant ? – « Le Reversi » ? – À vos souhaits. – Tu n’as pas la lumière à tous les étages toi… C’est un jeu de société également appelé Othello. Voici plus de détails concernant les règles. – D’accord, je vois le style, et bien allez, c’est parti ! »
Maintenant, il faut l’arranger pour notre jeu. Pour commencer, on s’occupe de placer une grille au centre de l’écran, ce sera notre plateau de jeu.
Elle n’est pas au centre ? Achetez-vous des lunettes… Et en plus, on me dit dans l’oreillette que les couleurs ne sont pas bonnes ? La couleur ? Vous n’êtes jamais content. Et là ? On est bon ? Aaah enfin
[...]
def grille(x, y, t_x, t_y, c, col):
for w in range(t_x):
for h in range(t_y):
rec(1+x+w*(c+2), 1+y+h*(c+2), c, c, col)
La fonction est assez simple, elle va simplement faire un nombre de petits carrés indiqués en paramètres. t_x pour la taille en horizontale et t_y pour la taille en vertical (il faut indiquer un nombre de carrés pour les 2 variables).
Ce n’est pas fini ! Même pas du tout 😥
On va s’occuper du système pour gérer les déplacements sur la grille en se basant sur ce qu’a proposé le prof dans son exemple. Avant ça, nous allons définir un paquet de variables qui nous serons utiles tout le long du code (et encore, cette liste n’est pas exhaustive) :
# Beaucoup... BEAUCOUP de couleurs...
col = ((100,203,111),(220,)*3,(60,)*3,(148,113,222),
(242,0,0),(255,183,52),(255,)*3,(60,139,72),
(90,)*3,(180,)*3,(160,)*3,(242,)*3)
# Différents paramètres, à commencer par la taille horizontale et verticale
# de notre grille (ce sont les var pour t_x et t_y)
len_x,len_y = 8,8
# On l'utilisera plus tard, ce sera la position par défaut du sélectionneur de case
pos = [2,3]
# la taille des carrés dans la grille, en pixel
c = 20
# De sombres calcul... Pour centrer la grille !
x_g = (320 - (1+len_x*(c+2))) // 2
y_g = (200 - (1+len_y*(c+2))) // 2
# Très important ! C'est notre grille, chaque valeur d'une case est renseignée dans cette matrice
g = [[0 for i in range(len_x)] for i in range(len_y)]
# Gestion des tours, des scores,
# et des tours où un joueur ne peut pas jouer (allez check les règles pour comprendre ça)
player_turn,sc_noir,sc_blanc,no_round = 2,0,0,0
Une partie de ces variables se situent dans une fonction init() qui permet de relancer une nouvelle partie lorsque la précédente est terminée et de gérer le système de paramètres (nous le verrons plus tard). Une grosse fonction play() s’occupe de vérifier les actions du joueur, c’est-à-dire les déplacements, s’il place un pion, etc.
Ce code permet de gérer les déplacements sur toutes la grille et permet lorsque l’on appuie sur la touche Ok ou EXE, de valider notre sélection et d’exécuter une fonction action() qui s’occupe de lancer différentes fonctions selon certaines conditions pour vérifier le placement du pion puis le placer ou non.
Revenons sur nos déplacements. Avant toute chose, nous allons créer deux fonctions bien pratiques : getCase() et setCase(). Comme leur nom l’indique, l’une nous renvoie des informations concernant une case, l’autre change la valeur d’une case. « Récupérer » des informations ou « changer » la valeur d’une case, c’est modifier/récupérer une valeur dans notre matrice.
def getCase(g_in, x, y, col_mode=False):
if col_mode:
if 0 <= g_in[y][x] <= 2:
return col[g_in[y][x]]
return None
return -1 if x < 0 or x >= len_x or y < 0 or y >= len_y else g_in[y][x]
def setCase(g_in_in, x, y, value):
g_in_in[y][x] = value
On peut voir que getCase(), en plus de pouvoir nous renvoyer un statut pour la case sélectionnée (-1 si non valide, 0 si vide, 1 si blanc et 2 si noir), peut également nous renvoyer la couleur de la case. setCase() est plus simpliste mais tout aussi importante. Voici notre fonction deplacement() :
def deplacement(pos_old=[],for_error=False):
global c
if not(for_error):
if pos_old != []:
x, y = pos_old
if getCase(g,x,y) in (1,2):
rec(x_g+1+x*(c+2),y_g+1+y*(c+2),c,c,col[0])
cercle(x_g+8+(c+2)*x,y_g+3+(c+2)*y,getCase(g,x,y,True))
else:
rec(x_g+1+x*(c+2),y_g+1+y*(c+2),c,c,getCase(g,x,y,True))
x, y = pos
rec(x_g+1+x*(c+2),y_g+1+y*(c+2),c,c,col[7])
cercle(x_g+8+(c+2)*x,y_g+3+(c+2)*y,col[1] if player_turn == 1 else col[2],1)
case = getCase(g,x,y)
if case != 0:
cercle(x_g+8+(c+2)*x,y_g+3+(c+2)*y,getCase(g,x,y,True))
else:
rec(x_g + 1 + pos[0]*(c+2),y_g + 1 + pos[1]*(c+2), c, c, col[4])
sleep(0.1542)
Cette fonction s’occupe de rendre le déplacement naturel et fluide. En effet, si l’on est à une case de coordonnée (1;1) et que l’on décide d’aller en (2;1), il faut que l’on affiche une surbrillance de sélection sur la nouvelle case, et que l’on rende à l’ancienne case son apparence d’origine. C’est très vulgarisé, car en plus de ça, ce code gère un affichage différents si le placement est incorrect, ainsi qu’une surbrillance différentes si on le joueur décide de se déplacer sur une case avec déjà un pion de placé.
On remarque une fonction cercle(). Elle a été proposé par le professeur, qui est chaleureusement remercié, car les pions, au lieu de ressembler à ça…
Petit faux raccord sur la couleur de la grille. Vous voyez également une des premières versions du sélectionneur, qui depuis a été bien amélioré.
…ressemblent plutôt à ça :
def cercle(x,y,col,s=0):
for d in range(6):
rec(x-d+(d==0)+2*s,y+d+(d==5)+2*s,6+2*d-2*(d==0)-4*s,16-2*d-2*(d==5)-4*s, col)
Que d’aventure… Nous avons maintenant un système de déplacement performant. Il faut maintenant s’attaquer à la logique du programme. On est à 21,73 % d’avoir fini le jeu !
Logique du jeux & IA
La partie la plus complexe. Je remercie mon père qui m’a beaucoup aidé pour cette fonction cœur de mon jeu. Cette fonction a deux modes de fonctionnement, elle peut vérifier si un pion peut être placé ; et placer un pion.
def do_pion(mode,pion_x,pion_y,directions_in,ai_ask=False):
if getCase(g,pion_x,pion_y) != 0:
return False
result = []
if ai_ask:
dist_totale = []
global sc_noir,sc_blanc
pion_adverse = 1 if player_turn == 2 else 2
for d in directions_in:
x,y,dir_x,dir_y = pion_x,pion_y,d[0],d[1]
current = getCase(g, x + dir_x, y + dir_y)
dist_parcourue = 1
while pion_adverse == current:
x,y = x+dir_x,y+dir_y
if mode == 1:
sc_blanc += 1-2*(player_turn!=1)
sc_noir += 1-2*(player_turn!=2)
setCase(g, x, y, player_turn)
current = getCase(g, x + dir_x, y + dir_y)
dist_parcourue += 1
if mode == 0 and dist_parcourue > 1 and current == player_turn:
if not(ai_ask):
result.append(d)
else:
dist_totale.append(dist_parcourue)
result.append(d)
else:
continue
if mode == 1:
if player_turn == 1:
sc_blanc += 1
else:
sc_noir += 1
setCase(g,pion_x,pion_y,player_turn)
else:
if ai_ask:
result.append(sum(dist_totale))
return result
Lorsqu’elle vérifie si un pion peut être placé, elle va vérifier dans les 8 directions si la situation permet un retournement de pions adverses. Si c’est le cas, elle stocke la direction, et continue. À la fin, elle renvoie une liste contenant toutes les directions valides. S’il y a au moins une direction valide, la fonction est réexécutée, mais cette fois-ci, elle changera les valeurs dans la matrice en appliquant les règles de retournement. La fonction qui appelle do_pion() et qui analyse le résultat de la première exécution de celle-ci, c’est la fonction action() :
def action(position):
global player_turn
result = do_pion(0,position[0],position[1],directions)
if result != [] and result != False:
do_pion(1,position[0],position[1],result)
player_turn = 1 if player_turn == 2 else 2
update() # Nous verrons cette fonction un peu plus tard.
else:
deplacement(for_error=True)
La fonction do_pion() s’occupe toute seule d’appliquer quasiment toutes les règles du Reversi, elle s’occupe même de mettre à jour le score. Cependant, il y a une règle dans le Reversi qui stipule qu’un joueur ne peut pas jouer si il n’a aucun « mouvement légal », c’est-à-dire qu’il n’a aucun placement possible sur le plateau (fun fact : ce plateau est appelé othellier) qui respecte les règles du jeu. Si c’est le cas, son tour est passé. C’est le job de la fonction has_legit_hit() :
def has_legal_hit():
legal_hits = []
for y in range(len_y):
for x in range(len_x):
if do_pion(0,x,y,directions):
legal_hits.append([x,y])
return legal_hits
Plus précisément, elle vérifie seulement. C’est la fonction play() qui s’occupe de passer le tour du joueur :
def play():
[...]
if has_legal_hit() == []:
player_turn = 1 if player_turn == 2 else 2
no_round += 1
if no_round == 2:
affichage() # Nous verrons cette fonction un peu plus tard.
break
affichage() # bis
continue
else:
no_round = 0
[...]
Ce petit bout de code s’occupe également d’arrêter la partie, car si les deux joueurs ont dû passer leur tour l’un après l’autre, c’est qu’aucune des deux ne pourra rejouer.
Alors, en effet, si vous êtes au fond de la classe – je ne suis responsable de rien – vous pouvez jouer avec votre voisin, mais si vous êtes devant ? Et bien oui, il faut penser à tout le monde.
Notre IA va utiliser tous les composants décris précédemment. La seule action qu’elle va faire d’elle-même est de choisir une case valide. Dans la version actuelle (1.2.1), l’IA a deux niveaux : un où elle choisit parmi toutes les cases valides une case aléatoirement, et un autre où elle choisit la case qui lui rapporte le plus de point. Si vous avez bien été observateur, certaines fonctions telles que do_pion() avaient une partie qui était réservée pour une utilisation par l’IA. C’est dans do_pion() qu’est calculé quel est la case qui rapportera le plus de point. Voici le code de la fonction ai() :
def ai(lvl):
if lvl == 0:
return choice(has_legal_hit())
elif lvl == 1:
legal_hits = has_legal_hit()
temp = []
plus_grand = [[],0]
for i in range(len(legal_hits)):
legal_hits[i] = [legal_hits[i],do_pion(0,legal_hits[i][0],legal_hits[i][1],directions,True)]
for i in legal_hits:
if i[1][-1] > plus_grand[1]:
plus_grand = [[i[0]],i[1][-1]]
elif i[1][-1] == plus_grand[1]:
plus_grand[0].append(i[0])
return choice(plus_grand[0])
Je suis sûr que vous ne savez pas quelque chose… Mais on est à 73,21 % du jeu là ! On y est presque !
Interface graphique, mise à jour de la grille, Game Over & système de paramètres
On y est presque et pourtant… Ce n’est pas encore fini 😵
C’est bien beau de faire des retournements et tout, mais moi ma grille est toujours vide sur mon écran !
Mettons en place une fonction update() qui va s’occuper de mettre à jour graphiquement la grille.
def update():
global c
for y in range(len_y):
for x in range(len_x):
if getCase(g,x,y) in (1,2):
rec(x_g+1+x*(c+2), y_g+1+y*(c+2), c, c, col[0])
cercle(x_g+8+(c+2)*x, y_g+3+(c+2)*y, getCase(g, x, y, True))
# Ce else pourrait être facultatif et ferait une belle optimisation
# si le code d'autres fonctions était adapté.
# Il est ici depuis très longtemps et s'est fait oublié xD
else:
rec(x_g+1+x*(c+2), y_g+1+y*(c+2), c, c, getCase(g, x, y, True))
deplacement()
Elle n’est pas très compliquée à comprendre. Je parcours toute la grille, je récupère la valeur de chaque case et je mets à jour ce qui doit apparaître (s’il doit y avoir un pion ou pas, et la couleur du pion).
Ensuite, le visuel est un peu pauvre, comme ceci sera bien mieux :
Il y a vraiment des délires sombres avec les couleurs…
Je remercie le professeur qui m’a aiguillé pour la conception de l’interface (après attention, il m’a juste suggéré d’aligner les informations avec des cases de la grille, ça va il a pas tout fait non plus 😂).
On ajoute un système permettant d’utiliser l’interface en début de partie pour paramétrer si on veut jouer en mode multijoueur ou contre IA, et le niveau de difficulté de l’IA :
Et un Game Over :
C’est M. ROBERT qui a joué cette partie, il est trop mauvais (🤪).
EDIT : Normalement je ne modifie pas les articles des élèves, mais je me dois de nier l’affirmation précédente qui porte atteinte à mon honneur. 😉
On assemble les pièces de puzzles correctement, et… Attendez, je crois que ça y est ! On a terminé ! Le jeu est fini à 100,42 % 😁
Le Reversi est un jeu de société pour deux joueurs qui se joue sur un plateau de 64 cases. Les joueurs placent tour à tour des pions noirs ou blancs sur le plateau en capturant les pions adverses et en retournant les pions de l’adversaire pour gagner le contrôle du plateau. Le joueur avec le plus de pions de sa couleur à la fin de la partie est déclaré vainqueur.
A la recherche d’un moyen de gérer tous vos serveurs de jeux, serveurs vocaux, et autres avec une seule et même interface facile à utiliser ? Le logiciel linux Pterodactyl est fait pour vous ! Sous forme d’un site lors de l’utilisation, il va vous permettre de créer et administrer vos serveurs de jeux et vocaux.
Le tutoriel peut paraître long, mais a été simplifié le plus possible. L’installation qui vous est présenté est pensée pour un cadre expérimental et de découverte de l’outil Pterodactyl. Cela veut donc dire que le tutoriel ne vous permettra pas de l’utiliser sur des serveurs de productions.
Teaser vidéo
Dans ce tutoriel, vous apprendrez à :
Installer une machine virtuelle sur son propre ordinateur,
Installer et configurer le système d’exploitation Ubuntu Server,
Installer et configurer Pterodactyl à l’aide d’un script
Utiliser quelques fonctionnalités de Pterodactyl (création de serveurs, création d’utilisateurs et présentations des outils pour gérer un serveur)
Qu’est-ce que Pterodactyl ?
Développé par Dane Everitt (et d’autres contributeurs) et disponible depuis 2015, Pterodactyl est un SaaS (« Software as a Service », « Logiciel en tant que service » en français, c’est-à-dire un logiciel plus généralement installé et utilisé sur un serveur) uniquement disponible sur des systèmes d’exploitation linux. Pour rappel, un serveur n’est rien d’autre qu’un ordinateur sans interface graphique (c’est-à-dire uniquement utilisé en ligne de commande) qui sert de point relais pour la communication entre d’autres machines sur internet et/ou à délivrer des services (Serveur Web, Mail, etc.). C’est pour cette raison que Pterodactyl peut être installé sur un ordinateur linux avec interface graphique. Vous apprendrez à l’installer pour une utilisation sur serveur. Sachez que l’installation sur un système d’exploitation linux avec interface graphique est strictement identique.
Pterodactyl est conçu avec le principal objectif d’assurer une sécurité avancée. Par exemple, chaque serveur qui sera créé sera techniquement séparé des autres, c’est-à-dire que si il y a un problème sur un des serveurs, cela ne se répercutera pas sur les autres serveurs.
Une fois l’importante étape qui est l’installation de Pterodactyl, vous pourrez :
Créer très simplement une multitude de serveurs de jeux, vocaux, de bots, etc.
Administrer les serveurs facilement, avec des fonctionnalités supplémentaires intégrées par défaut :
Création et gestion de backups pour les serveurs
Création de tâches programmables pour s’exécuter automatiquement, répétitivement, etc.
Création et gestion de bases de données.
(fonctionnalité très avancée) Ajouter des plugins payants proposés par Pterodactyl afin d’encore plus simplifier l’administration des serveurs (installation en un clic de plugins/mods pour les serveurs Minecraft par exemple).
Gestion avancée d’utilisateurs : Vous avez un projet de jeu mais votre ami en a aussi un complètement différent du votre ? Pterodactyl a un système de gestion de permissions, de propriété de serveur, etc.
Installation sur une machine virtuelle (VM)
⚠ Pour le reste du tutoriel, si vous avez un problème, des questions, des erreurs, etc. Je suis à votre disposition pour vous aider à nsi.aide.thomas@gmail.com ⚠
Je pense que ni vous ni moi louerait un serveur pour expérimenter Pterodactyl, c’est pour cette raison que vous ferez l’installation sur votre ordinateur personnel. Si vous êtes déjà sur une distribution Linux supportée par Pterodactyl tout va bien, vous pouvez passer cette étape. Les distributions supportées sont :
Système d’exploitation
Version
Supporté ?
Notes
Ubuntu
18.04
✅
20.04
✅
22.04
✅
Le tutoriel est basé sur ce système d’exploitation (conseillé).
CentOS
7
✅
Des dépôts, serveurs contenant des « paquets » (voir la définition d’apt1) supplémentaires sont requis.
8
✅
Des manipulations supplémentaires sont requises pour ce système d’exploitation. Cependant nous ne verrons pas son installation. Ce lien ainsi que la documentation officielle va vous permettre d’installer les prérequis supplémentaires.
Debian
10
✅
11
✅
1 Expliqué plus loin : Ctrl + F est votre ami (La définition d’apt est dans la partie Prérequis)
Sinon il va falloir installer un de ces systèmes d’exploitations dans une VM sur votre ordinateur. Une Virtual Machine est sous la forme d’un logiciel et simule un autre ordinateur sur son propre ordinateur. Cela permet d’avoir par exemple sur un MacBook un ordinateur virtuel Windows. Pour installer un système d’exploitation sur une VM, nous avons besoin d’un fichier de type ISO (généralement appelé image ISO). Avant, les systèmes d’exploitations s’installaient grâce à des DVD (aujourd’hui, c’est moins répandu). Une image ISO est l’équivalent d’un disque (DVD) mais en version numérique. Voilà pourquoi on utilise ce type de fichier. Pour ce tutoriel, nous utiliserons cette image ISO (si le lien a un souci, allez sur ubuntu.com/download/server puis cliquez le bouton vert en plein milieu « Download Ubuntu Server XX.XX.X XXX »). Nous utiliserons ce fichier plus tard dans le tutoriel.
Nous allons utiliser le logiciel VirtualBox proposé par Oracle. Commençons par télécharger le logiciel ici, le « VirtualBox 7.0.4 platform packages » (selon votre système d’exploitation) ainsi que le « VirtualBox 7.0.4 Oracle VM VirtualBox Extension Pack » juste en dessous. Exécutez l’installeur (« VirtualBox-7.0.4-154605-Win », notez que le nom peut différer un peu avec cet exemple). Suivez les instructions, puis démarrez le logiciel. Voici comment il se présente :
Nous allons être efficace : Cliquez sur le bouton encadré en rouge, puis « Extensions ». Cliquez ensuite sur « Install », importez le second fichier que nous avons téléchargé (« Oracle_VM_VirtualBox_Extension_Pack-7.0.4.vbox-extpack » ou nom similaire) et cliquez sur « Installation » (sans oublier de lire, 😉, puis accepter les conditions d’utilisations). L’extension devrait être ajoutée et visible dans la liste.
Pour revenir à l’interface de bienvenue, cliquez sur le bouton encadré en rouge et allez sur « Bienvenue ». Ici nous allons pouvoir créer notre machine virtuel. Pour se faire, vous avez le bouton « Nouvelle », qui vous ouvre cette fenêtre :
Nom : Un nom pour votre VM, cela a peu d’importance, c’est pour vous y retrouver.
Folder : La localisation des fichiers de la VM.
ISO Image : Il faut mettre ici le fichier ISO que nous avons téléchargé au début du tutoriel qui contient le système d’exploitation.
Edition / Type / Version : Ces options se sont normalement grisées après avoir importé l’image ISO. De plus ne cochez pas la petite case Skip Unattended Installation.
Nous devons maintenant choisir le nom d’utilisateur, ainsi qu’un mot de passe. Evitez les noms d’utilisateurs farfelus, choisissez quelque chose de simple, sans espace, ni majuscule. Changez également le mot de passe.
Hostname : Chaque appareil connecté à son réseau local a un « hostname ». C’est son nom sur le réseau. Choisissez-en un qui vous permettra de le reconnaitre. Notez qu’il doit être d’au minimum trois caractères et ne peut être écrit qu’avec des caractères alphanumériques ainsi que des petits tirets et des points (mais ne peut pas se terminer par un point). « ubuntu-vm » est un très bon choix par exemple.
Domain Name : ¯\_(ツ)_/¯ Mais pas important dans notre cas.
Ne cochez pas Install in Background ni Guest Additions.
Passons à l’allocation des ressources à notre VM, c’est-à-dire combien de mémoire vive (mémoire volatile pour stocker les données et programmes en cours d’exécution) et de cœurs du processeur (partie qui exécute des instructions, plus il y a en, plus le processeur peut faire des tâches simultanément). Il faut choisir cela en fonction des ressources de son ordinateur. Dans notre cadre expérimental, nous n’avons pas besoin de beaucoup de ressources. Je vous conseille au strict minimum 2 Go (2048 Mo) ainsi que 2 cœurs ou sinon 4 Go (4096 Mo) de mémoire vive et 4 cœurs.
Nous allons maintenant allouer le stockage. Encore une fois, faites cela en fonction de l’espace que vous avez. 25 Gio fera très bien l’affaire.
Nous avons ensuite un récapitulatif des caractéristiques de la VM. On peut appuyer sur « Finish ».
La VM s’est automatiquement lancée, une nouvelle fenêtre est apparue. Au passage, n’hésitez pas à fermer le menu à droite avec le bouton encadré en rouge.
Avant de se lancer dans la configuration du système d’exploitation, nous allons éteindre la machine en allant dans Machine (encadré en jaune), puis Extinction par ACPI. Attendez que la fenêtre se ferme. Ensuite allez dans le bouton Configuration, Réseau et pour Adapter 1 changer l’option Mode d’accès réseau par « Accès par pont » et pour l’option Name, choisissez le nom de votre carte réseau. Je ne connais pas ma carte réseau… Allez vérifiez que l’image ISO dans Stockage (Menu à gauche) est présente au niveau du « Contrôleur : IDE ». Si ce n’est pas le cas, cliquez sur le bouton DVD avec un + et ajoutez le fichier ISO (comparez les noms pour savoir si c’est le bon).
Maintenant, lancez la machine virtuelle, et vous devriez avoir à nouveau la première page de configuration où l’on sélectionne la langue. C’est parti !
Installation et configuration du système d’exploitation
Sachez que la souris n’existe pas ! Toutes les manipulations se feront aux claviers. Vous bougez avec les touches fléchés et appuyez sur la touche Entrée pour confirmer une action. Sélectionnez votre langue puis confirmez. Dans la page qui suit, vous avez 3 options en bas, prenez celle du haut, donc « Mise à jour vers le nouveau programme d’installation », confirmez puis patientez. Maintenant, vous pouvez choisir la langue de votre clavier :
Si vous allez sur un des menus déroulants (sur « French – French (legacy, alt.) » pour le menu Variant par exemple), vous pourrez appuyer sur la touche Entrée et changer la variante. Choisissez la première, « French » puis confirmez en allant sur « Terminé » (ou « Done »).
La page d’après vous donne la possibilité d’installer une version allégée de Ubuntu Server, mais dans notre cas, nous allons conserver la version classique, donc confirmez juste. La page suivante concerne la connexion avec le reste du réseau local. Vous ne devriez rien avoir à toucher, confirmez juste. On voit différentes informations intéressantes comme l’IP, c’est-à-dire son identifiant pour la reconnaitre par rapport aux autres, qu’aura notre machine et qui nous sera utile pour y accéder (vous pourriez avoir une IP différente de celle-là) ainsi que le nom de la carte réseau que l’on retrouve en gris en dessous.
Il n’y a rien à changer sur la page suivante, ni à la page d’après. Celle d’après est par rapport au stockage, mais tout est présélectionné correctement, donc il suffit de descendre sur le bouton Done et confirmer.
La seconde page est également en rapport avec le stockage. Je vais y aller pas à pas, suivez les étapes selon les images dans leur ordre.
Par défaut.Allez sur l’option où il y a le trait rouge, confirmez (touche Entrée) puis allez sur Unmount et confirmez.Allez sur l’option où il y a le trait rouge, confirmez puis allez sur Edit et confirmez.Ici, il faut changer la valeur de Size. Mettez la valeur entre les parenthèses (ici 22.996G). Attention à mettre un point et pas une virgule ! Il faut également changer la valeur de Mount. Allez dessus, appuyez sur Entrée, puis choisissez le « /« . Puis allez sur Save en bas et confirmez. Et pour finir allez sur Done et confirmez. Une popup apparaitra et vous demandera si vous êtes sûr. C’est le cas, donc confirmez.
Sur la page suivante, vous devez remettre les informations que nous avions mises lors de la configuration de la VM.
Your name : Ce n’est pas important, j’ai personnellement l’habitude de mettre la même chose que Pick a username.
You server’s name : Il faut mettre la même chose que le hostname (information renseignée lors de la création de la VM).
Pick a username : C’est le nom d’utilisateur, il doit être le même que username (information renseignée lors de la création de la VM).
Choose a password / Confirm your password : Mettez le mot de passe que vous aviez mis lors de la création de la VM.
Sur le page suivante, le service vous propose d’installer OpenSSH server. C’est un service qui vous permet d’accéder à distance à la console du serveur. C’est très pratique, on peut entièrement faire les installations avec, mais de la manière dont je me suis (mal) géré, je ne l’utiliserai que beaucoup plus tard dans le tutoriel pour une pauvre petite action. Déplacez vous en face de l’option (sauf si vous y êtes déjà) pour installer OpenSSH (encadré en rouge) et appuyez sur la touche Entrée, puis allez sur Done et confirmez.
La page qui suit vous propose une liste d’autres services à installer. Si nous avons besoin de ces services, nous les installerons en temps voulu. Confirmez en allant sur Done, et puis patientez, toutes les installations et configurations sont en train de se faire. Vous avez une page de log. Cela peut durer un certain temps, pas d’inquiétude. Lorsque l’installation sera achevé, le second bouton encadré en rouge changera, et deviendra Reboot Now. Allez dessus, confirmez puis patientez.
Une fois cela fait, une bonne quantité de texte va défiler, il faut attendre que ça se termine. Il est possible que l’image en dessous ne corresponde pas à ce que vous avez, mais ce n’est pas grave, sauf si il y a du rouge à la dernière ligne de texte… Si au bout d’un moment la ligne rouge reste et que plus rien d’autres se fait, rechercher le problème sur internet ou contactez moi
Sauf pour cette erreur là qui est « normale », il vous suffit simplement d’appuyer sur la touche Entrée.
Lorsque vous pourrez écrire, c’est que vous pourrez vous connectez, comme vous pouvez le voir sur l’image en dessous (ne faites pas ce que j’ai fait).
Ecrivez alors votre nom d’utilisateur puis appuyez sur la touche Entrée.
Une nouvelle ligne apparait, qui vous invite à écrire votre mot de passe, mais lorsque vous le faites… Rien n’apparait 😭. Mais détendez-vous ! C’est normal. Lorsqu’il faut écrire un mot de passe, linux ne fait jamais apparaitre de caractères, même pas des « * », mais il est quand même écrit ! Prenez donc le temps d’écrire le mot de passe, car souvent on l’écrit mal sans s’en rendre compte et du coup il nous le refuse.
😐Enfin ! On a réussi 😁
Installation de Pterodactyl
Bon, c’était déjà bien sportif l’installation de cette VM et de ce linux… Je peux comprendre que certains d’entre vous soyez saturés. Et c’est pour cela que je vous propose une méthode assez simple pour installer Pterodactyl. Nous utiliserons un script. Il faut savoir que c’est un script non officiel, et que Pterodactyl ne le supporte pas. En cas de souci lors de l’installation avec ce script, contactez moi. Voici un lien vers la documentation officielle pour installer manuellement Pterodactyl (vous verrez, vous apprendrez beaucoup de chose).
Prérequis
Voici un récapitulatif de quelques commandes linux qui seront utilisées avec une courte explication :
sudo : Terme à mettre au début d’une commande que l’on souhaite exécuter avec les plus hautes permissions. Il faut faire très attention car une mauvaise manipulation peut endommager définitivement le système d’exploitation, qui nécessiterait une réinstallation. Lorsque vous utiliserez une commande avec ce terme au début, il faudra mettre son mot de passe pour pouvoir l’exécuter.
apt : Simplement : L’App Store version ligne de commande. Plus précisément : Commande qui permet de gérer des paquets. Les paquets sont des archives permettant d’installer des logiciels, stockés dans des dépôts, que l’on peut changer si nécessaire.
apt update : Recherche si il y a des mises à jour disponibles.
apt upgrade : Applique les mises à jour trouvées (se lance donc après la commande apt update).
apt install [nom_paquet] : Installe le paquet donné en paramètre (il faut remplacer [nom_paquet] par le nom d’un paquet, par exemple nano).
reboot : Redémarrage la machine virtuelle.
su : Permet de se connecter en tant qu’un autre utilisateur. Nous utiliserons cette commande pour accéder à l’utilisateur « root ».
Une notion a maîtriser est le terme « root ». Ce terme est le nom de l’utilisateur avec les plus hauts privilèges possibles. Il faut faire très attention car une mauvaise manipulation peut endommager définitivement le système d’exploitation, qui nécessiterait une réinstallation.
Quelques notions à comprendre en rapport avec Pterodactyl :
Le « panel » (le front-end) : La partie graphique de Pterodactyl. Elle envoie des ordres au daemon.
Le « daemon », également appelé « wings » (le back-end) : La partie de Pterodactyl qui crée véritablement les serveurs, qui fait les actions que le panel lui demande.
Le « Node » : C’est la configuration du daemon. C’est avec lui que nous renseignerons diverses informations comme par exemple la quantité de ressources allouées (la mémoire vive, le processeur et l’espace disque).
Le terme « port » : Ce n’est pas réservé à Pterodactyl. Un port est un système permettant aux logiciels (côté client comme côté serveur) de recevoir ou d’émettre des informations. Il y a 65536 possibilités de ports qui sont classifiés dans 3 catégories : les « ports reconnus » qui sont réservés à des processus spécifiques (le port 80 pour héberger un site par exemple), les « ports enregistrés » qui sont assignés par L’IANA et pour finir les ports dynamiques qui sont généralement utilisés pour des communications temporaires. Vous pouvez donc utiliser les ports que vous voulez à l’exception de la plage 0 à 1023 (sauf si le service utilise un de ces ports par défaut, comme un site internet). Notez que beaucoup de logiciels ont un port par défaut, donc privilégiez l’utilisation de celui-ci (pour Minecraft c’est par exemple le port 25565) Une petite liste répertoriant quelques ports…
Nous devons d’abord vérifier si il y a des mises à jour disponibles :
cosmos@ubuntu-vm:~$ sudo apt update
Beaucoup de texte apparait, puis lorsque nous avons à nouveau cosmos@ubuntu-vm:~$ (cosmos le nom d’utilisateur et ubuntu-vm le « hostname ») qui apparait, c’est que la commande a fini de s’exécuter.
119 paquets peuvent être mis à jour. Exécutez « apt list --upgradable » pour les voir.
cosmos@ubuntu-vm:~$
Nous avons donc 119 paquets à mettre à jour. Et bien allons-y :
cosmos@ubuntu-vm:~$ sudo apt upgrade
Du texte défilera, puis vous aurez sûrement 5 dernières lignes similaires à celles-ci (peut-être en anglais) :
Si c’est écrit en français, vous devrez confirmer en appuyant sur le touche O de votre clavier puis appuyer sur la touche Entrée et si c’est en anglais, ce sera pareil sauf qu’il faudra appuyer sur la touche Y de votre clavier. Maintenant patienter, ça peut prendre un moment selon votre connexion internet.
Notez que j’ai eu une « pop-up » avec fond violet qui m’a informé d’une nouvelle version du « kernel » (le « noyau » du système d’exploitation). J’ai juste appuyé sur Entrée.
Ensuite, j’ai eu une autre « pop-up » avec une liste de services que je peux redémarrer. Dans tous les cas, nous redémarrerons le système, donc juste appuyer sur le touche Tab (la touche à gauche de la touche A sur votre clavier) deux fois, afin que Cancel soit avec une surbrillance rouge/orange puis appuyer sur Entrée.
Pour terminer, nous allons redémarrer la machine virtuelle, afin que tout soit correctement appliqué :
cosmos@ubuntu-vm:~$ sudo reboot
Installation rapide (avec script)
⚠ Notez que le script ne fonctionne pas sur CentOS 7 et 8 ! Suivez la documentation officielle pour installer Pterodactyl sur ces deux systèmes d’exploitations. ⚠ Les autres systèmes d’exploitation du tableau au début de l’article sont eux compatibles.
Avant toute chose, nous aurons besoin de savoir l’adresse IP de la machine virtuelle pour plus tard. Pour se faire, utilisez la commande ip addr:
cosmos@ubuntu-vm:~$ ip addr
Dans tout ce blabla bizarre, ce que nous cherchons est encadré en rouge ! Sachez que l’IP commencera toujours par 192.168 (si vous avez bien suivi le tutoriel) puis les deux autres nombres pourront varier. Notez l’adresse IP pour plus tard !
Nous allons utiliser le script d’installation écrit par vilhelmprytz. La page github du script est consultable ici. Pour s’assurer du bon fonctionnement du script, nous allons « passer en root », c’est-à-dire se connecter avec l’utilisateur root, pour se faire :
cosmos@ubuntu-vm:~$ sudo su
Mettez votre mot de passe, et vous remarquerez qu’à la place de votre nom d’utilisateur il y a le terme « root ». Exécutez maintenant cette ligne de commande :
L’installeur se lance et vous propose différentes options.
Ici, vous dites ce que vous souhaitez installer. Nous voulons le panel et le daemon, donc l’option 2.Vous configurez le nom de la base de donnée où seront stockés les informations, le compte utilisateur pour accéder à cette base de donnée et son mot de passe. Pour database name et database username, laissez par défaut, c’est-à-dire appuyez simplement sur Entrée. Concernant le mot de passe, c’est vous qui décidez. Personnellement j’ai juste appuyé sur Entrée pour que ça génère un mot de passe automatiquement. Vous devez maintenant choisir une Timezone, mettez ce que j’ai mis, c’est-à-dire Europe/Paris.Mettez une email valide, mais dans un cadre d’expérimentation, vous pouvez prendre une « email poubelle ».Créez le premier compte utilisateur admin pour le panel. L’email ne doit pas obligatoirement être valide. Vous devrez vous rappelez de username et du mot de passe pour vous connecter.Fully Qualified Domain Name. C’est l’IP (ou le nom de domaine) où est hébergé le panel. Notez que le panel et le daemon peuvent être installés sur deux serveurs séparés et cette option prend son sens dans ce cas là. Ici, ce sera sur la même machine, donc au final, ça revient au même. Donc, mettez l’IP de la machine virtuel que vous avez noté après avoir fait ip addr. Notez que le warning vous prévient que vous ne pourrez pas générer un certificat ssl pour sécuriser votre site (qu’il soit en https), ce qui n’est pas grave puisque nous faisons tout en local. « Let’s Encrypt » est un service gratuit permettant de générer des certificat ssl.Un pare-feu est très important, mais dans un cadre expérimental, ce n’est pas nécessaire. Mettez N puis confirmez.
On a ensuite un récapitulatif de la configuration puis nous devons confirmer que nous voulons cette configuration. Mettez Y et l’installation va débuter. Ensuite nous avons ceci :
Mettez no puis patientez. Et…Le script a fini d’installer le panel (le front-end), passons au daemon (le back-end). Mettez Y.Dans notre cas, le firewall est inutile. Mettez N.Ceci est pour permettre la création de bases de données pour les serveurs de jeux, vocaux, etc. C’est une fonctionnalité un peu avancé, donc pour ce tutoriel, sauf si vous vous y connaissez, mettez N.Puisque tout à l’heure, nous n’avions pas mis en place de certificat, il faut mettre N. Après avoir fait ça, mettez Y pour lancer l’installation.
L’installation est terminé, mais ce n’est pas encore fini…
Configuration du Node
Nous allons à présent configurer le Node. Il va falloir aller sur le panel. Mettez l’adresse IP de la machine virtuelle dans une page de votre navigateur.
Génial ! Connectez vous avec l’utilisateur créé plus tôt.Il y aura ici la liste des serveurs créés grâce au panel. Cliquez sur le bouton avec des engrenages. Il vous renvoie vers l’interface administrateur du panel. Ici c’est la partie pour administrer Pterodactyl, donc le panel, le daemon, etc. (pas les serveurs). Allez dans « Locations », puis « Create New ».Il faut savoir que l’on peut associer différents daemon à un même panel et la « location » permet de les catégoriser. Ce n’est pas vraiment important, mais mettez quelque chose qui a du sens pour vous y retrouver. Par exemple fr.home car le daemon est en France et hébergé chez vous. La description est facultative.Passons à la création d’un Node. Allez dans « Nodes », puis « Create New ».Cela peut faire peur, mais ne vous inquiétez pas, c’est simple :
– Name : Le petit nom que vous allez lui donner, pour vous y retrouver.
– Description : Une description (facultative).
– Location : Ce que nous avons créé il n’y a pas plus tard que 2 minutes.
– Node Visibility : Permet de le masquer dans la liste des Nodes disponibles lors de la création de serveurs. Dans notre cas, inutile, donc laissez Public.
– FQDN : Cela vous dit quelque chose non ? Mettez donc l’ip de votre machine.
– Communicate Over SSL : Il faut cocher la première option si le panel est sécurisé avec un certificat, ce n’est pas le cas, donc mettez la seconde option (c’est normal que la case soit rouge une fois cliquée).
– Behind Proxy : Si le daemon est derrière un proxy qui s’occupe de la connexion ssl, il faut mettre la seconde option. Je cite de la documentation : Si cette phrase n’a aucun sens pour vous, c’est que cela ne vous affecte pas. Alors, vous avez compris, laissez la première option 😉
– Daemon Server File Directory : Où sera sauvegardé le contenu des serveurs du node. Laissez par défaut.
– Total Memory : La quantité de mémoire vive que nous voulons allouer au Node. Il ne faut pas mettre la capacité maximale de la machine, car le système d’exploitation en nécessite un peu. Ici, je vais mettre 3Go. Notez que 1Go = 1024 MiB donc à multiplier par le nombre de Go que vous voulez.
– Memory Over-Allocation : La quantité en pourcentage de mémoire vive qui sera utilisé en plus si les 3Go sont complètement utilisés. En mettant -1, il n’y aura pas de limite et en mettant 0, cela empêchera de dépasser la quantité de mémoire allouée. Cela va dépendre de vos utilisations, mais puisque que nous expérimentons et que nous sommes limités à 4Go, je vais mettre 0.
– Total Disk Space : La quantité d’espace disque que nous voulons allouer au Node Il ne faut pas mettre la capacité maximale de la machine, car le système d’exploitation en nécessite un peu. Ici, je vais mettre 15Go. Notez que 1Go = 1024 MiB donc à multiplier par le nombre de Go que vous voulez.
– Disk Over-Allocation : Même chose que pour Memory Over-Allocation mais pour l’espace disque.
– Daemon Port / Daemon SFTP Port : Les ports utilisés par le daemon pour communiquer, écouter, etc. Pas touche !
Cliquez enfin sur « Create Node ».Il faut maintenant ajouter des allocations, c’est-à-dire des ports pour les serveurs qui seront créés. Pour IP Adress, mettre 0.0.0.0 signifie « toutes les adresses IPv4 de la machine locale », car une machine peut avoir plusieurs IPs. C’est ce que nous mettrons. IP alias est facultatif, ne nous en préoccupons pas. Et nous allons donc ajouter des Ports. On peut les ajouter un par un, avec un espace entre chaque port ou alors les écrire d’une certaine façon, par exemple utiliser un tiret : 25565-25575Et Bam ! Tout ça en moins de 10 secondes 😁 Mais ce n’est pas encore terminé !
Mais avant de continuer, nous allons avoir besoin d’utiliser OpenSSH (mentionné plus haut dans l’article). Pour vérifier que le service est installé et fonctionnel, chercher dans la barre de recherche de votre ordinateur « invite de commande » puis lancer l’application, puis taper la commande ssh.
Si vous avez une réponse, alors c’est bon ! OpenSSH est installé par défaut sur Linux et MacOS, pour Windows je ne suis pas sûr, donc voici un lien pour l’installer sur Windows.
Comment ça fonctionne ? Et bien c’est plutôt simple :
ssh username@ip
Vous avez donc compris, vous devez mettre votre d’utilisateur, puis « @ » suivi de l’IP de votre machine virtuelle par exemple : ssh cosmos@192.168.1.31 Il vous demande en suite le mot de passe du compte, et ici aussi, aucun caractère n’apparait mais c’est normal.
Et voilà, vous êtes connectés à distance (je vous rappelle qu’une machine virtuelle est un ordinateur séparé de celui sur lequel il est hébergé). L’avantage d’avoir fait tout ça, c’est que nous allons pouvoir faire des copier/coller… Et croyez moi, ce que vous allez copier/coller juste après, vous n’auriez pas aimé devoir le recopier… Cela à d’autres avantages comme avoir plusieurs consoles en simultané (rien n’empêche d’ouvrir 42 invites de commandes et se connecter avec), le défilement avec le souris, des consoles redimensionnables, etc.
Repassons sur Pterodactyl maintenant que vous vous êtes connectés en parallèle avec OpenSSH. Vous êtes donc dans l’onglet Configuration.
Cliquez sur Generate TokenCopiez la ligne (vérifiez que vous avez bien tout sélectionné) puis collez là (en faisant un clique droit pas un Ctrl+V) dans l’invite de commande où vous vous êtes connectés en ssh.Même si la mise en forme s’est fait la malle, y a bien écrit à la fin « Successfully configured wings ».
La configuration a bien été appliqué, mais le daemon (wings) n’est toujours pas lancé. On peut le lancer avec sudo wings
Pas de rouge… c’est rassurant ! Et si vous retournez sur le panel puis allez dans la catégorie « Node » vous verrez qu’un cœur vert apparait à côté du nom du Node.
En appuyant simultanément sur les touches Ctrl et C vous arrêterait le programme. tapez ensuite ces commandes :
cosmos@ubuntu-vm:/etc/pterodactyl$ cd /etc/systemd/system
Alors… Vous n’allez pas me croire… mais l’installation et la configuration de Pterodactyl est ENFIN terminée !
Installation d’un serveur Minecraft avec Pterodactyl
Pour installer un serveur Minecraft avec Pterodactyl, rendez-vous dans l’interface administrateur du panel puis dans la catégorie « Servers ». Puis cliquez sur « Create New ».
Dans « Core Details » :
– Server Name : Le nom du serveur qui apparaitra sur le panel. – Server Owner : Le propriétaire du serveur. Ecrivez les premiers caractères de l’adresse email du compte à qui appartiendra le serveur et celui-ci sera affiché dans la liste. – Server Description : Une description, c’est facultatif. – Start Server when Installed : Si coché, une fois l’installation terminé, le serveur se lancera automatiquement.
On passe à « Allocation Management » :
– Node : Le Node sera installé le serveur – Default Allocation : Le port par défaut associé au serveur. Ce sont les ports que nous avions ajouté dans la configuration du Node. Et dans ces ports, il y a le port 25565, le port par défaut de Minecraft. Mettons le. – Additional Allocation(s) : Si vous voulez allouer d’autres ports à ce serveur. Cela peut être utile si vous utilisez des plugins/mods en nécessitant (mais n’en ajoutez pas pour rien).
Puis à « Application Feature Limits » :
– Database/Backup/Allocation Limit : La limite du nombre de bases de données/backups/ports que le serveur aura. C’est-à-dire que les utilisateurs qui pourront créer ces éléments seront limités à ce nombre.Pour « Resource Management » :
– CPU Limit : Le pourcentage de ressource processeur que vous allouez au serveur. Un cœur correspond à 100 %. Cela veut dire que vous pouvez allouer un cœur et demi par exemple. Pour 2 cœurs -> 2 * 100 = 200 – CPU Pinning : Fonctionnalité avancé, cela permet de préciser si vous voulez que le serveur utilise un ou plusieurs cœurs en particulier. Ne vous embêtez pas avec cela, sauf si cela vous est vraiment nécessaire (laissez vide si vous ne voulez pas l’utiliser). – Memory : La quantité de mémoire vive que vous allez au serveur (en MiB). 1 Go = 1024 MiB – Swap : Utilise de l’espace de stockage comme mémoire vive. C’est-à-dire que si la quantité de mémoire vive est saturée, cela utilisera une quantité d’espace de stockage. C’est cependant à éviter car cela chute les performances. -1 ne met pas de limite et 0 n’en alloue pas. – Disk Space : La quantité d’espace de stockage que vous allouez au serveur (en MiB). – Block IO Weight : Fonctionnalité avancé que je n’ai jamais utilisé. – Enable OOM Killer : Permet de forcer l’extinction du serveur si il dépasse la limite de mémoire.
Passons à « Nest Configuration » :
– Nest : Catégorie classant différent fichier de configuration pour créer un serveur. – Egg : Le fichier de configuration pour créer un serveur. Vanilla Minecraft est le nom de la version classique de Minecraft. Forge Minecraft est utilisé pour les mods. Sponge n’est plus utilisé. Ignorez Bungeecord, c’est pour reliez différents serveurs Minecraft entre eux, c’est une notion avancé pour les administrateurs d’un serveur Minecraft. Pour finir, Paper est pour les plugins. – Skip Egg Install Script : Si vous cochez cette case, le script d’installation ne s’exécutera pas, dans notre cas, cela ne sert à rien. Continuons avec « Docker Configuration » : – Docker Image : Dans notre cas, c’est la version de java qui sera utilisé pour faire tourner le serveur Minecraft. Selon la version que vous voulez installer sur votre serveur Minecraft, il faut une version Java plus ou moins récentes. L’option « Or enter a custom image » est une fonctionnalité avancé, n’y prêtez pas attention.Et pour finir, « Startup Configuration » et « Service Variables » (ces options peuvent varier selon l’Egg que vous mettez) :
– Start Command : La commande qui lancera le serveur à chaque fois que vous appuierez sur le bouton « start ». N’y touchez pas, à moins de savoir ce que vous faites. – Server Jar File : Le nom du fichier Jar. Si vous ne savez pas ce qu’est un fichier Jar, n’y touchez pas. – Server Version : La version de votre serveur Minecraft. « latest » installe donc la dernière version stable et « snapshot » la dernière version snapshot. Vous pouvez aussi mettre le numéro d’une version.
Vous pouvez maintenant cliquer sur « Create Server ». Cliquez sur le bouton encadré en rouge pour quitter l’interface administrateur du panel.
Vous voyez à présent votre serveur dans la liste des serveurs disponibles.
J’en ai créé un autre pour vous montrer deux cas : On a le premier qui a fini de s’installer, on va pouvoir le démarrer alors que le second en train de s’installer, il faut donc patienter un peu.
Cliquez sur le serveur listé que vous venez de créer et une nouvelle interface apparait.
Vous avez au milieu une console, à droite 3 boutons pour respectivement démarrer, redémarrer et arrêter le serveur. Toujours à droite, vous avez l’adresse avec le port, le statut du serveur, l’utilisation de différentes ressources puis des graphiques en bas.
Concernant la partie tout en haut : – Console : Le menu sur lequel vous êtes – Files : Une gestionnaire de fichier pour le serveur – Databases : Pour gérer les bases de données liés au serveur (ne peut être utilisé avec notre installation) – Schedules : La fonctionnalité des tâches automatisés. Nous ne l’aborderons pas dans ce tutoriel. – Users : Pour ajouter des utilisateurs. Vous pourrez personnaliser leur accès (toutes les permissions sont commentés). Un peu plus bas vous sera expliqué comment créer un nouvelle utilisateur. – Backups : Pour créer des backups. – Network : Pour ajouter des ports. Ne fonctionnera pas sans ajouter un quota d’allocation lors de la création du serveur ni sans activer une option sur le panel administrateur se trouvant dans Settings > Advanced > Automatic Allocation Creation. – Startup : Permet de modifier le « docker image » et la valeur de certaines variables renseignées lors de la création du serveur. – Settings : Vous permet de changer certains éléments, et vous permet également de réinstaller le serveur. – Activity : une page de log des actions faites sur le panel en rapport avec le serveur. – La petit flèche permet d’accéder à la page en rapport avec le serveur sur le panel administrateur du panel, page qui permet de modifier à nouveau les informations précisées lors de la création du serveur.
Créer de nouveaux utilisateurs
Dans le panel administrateur (le bouton engrenage) vous avez une catégorie « Users ». Le compte que vous avez créé lors de l’installation de Pterodactyl est listé. Vous pouvez créer un nouvel utilisateur en cliquant sur Create New. Vous pourrez donc y ajouter une adresse email (mettez-en une, même invalide, cela vous permettra de l’identifier lorsque vous voudrez ajouter des accès à certains serveurs à cet utilisateur), un nom d’utilisateur, un prénom puis un nom. Default Langage est la langue par défaut que l’utilisateur aura pour le panel. C’est-à-dire qu’en remettant la main dans le cambouis, on peut ajouter des fichiers de langues, ce qui permet de changer la langue du panel. Mettre Administrator sur yes lui permettra d’accéder à l’interface administrateur du panel, ce que je vous déconseille. Pour le mot de passe, même si il est précisé que vous n’êtes pas obligé de mettre un mot de passe, dans notre cas si ! Le panel n’arrivera pas à envoyer un mail à l’adresse indiquée, qu’elle soit valide ou non.
Conclusion
Sachez qu’après avoir suivi ce tutoriel, les serveurs que vous créerez ne seront qu’accessible dans votre réseau local. Votre ami qui habite la ville d’à côté ne pourra pas y accéder, il y a d’autres manipulations à mettre en place.
Cet outil est très puissant mais requiert une certaine maitrise des systèmes linux. J’ai voulu rédiger ce tutoriel afin que vous découvriez Pterodactyl sans que vous soyez submerger par tout un paquet d’installations, de configurations, de problèmes, etc. ce qui implique de ne pas vous montrer toutes les fonctionnalités proposés par ce logiciel.
Si l’outil vous intéresse, et que vous voulez en apprendre plus ou l’utiliser sur un serveur de production, je peux déjà commencer par vous conseiller de recommencer l’installation mais en la faisant manuellement cette fois-ci, donc en suivant la documentation officielle. Sachez que le projet est très communautaire, en cas du moindre problème, vous pouvez rejoindre leur discord et poser des questions (en anglais).
Merci d’avoir lu cet article ! La principale source de cet article est le site officiel de Pterodactyl (et mes connaissances 😉).
« L’amas de trous noirs » est le nom de notre premier projet développé en Python. Notre petit programme vous permettra de profiter d’un magnifique ciel étoilé… avec quelques trous noirs. En effet, ce programme générera pour vous une image d’une beauté à couper le souffle… Enfin, nous espérons !
Introduction
Représenter ce que l’être humain ne peut atteindre est une de ses passions favorites. En effet, nous allons vous présenter notre projet Python se prénommant : L’amas de trous noirs. Effrayant, n’est-ce pas ?
Le résultat final doit ressembler à ceci, mais vous le savez déjà :
Cette image est un des multiples résultats de notre code. Nous y reviendrons plus tard.
Comme vous pouvez le voir, il y a plusieurs éléments sur cette image : des trous noirs, des étoiles et un arrière-plan noir. Commençons par le programme pour former les trous noirs.
Nous avons utilisé le module turtle de python qui permet de dessiner à l’écran. Dans la suite de l’article, nous ferons référence à ce module sous le nom de tortue.
Les trous noirs
Un code non optimisé
C’est la première chose sur laquelle nous avons travaillé. Afin que vous conserviez votre santé mental, nous éviterons de vous montrer le code qui forme cette image :
Si vous le voulez vraiment :
def cercle(rayon, x, y, r=1, v=1, b=1):
pensize(5)
while rayon > 372 and r < 254:
penup()
goto(x, y - rayon)
pendown()
pencolor((r, v, b))
circle(rayon)
rayon -= 1
r += 2
while rayon > 244 and v < 254:
penup()
goto(x, y - rayon)
pendown()
pencolor((r, v, b))
circle(rayon)
rayon -= 1
v += 2
while rayon > 119 and b < 254:
penup()
goto(x, y - rayon)
pendown()
pencolor((r, v, b))
circle(rayon)
rayon -= 1
b += 2
while r > 0 and v > 0 and b > 0:
penup()
goto(x, y - rayon)
pendown()
pensize(2)
pencolor((r, v, b))
circle(rayon)
rayon -= 1
b -= 16
v -= 16
r -= 16
cercle(500, 0, 0)
Ce code ne nous satisfait pas car il a des limites d’utilisations. C’est-à-dire qu’il est impossible de dessiner des trous noirs de n’importe quel diamètre. On a par exemple ce cas où l’on choisi un rayon de 200 :
Ici, on constate que le dégradé n’est pas celui que l’on souhaitait.
Maintenant que nous avons vu un code qui vous pique encore les yeux, nous allons voir une autre version plus optimisée et bien plus flexible.
Un code optimisé
Avant toute chose, nous définissons au début de notre programme colormode(255) qui nous permettra de définir des couleurs au format (r, v, b), r pour rouge, v pour vert et b pour bleu qui sont les trois teintes permettant de composer n’importe quelle couleur.
Ensuite nous définissons notre fonction trou_noir(). Nous allons la découper en plusieurs portions de code afin de vous l’expliquer étape par étape.
Première étape : Initialisation de diverses informations.
colormode(255)
def trou_noir(x, y, rayon):
color(0, 0, 0)
pensize(5)
r = -1
v = -1
b = -1
penup()
goto(x, y - rayon - 20)
pendown()
Notre fonction aura donc besoin de trois paramètres : les coordonnées x, y et le rayon du trou noir. Nous indiquons que la couleur du stylo sera noir, en raison de l’arrière-plan qui sera noir également. Nous définissons le taille du trait à 5, qui est la valeur la plus basse tout en évitant des artéfacts au niveau du trou noir comme ceci :
Nous définissons trois variables qui, comme leur nom l’indique, seront les variables liées au changement de couleur. Nous les définissons avec une valeur négative, ce qui peut paraitre assez étrange, car les valeurs minimales pour le vert, le rouge ou le bleu sont zéro. Nous vous expliquerons un peu plus bas pourquoi nous faisons cela.
La fonction penup() permet de lever le stylo, la fonction goto() permet de le déplacer. On met en paramètre les coordonnées x et y en paramètre. La fonction pendown() permet elle, de poser le stylo.
Concernant la fonction goto(), elle prend en paramètre les coordonnées x et y, qui représente un point de l’écran. La tortue n’est capable de dessiner un cercle qu’à partir de sa base (le point le plus bas du cercle). Nous souhaitons utiliser le centre géométrique d’un cercle pour les positionner. Nous allons donc décaler la coordonnée y de la distance du rayon. Cela donne le code suivant : goto(x, y - rayon).
Nous avons choisi d’ajouter autour de notre trou noir une zone noire (🥴) de 20 pixels d’épaisseur (cette valeur a été défini de façon arbitraire). Nous verrons par la suite que c’est un choix esthétique, cette marge de 20 pixels se retrouve donc dans l’appel de notre fonction goto() : goto(x, y - rayon - 20).
Ce disque a été rajouté afin d’améliorer la fonction trou_noir() : en effet, après une première version de la fonction trou noir, nous avons amélioré le rendu graphique en dessinant un premier disque noir sur lequel la tortue dessine le trou noir. Cela apporte deux avantages : produire la marge de 20 pixels autour du trou noir (cf. paragraphe au-dessus) et remplir le centre de notre trou noir. Voyez ainsi :
Ce disque noir sert de transition entre le ciel étoilé (que vous verrons par la suite) et le trou noir en lui-même.
Troisième étape : Construction du trou noir (dégradé du rouge vers le blanc).
while r < 255:
penup()
goto(x, y - rayon)
pendown()
r += 2
pencolor((r, 0, 0))
circle(rayon)
rayon -= rayon/500
while v < 255:
penup()
goto(x, y - rayon)
pendown()
v += 2
pencolor((r, v, 0))
circle(rayon)
rayon -= rayon/500
while b < 255:
penup()
goto(x, y - rayon)
pendown()
b += 2
pencolor((r, v, b))
circle(rayon)
rayon -= rayon/500
Ces trois boucles while sont très similaires. Elles servent à créer le dégradé du trou noir. Ce que notre tortue fait est de dessiner un cercle d’une couleur différente à chaque fois que la boucle se répète. La boucle se termine une fois que la variable de la couleur donnée dans la condition while a atteint son maximum, soit 255. A chaque itération, nous incrémentons de 2 chaque variable de couleur, et nous le faisons juste avant de dessiner le cercle. Avec cette incrémentation, il nous faudrait 128 itérations pour arriver à la valeur 256. Cette valeur est donc supérieur à 255, nous avons donc décidé d’initialiser chaque variable de couleur à -1 afin de compenser ce problème. Pour la variable r par exemple, la première fois que notre boucle va s’exécuter, elle va s’incrémenter de 2 ( -1 + 2 = 1 ), puis choisir une couleur pour le stylo : pencolor((r, 0, 0)), puis dessiner le cercle. Lors de la dernière itération, r est égal à 253, donc 253 + 2 = 255, la tortue dessine le cercle avec cette valeur. Nous sortirons ensuite de la boucle. Nous faisons ceci trois fois, pour les trois variables de couleurs.
La dernière ligne qui est intéressant dans l’extrait cité est rayon -= rayon/500. La valeur 500 provient de notre « programme d’essai », celui non optimisé. Nous avons utilisé la proportionnalité par rapport à la valeur trouvée grâce à ce programme. Dans le programme d’essai nous retirions 1 au rayon pour un cercle de rayon 500. Ici nous retirons au rayon le résultat de la division du rayon par 500. Par exemple si le rayon est 500, on retirera 1 ( 500/500 = 1 ), si c’est 250, on retirera 0,5 ( 250/500 = 0,5 ), si c’est 750, on retirera 1,5 ( 750/500 = 1,5 ), etc. Ceci permet de faire des trous noir de la taille que l’on souhaite.
Quatrième étape : Dégradé du blanc au noir de finition.
while r > 0 and v > 0 and b > 0:
penup()
goto(x, y - rayon)
pendown()
pencolor((r, v, b))
circle(rayon)
rayon -= rayon / 500
b -= 16
v -= 16
r -= 16
pencolor("black")
circle(rayon)
Cette dernière boucle permet de faire un fort dégradé entre le blanc et le centre du trou noir, qui est… noir (🥴). Elle n’a rien de particulier par rapport à ce que l’on a expliqué plus haut mis à part la valeur forte de la réduction des valeurs des trois variables de couleurs (que l’on décrémente de 16) et également la condition de la boucle qui permet qu’elle s’arrête une fois que la couleur atteinte est le noir.
Les deux dernières lignes de l’extrait permettent d’éviter ce problème :
Les étoiles
Voici le code pour former les étoiles :
def etoile(x, y, branche, longueur, couleur=(255, 230, 0)):
penup()
goto(x, y)
degre_angle = 180 - (360 / (branche * 2))
begin_fill()
fillcolor(couleur)
for i in range(branche):
forward(longueur)
right(degre_angle)
end_fill()
Cette fonction prend en paramètre les coordonnées x et y, le nombre de branche, la longueur des branches (plus précisément la longueur des segments constituants l’étoile) et la couleur (avec une couleur par défaut qui est un jaune-orange qui tend plus vers le jaune). On lève notre stylo, on se positionne de manière à ce que le centre de l’étoile soit les coordonnées données en paramètres et… pas de pendown() ? En effet celui-ci serait inutile pour former les étoiles car nous n’allons pas utiliser la fonction de tracé de ligne mais plutôt utiliser la fonction de remplissage (de couleur) de zone. Ensuite, nous créons une variable degre_angle. Elle est très importante car elle va définir le degré des angles qui vont permettre de former l’étoile. Regardez :
L’étoile est formée de la même manière que l’extrait cité sauf qu’ici le stylo est baissé, et il n’y a pas de couleur de remplissage. Les angles qui nous intéressent sont au bout des branches. Ce sont ces angles là que le programme contrôle.
En A), notre tortue avance. Elle est à une position avec un angle de 0°. L’angle en rouge est égal à 180°. C’est notre 180 dans la formule ! Et (360 / (branche * 2)) est en fait l’angle intérieur. Pour savoir la valeur de cet angle, il faut imaginer l’étoile dans un cercle. La somme de tous les sommets de cette étoile dans un cercle donne 360°. Et dans une étoile, le nombre de sommet est le double du nombre de branche.
Ensuite en B), elle s’oriente de l’angle calculé dans la variable degre_angle. Le trait violet est un trait de construction rajouté pour que le schéma soit plus clair.
Pour finir en C), la tortue avance. Ici on voit bien l’angle formé qui est celui de degre_angle. Le trait en cyan est un trait de construction.
D’une pierre deux coup, nous vous avons expliqué aussi la boucle qui est dans le programme. Avec begin_fill(), fillcolor(couleur) et end_fill() nous remplissons l’intérieur de l’étoile avec la couleur donnée en paramètre.
Le ciel (arrière-plan et étoiles)
Cool ! On a bien avancé. On continue avec cette fois-ci un bon bgcolor("black") qui est censé faire un arrière-plan en noir et… non. Cela ne va pas marcher, nous verrons cela un peu après, une fois le code entièrement présenté. Nous allons donc concevoir une fonction arriere_plan() :
def arriere_plan():
# .ps est incapable de récupérer le bgcolor() selon internet...
# Et des barres blanches apparaissent avec ce code dans le .png généré...
penup()
goto(-640, 0)
pendown()
pensize(900)
forward(1280)
Il y a un bavard dans le code de ce que je vois, mais il a raison (ou on a tous les deux torts, c’est une éventualité). Encore une fois, nous verrons ceci un peu plus tard. Cette fonction est très simple, elle conçoit simplement un fond noir, en se positionnant le plus à gauche du canvas (zone de dessin) et en traçant un trait de la taille du canvas (le résultat doit avoir une taille de 1280 pixels de largeur par 720 pixels d’hauteur) avec une taille exagérée.
def ciel():
arriere_plan()
liste_couleur = [(255, 255, 255), (255, 230, 0)]
for i in range(0, randint(71, 121)):
liste_couleur[1] = (255, randint(150, 230), 0)
etoile(randint(-630, 630), randint(-350, 350), 5, 10, choice(liste_couleur))
for i in range(0, randint(16, 31)):
liste_couleur[1] = (255, randint(150, 230), 0)
etoile(randint(-620, 620), randint(-340, 340), 7, 20, choice(liste_couleur))
for i in range(0, randint(6, 16)):
liste_couleur[1] = (255, randint(150, 230), 0)
etoile(randint(-600, 600), randint(-320, 320), 15, 40, choice(liste_couleur))
Occupons nous maintenant de la fonction ciel(). Nous appelons la fonction arriere_plan() puis le programme dessine des étoiles dans notre ciel. Nous utilisons simplement trois boucles qui dessineront 3 types d’étoiles différentes. En effet grâce aux paramètres branche et longueur, nous pouvons former des étoiles complètements différentes, diversifiant le ciel. Concernant le paramètre couleur, nous avons une liste qui varie à chaque fois que la boucle se répète. C’est plus précisément le second élément qui varie. On modifie aléatoirement la quantité de vert dans la couleur, rendant l’étoile plus ou moins orangée. La fonction choice() permet de choisir un élément aléatoirement parmi une liste donnée en paramètre. Pour finir sur ce bout de code, chaque boucle for génère un nombre aléatoire d’étoile entre deux valeurs précisées (ex : randint(71, 121)). On remarque que les étoiles plus petites apparaissent plus nombreuses.
Voici un rendu de la fonction ciel() :
Taille du canvas : 2560×1080 pixelsTaille du canvas : 1280×720 pixels
On remarque bien le fond généré « artificiellement » dans la première image, et comment cela rend dans les bonnes dimensions.
Assemblement et problèmes
Bien, nous avons notre ciel, plus qu’à rajouter les trous noirs. Nous en ferons trois :
On appelle donc notre fonction ciel() et nous générons trois trous noirs. Ils sont positionnés aléatoirement mais pour éviter une fusion de trou noir, nous les plaçons dans des coordonnées où ils ne peuvent s’entrechoquer. On remarque qu’il y a un décalage de 150 entre chaque plage de coordonnées x. C’est dû à la taille qui est aléatoire entre 50 et 150.
Une fusion de trou noir… C’est très beau, vous ne trouvez pas ?
Nous vous avions proposé un résultat en début d’article, en voici un autre :
Le rendu turtle Le fichier PNG généré
On remarque diverse imperfection. La plupart provienne d’un problème lors de la conception du fichier postscript et nous n’avons pas trouver comment régler ce problème.
Un autre problème que nous avons eu a été celui-là :
Effectivement, je n’ai plus l’impression de voir un ciel…
Bon, qu’est-ce qui s’est passé ? Lorsque nous avons généré cette image, nous utilisions la fonction bgcolor("black") pour générer le fond noir sauf qu’après une petite recherche sur internet, le fichier postscript ne prend pas en compte cette action, il ne la « voit » pas. Nous avons donc dû changer par la fonction arriere_plan() que nous vous avons expliqué plus haut.
Conclusion
Vous pouvez former des images complètement différentes de ce que l’on a généré simplement en modifiant quelques valeurs ou en rajoutant une boucle for… Vous pouvez faire un ciel beaucoup plus dense, des dégradés plus profonds, des trous noirs plus grands, etc. Nous avons voulu faire un programme qui puisse avec très peu de modification générer des résultats d’une grande diversité.
Télécharger le .py
Si l’envie vous prend de rendre ces personnalisations plus simples d’accès et/ou optimiser notre code, voici un petit cadeau :