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()