Étiquette : Jeux

Projets

Un reversi sur ta NumWorks !

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 ! »

Une base graphique

Il faut commencer quelque part ! Tout d’abord, on récupère un exemple proposé par notre professeur. Moins on en fait, mieux on se porte. 😎

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.

[...]
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.

def play():
  [...]
  while 1:
    key_pressed = None
    pos_old = list(pos)
    [...]
    key_pressed = wait()
    if key_pressed == 1:
      pos[1] = (pos[1]-1) % 8
    elif key_pressed == 2:
      pos[1] = (pos[1]+1) % 8
    elif key_pressed == 3:
      pos[0] = (pos[0]+1) % 8
    elif key_pressed == 0:
      pos[0] = (pos[0]-1) % 8
    elif key_pressed in (4,52):
      action(pos) # Simplifié
    if key_pressed != None:
      deplacement(pos_old)
  [...]

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 % 😁

La page de présentation du jeu + Téléchargement

Remerciements

  • Mon père pour ses conseils et aide pour le développement du jeu,
  • M. ROBERT pour ses ressources,
  • ChatGPT pour certaines parties de l’article (en fait, juste le paragraphe de présentation de l’article).
Projets

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

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

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

Un projet sous couche graphique

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

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

Les bibliothèques Python

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

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

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

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

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

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

La logique du jeu

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

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

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

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

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

L’initialisation des plateaux

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

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

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

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

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

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

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

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

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

nb_piques = [11]

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

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

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

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

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

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

La programmation du gameplay

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

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

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

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

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

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

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

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

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

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

vies = [3]

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

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

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

cases_retournees = []
cases_marquees = []

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

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

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

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

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

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

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

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

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

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

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

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

Ce curseur est formé de quatre rectangles fins.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

La mise en place du menu principal

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

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

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

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

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

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

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

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

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

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

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

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

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

Mot de fin

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

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

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

Merci d’avoir lu jusqu’ici !

Lien

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

NumApps

Une chasse au trésor sur la NumWorks

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

Règles

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

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

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

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

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

Voyez le schéma ci-dessous :

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

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

Commandes

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

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

Captures d’écran

Pour plus d’informations

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

Lien

Voici le lien vers le jeu depuis mon workshop :

Projets

Le jeu du motus en python

MOTUS est à l’origine un jeu télévisé diffusé entre 1990 et 2019 sur France 2. Ce programme d’origine américaine s’inspire du jeu MasterMind, les lettres ayant remplacés les chiffres. Ce jeu a connu depuis de nombreuses variantes dont le célèbre WORDLE qui connait un très grand succès actuellement sur internet.

Introduction

Les règles du jeu sont plutôt simples : l’objectif est de trouver un mot d’un nombre fixé de lettres en au plus 6 essais. La première lettre du mot à découvrir est donnée et après chaque tentative :

  • les lettres communes et bien placées sont signalées par une case rouge
  • les lettres communes mais mal placées sont, quant à elles, signalées par une case jaune

Si le joueur parvient à découvrir le mot avant les 6 essais, il remporte la partie, sinon ce mot lui est dévoilé.

Certaines versions du jeu, dont WORDLE, se jouent à une personne, même si à l’origine le jeu télévisé se joue à deux. Dans la version à 2 joueurs, aux règles précédentes s’ajoutent les suivantes : un joueur perd la main si le mot proposé :

  • n’est pas de la bonne longueur
  • ne commence pas par la lettre proposée
  • est mal épelé
  • n’est pas donné dans les 8 secondes imparties
  • ne figure pas dans le dictionnaire
  • n’a pas été trouvé au terme des 6 essais

Dans chacun de ces cas, le joueur adverse récupère la main et bénéficie d’une lettre bonus : la première lettre non découverte lui est dévoilée. Il dispose alors du nombre d’essais restants (du joueur précédent) pour trouver le mot. Dans le cas ou il ne dispose que d’une tentative pour découvrir le mot, il conserve la main pour la grille suivante même en cas d’échec.

Notre projet est donc de programmer en langage python ce jeu. D’une part parce que nous aimons bien jouer à WORDLE (nous avons découvert le jeu original en visionnant une partie sur Youtube) et d’autre part car la programmation de ce jeu nous semblait accessible (pour notre niveau) sans être trop simple et offrait de nombreuses possibilités d’évolutions et améliorations (mode 2 joueurs, prise en compte de règles supplémentaires, interface graphique).

La première question qui s’est naturellement posée dès le départ concernait le choix de l’interface : en mode console ou plutôt avec une interface graphique ? Après une mure réflexion et avoir consulté plusieurs sites notamment de NSI, nous nous sommes rendus à l’évidence : une interface graphique nécessitait un passage obligé par le module tkinter particulièrement adapté à ce jeu. Et même si ce module semble accessible pour les novices que nous sommes, nous avons jugé plus prudent dans un premier temps, de travailler sur un jeu en mode console.

Après avoir réfléchi à la structure de notre programme, nous avons écrit ensemble les différentes fonctions que devra utiliser le jeu. Puis, comme il nous restait du temps, nous avons décidé de programmer à partir de ces fonctions, deux modes de jeu : un mode 1 joueur et un mode à 2. Nous nous sommes répartis le travail et chacun de nous a développé son propre mode et nous les avons assemblés dans le programme final.

Notre programme

Nous avons essayé de rester les plus fidèles au jeu original même si comme nous le verrons plus tard, nous n’avons pas implémenté toutes les règles présentées précedemment.

Concernant l’affichage, avant que le joueur ne fasse sa proposition, nous affichons en majuscules les lettres (bien placées) qui ont été découvertes jusque là (la première lettre étant donnée dès le départ), les lettres manquantes étant remplacées par un tiret. Puis après la saisie du mot, nous affichons en majuscule les lettres communes avec les mots à découvrir bien placées et en minuscule les lettres communes mal placées.

La partie : notre jeu offre la possibilité de jouer à un ou à deux . Après avoir sélectionner le nombre de joueur, la longueur du mot à deviner est demandé (entre 5 et 10 lettres). Puis dans le cas d’une partie à 2, le nombre de grilles de jeu sur lesquelles les adversaires souhaitent s’affronter est demandé. Dans le mode solo, chaque partie s’effectue sur une seule grille.

Les règles : dans la version 1 joueur pour qu’un essai soit validé, le mot proposé doit figurer dans le dictionnaire (et doit être bien orthographié), il doit commencer par la bonne lettre et doit être de la bonne longueur. Si le mot n’est pas valide, il est redemandé au joueur sans qu’il ne soit pénalisé par la perte d’un essai. Voici un exemple de partie :

Dans la version 2 joueurs, la main passe

  • dans chacun des cas précédents
  • au cas ou le mot n’est pas découvert au terme des 6 essais, auquel cas l’adversaire bénéficie d’une seule tentative pour découvrir le mot, mais conserve la main pour la grille suivante en cas d’échec.

Les fonctions

la fonction dico_n_lettres

def dico_n_lettres(dico):
#fonction qui crée une fois pour toute des dictionnaires
#de mots avec un nombre donné de lettres à partir d un
#fichier texte
   for n in range(1,25):
      path=os.getcwd()+'/'                   # définit le répertoire ou se trouve le ditionnaire
      dico1 = path+dico+".txt"               # fichier contenant le dictionnaire 
      dico2 = path+dico+str(n)+".txt"        # fichier qui contiendra le dictionnaire des mots à n lettres 
      fichier1 = open(dico1, "r")
      fichier2 = open(dico2, "w")
      for mot in fichier1:
          if len(mot) == n+1:                # n+1 car on doit tenir compte du caractère de retour à la ligne
              fichier2.write(mot)     
      fichier1.close()
      fichier2.close()

Nous avons récupéré sur le site https://www.lama.univsavoie.fr/pagesmembres/hyvernat/Enseignement/1415/info113/tp6.html deux dictionnaires sous la forme de fichiers .txt . Le premier contrairement au second contient les formes conjuguées des verbes et les pluriels des noms. La fonction précédente a pour but de créer une seule fois (ils sont installés sur le répertoire courant lors de la première exécution du programme), à partir du dictionnaire choisi, des dictionnaires constitués uniquement des mots de même longueur.

La fonction choisir_un_mot

def choisir_un_mot(dico,n):
#fonction qui va choisir aléatoirement un mot
#de n lettres
    path=os.getcwd()+'/'
    dico = path+dico+str(n)+".txt"
    fichier = open(dico, "r")
    liste_mots = fichier.readlines() # met tous les mots du fichiers dans une liste
    i = randint(1,len(liste_mots))
    mot = liste_mots[i]              # prend au hasard un mot dans la liste
    mot = mot.replace('\n','')       # supprime le caractère de retour à la ligne
    fichier.close()
    return(mot)

Cette fonction va utiliser le dictionnaire créé par la fonction précédente pour sélectionner aléatoirement un mot de n lettres qui servira de mot à deviner dans le jeu.

La fonction lettres_placées

def lettres_placees(liste1,liste2,liste3):
# fonction qui met dans liste1 les lettres 
# communes bien placées des liste2 et liste3
    liste = []
    liste0 = []
    for i in range(len(liste2)):
        if liste2[i]==liste3[i]:
            liste0.append(liste2[i])
        else:
            liste0.append('')
    for i in range(len(liste1)):
        if liste1[i]==liste0[i]:
            liste.append(liste1[i])
        else:
            liste.append(liste1[i]+liste0[i])
    return(liste)

Avec la fonction lettres_communes elles sont le cœur de notre programme. Cette fonction a pour but de mettre à jour la liste des lettres bien placées découvertes depuis le début de la partie. La variable liste1 contient la liste des lettres bien placées à mettre à jour et liste2 et liste3 les listes à comparer (le mot mystère et le dernier mot joué) et dont les lettres communes et bien placées vont être ajoutées à liste1.

La fonction lettres_communes

def lettres_communes(liste1,liste2):
# Retourne une liste avec toutes les lettres
# de la liste1 se trouvant dans la liste2
# les lettres bien placées sont en majuscule
# les autres sont en minuscule.
    liste = []
    liste3 = []
    for i in range(len(liste1)):
       liste.append('')
       liste3.append(liste2[i])
    for i in range(len(liste1)):
        if liste1[i]==liste3[i]:
            liste[i]=liste1[i].upper()
            liste3[i]=''
    for i in range(len(liste1)):
        for j in range(len(liste1)):
            if liste1[i]==liste3[j] and liste[i]=='':
                liste[i]=liste1[i].lower()
                liste3[j]=''
                break     
    return(liste)

Cette fonction est chargée de trouver les lettres communes entre liste1 et liste2, (qui représentent le mot joué et le mot à deviner) retournant ces lettres en majuscule pour les lettres bien placées et en minuscules pour les autres. Une des principales difficulté dans la conception de cette fonction a été la gestion des lettres dont le nombre d’apparition était différente dans les deux mots.

La fonction teste_mot

def teste_mot(liste,lettre,dico,n):
# Teste si le mot proposé par le joueur
# est valide : il doit commencer par 'lettre'
# se trouver dans dico et être de n lettres.
    test=True
    mot=''.join(liste).lower()   #transforme 'liste' en chaine de caractère en minuscule
    path=os.getcwd()+'/'
    dico = path+dico+str(n)+".txt"
    fichier = open(dico, "r")
    liste1 = fichier.readlines() # met tous les mots du dico dans une liste
    liste_mots=[]
    for mot1 in liste1:
        liste_mots.append(mot1.replace('\n',''))
    if  len(liste)!=n:                # teste si le mot est de la bonne longueur
       test=False
       print('Ce mot n est pas de la bonne longueur.')
    elif (mot not in liste_mots):     # teste si le mot est dans le dico
       test=False
       print('Ce mot n est pas dans le dictionnaire.')
    elif liste[0]!=lettre:            # teste si le mot commence par la bonne lettre
       test=False            
       print('Ce mot ne commence pas par la lettre '+lettre)
    fichier.close()
    return(test)

Cette fonction a pour but la validation (ou pas) de chaque mot entré par le joueur. Le mot est validé s’il commence par la bonne lettre, est constitué du bon nombre de lettres et se trouve dans le dictionnaire.

Les difficultés rencontrées

1. Pour la création et la gestion des dictionnaires nous nous sommes aidés du site https://python.sdv.univ-paris-diderot.fr/07_fichiers/ qui explique clairement les manipulations de base des fichiers (ouverture en mode lecture/écriture, fermeture, mise sous forme de liste de chaîne de caractères du contenu,…) et nous avons dû nous familiariser avec le module os pour indiquer le répertoire dans lequel se trouve les dictionnaires. Une des difficulté qui est apparue lors de la création des dictionnaires vient du fait que la longueur des mots ne correspondait pas à celle demandée (par exemple les mots de 5 lettres apparaissaient lorsqu’on en demandait 6. C’est après plusieurs tests que nous avons compris que le décalage venait du caractère ‘saut de ligne’ présent mais qui n’apparaissait pas quand on affichait les mots.

2. Pour la fonction « lettres_communes » la gestion des lettres dont le nombre d’apparition était différente entre les mots à comparer nous a aussi posé quelques problèmes. Pour y remédier, la solution que nous avons trouvé a été d’ « effacer » du mot à deviner une lettre commune une fois qu’elle a été trouvée (pour éviter de la recompter si elle apparaît une nouvelle fois dans le mot joué), en remplaçant la lettre en question par le caractère ‘ ‘. Alors que cette opération d’effacement s’effectuait en local (uniquement à l’intérieur de la fonction), la liste modifiée l’était également dans le programme principal. Pour régler le problème, nous avons décidé d’utiliser une liste intermédiaire, pour éviter de toucher à la liste passée en argument. Mais le résultat fut le même. Après quelques recherches sur internet, nous avons compris que le problème était inhérent aux listes et après plusieurs tentatives , nous sommes parvenus à régler le problème en passant par une liste auxiliaire créée non plus par une simple affectation mais en la remplissant grâce à une boucle et la commande .append().

3. Nous avons tenté de prendre en compte la gestion du temps, c’est-a-dire limiter à 10 s la durée de chaque essai mais la simple utilisation du module time ne donnait pas de résultat satisfaisant. En cherchant sur internet, nous avons vu qu’il était possible de créer un minuteur grâce au module threading mais après quelques essais cela s’est avéré plus compliqué que prévu et nous avons décidé de renoncer à cette fonctionnalité pour cette version mais nous espérons et envisageons de l’ajouter dans le futur.

Le fichier motus.py et les dictionnaires

Conclusion

Ce projet libre nous a permis de réaliser notre premier jeu en python. Après avoir longuement hésité, nous avons finalement opté pour MOTUS qui s’est finalement révélé très intéressant à programmer avec quelques challenges. Bien sûr, il reste encore beaucoup à accomplir pour se rapprocher du jeu original, notamment l’utilisation d’une interface graphique grâce au module tkinter ou encore la gestion du temps. Nous avions également pensé à intégrer des niveaux de difficulté en créant par exemple des dictionnaires adaptés (en associant à chaque mot un coefficient indiquant sa « difficulté » sur le même principe que le Scrabble). Toutes ces améliorations feront peut-être l’objet d’un futur projet.

Projets

Jeu vidéo en Python à l’aide de la librairie…

Dans le cadre du projet libre de fin d’année en NSI, je vous présente mon premier jeux vidéo en python fait à l’aide de pygame, une librairie spécialisé pour la création de jeux vidéo.

Librairie Pygame

Pygame est une bibliothèque de jeu open-source pour Python, qui permet de créer des jeux et des applications multimédias. Elle fournit des outils pour gérer les graphismes, le son, les entrées utilisateur, le réseau et plus encore.

Pygame utilise la bibliothèque SDL (Simple DirectMedia Layer) pour accéder aux fonctions du système d’exploitation, ce qui signifie que les jeux créés avec Pygame peuvent fonctionner sur de nombreuses plateformes, notamment Windows, macOS et Linux.

Pygame est une excellente option pour les débutants qui cherchent à créer des jeux simples (des base en programmation sont cependant nécessaire), mais il peut également être utilisé pour créer des jeux professionnels et des applications multimédias complexes.

Le Projet

Mon objectif sur ce projet a été de faire mes premiers pas avec la bibliothèque Pygame en Python. Cela a été un véritable défi qui m’aura fait découvrir de nombreuses mécaniques en Python, telles que le système de classe ou la programmation orientée objet, qui ont représenté pour moi un pas dans l’inconnu. Mon défi a donc été de créer un jeu simple pour débuter. Le but est de contrôler un personnage avec sa souris et d’éviter des briques qui apparaissent aléatoirement sur le côté de l’écran, tout cela en ayant le choix entre trois niveaux de difficulté différents.

Structure du Script

Pour la réalisation de mon jeu, j’ai décidé de me lancer un défi supplémentaire : l’utilisation de la Programmation Orientée Objet. Mais qu’est-ce que la Programmation Orientée Objet ?

La Programmation Orientée Objet est un paradigme de programmation qui permet de structurer un programme en utilisant des objets, qui sont des entités possédant des caractéristiques et des comportements spécifiques. Cette approche permet de modéliser des concepts du monde réel de manière plus naturelle et intuitive, en regroupant des fonctionnalités liées dans des classes, et en instanciant ces classes pour créer des objets.

En résumé, cela consiste à organiser son code de manière à ce qu’il soit ordonné à l’aide de classes contenant des fonctions et des constantes générales, ce qui conduit à un code plus lisible, plus maintenable et plus ordonné. Cependant, cela peut être relativement perturbant au début.

Voici un simple scripte sans utilisation de Programmation Orienté Objet qui permet de faire bougé une image nommé « ball.png » à l’aide des flèches directionnelles :

import pygame

pygame.init()
screen = pygame.display.set_mode((400, 400))
running = True

image = pygame.image.load("ball.png")

x = 0
y = 0

clock = pygame.time.Clock()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_LEFT]:
        x -= 1
    if pressed[pygame.K_RIGHT]:
        x += 1
    if pressed[pygame.K_UP]:
        y -= 1
    if pressed[pygame.K_DOWN]:
        y += 1

    screen.fill((0, 0, 0))
    screen.blit(image, (x, y))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

code by TNtube

Le scripte est simple et efficace, pour autant si il venais à se complexifier cela deviendrait vite illisible.
voici maintenant le même scripte avec l’utilisation de la Programmation Orienté Objet :

player.py

import pygame

class Player:
    def __init__(self, x, y):
        self.image = pygame.image.load("player.png")
        self.image
        self.rect = self.image.get_rect(x=x, y=y)
        self.speed = 5
        self.velocity = [0, 0]

    def move(self):
        self.rect.move_ip(self.velocity[0] * self.speed, self.velocity[1] * self.speed)

    def draw(self, screen):
        screen.blit(self.image, self.rect)

Main.py

import pygame
from player import Player


class Game:
    def __init__(self, screen):
        self.screen = screen
        self.running = True
        self.clock = pygame.time.Clock()
        self.player = Player(0, 0)

    def handling_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False

        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.player.velocity[0] = -1
        elif keys[pygame.K_RIGHT]:
            self.player.velocity[0] = 1
        else:
            self.player.velocity[0] = 0

        if keys[pygame.K_UP]:
            self.player.velocity[1] = -1
        elif keys[pygame.K_DOWN]:
            self.player.velocity[1] = 1
        else:
            self.player.velocity[1] = 0

    def update(self):
        pass

    def display(self):
        self.screen.fill("white")
        pygame.display.flip()

    def run(self):
        while self.running:
            self.handling_events()
            self.update()
            self.display()
            self.clock.tick(60)


pygame.init()
screen = pygame.display.set_mode((1080, 720))
game = Game(screen)
game.run()

pygame.quit()

code by TNtube

La particularité ici réside dans la clarté de chaque fonction et de leur utilité respective : « display » pour l’affichage, « handling_event » pour les interactions claviers-souris, « move » pour les mouvements du joueur… De plus, le programme est divisé en deux fichiers distincts : l’un pour le programme principal et l’autre pour le joueur. Ainsi, il est possible d’ajouter aisément de nouvelles fonctions pour des améliorations futures. Par exemple, la fonction « Update », actuellement inactive, pourrait être employée pour effectuer des vérifications, telles que les collisions.

Analyse du script

Passons maintenant à l’analyse du code.

Commençons par mon fichier « player.py », avec l’appel du module Pygame, la création de la classe et la fonction d’initialisation.

import pygame #On importe le module Pygame

class Player: #On crée la classe Player
    
# La fonction init est une fonction d'initialisation qui permet de déclarer toutes nos variables.
# Elle prend comme arguments self, qui est pris dans chaque fonction de la classe, x et y qui servent à définir la position initiale du joueur sur l'écran.
def init(self, x, y):
self.image = pygame.image.load("asset/Player.png")# On charge l'image du joueur
#Pour que vous puissiez être familiarisé avec le concept de rect, ce sont des rectangles que l'on ne voit pas, qui ont une taille que l'on leur définit et c'est grâce à ces rect, que l'on pourrait appeler hitbox, que l'on peut détecter des collisions.
self.rect = self.image.get_rect(center=(x, y))# On récupère le rectangle de l'image et on le centre en (x,y)
self.velocity = [0, 0]# On initialise la vitesse à 0 dans les deux directions

Une fois la mise en place de notre classe et de la fonction d’initialisation effectuée, nous créons la fonction de mouvement du joueur appelée « move », ainsi qu’une fonction « draw » qui permettra d’afficher notre personnage avec son rectangle.

def move(self, mouse_pos):# La fonction move permet de déplacer le joueur en fonction de la position de la souris, l'argument mouse_pos représente la position de la souris sur l'écran. Cette position est utilisée pour calculer la vitesse de déplacement du joueur, en soustrayant la position de la souris à la position actuelle du joueur.
    # On calcule la différence entre la position de la souris et la position actuelle du joueur
    self.velocity[0] = (mouse_pos[0] - self.rect.centerx)
    self.velocity[1] = (mouse_pos[1] - self.rect.centery)
    self.rect.move_ip(self.velocity)# On déplace le rectangle du joueur en fonction de la vitesse calculée

def draw(self, screen):# La fonction draw permet d'afficher le joueur sur l'écran
    screen.blit(self.image, self.rect)# On blitte (st utilisée pour dessiner une surface sur une autre surface.) l'image du joueur sur le rectangle de celui-ci

Maintenait que je vous ai introduis mon fichier joueur passons au fichier jeu.

import pygame
import random
from player import Player

On importe les modules nécessaires au fonctionnement du jeu : Pygame pour gérer l’affichage graphique et l’interaction avec l’utilisateur, random pour générer des nombres aléatoires, et Player qui est une classe définie dans un fichier séparé et qui représente le joueur.

class Game:
    def __init__(self, screen):
        self.screen = screen
        self.running = True
        self.clock = pygame.time.Clock()
        self.player = Player(60, 60)
        self.background = pygame.image.load("asset/background.jpg")
        self.brique = pygame.image.load("asset/brique.jpg")
        self.brique_rects = []
        self.brique_positions = []
        self.brique_speed = 3
        self.brique_spawn_rate = 120
        self.brique_spawn_counter = 0
        self.brique_spawn_augmentation = 15
        self.brique_spawn_increase_counter = 0
        self.total_time = 0
        self.total_time_max = 0
        self.difficulté = 20
        pygame.mouse.set_visible(False)
        
        choix = input("_____________________________________________________________________________ \n choisissez votre difficulté : \n 1- Facile \n 2- Normal \n 3- Difficile \n _____________________________________________________________________________ \n Votre choix : ")
        choix = int(choix)
        if choix == 1:
            self.difficulté = 1
        elif choix == 2:
            self.difficulté = 5
        elif choix == 3:
            self.difficulté = 10
        else:
            print("Error : s'il vous plait entré un entier positif entre 1 et 3")
            self.running = False

On définit la classe Game, qui représente le jeu en lui-même. Son constructeur prend en paramètre l’objet screen qui représente la surface sur laquelle le jeu sera affiché.

On initialise différentes variables, comme self.running qui permet de savoir si le jeu est en cours d’exécution ou non, self.player qui représente le joueur, self.background qui est l’image de fond, self.brique qui est l’image représentant les obstacles à éviter, et plusieurs variables liées à ces obstacles (self.brique_positions, self.brique_speed, self.brique_spawn_rate, self.brique_spawn_counter, self.brique_spawn_augmentation, self.brique_spawn_increase_counter).

On définit également self.total_time et self.total_time_max pour gérer le temps de jeu, et self.difficulté qui est utilisé pour régler la difficulté du jeu en fonction de la réponse de l’utilisateur à une question posée par input.

Enfin, on masque le curseur de la souris avec pygame.mouse.set_visible(False).

    def gestion_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False

        self.player.move(pygame.mouse.get_pos())

La méthode gestion_events permet de gérer les événements liés à l’interaction de l’utilisateur avec la fenêtre Pygame.

Le code utilise une boucle for pour récupérer chaque événement dans la liste des événements de Pygame. Si l’un des événements est de type QUIT, cela signifie que l’utilisateur a cliqué sur le bouton de fermeture de la fenêtre. Dans ce cas, la variable self.running est définie sur False, ce qui interrompt la boucle de jeu.

La méthode appelle également la méthode move de l’objet Player pour mettre à jour sa position en fonction de la position de la souris de l’utilisateur.

   def adaptative_brique_rects(self):
        self.brique_rects = []

        for pos in self.brique_positions:
            rect = pygame.Rect(pos[0], pos[1], self.brique.get_width(), self.brique.get_height())
            self.brique_rects.append(rect)

La méthode adaptative_brique_rects est appelée pour mettre à jour les rects de collision des briques en fonction de leurs nouvelles positions.

 def spawn_brique(self):
        y = random.randint(0, self.screen.get_height() - self.brique.get_height())
        self.brique_positions.append((-self.brique.get_width(), y))

La méthode spawn_brique est appelée pour générer une nouvelle brique à une position aléatoire sur l’axe y et avec une position initiale en dehors de la fenêtre sur l’axe x.

    def move_briques(self):
        for i in range(len(self.brique_positions)):
            pos = list(self.brique_positions[i])
            pos[0] += self.brique_speed
            self.brique_positions[i] = tuple(pos)

        self.adaptative_brique_rects()

La méthode move_briques met à jour la position des briques en ajoutant la valeur de self.brique_speed à la coordonnée x de chaque position dans self.brique_positions.

def update(self):
    # Incrémenter le compteur de temps de spawn des briques par la difficulté du jeu
    self.brique_spawn_counter += self.difficulté

    # Augmenter la vitesse des briques si le compteur de spawn des briques est un multiple de 120
    if self.brique_spawn_counter % 120 == 0:
        self.brique_speed += 0.2

    # Vérifier si le compteur d'augmentation de spawn des briques a atteint sa limite
    self.brique_spawn_increase_counter += 1
    if self.brique_spawn_increase_counter >= self.brique_spawn_augmentation * 60:
        self.brique_spawn_increase_counter = 0
        self.brique_spawn_rate -= 10

    # Ajouter une nouvelle brique à la liste des positions de briques si le compteur de spawn des briques a atteint le taux de spawn des briques
    if self.brique_spawn_counter >= self.brique_spawn_rate:
        self.brique_spawn_counter = 0
        self.spawn_brique()

    # Déplacer toutes les briques dans la direction de la vitesse actuelle des briques
    self.move_briques()

    # Incrémenter le temps total de jeu de 1
    self.total_time += 1  

    # Vérifier si le joueur a perdu en entrant en collision avec une brique
    for rect in self.brique_rects:
        if rect.colliderect(self.player.rect):
            print("_____________________________________________________________________________\n")
            print("    Vous avez perdu, Votre temps est de", self.total_time/100, "secondes, bien joué à vous ;)")
            print("_____________________________________________________________________________\n")
            self.running = False
            break
        else:
            pass

La méthode update est appelée à chaque boucle de jeu pour mettre à jour les positions des briques, vérifier si le joueur a perdu en entrant en collision avec une brique et pour augmenter la difficulté du jeu.

Elle commence par incrémenter le compteur de temps de spawn des briques par la difficulté du jeu, pour que les briques apparaissent plus rapidement avec des niveaux de difficulté plus élevés.

Ensuite, si le compteur de spawn des briques est un multiple de 120, la vitesse de déplacement des briques est augmentée de 0,2.

La méthode vérifie également si le compteur d’augmentation de spawn des briques a atteint sa limite, qui est définie par self.brique_spawn_augmentation * 60. Si c’est le cas, le compteur est réinitialisé et le taux de spawn des briques est diminué de 10.

Si le compteur de spawn des briques atteint le taux de spawn des briques défini par self.brique_spawn_rate, la méthode spawn_brique est appelée pour ajouter une nouvelle brique à la liste des positions de briques.

La méthode move_briques est ensuite appelée pour déplacer toutes les briques dans la direction de la vitesse actuelle des briques.

Le temps total de jeu est ensuite incrémenté de 1.

Enfin, pour chaque rect de brique dans la liste self.brique_rects, la méthode vérifie s’il y a une collision avec le rectangle de la zone de collision du joueur. Si c’est le cas, la méthode affiche le message de fin de jeu et met la variable self.running à False pour arrêter la boucle de jeu.

def display(self):
    # Afficher le fond d'écran
    self.screen.blit(self.background, (-200, -350))

    # Parcourir toutes les positions des briques et les afficher
    for pos in self.brique_positions:
        self.screen.blit(self.brique, pos)

    # Afficher le joueur
    self.player.draw(self.screen)

    # Mettre à jour l'affichage
    pygame.display.flip()

La méthode display(self) affiche les éléments du jeu sur l’écran. Elle utilise la méthode blit pour afficher le fond d’écran, chaque brique de la liste brique_positions et le joueur (player) sur l’écran. Ensuite, elle appelle la méthode flip de la classe pygame.display pour mettre à jour l’affichage.

def run(self):
    # Boucle principale du jeu
    while self.running:
        # Gérer les événements (ex: appuyer sur une touche, quitter le jeu)
        self.gestion_events()

        # Mettre à jour le jeu (ex: déplacer les briques, vérifier les collisions)
        self.update()

        # Afficher le jeu
        self.display()

        # Limiter le nombre de frames par seconde à 60
        self.clock.tick(60)

    # Réafficher la souris et quitter Pygame
    pygame.mouse.set_visible(True)
    pygame.quit()

La méthode run(self) contient la boucle principale du jeu. Elle s’exécute tant que l’attribut running est vrai. À chaque itération de la boucle, elle gère les événements (appel à gestion_events()), met à jour les éléments du jeu (appel à update()), affiche le contenu du jeu sur l’écran (appel à display()) et attend un certain temps défini par la méthode tick de pygame.time.Clock. Enfin, elle quitte le jeu (pygame.quit()) lorsque la boucle est terminée.

# Initialiser Pygame
pygame.init()

# Créer une fenêtre de jeu
screen = pygame.display.set_mode((1080, 720))

# Créer une instance de la classe Game en lui passant la fenêtre de jeu en argument
game = Game(screen)

# Lancer le jeu
game.run()

# Quitter Pygame
pygame.quit()

La partie en dehors de la classe initialise la bibliothèque Pygame (pygame.init()) et crée une fenêtre de jeu (pygame.display.set_mode((1080, 720))). Ensuite, elle crée une instance de la classe Game avec l’écran en tant qu’argument et appelle la méthode run de l’instance pour démarrer le jeu. Enfin, elle quitte la bibliothèque Pygame (pygame.quit()) après la fin du jeu.

Sources :

Conclusion

En conclusion, ce projet est un petit jeu simple mais amusant, il est sur l’univers de Mario. Si vous voulez essayer ce jeu par vous-même, vous pouvez le télécharger en cliquant sur le lien ci-dessous. Pour lancer le jeu, assurez-vous d’écrire le niveau de difficulté souhaité (1, 2 ou 3) dans la console Python. Si vous ne le faites pas, la fenêtre restera noire et le jeu ne se lancera pas. On commande le personnage avec sa souris.

Amusez-vous bien !

NumApps

Sudoku en python, NumWorks

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

Fonctionnalités

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

Captures d’écran

Commandes

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

En savoir plus

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

Télécharger

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

NumApps

Reversi en python, NumWorks

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

Fonctionnalités

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

Captures d’écran

Commandes

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

En savoir plus

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

Télécharger

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

Tutoriels

Découvrir un outil linux pour gérer ses serveurs de…

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 à :

  1. Installer une machine virtuelle sur son propre ordinateur,
  2. Installer et configurer le système d’exploitation Ubuntu Server,
  3. Installer et configurer Pterodactyl à l’aide d’un script
  4. 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’exploitationVersionSupporté ?Notes
Ubuntu18.04
20.04
22.04Le tutoriel est basé sur ce système d’exploitation (conseillé).
CentOS7Des dépôts, serveurs contenant des « paquets » (voir la définition d’apt1) supplémentaires sont requis.
8Des 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.
Debian10
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 !

Pour en apprendre plus sur VirtualBox.

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.

Une documentation très complète en français de l’utilisation d’Ubuntu est disponible si vous souhaitez en apprendre plus.

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…
  • Une liste d’autres notions en rapport avec Pterodactyl (notamment Nest, Egg, etc.)

Mettre à jour le système (IMPORTANT)

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 :

root@ubuntu-vm:/home/cosmos$ bash <(curl -s https://pterodactyl-installer.se)

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-25575
Et 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 Token
Copiez 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
cosmos@ubunt-vm:/etc/systemd/system$ sudo nano wings.service

Cela vous ouvre une « interface » ressemblant à ceci :

« nano » est un éditeur de texte en ligne de commande.

Copiez et coller (clique droit pour rappel) ce texte dans l’éditeur de texte :

[Unit]
Description=Pterodactyl Wings Daemon
After=docker.service
Requires=docker.service
PartOf=docker.service

[Service]
User=root
WorkingDirectory=/etc/pterodactyl
LimitNOFILE=4096
PIDFile=/var/run/wings/daemon.pid
ExecStart=/usr/local/bin/wings
Restart=on-failure
StartLimitInterval=180
StartLimitBurst=30
RestartSec=5s

[Install]
WantedBy=multi-user.target

Appuyez ensuite simultanément sur les touches Ctrl et S, cela ajoutera ce petit texte signifiant que cela bien sauvegardé ce que nous avons écrit.

Appuyez ensuite simultanément sur les touches Ctrl et X pour fermer l’éditeur puis écrivez cette commande :

cosmos@ubuntu-vm:/etc/systemd/system$ sudo systemctl enable --now wings

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 😉).
Tutoriels

Installer l’émulateur LDPlayer afin d’utiliser des applications ainsi que…

Vous en avez marre de jouer à des jeux et d’utiliser des applications sur votre petit écran de téléphone ? Pas de panique, ce tutoriel vous apprendra comment passer au grand écran en installant l’émulateur LDPlayer qui permet d’utiliser des jeux et applications mobiles sur votre ordinateur.

Qu’est-ce qu’un émulateur ?

Un émulateur est un logiciel ou un matériel qui imite le comportement d’un autre système informatique. En d’autres termes, un émulateur permet à un ordinateur de simuler le fonctionnement d’un autre système informatique, ce qui permet d’exécuter des logiciels ou des jeux conçus pour fonctionner sur ce système.

Les émulateurs sont souvent utilisés pour jouer à des jeux anciens sur de nouveaux ordinateurs mais ils peuvent également être utilisés pour exécuter des applications mobiles sur un ordinateur. L’un des avantages de l’utilisation d’un émulateur, c’est qu’il offre la possibilité de profiter de la puissance de votre ordinateur pour faire fonctionner vos jeux et applications mobiles de manière fluide et sans aucun problème de performance.

1) Téléchargement et installation

Tout d’abord, il faut commencer par télécharger l’émulateur LdPlayer qui est totalement gratuit. Vous pouvez le télécharger en cliquant ici. Le logo de l’émulateur se présentera sur votre bureau comme ceci :

Vous pouvez alors le lancer et cliquer sur « Installer » pour démarrer l’installation.

L’installation ne prend normalement que quelques minutes mais, tout dépend de votre connexion internet. Une fois l’installation terminée, vous pouvez cliquer sur « Démarrer » pour lancer l’émulateur et arriver dans le menu.

2) Se servir de l’émulateur

Vous voilà arrivé dans le menu de l’émulateur qui se présente comme ci-dessous :

Pour commencer à télécharger des jeux ou des applications, le principe est identique à celui d’un téléphone, il suffit d’écrire dans la barre de recherche votre souhait.

En cliquant sur la barre de recherche, une nouvelle interface s’affiche avec les suggestions ainsi que les jeux les plus recherchés. Pratique si l’on souhaite découvrir de nouveaux jeux.

À présent, vous n’avez qu’à chercher le jeu ou l’application que vous souhaitez installer. Une fois que c’est fait, vous pouvez appuyer sur le bouton jaune « Installer« .

3) Connexion au Play Store

Une fois le bouton cliqué, une page va apparaitre afin que l’utilisateur puisse se connecter avec son compte Google pour ensuite accéder au Play Store. Le Play Store est le magasin qui vous permet de télécharger et d’installer des applications sur votre appareil Android de manière simple et sécurisée. (Cette étape est unique puisque votre compte Google restera enregistré dans l’émulateur pour de futurs téléchargements d’applications et de jeux).

Vous pouvez désormais cliquer sur le deuxième bouton « Installer » qui démarrera directement l’installation du jeu ou de votre application.

Le logo va donc s’ajouter sur le menu de l’émulateur et votre application/jeu est maintenant installé(e) et prêt(e) à être lancé(e).

Le lancement du jeu est identique à celui d’un téléphone ou d’une tablette. De plus, vous pourrez profiter de meilleures performances graphiques ainsi que d’un grand écran.

Et voilà le tour est joué ! En suivant ce tutoriel, vous devriez maintenant être en mesure d’installer et d’utiliser l’émulateur LDPlayer de manière efficace, ce qui vous permettra de profiter pleinement des applications et jeux mobiles sur votre ordinateur. J’espère que ce tutoriel vous aura été utile et que vous apprécierez l’expérience de jeu que vous offres l’émulateur LDPlayer !

Tutoriels

Comment crée des replays instantanée ?

Vous souhaitez conserver vos moments favoris ? Mais vous avez pas envie d’enregistrer tout le temps ? J’ai la solution pour vous, rapide et simple !

Medal.TV : logiciel de replay instantanée !

Bonjour à tous et bienvenue sur mon tutoriel sur comment faire des replays instantanée de manière simple et rapide !

Tout d’abord il vous faudra impérativement Windows 10/11

Puis : cliquez ici

Ce fichier ce télécharge. Une fois terminé lancer le :

Une fenêtre s’ouvre, elle vous permettra d’installer le logiciel. Par la suite une fenêtre de ce genre s’ouvre

Il vous suffit de patienté. Une fois ouvert on arrive sur cette grande page :

Cliquez ici :

On arrive sur cette page. Il vous suffiras de configuré le taux d’enregistrement par défaut etc.

Pensez bien a régler votre micro dans la section son

Maintenant lancer un jeu. Pour ma part je vais lancer Sea Of Thieves. Lorsque je lance le jeu une pop-up apparait :

Si jamais j’appuie sur la touche F9 alors un replay de la dernière minute va ce crée, voici un exemple :

La pop-up s’affiche pour nous dire que l’ordre a bien été reçu ! Une fois que je ferme mon jeu Medal me montre ce que j’ai enregistrer :

Je peux cliquer dessus pour avoir un grand aperçu :

Vous pouvez donner un titre, des hastags puis le publié ou alors faire un mini montage !

Si jamais l’on veux crée un montage simple sans aucune connaisance c’est très simple, on clique sur Open in Editor :

En cliquant sur les icones a droite je peux :

Mettre une musique ;

Insérer des meme ;

Mettre des effets en 1 clic ;

Ajouté du texte ;

Ajouté des stickers/gif ;

Et enfin importer des fichiers personnalisé ! Petit exemple de ce que l’on peux faire :

https://medal.tv/games/sea-of-thieves/clips/MEWKWCZVgntvW/d1337MAGHEVe?invite=cr-MSx2ejIsNTE0NzYwODAs

Après tout est possible :

https://medal.tv/games/csgo/clips/MF71Yly9MicDz/d1337FbLsmVB?invite=cr-MSxneGosNTE0NzYwODAs

Maintenant vous serez les pro des replays instantanée !

Si vous avez des questions n’hésitez pas a les déposer dans les commentaires !