Présentation⚓︎
Citation
"Pyxel est un moteur de jeu vidéo rétro pour Python. seulement 16 couleurs peuvent être affichées et que seulement 4 sons peuvent être lus en même temps, vous pouvez vous sentir libre de créer des jeux vidéo dans le style pixel art. Les spécifications et les API de Pyxel sont inspirées de PICO-8 et TIC-80. Pyxel est un logiciel libre et open source."
Extrait de github.com/kitao/pyxel
En complément des exercices proposés dans ce tutoriel, deux références sur Pyxel :
- Le dépôt Github avec l'API et des informations sur l'installation, l'utilisation ...
- Les tutoriels conçus par Laurent Abbal pour l'événement Nuit du code
Pour utiliser pyxel, merci à Laurent Abbal de proposer les outils suivants :
- installer sur une clef USB la distribution portable Edupyter qui contient le module pyxel
- tester son code en ligne sur https://www.pyxelstudio.net/ (expérimental)
Premiers pas avec pyxel
⚓︎
On veut écrire un programme qui affiche dans une fenêtre de dimensions 50 x 50
avec :
- au lancement de l'application, un menu d'accueil composé de trois textes et quatre images insérés dans les coins ;
- si on appuie sur la touche
S
un écran apparaît avec un damier dont les cases changent de couleur chaque seconde ; - si on appuie sur la touche
Q
un écran de fin apparaît composé de trois textes et quatre images insérés dans les coins.
Exercice 1
Pour commencer, téléchargez l'archive materiel.zip et ouvrez le fichier pyxel_decouverte.py
dans le dossier premiers_pas
. Complétez le squelette de code donné ci-dessous dans un éditeur Python pour satisfaire le cahier des charges fixé précédemment. On s'appuiera sur la documentation de Pyxel et le tutoriel de Laurent Abbal, en particulier pour l'utilisation de l'éditeur d'images. Les images sont dans le fichier scripts/game_of_life.pyxres qui doit être placé dans le même répertoire que le script et qui peut être édité depuis une ligne de commandes avec pyxel edit game_of_life.pyxres
.
"""
Découverte du module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
import pyxel
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 50
HAUTEUR = 50
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}
# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="découverte de pyxel")
# =========================================================
# == CHARGEMENT DES IMAGES
# =========================================================
pyxel.load("game_of_life.pyxres")
# =========================================================
# == FONCTIONS
# =========================================================
def initialiser_grille():
"""Initialise une grille de dimensions LARGEUR x HAUTEUR avec :
grille[lig][col] = MORT si lig et col de même parité
sinon grille[lig][col] = VIE
"""
# à compléter
def inverser_etat(etat):
"""Inverse l'état d'une case dans une grille : MORT- > VIE et VIE -> MORT"""
# à compléter
def evolution_grille(grille):
"""Mise à jour de la grille en inversant l'éat de chaque case"""
return grille
# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================
def afficher_menu():
"""Affichage du menu d'accueil"""
pyxel.text(
int(LARGEUR * 0.25), int(HAUTEUR * 0.25), "Hello", COULEUR[TEXTE]
)
# à compléter pour afficher les 2 autres textes
# positionnement d'images 3 x 3 dans les 4 coins
pyxel.blt(0, 0, 0, 0, 0, 3, 3)
# à compléter pour afficher les 3 autres images
def afficher_fin():
"""Affichage de fin"""
pyxel.text(int(LARGEUR * 0.4), int(HAUTEUR * 0.5), "Fin", COULEUR[TEXTE])
# positionnement d'images 8 x 8 dans les 4 coins
# à compléter pour afficher les 4 images
# =========================================================
# == UPDATE
# =========================================================
def update():
"""mise à jour des variables (30 fois par seconde)"""
# une génération par seconde
if pyxel.btn(pyxel.KEY_S):
jeu["menu"] = False
# à compléter pour traiter l'appui sur la touche Q
if pyxel.frame_count % 30 == 0:
# mise à jour de la grille
jeu["grille"] = evolution_grille(jeu["grille"])
# =========================================================
# == DRAW
# =========================================================
def draw():
"""création des objets (30 fois par seconde)"""
# vide la fenetre
pyxel.cls(COULEUR[FONDS])
if jeu["fin"]:
afficher_fin()
elif jeu["menu"]:
afficher_menu()
# à compléter avec le dessin du damier
# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
"grille": initialiser_grille(),
"menu": True,
"fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
# lancement de l'application
pyxel.run(update, draw)
"""
Découverte du module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
import pyxel
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 50
HAUTEUR = 50
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}
# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="découverte de pyxel")
# =========================================================
# == CHARGEMENT DES IMAGES
# =========================================================
pyxel.load("game_of_life.pyxres")
# =========================================================
# == FONCTIONS
# =========================================================
def initialiser_grille():
"""Initialise une grille de dimensions LARGEUR x HAUTEUR avec :
grille[lig][col] = MORT si lig et col de même parité
sinon grille[lig][col] = VIE
"""
# à compléter
# BEGIN CUT
grille = [[MORT for col in range(LARGEUR)] for lig in range(HAUTEUR)]
for lig in range(HAUTEUR):
for col in range(LARGEUR):
if (lig + col) % 2 == 0:
grille[lig][col] = VIE
return grille
# END CUT
def inverser_etat(etat):
"""Inverse l'état d'une case dans une grille : MORT- > VIE et VIE -> MORT"""
# à compléter
# BEGIN CUT
if etat == VIE:
return MORT
else:
return VIE
# END CUT
def evolution_grille(grille):
"""Mise à jour de la grille en inversant l'éat de chaque case"""
# BEGIN CUT
for lig in range(HAUTEUR):
for col in range(LARGEUR):
grille[lig][col] = inverser_etat(grille[lig][col])
# END CUT
return grille
# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================
def afficher_menu():
"""Affichage du menu d'accueil"""
pyxel.text(
int(LARGEUR * 0.25), int(HAUTEUR * 0.25), "Hello", COULEUR[TEXTE]
)
# à compléter pour afficher les 2 autres textes
# BEGIN CUT
pyxel.text(
int(LARGEUR * 0.05), int(HAUTEUR * 0.5), "S -> start", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.05), int(HAUTEUR * 0.75), "Q -> quit", COULEUR[TEXTE]
)
# END CUT
# positionnement d'images 3 x 3 dans les 4 coins
pyxel.blt(0, 0, 0, 0, 0, 3, 3)
# à compléter pour afficher les 3 autres images
# BEGIN CUT
pyxel.blt(HAUTEUR - 3, 0, 0, 8, 0, 3, 3)
pyxel.blt(0, LARGEUR - 3, 0, 0, 5, 3, 3)
pyxel.blt(LARGEUR - 3, HAUTEUR - 3, 0, 8, 5, 3, 3)
# END CUT
def afficher_fin():
"""Affichage de fin"""
pyxel.text(int(LARGEUR * 0.4), int(HAUTEUR * 0.5), "Fin", COULEUR[TEXTE])
# positionnement d'images 8 x 8 dans les 4 coins
# à compléter pour afficher les 4 images
# BEGIN CUT
pyxel.blt(0, 0, 0, 0, 8, 8, 8)
pyxel.blt(HAUTEUR - 8, 0, 0, 0, 8, 8, 8)
pyxel.blt(0, LARGEUR - 8, 0, 0, 8, 8, 8)
pyxel.blt(LARGEUR - 8, HAUTEUR - 8, 0, 0, 8, 8, 8)
# END CUT
# =========================================================
# == UPDATE
# =========================================================
def update():
"""mise à jour des variables (30 fois par seconde)"""
# une génération par seconde
if pyxel.btn(pyxel.KEY_S):
jeu["menu"] = False
# à compléter pour traiter l'appui sur la touche Q
# BEGIN CUT
if pyxel.btn(pyxel.KEY_Q):
jeu["fin"] = True
# END CUT
if pyxel.frame_count % 30 == 0:
# mise à jour de la grille
jeu["grille"] = evolution_grille(jeu["grille"])
# =========================================================
# == DRAW
# =========================================================
def draw():
"""création des objets (30 fois par seconde)"""
# vide la fenetre
pyxel.cls(COULEUR[FONDS])
if jeu["fin"]:
afficher_fin()
elif jeu["menu"]:
afficher_menu()
# à compléter avec le dessin du damier
# BEGIN CUT
else:
# dessin de la grille
for lig in range(HAUTEUR):
for col in range(LARGEUR):
pyxel.rect(col, lig, 1, 1, COULEUR[jeu["grille"][lig][col]])
# END CUT
# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
"grille": initialiser_grille(),
"menu": True,
"fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
# lancement de l'application
pyxel.run(update, draw)
Video
Jeu de la vie⚓︎
Présentation⚓︎
Sources :
- Un site de Sébastien Hoareau maître de conférence en informatique à l'université de la Réunion : https://sebhoa.gitlab.io/lupy/03_autre_puzzle/JeuDeLaVie/game_life/
- L'article Wikipedia : https://fr.wikipedia.org/wiki/Jeu_de_la_vie
Le jeu de la vie inventé par le mathématicien anglais J. H. Conway en 1970, est un automate cellulaire en deux dimensions.
Les automates cellulaires et le jeu de la vie en particulier sont des systèmes complexes étudiés en mathématiques et en informatique théorique. Ainsi, l'automate de Conway a été prouvé turing-complet c'est-à-dire qu'il peut exécuter les mêmes algorithmes qu'un ordinateur.
L'univers du jeu est une grille infinie de cases appelées cellules.
Au départ un nombre fini de cellules sont vivantes et toutes les autres sont mortes.
Chaque cellule possède un voisinage de 8 cellules voisines (sauf les cellules sur les bords dans notre simulation).
Info
A chaque génération, l'univers évolue selon trois règles simples de changement d'état pour chaque cellule :
- Règle 1 : Une cellule vivante qui n'a pas au moins 2 voisines vivantes meurt par isolement.
- Règle 2 : Une cellule vivante qui possède 4 voisines vivantes ou plus meurt par étouffement.
- Règle 3 : Une cellule morte qui possède exactement trois cellules vivantes, devient vivante, sinon elle reste morte.
Programmation avec pyxel
étape 1⚓︎
Vous allez programmer le jeu de la vie en quelques étapes.
Exercice 2
- Si vous avez sauté l'étape précédente, téléchargez l'archive materiel.zip.
- Oepuis le dossier
etape1
, éditez le scriptgame_of_life_outils.py
.
Les cellules en nombre fini seront enregistrées dans une grille de dimensions fixées par les variables globales LARGEUR
et HAUTEUR
.
Dans l'exercice suivant, vous allez écrire des fonctions outils de manipulation de la grille. Copiez puis complétez le squelette fourni dans game_of_life1_outils.py
ouvert avec votre IDE Python.
Exercice 3
"""
Jeu de la vie avec le module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 80
HAUTEUR = 80
VIE = 1
MORT = 0
# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
"""Renvoie une copie profonde de la grille"""
return [[grille[lig][col] for col in range(LARGEUR)] for lig in range(HAUTEUR)]
def test_copie_grille():
import random
t = [[random.randint(1, 100) for _ in range(LARGEUR)] for _ in range(HAUTEUR)]
t2 = copie_grille(t)
assert (t2 == t) and (id(t2) != id(t))
print("Tests réussis pour copie_grille")
def initialiser_grille(grille, motif):
"""
Affecte l'état VIE à toutes les cases de grille
listées dans motif comme couples (lig, col)
"""
def test_initialiser_grille():
grille = [[MORT for _ in range(LARGEUR)] for __ in range(HAUTEUR)]
motif = [(random.randint(0, 79), random.randint(0, 79)) for _ in range(50)]
initialiser_grille(grille, motif)
for lig in range(HAUTEUR):
for col in range(LARGEUR):
assert (((lig, col) in motif) and grille[lig][col] == VIE) or grille[lig][col] == MORT
print("Tests réussis pour initialiser_grille")
def nombre_voisins_vivants(grille, lig, col):
"""Renvoie le nombre de voisins vivants de la cellule en (lig, col)"""
def test_nombre_voisins_vivants():
grille1 = [[VIE, MORT, VIE], [VIE, VIE, MORT], [MORT, MORT, VIE]]
assert nombre_voisins_vivants(grille1, 1, 1) == 4
grille2 = [[VIE, MORT, VIE], [VIE, MORT, MORT], [MORT, MORT, MORT]]
assert nombre_voisins_vivants(grille2, 1, 1) == 3
grille3 = [[MORT, MORT, MORT], [VIE, VIE, VIE], [MORT, MORT, MORT]]
assert nombre_voisins_vivants(grille3, 1, 1) == 3
print("Tests réussis pour nombre_voisins_vivants")
def evolution_cellule(grille, lig, col):
"""
Renvoie le nouvel état (VIE ou MORT)de la cellule en (lig, col)
dans grille en fonction de son nombre de voisins vivants
"""
def test_evolution_cellule():
grille1 = [[VIE, MORT, VIE], [VIE, VIE, MORT], [MORT, MORT, VIE]]
assert nombre_voisins_vivants(grille1, 1, 1) == MORT
grille2 = [[VIE, MORT, VIE], [VIE, MORT, MORT], [MORT, MORT, VIE]]
assert nombre_voisins_vivants(grille2, 1, 1) == VIE
grille3 = [[MORT, MORT, MORT], [VIE, VIE, VIE], [MORT, MORT, MORT]]
assert nombre_voisins_vivants(grille3, 1, 1) == MORT
grille4 = [[MORT, MORT, VIE], [VIE, VIE, VIE], [MORT, MORT, MORT]]
assert nombre_voisins_vivants(grille3, 1, 1) == VIE
grille5 = [[MORT, MORT, VIE], [VIE, VIE, MORT], [MORT, MORT, MORT]]
assert nombre_voisins_vivants(grille3, 1, 1) == MORT
print("Tests réussis pour nombre_evolution_cellule")
def evolution_grille(grille):
"""
Crée une copie profonde de grille (génération n)
Remplit cette copie avec l'évolution de chaque cellule de grille
Renvoie cette nouvelle grille (génération n + 1)
"""
# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
"""Renvoie une copie profonde de la grille"""
# à compléter
# BEGIN CUT
return [[grille[lig][col] for col in range(LARGEUR)] for lig in range(HAUTEUR)]
# END CUT
def initialiser_grille(grille, motif):
"""
Affecte l'état VIE à toutes les cases de grille
listées dans motif comme couples (lig, col)
"""
# à compléter
# BEGIN CUT
for (lig, col) in motif:
grille[lig][col] = VIE
# END CUT
def nombre_voisins_vivants(grille, lig, col):
"""Renvoie le nombre de voisins vivants de la cellule en (lig, col)"""
# à compléter
# BEGIN CUT
v = 0
for dl in range(-1, 2):
for dc in range(-1, 2):
if (
(0 <= lig + dl < HAUTEUR)
and (0 <= col + dc < LARGEUR)
and (dl, dc) != (0, 0)
and grille[lig + dl][col + dc] == VIE
):
v = v + 1
return v
# END CUT
def evolution_cellule(grille, lig, col):
"""
Renvoie le nouvel état (VIE ou MORT)de la cellule en (lig, col)
dans grille en fonction de son nombre de voisins vivants
"""
# à compléter
# BEGIN CUT
etat = grille[lig][col]
voisins_vivants = nombre_voisins_vivants(grille, lig, col)
if etat == MORT:
if voisins_vivants == 3:
return VIE
else:
return MORT
else:
if 2 <= voisins_vivants <= 3:
return VIE
else:
return MORT
# END CUT
def evolution_grille(grille):
"""
Crée une copie profonde de grille (génération n)
Remplit cette copie avec l'évolution de chaque cellule de grille
Renvoie cette nouvelle grille (génération n + 1)
"""
# à compléter
# BEGIN CUT
grille_nouvelle = copie_grille(grille)
for lig in range(HAUTEUR):
for col in range(LARGEUR):
grille_nouvelle[lig][col] = evolution_cellule(grille, lig, col)
return grille_nouvelle
# END CUT
Programmation avec pyxel
étape 2⚓︎
On considère que l'archive materiel.zip a été déballée.
Complétez les fonctions d'affichage dans game_of_life1_eleves.py
(du dossier etape1
) pour obtenir la simulation de l'automate nommé planeur (glider en anglais).
Copiez et testez le code ouvert avec votre IDE Python.
Info
La configuration initiale de l'automate est enregistrée dans un fichier
texte au format Plain décrit dans https://conwaylife.com/wiki/Plaintext.
Elle est chargée par le programme dans un tableau de tableaux à l'aide de la fonction charger_fichier
.
Deux automates sont livrés dans materiel.zip :
glider.cells
period60glidergun.cells
Testez le code dans un environnement de programmation Python où le module pyxel est installé.
On donne ci-dessous l'affichage avant et après que le tout le code manquant soit complété.
Avant / Après
Exercice 4
"""
Jeu de la vie avec le module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
import pyxel
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 80
HAUTEUR = 80
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}
# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="jeu de la vie")
# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
"""Renvoie une copie profonde de la grille"""
# à compléter
def initialiser_grille(grille, motif):
"""
Affecte l'état VIE à toutes les cases de grille
listées dans motif comme couples (lig, col)
"""
# à compléter
def nombre_voisins_vivants(gille, lig, col):
"""Renvoie le nombre de voisins vivants de la cellule en (lig, col)"""
# à compléter
def evolution_cellule(grille, lig, col):
"""
Renvoie le nouvel état (VIE ou MORT)de la cellule en (lig, col)
dans grille en fonction de son nombre de voisins vivants
"""
# à compléter
def evolution_grille(grille):
"""
Crée une copie profonde de grille (génération n)
Remplit cette copie avec l'évolution de chaque cellule de grille
Renvoie cette nouvelle grille (génération n + 1)
"""
# à compléter
# =========================================================
# == INITIALISER LE MOTIF
# =========================================================
def charger_fichier(chemin):
"""
Charger un motif stocké dans un fichier au format Plain
https://conwaylife.com/wiki/Plaintext
"""
f = open(chemin)
ligne1 = f.readline().rstrip()
titre = " ".join(ligne1.split()[1:])
motif = []
for ligne in f:
if ligne[0] != "!":
motif.append([c for c in ligne.rstrip()])
return titre, motif
def motif_dans_grille(lig0, col0, motif, grille):
"""
Recopie un motif (sous-grille) dans grille à partir de
la position (lig0, col0) en coin supérieur gauche
"""
decodage = {".": MORT, "O": VIE}
largeur_motif = len(motif[0])
hauteur_motif = len(motif)
assert lig0 + hauteur_motif <= len(grille) and col0 + largeur_motif <= len(
grille[0]
), "Motif trop grand"
for lig in range(lig0, lig0 + hauteur_motif):
for col in range(col0, col0 + largeur_motif):
grille[lig][col] = decodage[motif[lig - lig0][col - col0]]
# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================
def afficher_menu():
"""Affichage du menu d'accueil"""
pyxel.text(
int(LARGEUR * 0.25), int(HAUTEUR * 0.25), f"{jeu['nom']}", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.5), "Press s to start", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.6), "Press q to quit", COULEUR[TEXTE]
)
# Positionnement d'une image dans chaque coin
# à compléter
def afficher_fin():
"""Affichage de fin"""
pyxel.text(int(LARGEUR * 0.25), int(HAUTEUR * 0.5), "Fin de la vie", COULEUR[TEXTE])
# Positionnement d'une image dans chaque coin
# à compléter
# =========================================================
# == UPDATE
# =========================================================
def update():
"""mise à jour des variables (30 fois par seconde)"""
# une génération par seconde
if pyxel.btn(pyxel.KEY_S):
jeu["menu"] = False
if pyxel.btn(pyxel.KEY_Q):
jeu["fin"] = True
# mise à jour de la grille (3 fois par seconde)
if pyxel.frame_count % 10 == 0:
# à compléter
"ecrire votre code ici"
# =========================================================
# == DRAW
# =========================================================
def draw():
"""création des objets (30 fois par seconde)"""
# vide la fenetre
pyxel.cls(COULEUR[FONDS])
if jeu["fin"]:
afficher_fin()
elif jeu["menu"]:
afficher_menu()
else:
# dessin de la grille à compléter
# dessinez chaque case avec pyxel.rect
# un exemple avec 4 cases
pyxel.rect(0, 0, 1, 1, COULEUR[MORT])
pyxel.rect(LARGEUR // 2, HAUTEUR // 2, 1, 1, COULEUR[VIE])
pyxel.rect(0, HAUTEUR - 1, 1, 1, COULEUR[VIE])
pyxel.rect(LARGEUR - 1, HAUTEUR - 1, 1, 1, COULEUR[MORT])
# à compléter pour tracer la grille complète
# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
"grille": [[MORT for __ in range(LARGEUR)] for _ in range(HAUTEUR)],
"menu": True,
"fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
# on charge l'automate enregistré dans le fichier "glider.cells" au format Plain
# voir https://conwaylife.com/wiki/Plaintext
nom, motif = charger_fichier("glider.cells")
jeu["nom"] = nom
motif_dans_grille(0, 0, motif, jeu["grille"])
# lancement de l'application
pyxel.run(update, draw)
"""
Jeu de la vie avec le module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
import pyxel
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 80
HAUTEUR = 80
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}
# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="jeu de la vie")
# BEGIN CUT
# =========================================================
# == CHARGEMENT DES IMAGES
# =========================================================
pyxel.load("game_of_life.pyxres")
# END CUT
# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
"""Renvoie une copie profonde de la grille"""
# à compléter
# BEGIN CUT
return [[grille[lig][col] for col in range(LARGEUR)] for lig in range(HAUTEUR)]
# END CUT
def initialiser_grille(grille, motif):
"""
Affecte l'état VIE à toutes les cases de grille
listées dans motif comme couples (lig, col)
"""
# à compléter
# BEGIN CUT
for (lig, col) in motif:
grille[lig][col] = VIE
# END CUT
def nombre_voisins_vivants(grille, lig, col):
"""Renvoie le nombre de voisins vivants de la cellule en (lig, col)"""
# à compléter
# BEGIN CUT
v = 0
for dl in range(-1, 2):
for dc in range(-1, 2):
if (
(0 <= lig + dl < HAUTEUR)
and (0 <= col + dc < LARGEUR)
and (dl, dc) != (0, 0)
and grille[lig + dl][col + dc] == VIE
):
v = v + 1
return v
# END CUT
def evolution_cellule(grille, lig, col):
"""
Renvoie le nouvel état (VIE ou MORT)de la cellule en (lig, col)
dans grille en fonction de son nombre de voisins vivants
"""
# à compléter
# BEGIN CUT
etat = grille[lig][col]
voisins_vivants = nombre_voisins_vivants(grille, lig, col)
if etat == MORT:
if voisins_vivants == 3:
return VIE
else:
return MORT
else:
if 2 <= voisins_vivants <= 3:
return VIE
else:
return MORT
# END CUT
def evolution_grille(grille):
"""
Crée une copie profonde de grille (génération n)
Remplit cette copie avec l'évolution de chaque cellule de grille
Renvoie cette nouvelle grille (génération n + 1)
"""
# à compléter
# BEGIN CUT
grille_nouvelle = copie_grille(grille)
for lig in range(HAUTEUR):
for col in range(LARGEUR):
grille_nouvelle[lig][col] = evolution_cellule(grille, lig, col)
return grille_nouvelle
# END CUT
# =========================================================
# == INITIALISER LE MOTIF
# =========================================================
def charger_fichier(chemin):
"""
Charger un motif stocké dans un fichier au format Plain
https://conwaylife.com/wiki/Plaintext
"""
f = open(chemin)
ligne1 = f.readline().rstrip()
titre = " ".join(ligne1.split()[1:])
motif = []
for ligne in f:
if ligne[0] != "!":
motif.append([c for c in ligne.rstrip()])
return titre, motif
def motif_dans_grille(lig0, col0, motif, grille):
"""
Recopie un motif (sous-grille) dans grille à partir de
la position (lig0, col0) en coin supérieur gauche
"""
decodage = {".": MORT, "O": VIE}
largeur_motif = len(motif[0])
hauteur_motif = len(motif)
assert lig0 + hauteur_motif <= len(grille) and col0 + largeur_motif <= len(
grille[0]
), "Motif trop grand"
for lig in range(lig0, lig0 + hauteur_motif):
for col in range(col0, col0 + largeur_motif):
grille[lig][col] = decodage[motif[lig - lig0][col - col0]]
# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================
def afficher_menu():
"""Affichage du menu d'accueil"""
pyxel.text(
int(LARGEUR * 0.25), int(HAUTEUR * 0.25), f"{jeu['nom']}", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.5), "Press s to start", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.6), "Press q to quit", COULEUR[TEXTE]
)
def afficher_fin():
"""Affichage de fin"""
pyxel.text(int(LARGEUR * 0.25), int(HAUTEUR * 0.5), "Fin de la vie", COULEUR[TEXTE])
# =========================================================
# == UPDATE
# =========================================================
def update():
"""mise à jour des variables (30 fois par seconde)"""
# une génération par seconde
if pyxel.btn(pyxel.KEY_S):
jeu["menu"] = False
if pyxel.btn(pyxel.KEY_Q):
jeu["fin"] = True
# mise à jour de la grille (3 fois par seconde)
if pyxel.frame_count % 10 == 0:
# à compléter
"ecrire votre code ici"
# BEGIN CUT
jeu["grille"] = evolution_grille(jeu["grille"])
# END CUT
# =========================================================
# == DRAW
# =========================================================
def draw():
"""création des objets (30 fois par seconde)"""
# vide la fenetre
pyxel.cls(COULEUR[FONDS])
if jeu["fin"]:
afficher_fin()
elif jeu["menu"]:
afficher_menu()
else:
# dessin de la grille à compléter
# dessinez chaque case avec pyxel.rect
# un exemple avec 4 cases
pyxel.rect(0, 0, 1, 1, COULEUR[MORT])
pyxel.rect(LARGEUR // 2, HAUTEUR // 2, 1, 1, COULEUR[VIE])
pyxel.rect(0, HAUTEUR - 1, 1, 1, COULEUR[VIE])
pyxel.rect(LARGEUR - 1, HAUTEUR - 1, 1, 1, COULEUR[MORT])
# à compléter pour tracer la grille complète
# BEGIN CUT
for lig in range(HAUTEUR):
for col in range(LARGEUR):
pyxel.rect(col, lig, 1, 1, COULEUR[jeu["grille"][lig][col]])
# END CUT
# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
"grille": [[MORT for __ in range(LARGEUR)] for _ in range(HAUTEUR)],
"menu": True,
"fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
# on charge l'automate enregistré dans le fichier "glider.cells" au format Plain
# voir https://conwaylife.com/wiki/Plaintext
nom, motif = charger_fichier("glider.cells")
jeu["nom"] = nom
motif_dans_grille(0, 0, motif, jeu["grille"])
# lancement de l'application
pyxel.run(update, draw)
Programmation avec pyxel
étape 3⚓︎
Dans l'archive materiel.zip se trouve un fichier game_of_life.pyxres
. Éditez ce fichier depuis une console avec l'éditeur d'image de pyxel et la commande pyxel edit game_of_life.pyxres
.
Info
Voir https://nuitducode.github.io/DOCUMENTATION/PYTHON/08-tutoriel-06/ ou la documentation pour l'utilisation de l'éditeur assez intuitive.
On charge les images depuis le fichier game_of_life.pyxres
avec pyxel.load("game_of_life.pyxres")
.
pyxel.blt(0, 0, 0, 0, 0, 3, 3)
permet de placer en (0,0)
la partie de l'image 0
contenue dans game_of_life.pyxres
qui se trouve en (0,0)
et de dimensions (3,3)
.
On veut insérer des images dans les quatre coins de la page de menu d'accueil et de fin.
On donne ci-dessous l'affichage avant et après que le tout le code manquant soit complété.
Avant / Après
On considère que l'archive materiel.zip a été déballée.
Recopiez et complétez le squelette de code dans le fichier game_of_life2_eleves.py
(du dossier etape2
) ouvert avec votre IDE Python.
Exercice 5
"""
Jeu de la vie avec le module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
import pyxel
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 80
HAUTEUR = 80
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}
# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="jeu de la vie")
# =========================================================
# == CHARGEMENT DES IMAGES
# =========================================================
pyxel.load("game_of_life.pyxres")
# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
"""Renvoie une copie profonde de la grille"""
return [[grille[lig][col] for col in range(LARGEUR)] for lig in range(HAUTEUR)]
def initialiser_grille(grille, motif):
"""
Affecte l'état VIE à toutes les cases de grille
listées dans motif comme couples (lig, col)
"""
for (lig, col) in motif:
grille[lig][col] = VIE
def nombre_voisins_vivants(grille, lig, col):
"""Renvoie le nombre de voisins vivants de la cellule en (lig, col)"""
v = 0
for dl in range(-1, 2):
for dc in range(-1, 2):
if (
(0 <= lig + dl < HAUTEUR)
and (0 <= col + dc < LARGEUR)
and (dl, dc) != (0, 0)
and grille[lig + dl][col + dc] == VIE
):
v = v + 1
return v
def evolution_cellule(grille, lig, col):
"""
Renvoie le nouvel état (VIE ou MORT)de la cellule en (lig, col)
dans grille en fonction de son nombre de voisins vivants
"""
etat = grille[lig][col]
voisins_vivants = nombre_voisins_vivants(grille, lig, col)
if etat == MORT:
if voisins_vivants == 3:
return VIE
else:
return MORT
else:
if 2 <= voisins_vivants <= 3:
return VIE
else:
return MORT
def evolution_grille(grille):
"""
Crée une copie profonde de grille (génération n)
Remplit cette copie avec l'évolution de chaque cellule de grille
Renvoie cette nouvelle grille (génération n + 1)
"""
grille_nouvelle = copie_grille(grille)
for lig in range(HAUTEUR):
for col in range(LARGEUR):
grille_nouvelle[lig][col] = evolution_cellule(grille, lig, col)
return grille_nouvelle
# =========================================================
# == INITIALISER LE MOTIF
# =========================================================
def charger_fichier(chemin):
"""
Charger un motif stocké dans un fichier au format Plain
https://conwaylife.com/wiki/Plaintext
"""
f = open(chemin)
ligne1 = f.readline().rstrip()
titre = " ".join(ligne1.split()[1:])
motif = []
for ligne in f:
if ligne[0] != "!":
motif.append([c for c in ligne.rstrip()])
return titre, motif
def motif_dans_grille(lig0, col0, motif, grille):
"""
Recopie un motif (sous-grille) dans grille à partir de
la position (lig0, col0) en coin supérieur gauche
"""
decodage = {".": MORT, "O": VIE}
largeur_motif = len(motif[0])
hauteur_motif = len(motif)
assert lig0 + hauteur_motif <= len(grille) and col0 + largeur_motif <= len(
grille[0]
), "Motif trop grand"
for lig in range(lig0, lig0 + hauteur_motif):
for col in range(col0, col0 + largeur_motif):
grille[lig][col] = decodage[motif[lig - lig0][col - col0]]
# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================
def afficher_menu():
"""Affichage du menu d'accueil"""
pyxel.text(
int(LARGEUR * 0.25), int(HAUTEUR * 0.25), f"{jeu['nom']}", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.5), "Press s to start", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.6), "Press q to quit", COULEUR[TEXTE]
)
# Positionnement d'une image dans chaque coin
# image dans le coin supérieur gauche
pyxel.blt(0, 0, 0, 0, 0, 3, 3)
# à compléter
def afficher_fin():
"""Affichage de fin"""
pyxel.text(int(LARGEUR * 0.25), int(HAUTEUR * 0.5), "Fin de la vie", COULEUR[TEXTE])
# Positionnement d'une image dans chaque coin
# à compléter
# =========================================================
# == UPDATE
# =========================================================
def update():
"""mise à jour des variables (30 fois par seconde)"""
# une génération par seconde
if pyxel.btn(pyxel.KEY_S):
jeu["menu"] = False
if pyxel.btn(pyxel.KEY_Q):
jeu["fin"] = True
# mise à jour de la grille (3 fois par seconde)
if pyxel.frame_count % 10 == 0:
jeu["grille"] = evolution_grille(jeu["grille"])
# =========================================================
# == DRAW
# =========================================================
def draw():
"""création des objets (30 fois par seconde)"""
# vide la fenetre
pyxel.cls(COULEUR[FONDS])
if jeu["fin"]:
afficher_fin()
elif jeu["menu"]:
afficher_menu()
else:
# dessin de la grille à compléter
# dessinez chaque case avec pyxel.rect
# un exemple avec 4 cases
pyxel.rect(0, 0, 1, 1, COULEUR[MORT])
pyxel.rect(LARGEUR // 2, HAUTEUR // 2, 1, 1, COULEUR[VIE])
pyxel.rect(0, HAUTEUR - 1, 1, 1, COULEUR[VIE])
pyxel.rect(LARGEUR - 1, HAUTEUR - 1, 1, 1, COULEUR[MORT])
for lig in range(HAUTEUR):
for col in range(LARGEUR):
pyxel.rect(col, lig, 1, 1, COULEUR[jeu["grille"][lig][col]])
# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
"grille": [[MORT for __ in range(LARGEUR)] for _ in range(HAUTEUR)],
"menu": True,
"fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
# on charge l'automate enregistré dans le fichier "glider.cells" au format Plain
# voir https://conwaylife.com/wiki/Plaintext
nom, motif = charger_fichier("period60glidergun.cells")
jeu["nom"] = nom
motif_dans_grille(0, 0, motif, jeu["grille"])
# lancement de l'application
pyxel.run(update, draw)
"""
Jeu de la vie avec le module pyxel
https://github.com/kitao/pyxel/blob/main/doc/README.fr.md
"""
import pyxel
# =========================================================
# == CONSTANTES
# =========================================================
LARGEUR = 80
HAUTEUR = 80
VIE = 1
MORT = 0
TEXTE = 2
FONDS = 3
COULEUR = {VIE: 8, MORT: 0, TEXTE: 5, FONDS: 10}
# =========================================================
# == INITIALISATION FENETRE
# =========================================================
pyxel.init(LARGEUR, HAUTEUR, title="jeu de la vie")
# =========================================================
# == CHARGEMENT DES IMAGES
# =========================================================
pyxel.load("game_of_life.pyxres")
# =========================================================
# == FONCTIONS DE GRILLE
# =========================================================
def copie_grille(grille):
"""Renvoie une copie profonde de la grille"""
return [[grille[lig][col] for col in range(LARGEUR)] for lig in range(HAUTEUR)]
def initialiser_grille(grille, motif):
"""
Affecte l'état VIE à toutes les cases de grille
listées dans motif comme couples (lig, col)
"""
for (lig, col) in motif:
grille[lig][col] = VIE
def nombre_voisins_vivants(grille, lig, col):
"""Renvoie le nombre de voisins vivants de la cellule en (lig, col)"""
v = 0
for dl in range(-1, 2):
for dc in range(-1, 2):
if (
(0 <= lig + dl < HAUTEUR)
and (0 <= col + dc < LARGEUR)
and (dl, dc) != (0, 0)
and grille[lig + dl][col + dc] == VIE
):
v = v + 1
return v
def evolution_cellule(grille, lig, col):
"""
Renvoie le nouvel état (VIE ou MORT)de la cellule en (lig, col)
dans grille en fonction de son nombre de voisins vivants
"""
etat = grille[lig][col]
voisins_vivants = nombre_voisins_vivants(grille, lig, col)
if etat == MORT:
if voisins_vivants == 3:
return VIE
else:
return MORT
else:
if 2 <= voisins_vivants <= 3:
return VIE
else:
return MORT
def evolution_grille(grille):
"""
Crée une copie profonde de grille (génération n)
Remplit cette copie avec l'évolution de chaque cellule de grille
Renvoie cette nouvelle grille (génération n + 1)
"""
grille_nouvelle = copie_grille(grille)
for lig in range(HAUTEUR):
for col in range(LARGEUR):
grille_nouvelle[lig][col] = evolution_cellule(grille, lig, col)
return grille_nouvelle
# =========================================================
# == INITIALISER LE MOTIF
# =========================================================
def charger_fichier(chemin):
"""
Charger un motif stocké dans un fichier au format Plain
https://conwaylife.com/wiki/Plaintext
"""
f = open(chemin)
ligne1 = f.readline().rstrip()
titre = " ".join(ligne1.split()[1:])
motif = []
for ligne in f:
if ligne[0] != "!":
motif.append([c for c in ligne.rstrip()])
return titre, motif
def motif_dans_grille(lig0, col0, motif, grille):
"""
Recopie un motif (sous-grille) dans grille à partir de
la position (lig0, col0) en coin supérieur gauche
"""
decodage = {".": MORT, "O": VIE}
largeur_motif = len(motif[0])
hauteur_motif = len(motif)
assert lig0 + hauteur_motif <= len(grille) and col0 + largeur_motif <= len(
grille[0]
), "Motif trop grand"
for lig in range(lig0, lig0 + hauteur_motif):
for col in range(col0, col0 + largeur_motif):
grille[lig][col] = decodage[motif[lig - lig0][col - col0]]
# =========================================================
# == AFFICHAGES MENU / FIN
# =========================================================
def afficher_menu():
"""Affichage du menu d'accueil"""
pyxel.text(
int(LARGEUR * 0.25), int(HAUTEUR * 0.25), f"{jeu['nom']}", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.5), "Press s to start", COULEUR[TEXTE]
)
pyxel.text(
int(LARGEUR * 0.1), int(HAUTEUR * 0.6), "Press q to quit", COULEUR[TEXTE]
)
# Positionnement d'une image dans chaque coin
# à compléter
# BEGIN CUT
pyxel.blt(0, 0, 0, 0, 0, 3, 3)
pyxel.blt(HAUTEUR - 3, 0, 0, 8, 0, 3, 3)
pyxel.blt(0, LARGEUR - 3, 0, 0, 5, 3, 3)
pyxel.blt(LARGEUR - 3, HAUTEUR - 3, 0, 8, 5, 3, 3)
# END CUT
def afficher_fin():
"""Affichage de fin"""
pyxel.text(int(LARGEUR * 0.25), int(HAUTEUR * 0.5), "Fin de la vie", COULEUR[TEXTE])
# Positionnement d'une image dans chaque coin
# à compléter
# BEGIN CUT
pyxel.blt(0, 0, 0, 0, 8, 8, 8)
pyxel.blt(HAUTEUR - 8, 0, 0, 0, 8, 8, 8)
pyxel.blt(0, LARGEUR - 8, 0, 0, 8, 8, 8)
pyxel.blt(LARGEUR - 8, HAUTEUR - 8, 0, 0, 8, 8, 8)
# END CUT
# =========================================================
# == UPDATE
# =========================================================
def update():
"""mise à jour des variables (30 fois par seconde)"""
# une génération par seconde
if pyxel.btn(pyxel.KEY_S):
jeu["menu"] = False
if pyxel.btn(pyxel.KEY_Q):
jeu["fin"] = True
# mise à jour de la grille (3 fois par seconde)
if pyxel.frame_count % 10 == 0:
jeu["grille"] = evolution_grille(jeu["grille"])
# =========================================================
# == DRAW
# =========================================================
def draw():
"""création des objets (30 fois par seconde)"""
# vide la fenetre
pyxel.cls(COULEUR[FONDS])
if jeu["fin"]:
afficher_fin()
elif jeu["menu"]:
afficher_menu()
else:
# dessin de la grille à compléter
# dessinez chaque case avec pyxel.rect
# un exemple avec 4 cases
pyxel.rect(0, 0, 1, 1, COULEUR[MORT])
pyxel.rect(LARGEUR // 2, HAUTEUR // 2, 1, 1, COULEUR[VIE])
pyxel.rect(0, HAUTEUR - 1, 1, 1, COULEUR[VIE])
pyxel.rect(LARGEUR - 1, HAUTEUR - 1, 1, 1, COULEUR[MORT])
for lig in range(HAUTEUR):
for col in range(LARGEUR):
pyxel.rect(col, lig, 1, 1, COULEUR[jeu["grille"][lig][col]])
# =========================================================
# == DICTIONNAIRE GLOBAL DU JEU
# =========================================================
jeu = {
"grille": [[MORT for __ in range(LARGEUR)] for _ in range(HAUTEUR)],
"menu": True,
"fin": False,
}
# =========================================================
# == PROGRAMME PRINCIPAL
# =========================================================
# on charge l'automate enregistré dans le fichier "glider.cells" au format Plain
# voir https://conwaylife.com/wiki/Plaintext
nom, motif = charger_fichier("period60glidergun.cells")
jeu["nom"] = nom
motif_dans_grille(0, 0, motif, jeu["grille"])
# lancement de l'application
pyxel.run(update, draw)