Skip to content

Instantly share code, notes, and snippets.

@MircoT
Created February 5, 2020 09:14
Show Gist options
  • Select an option

  • Save MircoT/96998a30613aa32bd17ba857a031dd96 to your computer and use it in GitHub Desktop.

Select an option

Save MircoT/96998a30613aa32bd17ba857a031dd96 to your computer and use it in GitHub Desktop.
Snake game with Python and 2 different libraries (raylib and termbox)
from random import randint
class Direction:
"""The possible directions of the snake parts."""
LEFT = 0
RIGHT = 1
UP = 2
DOWN = 3
class State:
"""All game states."""
ALIVE = 0
GAME_OVER = 1
class Component:
"""A basic game component."""
def __init__(self, x, y):
self._x = x
self._y = y
@property
def x(self):
"""Get x coordinate."""
return self._x
@property
def y(self):
"""Get y coordinate."""
return self._y
def move(self, dx, dy):
"""Move the component."""
self._x += dx
self._y += dy
def drop(self, x, y):
"""Put the component in a new position."""
self._x = x
self._y = y
class SnakePart(Component):
"""A part of the snake."""
def __init__(self, x, y, direction):
super().__init__(x, y)
self.dir = direction
class Food(Component):
"""Food component."""
def __init__(self, x, y):
super().__init__(x, y)
class SnakeGame:
"""The snake game."""
def __init__(self, width, height):
"""Initialize the game stuff."""
self._parts = []
self._width = width - 2
self._height = height - 2
self._state = State.GAME_OVER
self._food = Food(randint(0, self._width - 2),
randint(0, self._height - 2))
self.reset()
def reset(self):
"""Reset the game to the initial state."""
self._parts = [
SnakePart(int(self._width / 2),
int(self._height / 2), Direction.RIGHT),
SnakePart(int(self._width / 2) - 1,
int(self._height / 2), Direction.RIGHT),
SnakePart(int(self._width / 2) - 2,
int(self._height / 2), Direction.RIGHT),
SnakePart(int(self._width / 2) - 3,
int(self._height / 2), Direction.RIGHT),
]
self._state = State.ALIVE
self._food.drop(
randint(0, self._width - 2),
randint(0, self._height - 2)
)
def _update_part_dirs(self):
"""Update the directions of the snake parts."""
for idx in reversed(range(len(self._parts) - 1)):
self._parts[idx+1].dir = self._parts[idx].dir
def _update_part_positions(self):
"""Update the positions of the snake parts."""
for part in self._parts:
if part.dir == Direction.RIGHT:
part.move(1, 0)
elif part.dir == Direction.LEFT:
part.move(-1, 0)
elif part.dir == Direction.UP:
part.move(0, -1)
elif part.dir == Direction.DOWN:
part.move(0, 1)
def _check_eat_food(self):
"""Check if the snake has eaten the food.
NOTE: This method reallocate the food when it's eaten
"""
head = self._head
tail = self._tail
if head.x == self._food.x and head.y == self._food.y:
if tail.dir == Direction.RIGHT:
self._parts.append(SnakePart(tail.x-1, tail.y, tail.dir))
elif tail.dir == Direction.LEFT:
self._parts.append(SnakePart(tail.x+1, tail.y, tail.dir))
elif tail.dir == Direction.UP:
self._parts.append(SnakePart(tail.x, tail.y+1, tail.dir))
elif tail.dir == Direction.DOWN:
self._parts.append(SnakePart(tail.x, tail.y-1, tail.dir))
self._food.drop(
randint(0, self._width - 2),
randint(0, self._height - 2)
)
def _check_game_over(self):
"""Check if the game comes to end."""
head = self._head
if head.x >= self._width or head.x < 0:
self._state = State.GAME_OVER
if head.y >= self._height or head.y < 0:
self._state = State.GAME_OVER
for part in self._parts[1:]:
if head.x == part.x and head.y == part.y:
self._state = State.GAME_OVER
def update(self):
"""Update the game logic."""
if self._state == State.ALIVE:
self._update_part_positions()
self._check_eat_food()
self._check_game_over()
self._update_part_dirs()
def change_head_dir(self, direction):
"""Change the direction of the snake's head."""
head = self._head
if head.dir == Direction.RIGHT and direction == Direction.LEFT:
return
elif head.dir == Direction.LEFT and direction == Direction.RIGHT:
return
elif head.dir == Direction.UP and direction == Direction.DOWN:
return
elif head.dir == Direction.DOWN and direction == Direction.UP:
return
head.dir = direction
@property
def state(self):
"""Get the game state."""
return self._state
@property
def _head(self) -> 'SnakePart':
"""Get the head of the snake."""
return self._parts[0]
@property
def _tail(self) -> 'SnakePart':
"""Get the tail of the snake."""
return self._parts[-1]
@property
def snake_parts(self) -> list:
"""Get all the snake parts."""
return self._parts
@property
def food(self) -> 'Food':
"""Get the food component."""
return self._food
import raylibpy as ray
from snake import Direction, SnakeGame, State
_CELL_SIZE = 24
def draw_borders(width, height):
"""Render game borders."""
ray.draw_rectangle(0, 0, width, _CELL_SIZE, ray.GRAY)
ray.draw_rectangle(0, height - _CELL_SIZE, width, _CELL_SIZE, ray.GRAY)
ray.draw_rectangle(0, 0, _CELL_SIZE, height, ray.GRAY)
ray.draw_rectangle(width - _CELL_SIZE, 0, _CELL_SIZE, height, ray.GRAY)
def draw_snake(parts, state):
"""Render the snake."""
color = ray.YELLOW if state == State.ALIVE else ray.RED
for idx, part in enumerate(parts):
ray.draw_rectangle(
part.x * _CELL_SIZE + _CELL_SIZE,
part.y * _CELL_SIZE + _CELL_SIZE,
_CELL_SIZE, _CELL_SIZE,
color if idx != 0 else ray.ORANGE
)
def draw_food(food):
"""Render the food."""
ray.draw_rectangle(
food.x * _CELL_SIZE + _CELL_SIZE,
food.y * _CELL_SIZE + _CELL_SIZE,
_CELL_SIZE, _CELL_SIZE,
ray.GREEN
)
def main():
screen_width: int = 640
screen_height: int = 480
ray.init_window(screen_width, screen_height, "Snake")
ray.set_target_fps(10)
snake = SnakeGame(
int(screen_width / _CELL_SIZE),
int(screen_height / _CELL_SIZE)
)
while not ray.window_should_close():
##
# Inputs
if ray.is_key_down(ray.KEY_RIGHT):
snake.change_head_dir(Direction.RIGHT)
if ray.is_key_down(ray.KEY_LEFT):
snake.change_head_dir(Direction.LEFT)
if ray.is_key_down(ray.KEY_UP):
snake.change_head_dir(Direction.UP)
if ray.is_key_down(ray.KEY_DOWN):
snake.change_head_dir(Direction.DOWN)
if ray.is_key_down(ray.KEY_R):
snake.reset()
##
# Game logic
snake.update()
##
# Rendering
ray.clear_background(ray.RAYWHITE)
draw_borders(screen_width, screen_height)
draw_snake(snake.snake_parts, snake.state)
draw_food(snake.food)
ray.end_drawing()
ray.close_window()
if __name__ == "__main__":
main()
from time import sleep
import termbox as tbx
from snake import Direction, SnakeGame, State
def draw_borders(box, width, height):
"""Render game borders."""
for idx in range(width):
box.change_cell(idx, 0, ord(u'#'), tbx.WHITE, tbx.WHITE)
box.change_cell(idx, height-1, ord(u'#'), tbx.WHITE, tbx.WHITE)
for idx in range(height):
box.change_cell(0, idx, ord(u'#'), tbx.WHITE, tbx.WHITE)
box.change_cell(width-1, idx, ord(u'#'), tbx.WHITE, tbx.WHITE)
def draw_snake(box, parts, state):
"""Render the snake."""
color = tbx.YELLOW if state == State.ALIVE else tbx.RED
for idx, part in enumerate(parts):
box.change_cell(
part.x + 1, part.y + 1,
ord(u'#'),
color if idx != 0 else tbx.MAGENTA,
color if idx != 0 else tbx.MAGENTA
)
def draw_food(box, food):
"""Render the food."""
box.change_cell(food.x + 1, food.y + 1, ord(u'@'), tbx.GREEN, tbx.GREEN)
def main():
with tbx.Termbox() as box:
box.clear()
box.present()
width = box.width()
height = box.height()
run_app = True
snake = SnakeGame(width, height)
while run_app:
##
# Inputs
event_here = box.peek_event()
while event_here:
type_, ch, key, mod, w, h, x, y = event_here
if type_ == tbx.EVENT_KEY:
if key == tbx.KEY_ESC:
run_app = False
elif key == tbx.KEY_ARROW_UP:
snake.change_head_dir(Direction.UP)
elif key == tbx.KEY_ARROW_DOWN:
snake.change_head_dir(Direction.DOWN)
elif key == tbx.KEY_ARROW_LEFT:
snake.change_head_dir(Direction.LEFT)
elif key == tbx.KEY_ARROW_RIGHT:
snake.change_head_dir(Direction.RIGHT)
elif ch == "r":
snake.reset()
event_here = box.peek_event()
##
# Game logic
snake.update()
##
# Rendering
box.clear()
draw_borders(box, width, height)
draw_food(box, snake.food)
draw_snake(box, snake.snake_parts, snake.state)
box.present()
sleep(0.066)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment