Le Puissance 4 est probablement un jeu de votre enfance, c’est pourquoi je vous propose de jouer à ma version du Puissance 4 directement sur votre NumWorks avec un ami.
Sommaire
L’idée
L’idée du Puissance 4 a été longuement réfléchi car le projet de ces vacances était libre et j’avais eu l’idée de faire un jeu sur la NumWorks mais je ne savais pas lequel faire. Après avoir parcouru le site à la recherche de jeu original qui n’avait pas été déjà fait, l’idée du Puissance 4 m’est venu à l’esprit.
La réalisation
Pour réaliser le jeu j’ai avant tout fait en sorte qu’il soit jouable sur la console avant de faire la partie graphique de la calculatrice (même si certains ajouts ont été fait pendant le développement de la partie graphique).
Le Script
Je vais maintenant vous présenter le script et vous faire une brève explication de chacune des fonctions :
from kandinsky import fill_rect as rect, draw_string as txt from time import sleep from ion import keydown
Au tout début du script nous avons évidemment les appels des différents modules :
- Kandinsky : c’est un des modules propriétaire de NumWorks qui est utilisé afin de dessiner des rectangles ou même écrire du texte sur l’écran de la calculatrice
- Time : permet avec la fonction sleep que j’appel de mettre des pause dans le script
- Ion : c’est le second module propriétaire de NumWorks qui permet de prendre en compte l’appui des touches pendant le script.
Nous avons ensuite la définition des variables globales :
# 1 = rouge // 2 = jaune # Variables globales player = 1 #Définit le joueur qui doit jouer grille_preview = [0, 0, 0, 0, 0, 0, 0] #Est utilisé pour déterminer les positions possible du preview grille = [[0 for i in range(7)] for i in range(6)] #La matrice qui représente la grille de jeu #Les couleurs utilisées rouge = (182, 2, 5) jaune = (255, 181, 49) gris = (191, 189, 193) pos = 3 #Donne la position du preview #Les points des joueurs points_rouge = 0 points_jaune = 0
Ensuite une des fonctions majeures du jeu : la fonction de vérification. Cette fonction va après chaque coup vérifier toutes les positions afin de voir si il y a un gagnant ou pas.
def verifie(): #Gagnant ? for i in range(6): #lignes for j in range(4): if grille[i][j] == player and grille[i][j+1] == player and grille[i][j+2] == player and grille[i][j+3] == player: gagnant(player) for i in range(3): #colonnes for j in range(7): if grille[i][j] == player and grille[i+1][j] == player and grille[i+2][j] == player and grille[i+3][j] == player: gagnant(player) for i in range(3): #diagonales for j in range(4): if grille[i][j] == player and grille[i+1][j+1] == player and grille[i+2][j+2] == player and grille[i+3][j+3] == player: gagnant(player) for i in range(3, 6): for j in range(4): if grille[i][j] == player and grille[i-1][j+1] == player and grille[i-2][j+2] == player and grille[i-3][j+3] == player: gagnant(player)
Cependant je n’ai pas tout dit à propos de cette fonction car celle ci n’est pas tout à fait de moi : en effet avant d’avoir cette fonction là j’avais codé une fonction vérification mais celle ci faisait environ 70 lignes et n’était pas du tout optimisé ce qui amenait à des ralentissement, ChatGPT m’a donc aidé à réduire la fonction afin d’avoir la version ci-dessus. Et donc voici en exclusivité ma fonction de vérification originelle :
def verifie(): #horizontalement for i in range(6): for j in range(4): rouge = 0 jaune = 0 for k in range(4): if grille[i][j+k] == 1 : rouge += 1 elif grille[i][j+k] == 2 : jaune += 1 if rouge == 4 : player_won(1) elif jaune == 4 : player_won(2) #verticalement for i in range(3): for j in range(7): rouge = 0 jaune = 0 for k in range(4): if grille[i+k][j] == 1 : rouge += 1 elif grille[i+k][j] == 2 : jaune += 1 if rouge == 4 : player_won(1) elif jaune == 4 : player_won(2) #diagonalement """(ij) / 2 diag de 4 : 03 12 21 30 // 26 35 44 53 2 diag de 5 : 04 13 22 31 40 // 16 25 34 43 52 2 diag de 6 : 05 14 23 32 41 50 // 06 15 24 33 42 51 \ 2 diag de 4 : 03 14 25 36 // 20 31 42 53 2 diag de 5 : 02 13 24 35 46 // 10 21 32 43 54 2 diag de 6 : 00 11 22 33 44 55 // 01 12 23 34 45 56 """ #création des listes pour vérifier diag4 = [[[0, 3], [1, 2], [2, 1], [3, 0]], [[2, 6], [3, 5], [4, 4], [5, 3]], [[0,3], [1,4], [2,5], [3,6]], [[2,0], [3,1], [4,2], [5,3]]] diag5 = [[[0, 4], [1, 3], [2, 2], [3, 1], [4, 0]], [[1, 6], [2, 5], [3, 4], [4, 3], [5, 2]], [[0, 2], [1, 3], [2, 4], [3, 5], [4, 6]], [[1, 0], [2, 1], [3, 2], [4, 3], [5, 4]]] diag6 = [[[0, 5], [1, 4], [2, 3], [3, 2], [4, 1], [5,0]],[[0, 6], [1, 5], [2, 4], [3, 3], [4, 2], [5,1]],[[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5,5]],[[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5,6]]] #vérification des diagonales de 4 for i in range(4): if grille[diag4[i][0][0]][diag4[i][0][1]] == grille[diag4[i][1][0]][diag4[i][1][1]] == grille[diag4[i][2][0]][diag4[i][2][1]] == grille[diag4[i][3][0]][diag4[i][3][1]] : if grille[diag4[i][0][0]][diag4[i][0][1]] == 1 : player_won(1) elif grille[diag4[i][0][0]][diag4[i][0][1]] == 2 : player_won(2) #vérification des diagonales de 5 for i in range(4): for j in range(2): if grille[diag5[i][0+j][0]][diag5[i][0+j][1]] == grille[diag5[i][1+j][0]][diag5[i][1+j][1]] == grille[diag5[i][2+j][0]][diag5[i][2+j][1]] == grille[diag5[i][3+j][0]][diag5[i][3+j][1]] : if grille[diag5[i][0+j][0]][diag5[i][0+j][1]] == 1 : player_won(1) elif grille[diag5[i][0+j][0]][diag5[i][0+j][1]] == 2 : player_won(2) #vérification des diagonales de 6 for i in range(4): for j in range(3): if grille[diag6[i][0+j][0]][diag6[i][0+j][1]] == grille[diag6[i][1+j][0]][diag6[i][1+j][1]] == grille[diag6[i][2+j][0]][diag6[i][2+j][1]] == grille[diag6[i][3+j][0]][diag6[i][3+j][1]] : if grille[diag6[i][0+j][0]][diag6[i][0+j][1]] == 1 : player_won(1) elif grille[diag6[i][0+j][0]][diag6[i][0+j][1]] == 2 : player_won(2)
Ma fonction donc vérifiait chaque possibilité afin de gagner au jeu mais le plus long était surtout les diagonales qui n’étaient pas automatisées avec des boucles sans variables définies mais plutôt avec des listes interminables des positions possibles des diagonales et cela ralentissait considérablement le script. C’est pour cela que j’ai préféré utilisé la fonction de ChatGPT.
Par la suite nous avons une autre fonction majeure qui est celle qui est appelée dans le cas d’une fin de partie donc soit ci un joueur a gagné ou si il y a égalité et c’est cette même fonction qui redémarre une partie donc rénitialise la grille et actualise les scores :
def gagnant(winner): #Met fin à une partie gagnant ou pas global points_jaune, points_rouge, player, nb_partie, grille, partie affichage_grille() if winner == 1 : #Vérifie la variable winner pour savoir qui a gagné et agir en conséquence points_rouge += 1 player = 2 txt("Rouge a gagné !", 88, 20) elif winner == 2 : points_jaune += 1 player = 1 txt("Jaune a gagné !", 88,20) else : txt("C'est une égalité", 76, 20) wait() #La fonction attend qu'il y ai une touche de pressé (permet de voir la grille avant sa rénitialisation actu_src() #Actualisation des score grille = [[0 for i in range(7)] for i in range(6)] #Rénitialise la grille affichage_grille() #Affiche la grille
Ensuite nous avons la fonction selection qui est je dirai la dernière fonction majeure du script, elle permet au joueur de selectionner la colonne ou il souhaite ajouter son jeton et appel la fonction jouer que nous allons voir juste après :
def selection(): global pos affichage_grille() preview(pos) #Affiche le preview du jeton (par défaut la 4ème colonne) add = 1 while True : # Boucle qui permet de prendre en compte l'appui des touches et agit en conséquence. preview(pos) while colonne_pleine(pos): pos = (pos+add)%7 preview(pos) key_pressed = wait() #Récupère la touche appuyé if key_pressed == 0: #Flèche de gauche // décale le preview vers la gauche add = -1 if key_pressed == 3:#Flèche de droite // décale le preview vers la droite add = +1 if key_pressed==0 or key_pressed==3: #Calcul la position du preview pos = (pos+add)%7 preview(pos) if key_pressed == 4 or key_pressed == 52 : #OK ou EXE (permet de jouer le coup) rect(75, 17, 170, 20, (255,255,255)) jouer(pos)
Suite à cela il y a la fonction jouer qui est appelé dans la fonction selection et qui permet de jouer le coup dans la colonne sélectionné dans selection() :
def jouer(colonne): # Ajoute un jeton dans la colonne donnée global player if player == 1 : #Si c'est au tour de rouge animation(colonne) # Fais l'animation de chute du jeton grille[gravite(colonne)][colonne] = 1 #Modifie la matrice verifie() #Vérifie si il y a un gagant player = 2 #Change de joueur elif player == 2 : #La même chose que pour le rouge mais pour le jaune animation(colonne) grille[gravite(colonne)][colonne] = 2 verifie() player = 1 grille_pleine() #Vérifie si la grille est pleine dans le cas d'un nul
Après il y a la fonction qui anime la chute du jeton dans la grille :
def animation(colonne): #Animation chute if player == 1 : #Définit la couleur du jeton color = rouge else : color = jaune for i in range(0, gravite(colonne)+1): #Dessine des jeton à la suite jursqu'à la dernière ligne ligne = (i-1)*(i!=0) rect(75+(25*colonne),42+(ligne*25),20,20,gris) sleep(0.05) draw_cercle(75 + (25*colonne) + 7, 42 + (i*25) + 2, color) sleep(0.05)
Ensuite la fonction qui dessine les cercles mais qui n’est pas de moi mais de M.Robert :
def draw_cercle(x,y,color): #Fait des cercles (Par VR) for d in range(6): rect(x-d+(d==0),y+d+(d==5),6+2*d-2*(d==0),16-2*d-2*(d==5), color)
Par la suite il y a ma fonction colonne_pleine qui dit si la colonne donnée en paramètre est pleine ou pas :
def colonne_pleine(colonne): if (grille[0][colonne] == 1) or (grille[0][colonne] == 2): return True return False
Une fonction similaire à la précedente : fonction grille_pleine utilise colonne_pleine afin de déterminer si la grille est pleine ou pas donc si il y a égalité :
def grille_pleine(): #Vérifie si il y a une égalité soit si la grille est pleine colonne_pleines = 0 for i in range(7): #Passe en revue les premières lignes de chaque colonne if colonne_pleine(i): colonne_pleines += 1 if colonne_pleines == 7: #Si toutes les colonnes sont pleine gagnant(0) #Appelle la fonction gagnant pour mettre fin à la partie
Il y a aussi la fonction gravité qui va retourner la ligne la plus basse où le jeton peut aller :
def gravite(colonne): #Détermine la ligne où le jeton peut se placer ligne = 5 while grille[ligne][colonne] != 0: #parcours la colonne de haut en bas jusqu'à trouver la ligne vide la plus basse if ligne == 0 : return ligne ligne -= 1 return ligne
Une autre des fonctions principales : affichage_grille permet comme son nom l’indique d’afficher la grille entière :
def affichage_grille(): rect(75, 42, 175, 150, (255,255,255)) pos_x, pos_y_base, marge = 50, 42, 25 for i in range(7): pos_x += marge pos_y = pos_y_base for y in range(6): cote = 20 if grille[y][i] == 1: color = rouge elif grille[y][i] == 2: color = jaune else : color = gris if grille[y][i] == 1 or grille[y][i] == 2: rect(pos_x, pos_y, cote, cote, gris) draw_cercle(pos_x + 7, pos_y + 2,color) else: rect(pos_x, pos_y, cote, cote, gris) pos_y += marge
La fonction efface une possible grille avec un rectange blanc de la même taille que la grille puis réaffiche la grille
Voici ce que produit la fonction :
Ensuite il y a affichage_preview qui va afficher le preview du jeton au bon endroit :
def affichage_preview(col_preview): rect(75, 17, 170, 20, (255,255,255)) #Efface l'ancien preview if player == 1 : #Choisi la bonne couleur color = rouge else : color = jaune draw_cercle(75 + (col_preview * 25) + 7, 17+2, color) #Fait le preview au bon endroit
Il y a la fonction wait qui retourne la touche sur laquelle vous appuyez :
def wait(buttons=(0,1,2,3,4,52)): #Retourne la touche appuyée while True: for i in buttons: if keydown(i): while keydown(i): True return i
Et enfin les deux dernières fonctions qui sont liées : les fonctions du score, il y a la fonction affichage_src qui est appelé au début du jeu afin d’afficher le score :
#Score par Thomas S. mais code par Robin C. def affichage_src(): txt("J-1", 22, 42) draw_cercle(35,70,rouge) txt("Score", 12, 117) if points_rouge < 10 : largeur = 32 else : largeur = 27 txt(str(points_rouge),32,142) txt("J-2", 267, 42) draw_cercle(280,70,jaune) txt("Score", 257, 117) if points_jaune < 10 : largeur = 277 else : largeur = 272 txt(str(points_jaune),277,142) def actu_src(): #Actualise le score txt(str(points_rouge),37-5*len(str(points_rouge)),142) txt(str(points_jaune),282-5*len(str(points_jaune)),142)
Et il y a aussi la fonction actu_src qui va à chaque fin de partie, actualiser le score en écrasant l’ancien score.
Voici ce que produit la fonction affichage_src() seule :
Et pour finir il y a le lancement du jeu à la fin avec juste avant le code qui permet d’afficher le lien en bas de l’écran :
#Lien article rect(0,200,320,22,(148,113,222)) txt("Code by nsi.xyz/puissance4",33,202,(242,)*3,(148,113,222)) #lancement du jeu affichage_src() selection()
Images du jeu
Conclusion
Pour conclure le tout, ce projet a été très plaisant à faire et très instructif. Il faut savoir que faire un jeu sur la NumWorks comme le mien demande du travail notamment sur la prise en main de la partie graphique qui n’a pas été sans problèmes pour moi mais avec les entrainements de M.Robert sur twitter la prise en main fut plus simple.
Lien du jeu
Il existe deux liens pour le jeu, sachez que seul le premier garanti de disposer de la dernière version et que le deuxième est un lien alternatif qui peut être dépassé.
Étudiant en classe de terminale générale au lycée Louis Pasteur à Avignon (2023-2024).
En train de se balader dans le nether.