# Créer le jeu du Serpent à l'aide du module Pyxel
## Objectif élève
À la fin de la séquence, les élèves doivent obtenir un Snake où :
- Le serpent se déplace automatiquement sur une grille.
- L’utilisateur peut changer la direction avec les flèches du clavier.
- Une pomme apparaît ; si le serpent la mange, il grandit et le score augmente.
- La partie se termine en cas de collision avec un mur ou avec soi-même.
## I – Prise en main de Pyxel & grille de jeu
Le point de départ est la construction d'une grille dans laquelle le serpent peut se déplacer.
La commande `pyxel.rect(20, 30, 40, 50, 8)` permet de dessiner un rectangle de couleur rouge dont le coin supérieur gauche a pour coordonnées `(20, 30)`, une largeur de `40` et une hauteur de `50`.
Le canevas de code ci-dessous que vous allez recopier dans votre interface Thonny pour démarrer le travail
``` python
import pyxel
LARGEUR = 128
HAUTEUR = 128
TAILLE_CASE = 8
def update():
# à faire : logique du jeu
pass
def draw():
pyxel.cls(0) # fond noir, on efface l'écran
pyxel.rect(20, 30, 40, 50, 8) # tracer du rectangle d'exemple
pyxel.init(LARGEUR, HAUTEUR, title="Snake - proto")
pyxel.run(update, draw)
```
**à faire :**
- modifier la taille du rectangle ainsi que sa position
- à l'aide d'une ou 2 boucle(s), générer un pavage de l'écran de façon à dessiner un échiquier de 16x16
## II - Gestion du déplacement de la tête du serpent
La tête du serpent se déplace en permanence est n'est jamais immobile.
elle est définie par sa position `x` et `y` et de sa *vitesse* `vx` et `vy`
on peut la définir par un dictionnaire :
```python
snake = {'x':64,'y':64,'vx':1,'vy':0}
```
Ainsi le serpent apparaît initialement au milieu de l'écran et on attend qu'il se déplace horizontalement vers la gauche.
Il est maintenant possible de compléter la partie `update()` qui se charge de prendre en compte les touches sur lesquelles le joueur appuie pour faire changer la direction du snake, puis gère son déplacement
```python
import pyxel
LARGEUR = 128
HAUTEUR = 128
TAILLE_CASE = 8
snake = {'x':64,'y':64,'vx':1,'vy':0}
def update():
global snake
if pyxel.btn(pyxel.KEY_UP):
snake['vy'] = # pour aller vers le up
# mais il faut aussi compléter pour ne pas continuer dans la direction précédente !
if pyxel.btn(pyxel.KEY_DOWN):
snake['vy'] = # pour aller vers le bas
if pyxel.btn(pyxel.KEY_LEFT):
snake['vx'] = # pour aller vers la gauche
if pyxel.btn(pyxel.KEY_RIGHT):
snake['vx'] = # pour aller vers la droite
snake['x'] += snake['vx']
snake['y'] += snake['vy']
def draw():
pyxel.cls(0) # fond noir, on efface l'écran
pyxel.rect(snake['x'], snake['y'], 8, 8, 8) # tracer de la tete
pyxel.init(LARGEUR, HAUTEUR, title="Snake - proto")
pyxel.run(update, draw)
```
**à faire :**
- compléter le code pour pouvoir commander le déplacement du serpent
- corriger de façon à ce qu'il ne se déplace qu'à vitesse constante et pas en diagonale
- faire en sorte que s'il sorte de l'écran à droite, il réapparaisse à gauche et inversement et que s'il sort par le haut, il réapparaisse vers le bas et inversement
- (pour plus tard) : le serpent ne peut pas partir immédiatement dans la direction opposée à son déplacement
## III - On ajoute le corps !
dans un premier temps, votre serpent ne contient que 3 segments :
- une tête
- 2 segments pour le corps
cela fait qu'on ne va définir le corps que comme une liste supplémentaire de coordonnées : `corps = [(x1,y1),(x2,y2)]`
**à faire :**
- Compléter le code pour qu'initialement le serpent ait sa tête aux coordonnées (64,64) et que son corps soit horizontal sur sa gauche
- le corps doit suivre la tête : pour cela il est nécessaire qu'à chaque frame la nouvelle coordonnée de la 1ere entrée du corps soit remplacée par les *anciennes* coordonnées de la tête du serpent avant que celle-ci ne soit mise à jour par le mouvement ; et la 2nde entrée du corps doit être remplacée par la coordonnée de la 1ere entrée avant que celle-ci ne soit effacée !
=> Ecrire une fonction `ramper()` qui se chargera de transferer les coordonnées de la tête au corps pouis entre chaque segment du corps
- la fonction `draw` dessine le corps du serpent d'une façon différente de la tête afin que le joueur puisse la différencier
## IV - on ajoute les pommes
On ajoute une liste `pommes = []` qui va contenir des pommes que le serpent doit attraper.
**à faire :**
- Générer la fonction `new_pomme()` qui ajoute à la liste `pommes` un *tuple* de coordonnées aléatoires **entières et multiples de 8** à l'intérieur de l'écran
- dans `draw()` faire une boucle affichant les pommes en traçant des cercles de la couleur de votre choix au milieu des cases
## V - Gestion des collisions
**à faire :**
- lorsque le serpent arrive sur une pomme, celle-ci est supprimée de la liste `pommes`
- le serpent gagne un segment de corps lorsqu'une pomme est mangée
- le jeu s'arrête lorsque la tête du serpent entre en collision avec une partie de son corps
##### corrigé #####
```python
import pyxel
import random
LARGEUR = 128
HAUTEUR = 128
NB_CASES = 16
TAILLE_CASE = LARGEUR // NB_CASES
snake = {'x':64,'y':64,'vx':8,'vy':0}
corps = [(56, 64), (48, 64)]
pommes = []
max_pommes = 5
def new_pomme():
global max_pommes
pomme_x = random.randrange(0,NB_CASES) * TAILLE_CASE
pomme_y = random.randrange(0,NB_CASES) * TAILLE_CASE
pommes.append((pomme_x, pomme_y))
if len(pommes) > max_pommes :
pommes.pop(0)
def ramper():
for i in range(len(corps) - 1, 0,-1):
corps[i] = corps[i - 1]
corps[0] = (snake['x'],snake['y'])
def update():
global snake
if pyxel.btn(pyxel.KEY_UP) and snake['vy'] == 0:
snake['vy'] = -8
snake['vx'] = 0
# mais il faut aussi compléter pour ne pas continuer dans la direction précédente !
if pyxel.btn(pyxel.KEY_DOWN) and snake['vy'] == 0:
snake['vy'] = 8# pour aller vers le bas
snake['vx'] = 0
if pyxel.btn(pyxel.KEY_LEFT) and snake['vx'] == 0:
snake['vx'] = -8# pour aller vers la gauche
snake['vy'] = 0
if pyxel.btn(pyxel.KEY_RIGHT) and snake['vx'] == 0:
snake['vx'] = 8# pour aller vers la droite
snake['vy'] = 0
if pyxel.frame_count % 8 == 0: # on ne gère les déplacements que toutes les 8 frames
if random.random() < 0.25 :
new_pomme()
ramper()
snake['x'] += snake['vx']
snake['y'] += snake['vy']
if snake['x'] < 0 :
snake['x'] = 128
if snake['y'] < 0 :
snake['y'] = 128
if snake['x'] > 128 :
snake['x'] = 0
if snake['y'] > 128 :
snake['y'] = 0
def draw():
pyxel.cls(7) # fond noir, on efface l'écran
for x in range(0, 128, 16):
for y in range(0, 128, 8):
k = 0
if (y // 8) % 2 == 1:
k = 8
pyxel.rect(x + k, y, 8, 8, 13) # tracer du rectangle d'exemple
for i in range(len(pommes)):
pyxel.circ(pommes[i][0] + 4, pommes[i][1] + 4, 3, 5+i)
for segment in corps:
pyxel.rect(segment[0], segment[1], 8, 8, 8)
pyxel.rect(snake['x'], snake['y'], 8, 8, 14) # tracer de la tete
pyxel.init(LARGEUR, HAUTEUR, title="Snake - proto")
pyxel.run(update, draw)
```