Projets
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 grilleEnsuite 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 nulAprè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 partieIl 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 ligneUne 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 += margeLa 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 endroitIl 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 iEt 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é.
Ancien étudiant au lycée Louis Pasteur à Avignon (2021-2024).
Actuellement en deuxième année de BUT Informatique à l’IUT de Montpellier