serpent — Un jeu de serpent
Ceci est le jeu classique du serpent, sauf que la carte microbit joue toute seule. Elle fait tourner le serpent aléatoirement, la seule condition étant de ne pas foncer dans un mur.
Regarder la carte jouer est étrangement frustrant et addictif.
1# Copyright 2023 Louis Paternault
2#
3# This file is part of Jouets.
4#
5# Jouets is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Jouets is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Jouets. If not, see <http://www.gnu.org/licenses/>.
17
18"""Jeu de serpent, qui joue au hasard (en évitant autant que possible les murs)."""
19
20# pylint: disable=non-ascii-name, consider-using-f-string
21
22import random
23
24from microbit import * # pylint: disable=wildcard-import, unused-wildcard-import
25
26
27class Direction:
28 """Direction de déplacement du serpent."""
29
30 def __init__(self, x, y):
31 self.x = x
32 self.y = y
33
34 def plus(self, départ):
35 """Renvoit les coordonnées de la nouvelle position, en partant de départ.
36
37 Il aurait été plus propre d'utiliser __radd__,
38 mais cela ne semble pas supporté par micropython.
39 """
40 return (départ[0] + self.x, départ[1] + self.y)
41
42 def opposé(self):
43 """Renvoit la direction opposée à la direction courante.
44
45 Il aurait été plus propre d'utiliser __neg__,
46 mais cela ne semble pas supporté par micropython.
47 """
48 return Direction(-self.x, -self.y)
49
50 def __repr__(self):
51 return "({}, {})".format(self.x, self.y)
52
53 def __eq__(self, other):
54 return self.x == other.x and self.y == other.y
55
56
57DIRECTIONS = (
58 Direction(-1, 0),
59 Direction(1, 0),
60 Direction(0, -1),
61 Direction(0, 1),
62)
63
64
65def nouvelle_pomme(serpent):
66 """Affiche et renvoit les coordonnées d'une nouvelle pomme."""
67 pomme = random.choice(
68 tuple((x, y) for x in range(5) for y in range(5) if (x, y) not in serpent)
69 )
70 display.set_pixel(pomme[0], pomme[1], 9)
71 return pomme
72
73
74def libre(serpent, direction):
75 """Renvoit True si et seulement si le serpent peut avancer dans la direction donnée."""
76 prochain = direction.plus(serpent[0])
77 return (prochain not in serpent) and 0 <= prochain[0] <= 4 and 0 <= prochain[1] <= 4
78
79
80def clignote():
81 """Fait clignoter l'écran"""
82 screen = [[display.get_pixel(x, y) for y in range(5)] for x in range(5)]
83 for _ in range(10):
84 display.clear()
85 sleep(500)
86 for x in range(5):
87 for y in range(5):
88 display.set_pixel(x, y, screen[x][y])
89 sleep(500)
90
91
92def partie():
93 """Initialise une partie de Serpent, et joue jusqu'à la défaite."""
94 display.clear()
95
96 serpent = [(random.randint(0, 4), random.randint(0, 4))]
97 display.set_pixel(serpent[0][0], serpent[0][1], random.randint(2, 7))
98
99 pomme = nouvelle_pomme(serpent)
100 direction = random.choice(tuple(DIRECTIONS))
101
102 while True:
103 # Changement de direction ?
104 candidats = tuple(
105 candidat
106 for candidat in DIRECTIONS
107 if libre(serpent, candidat) and candidat != direction.opposé()
108 )
109 if not candidats:
110 clignote()
111 return
112 if (not libre(serpent, direction)) or (random.random() < 0.4):
113 direction = random.choice(candidats)
114
115 # Mouvement
116 serpent.insert(0, direction.plus(serpent[0]))
117 display.set_pixel(serpent[0][0], serpent[0][1], random.randint(2, 7))
118
119 # Pomme
120 if serpent[0] == pomme:
121 pomme = nouvelle_pomme(serpent)
122 else:
123 display.set_pixel(serpent[-1][0], serpent[-1][1], 0)
124 serpent.pop()
125 sleep(500)
126
127
128while True:
129 partie()