Snake en Python con curses

Juego Snake completo en Python usando el módulo curses de la biblioteca estándar. Sin dependencias externas, controles de teclado y WASD, velocidad configurable y detección de colisiones. Compatible Python 3.8+.
				"""Snake en Python con curses — compatible Python 3.8+"""

import curses
import random
import time

# Dimensiones del área de juego (dentro del borde)
WIDTH = 40
HEIGHT = 20

# Teclas de dirección (flechas y WASD)
UP    = {curses.KEY_UP,    ord('w'), ord('W')}
DOWN  = {curses.KEY_DOWN,  ord('s'), ord('S')}
LEFT  = {curses.KEY_LEFT,  ord('a'), ord('A')}
RIGHT = {curses.KEY_RIGHT, ord('d'), ord('D')}

# Velocidad: segundos por tick
TICK = 0.1


def spawn_food(snake):
    """Coloca la comida en una posición libre."""
    body = set(snake)
    while True:
        pos = (random.randint(1, HEIGHT - 2), random.randint(1, WIDTH - 2))
        if pos not in body:
            return pos


def main(stdscr):
    curses.curs_set(0)         # Ocultar cursor
    stdscr.nodelay(True)       # No bloquear en getch()
    stdscr.timeout(0)

    # Colores
    curses.start_color()
    curses.init_pair(1, curses.COLOR_GREEN,  curses.COLOR_BLACK)  # serpiente
    curses.init_pair(2, curses.COLOR_RED,    curses.COLOR_BLACK)  # comida
    curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)  # score
    curses.init_pair(4, curses.COLOR_CYAN,   curses.COLOR_BLACK)  # borde

    def play():
        stdscr.clear()

        # Estado inicial
        cy, cx = HEIGHT // 2, WIDTH // 2
        snake = [(cy, cx), (cy, cx - 1), (cy, cx - 2)]
        direction = (0, 1)   # (dy, dx) ? derecha
        food = spawn_food(snake)
        score = 0
        game_over = False

        last_tick = time.monotonic()

        while not game_over:
            # ?? Input ????????????????????????????????????????
            key = stdscr.getch()
            if key in UP    and direction != (1, 0):   direction = (-1, 0)
            elif key in DOWN  and direction != (-1, 0): direction = (1, 0)
            elif key in LEFT  and direction != (0, 1):  direction = (0, -1)
            elif key in RIGHT and direction != (0, -1): direction = (0, 1)
            elif key == ord('q'):
                return False  # salir sin reiniciar

            # ?? Lógica (un tick cada TICK segundos) ??????????
            now = time.monotonic()
            if now - last_tick >= TICK:
                last_tick = now

                head = (snake[0][0] + direction[0], snake[0][1] + direction[1])

                # Colisión con borde
                if not (0 < head[0] < HEIGHT - 1 and 0 < head[1] < WIDTH - 1):
                    game_over = True
                    break

                # Colisión consigo misma
                if head in snake[:-1]:
                    game_over = True
                    break

                snake.insert(0, head)

                if head == food:
                    score += 10
                    food = spawn_food(snake)
                else:
                    snake.pop()

            # ?? Dibujo ???????????????????????????????????????
            stdscr.erase()

            # Borde
            for x in range(WIDTH):
                stdscr.addch(0, x, '#', curses.color_pair(4))
                stdscr.addch(HEIGHT - 1, x, '#', curses.color_pair(4))
            for y in range(HEIGHT):
                stdscr.addch(y, 0, '#', curses.color_pair(4))
                stdscr.addch(y, WIDTH - 1, '#', curses.color_pair(4))

            # Comida
            stdscr.addch(food[0], food[1], '*', curses.color_pair(2))

            # Serpiente
            for i, (y, x) in enumerate(snake):
                char = '@' if i == 0 else 'o'
                stdscr.addch(y, x, char, curses.color_pair(1))

            # Score
            stdscr.addstr(0, 2, f' Puntuacion: {score} ', curses.color_pair(3))
            stdscr.addstr(HEIGHT, 0, 'WASD / flechas para mover  |  Q para salir', curses.color_pair(3))

            stdscr.refresh()
            time.sleep(0.016)  # ~60 fps de dibujado

        # ?? Game over ?????????????????????????????????????????
        msg = f'GAME OVER — Puntuacion: {score}'
        stdscr.addstr(HEIGHT // 2, (WIDTH - len(msg)) // 2, msg, curses.color_pair(2) | curses.A_BOLD)
        stdscr.addstr(HEIGHT // 2 + 1, (WIDTH - 24) // 2, 'R para reiniciar  Q para salir')
        stdscr.refresh()

        stdscr.nodelay(False)
        while True:
            k = stdscr.getch()
            if k in (ord('r'), ord('R')):
                return True   # reiniciar
            if k in (ord('q'), ord('Q'), 27):
                return False  # salir

    # Bucle de reinicio
    while play():
        pass


if __name__ == '__main__':
    curses.wrapper(main)

			
Descargar adjuntos
COMPARTE ESTE TUTORIAL

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
TUTORIAL ANTERIOR