Projets : structure par projet (jeu_de_la_vie, labyrinthe, mastermind, wator)

This commit is contained in:
2026-06-15 12:57:25 +02:00
parent 47ccdbb255
commit b17a21b394
75 changed files with 4304 additions and 0 deletions

329
Projets/pyxel_text.py Normal file
View File

@@ -0,0 +1,329 @@
"""
Module de gestion de texte pour Pyxel
Permet d'afficher du texte et de capturer la saisie utilisateur
"""
import pyxel
# Mapping des touches Pyxel vers les caractères
KEY_MAP = {
pyxel.KEY_A: ('a', 'A'), pyxel.KEY_B: ('b', 'B'), pyxel.KEY_C: ('c', 'C'),
pyxel.KEY_D: ('d', 'D'), pyxel.KEY_E: ('e', 'E'), pyxel.KEY_F: ('f', 'F'),
pyxel.KEY_G: ('g', 'G'), pyxel.KEY_H: ('h', 'H'), pyxel.KEY_I: ('i', 'I'),
pyxel.KEY_J: ('j', 'J'), pyxel.KEY_K: ('k', 'K'), pyxel.KEY_L: ('l', 'L'),
pyxel.KEY_M: ('m', 'M'), pyxel.KEY_N: ('n', 'N'), pyxel.KEY_O: ('o', 'O'),
pyxel.KEY_P: ('p', 'P'), pyxel.KEY_Q: ('q', 'Q'), pyxel.KEY_R: ('r', 'R'),
pyxel.KEY_S: ('s', 'S'), pyxel.KEY_T: ('t', 'T'), pyxel.KEY_U: ('u', 'U'),
pyxel.KEY_V: ('v', 'V'), pyxel.KEY_W: ('w', 'W'), pyxel.KEY_X: ('x', 'X'),
pyxel.KEY_Y: ('y', 'Y'), pyxel.KEY_Z: ('z', 'Z'),
pyxel.KEY_0: ('0', ')'), pyxel.KEY_1: ('1', '!'), pyxel.KEY_2: ('2', '@'),
pyxel.KEY_3: ('3', '#'), pyxel.KEY_4: ('4', '$'), pyxel.KEY_5: ('5', '%'),
pyxel.KEY_6: ('6', '^'), pyxel.KEY_7: ('7', '&'), pyxel.KEY_8: ('8', '*'),
pyxel.KEY_9: ('9', '('),
pyxel.KEY_SPACE: (' ', ' '),
pyxel.KEY_MINUS: ('-', '_'),
pyxel.KEY_PERIOD: ('.', '>'),
pyxel.KEY_COMMA: (',', '<'),
pyxel.KEY_SLASH: ('/', '?'),
pyxel.KEY_SEMICOLON: (';', ':'),
pyxel.KEY_APOSTROPHE: ("'", '"'),
}
class TextInput:
"""
Champ de saisie de texte pour Pyxel
Exemple d'utilisation:
input_field = TextInput(x=10, y=50, max_length=20)
def update():
input_field.update()
if input_field.submitted:
print(f"Texte saisi: {input_field.text}")
input_field.reset()
def draw():
input_field.draw()
"""
def __init__(self, x=0, y=0, max_length=32, width=None, color=7, bg_color=0, cursor_color=8):
"""
Initialise un champ de saisie
Args:
x, y: Position du champ
max_length: Nombre maximum de caractères
width: Largeur du champ en pixels (auto si None)
color: Couleur du texte (0-15)
bg_color: Couleur de fond (0-15, None pour transparent)
cursor_color: Couleur du curseur (0-15)
"""
self.x = x
self.y = y
self.max_length = max_length
self.width = width if width else (max_length * 4 + 4)
self.color = color
self.bg_color = bg_color
self.cursor_color = cursor_color
self.text = ""
self.cursor_pos = 0
self.active = True
self.submitted = False
self._cursor_visible = True
self._cursor_timer = 0
def update(self):
"""Met à jour le champ de saisie (à appeler dans update())"""
if not self.active:
return
self.submitted = False
# Clignotement du curseur
self._cursor_timer += 1
if self._cursor_timer >= 15:
self._cursor_timer = 0
self._cursor_visible = not self._cursor_visible
# Validation avec Entrée
if pyxel.btnp(pyxel.KEY_RETURN):
self.submitted = True
return
# Suppression avec Backspace
if pyxel.btnp(pyxel.KEY_BACKSPACE, hold=10, repeat=3):
if self.cursor_pos > 0:
self.text = self.text[:self.cursor_pos-1] + self.text[self.cursor_pos:]
self.cursor_pos -= 1
# Suppression avec Delete
if pyxel.btnp(pyxel.KEY_DELETE, hold=10, repeat=3):
if self.cursor_pos < len(self.text):
self.text = self.text[:self.cursor_pos] + self.text[self.cursor_pos+1:]
# Navigation avec flèches
if pyxel.btnp(pyxel.KEY_LEFT, hold=10, repeat=3):
self.cursor_pos = max(0, self.cursor_pos - 1)
if pyxel.btnp(pyxel.KEY_RIGHT, hold=10, repeat=3):
self.cursor_pos = min(len(self.text), self.cursor_pos + 1)
if pyxel.btnp(pyxel.KEY_HOME):
self.cursor_pos = 0
if pyxel.btnp(pyxel.KEY_END):
self.cursor_pos = len(self.text)
# Saisie de caractères
if len(self.text) < self.max_length:
shift = pyxel.btn(pyxel.KEY_LSHIFT) or pyxel.btn(pyxel.KEY_RSHIFT)
for key, chars in KEY_MAP.items():
if pyxel.btnp(key, hold=15, repeat=3):
char = chars[1] if shift else chars[0]
self.text = self.text[:self.cursor_pos] + char + self.text[self.cursor_pos:]
self.cursor_pos += 1
break
def draw(self):
"""Dessine le champ de saisie (à appeler dans draw())"""
# Fond
if self.bg_color is not None:
pyxel.rect(self.x, self.y, self.width, 9, self.bg_color)
# Bordure si actif
if self.active:
pyxel.rectb(self.x - 1, self.y - 1, self.width + 2, 11, self.color)
# Texte
pyxel.text(self.x + 2, self.y + 1, self.text, self.color)
# Curseur
if self.active and self._cursor_visible:
cursor_x = self.x + 2 + self.cursor_pos * 4
pyxel.rect(cursor_x, self.y + 1, 1, 7, self.cursor_color)
def reset(self):
"""Réinitialise le champ"""
self.text = ""
self.cursor_pos = 0
self.submitted = False
def set_text(self, text):
"""Définit le texte du champ"""
self.text = text[:self.max_length]
self.cursor_pos = len(self.text)
class TextBox:
"""
Zone de texte multiligne avec retour à la ligne automatique
Exemple d'utilisation:
textbox = TextBox(x=10, y=10, width=100, color=7)
textbox.set_text("Ceci est un long texte qui sera automatiquement découpé en plusieurs lignes.")
def draw():
textbox.draw()
"""
def __init__(self, x=0, y=0, width=100, color=7, line_spacing=8):
"""
Initialise une zone de texte
Args:
x, y: Position de la zone
width: Largeur en pixels
color: Couleur du texte (0-15)
line_spacing: Espacement entre les lignes
"""
self.x = x
self.y = y
self.width = width
self.color = color
self.line_spacing = line_spacing
self.lines = []
def set_text(self, text):
"""Définit le texte avec retour à la ligne automatique"""
self.lines = []
chars_per_line = self.width // 4
paragraphs = text.split('\n')
for paragraph in paragraphs:
words = paragraph.split(' ')
current_line = ""
for word in words:
test_line = current_line + (" " if current_line else "") + word
if len(test_line) <= chars_per_line:
current_line = test_line
else:
if current_line:
self.lines.append(current_line)
# Mot trop long, on le coupe
while len(word) > chars_per_line:
self.lines.append(word[:chars_per_line])
word = word[chars_per_line:]
current_line = word
if current_line:
self.lines.append(current_line)
def draw(self):
"""Dessine la zone de texte"""
for i, line in enumerate(self.lines):
pyxel.text(self.x, self.y + i * self.line_spacing, line, self.color)
def get_height(self):
"""Retourne la hauteur totale du texte"""
return len(self.lines) * self.line_spacing
class TypeWriter:
"""
Effet machine à écrire pour afficher du texte progressivement
Exemple d'utilisation:
typewriter = TypeWriter(x=10, y=10, speed=3)
typewriter.set_text("Bienvenue dans le jeu...")
def update():
typewriter.update()
def draw():
typewriter.draw()
"""
def __init__(self, x=0, y=0, width=100, color=7, speed=2, line_spacing=8):
"""
Args:
x, y: Position
width: Largeur en pixels
color: Couleur du texte
speed: Frames entre chaque caractère (plus bas = plus rapide)
line_spacing: Espacement entre les lignes
"""
self.textbox = TextBox(x, y, width, color, line_spacing)
self.speed = speed
self.full_text = ""
self.displayed_chars = 0
self.timer = 0
self.finished = False
def set_text(self, text):
"""Définit le texte à afficher progressivement"""
self.full_text = text
self.displayed_chars = 0
self.timer = 0
self.finished = False
def update(self):
"""Met à jour l'animation"""
if self.finished:
return
self.timer += 1
if self.timer >= self.speed:
self.timer = 0
self.displayed_chars += 1
if self.displayed_chars >= len(self.full_text):
self.displayed_chars = len(self.full_text)
self.finished = True
self.textbox.set_text(self.full_text[:self.displayed_chars])
def draw(self):
"""Dessine le texte"""
self.textbox.draw()
def skip(self):
"""Affiche tout le texte immédiatement"""
self.displayed_chars = len(self.full_text)
self.finished = True
self.textbox.set_text(self.full_text)
def is_finished(self):
"""Retourne True si tout le texte est affiché"""
return self.finished
# --- Exemple d'utilisation ---
if __name__ == "__main__":
class Demo:
def __init__(self):
pyxel.init(200, 150, title="Demo TextInput")
self.input_field = TextInput(x=10, y=60, max_length=25, color=7)
self.typewriter = TypeWriter(x=10, y=10, width=180, speed=2)
self.typewriter.set_text("Bienvenue ! Entrez votre nom ci-dessous et appuyez sur Entree.")
self.messages = []
pyxel.run(self.update, self.draw)
def update(self):
self.typewriter.update()
self.input_field.update()
# Skip le typewriter avec espace
if pyxel.btnp(pyxel.KEY_SPACE) and not self.input_field.active:
self.typewriter.skip()
# Validation de la saisie
if self.input_field.submitted and self.input_field.text:
self.messages.append(f"Bonjour, {self.input_field.text} !")
self.input_field.reset()
def draw(self):
pyxel.cls(0)
self.typewriter.draw()
pyxel.text(10, 50, "Votre nom:", 6)
self.input_field.draw()
# Afficher les messages
for i, msg in enumerate(self.messages[-3:]):
pyxel.text(10, 90 + i * 10, msg, 11)
Demo()