Description
Il y a quelque semaine on a entamé la création d’un clone de « Snake », en modalité texte.
Nous sommes partis d’un vidéo sur YouTube, où le but étais de construire un « Snake » en python dans 5 minutes.
On a utilisé la librairie curses de Python, puis nous avons ajouté la gestion des couleurs.

Nous avons compliqué les choses en partageant l’écran en deux fenêtres, une pour les communications « pendant » le jeu, et l’autre pour la fenêtre de jeu lui-même.

Nous avons appris à gérer les boucles de jeux, l’affichage des points (sur une fenêtre dédiée à la communication), et aussi la fin du jeu.

Enfin, nous avons appris à gérer la notion de « high score », dans la façon la plus simple : En écrivant et lisant un fichier de texte. L’input ici est fait a “l’ancienne”, on gère lettre par lettre, et le nom du joueur est au maximum de 3 lettres.

Le diagramme d’états
Voilà à grandes lignes le diagramme des états du jeu :

Le code
Et voilà le code (une des versions, nous avons implémenté des moutures légèrement différents) :
import random
import curses
import art
from time import sleep
from random import randint
# On regarde si il y a un record
try:
record = open("snake.record")
recordMan = record.readline()
recordScore = int(record.readline())
record.close()
except :
recordMan = "ABC"
recordScore = 3
s = curses.initscr()
curses.curs_set(0)
# Ordre est important!
curses.start_color()
curses.noecho()
sh, sw = s.getmaxyx()
w_comm = curses.newwin(sh, 20, 0, 0)
win = curses.newwin(sh, sw-20, 0, 20)
win.keypad(1)
win.timeout(100)
def afficheText(win, y, x, text, color, sleepTime=0):
textList=art.text2art(text).splitlines()
i = 0
for line in textList:
win.addstr(y+i, x, line, color)
i+=1
win.refresh()
sleep(sleepTime)
# Ordre est important!
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_GREEN)
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_CYAN)
curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_BLACK)
colors = {'green': curses.color_pair(1),
'red': curses.color_pair(2),
'blue': curses.color_pair(3),
'yellow': curses.color_pair(4),
'black': curses.color_pair(5)}
color_list = colors.values
# il est possible d'utiliser box() aussi
afficheText(win, int(sh/3), int(sw/4)-10, "SnAkE", colors['yellow'], 2)
afficheText(win, int(sh/3), int(sw/4)-10, "SnAkE", colors['black'], 0)
while True:
win.clear()
win.border()
w_comm.box()
w_comm.refresh()
snk_x = int(sw/4)
snk_y = int(sh/2)
snake = [
[snk_y, snk_x],
[snk_y, snk_x-1],
[snk_y, snk_x-2]
]
food = [int(sh/2), int(sw/2)]
win.addch(food[0], food[1], curses.ACS_DIAMOND, colors['red'])
key = curses.KEY_RIGHT
pause = False
touches = [ord('p'), ord('q'), curses.KEY_DOWN, curses.KEY_UP, curses.KEY_LEFT, curses.KEY_RIGHT]
manges = 0
afficheText(w_comm, 5, 5, str(manges), colors['red'])
w_comm.refresh()
while True:
next_key = win.getch()
if next_key not in touches:
next_key=-1
if next_key == ord('p'):
pause = not pause
if pause :
old_key = key
else :
next_key = old_key
if next_key == ord('q'):
curses.endwin()
quit()
if not pause:
key = key if next_key == -1 else next_key
if snake[0][0] in [0, sh] or snake[0][1] in [0, sw-1] or snake[0] in snake[1:]:
afficheText(win, int(sh/3), int(sw/5), "PERDU", colors['red'], 2)
break
new_head = [snake[0][0], snake[0][1]]
if key == curses.KEY_DOWN:
new_head[0] += 1
if key == curses.KEY_UP:
new_head[0] -= 1
if key == curses.KEY_LEFT:
new_head[1] -= 1
if key == curses.KEY_RIGHT:
new_head[1] += 1
snake.insert(0, new_head)
if snake[0] == food:
food = None
while food is None:
nf = [
random.randint(2, sh-3),
random.randint(21, sw-3)
]
food = nf if nf not in snake else None
try:
win.addch(food[0], food[1], curses.ACS_DIAMOND, colors['red'])
except:
food = None
afficheText(w_comm, 5, 5, str(manges), colors['black'])
manges+=1
afficheText(w_comm, 5, 5, str(manges), colors['red'])
#w_comm.addstr(5, 5, str(manges))
w_comm.refresh()
else:
tail = snake.pop()
win.addch(tail[0], tail[1], ' ')
try:
win.addch(snake[0][0], snake[0][1], curses.ACS_CKBOARD,colors['green'])
for i in range (1,len(snake)-1):
win.addch(snake[i][0], snake[i][1], curses.ACS_CKBOARD,colors['blue'])
except:
afficheText(win, int(sh/3), int(sw/5), "PERDU", colors['red'], 2)
curses.endwin()
break
# Si le dernier score est plus du record? Il faut mettre à jour le record
if manges > recordScore :
# Nettoyer la fenetre
win.clear()
win.border()
# Premier charactere...
# On permet les lettres, les chiffres et les fleches pour bouger...
# Position = 0
prenom = "_"
afficheText(win, 5, 5, prenom, colors['red'])
touchesCTRL = [curses.KEY_DOWN, curses.KEY_UP, curses.KEY_LEFT, curses.KEY_RIGHT]
alphabet="_ABCDEFGJKLMNOPQRSTUVWXYZ0123456789"
index = 0
position = 0
while position < 3:
next_key = win.getch()
if next_key in touchesCTRL:
if next_key == curses.KEY_UP:
index+=1
if index >=len(alphabet):
index = 0
prenom = prenom[:-1] + alphabet[index]
if next_key == curses.KEY_DOWN:
index-=1
if index < 0 :
index = len(alphabet)-1
prenom = prenom[:-1] + alphabet[index]
if next_key == curses.KEY_RIGHT:
position+=1
if position < 3:
prenom = prenom + alphabet[index]
if next_key == curses.KEY_LEFT:
if position > 0:
position-=1
prenom = prenom[:-1]
win.clear()
win.border()
afficheText(win, 5, 5, prenom, colors['red'])
if manges > recordScore :
win.clear()
win.border()
afficheText(win, 5, 5, "MERCI!", colors['red'],1)
record = open("/home/antonio/Dev/Python/snake.record", 'w')
record.write(prenom +"\n")
record.write(str(manges))
record.close()
recordScore = manges
# Demander si rejouer
# Nettoyer la fenetre
win.clear()
win.border()
afficheText(win, 5, 5, "Encore? O/N", colors['red'])
# Gerer les touches
while True :
next_key = win.getch()
if next_key == ord('o') or next_key == ord('O'):
break
if next_key == ord('q') or next_key == ord('n'):
curses.endwin()
quit()



