Initiall development.
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
# general things to ignore
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
*.egg
|
||||
*.py[cod]
|
||||
__pycache__/
|
||||
*.so
|
||||
*~
|
||||
|
||||
# due to using tox and pytest
|
||||
.tox
|
||||
.cache
|
||||
|
||||
# editor ignores
|
||||
.idea
|
||||
|
||||
# package ignores
|
||||
sensehatsnake/config.py
|
||||
data/database.json
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
language: python
|
||||
|
||||
env:
|
||||
- TOXENV=py27
|
||||
- TOXENV=py33
|
||||
- TOXENV=py34
|
||||
|
||||
install: pip install tox
|
||||
|
||||
script: tox
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Bradley Cornford <me@bradleycornford.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Include the license file
|
||||
include LICENSE.txt
|
||||
|
||||
# Include the data files
|
||||
recursive-include data *
|
||||
@@ -0,0 +1,43 @@
|
||||
# Raspberry Pi Python sense hat snake
|
||||
|
||||
A project to create a Snake game on a Raspberry Pi using a Sense Hat.
|
||||
|
||||
## Requirements
|
||||
|
||||
This package requires the following system packages to be installed:
|
||||
|
||||
- python-pip
|
||||
- python-dev
|
||||
|
||||
## Installation
|
||||
|
||||
Begin by installing this packages requirements:
|
||||
|
||||
pip install -e .
|
||||
|
||||
Finally copy the example configuration file `example.config.py`, and save it as `config.py`
|
||||
|
||||
cp sensehatsnake/example.config.py sensehatsnake/config.py
|
||||
|
||||
## Configuration
|
||||
|
||||
You can now configure Sense-Hat-Snake in a few simple steps. Open `sensehatsnake/config.py` and update the options as needed.
|
||||
|
||||
- `columns` - The number of columns the board has.
|
||||
- `rows` - The number of rows the board has.
|
||||
- `fps` - The games frames per second.
|
||||
- `countdown` - The game countdown in seconds.
|
||||
- `interval` - The game tick interval in milliseconds.
|
||||
- `score_increment` - The number to increment score by in game.
|
||||
- `level_increment` - The score when to increment the level by in game.
|
||||
- `interval_increment` - The number to reduce the game tick interval by in milliseconds.
|
||||
|
||||
## Usage
|
||||
|
||||
It's really as simple as running the main file
|
||||
|
||||
sudo python sensehatsnake/main.py
|
||||
|
||||
### License
|
||||
|
||||
Sense-Hat-Snake is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
|
||||
@@ -0,0 +1 @@
|
||||
{"_default": {"1": {"score": 0}, "2": {"score": 0}, "3": {"score": 1}, "4": {"score": 0}, "5": {"score": 0}, "6": {"score": 0}, "7": {"score": 0}, "8": {"score": 0}}}
|
||||
@@ -0,0 +1,10 @@
|
||||
game = {
|
||||
'columns': 8,
|
||||
'rows': 8,
|
||||
'fps': 30,
|
||||
'countdown': 5,
|
||||
'interval': 800,
|
||||
'score_increment': 1,
|
||||
'level_increment': 8,
|
||||
'interval_increment': 50
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
class Apple:
|
||||
COORDINATES = None
|
||||
|
||||
X = None
|
||||
Y = None
|
||||
|
||||
def __init__(self, coordinates, x=0, y=0):
|
||||
print("[Apple][info] Initialising Apple")
|
||||
|
||||
self.COORDINATES = coordinates
|
||||
self.X = x
|
||||
self.Y = y
|
||||
|
||||
def set_coordinates(self, coordinates):
|
||||
print("[Apple][info] Setting Apple coordinates")
|
||||
|
||||
self.COORDINATES = coordinates
|
||||
|
||||
def set_x(self, x):
|
||||
print("[Apple][info] Setting Apple X position")
|
||||
|
||||
self.X = x
|
||||
|
||||
def set_y(self, y):
|
||||
print("[Apple][info] Setting Apple Y position")
|
||||
|
||||
self.Y = y
|
||||
|
||||
def set_position(self, coordinates):
|
||||
print("[Apple][info] Setting Apple X, Y position")
|
||||
|
||||
x, y = coordinates
|
||||
|
||||
self.X = x
|
||||
self.Y = y
|
||||
|
||||
def coordinates(self):
|
||||
print("[Apple][info] Getting Apple coordinates")
|
||||
|
||||
return self.COORDINATES
|
||||
|
||||
def x(self):
|
||||
print("[Apple][info] Getting Apple X position")
|
||||
|
||||
return self.X
|
||||
|
||||
def y(self):
|
||||
print("[Apple][info] Getting Apple Y position")
|
||||
|
||||
return self.Y
|
||||
|
||||
def position(self):
|
||||
print("[Apple][info] Getting Apple X, Y position")
|
||||
|
||||
return (self.X, self.Y)
|
||||
|
||||
def clear(self):
|
||||
print("[Apple][info] Clearing Apple")
|
||||
|
||||
self.COORDINATES = None
|
||||
self.X = None
|
||||
self.Y = None
|
||||
|
||||
def cleanup(self):
|
||||
print("[Apple][info] Apple clean up")
|
||||
|
||||
self.clear()
|
||||
|
||||
def __exit__(self):
|
||||
print("[Apple][info] Apple exit")
|
||||
|
||||
self.cleanup()
|
||||
@@ -0,0 +1,68 @@
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
class Board:
|
||||
COLUMNS = None
|
||||
|
||||
ROWS = None
|
||||
|
||||
COORDINATES = None
|
||||
|
||||
def __init__(self, columns, rows):
|
||||
print("[Board][info] Initialising Board")
|
||||
|
||||
self.COLUMNS = columns
|
||||
self.ROWS = rows
|
||||
self.__generate()
|
||||
|
||||
def __generate(self):
|
||||
self.COORDINATES = [
|
||||
[0 for x in xrange(self.COLUMNS)]
|
||||
for y in xrange(self.ROWS)
|
||||
]
|
||||
|
||||
def columns(self):
|
||||
print("[Board][info] Getting Board columns")
|
||||
|
||||
return self.COLUMNS
|
||||
|
||||
def rows(self):
|
||||
print("[Board][info] Getting Board rows")
|
||||
|
||||
return self.ROWS
|
||||
|
||||
def coordinates(self):
|
||||
print("[Board][info] Getting Board coordinates")
|
||||
|
||||
return self.COORDINATES
|
||||
|
||||
def check_collision(self, snake, offset):
|
||||
print("[Board][info] Checking for Snake collision")
|
||||
|
||||
offset_x, offset_y = offset
|
||||
|
||||
if 0 > offset_x or offset_x > self.ROWS - 1 or 0 > offset_y or offset_y > self.COLUMNS - 1:
|
||||
return True
|
||||
else:
|
||||
for segment in snake.segments():
|
||||
if segment[0] == offset_x and segment[1] == offset_y:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def clear(self):
|
||||
print("[Board][info] Clearing board")
|
||||
|
||||
self.COORDINATES = None
|
||||
self.COLUMNS = None
|
||||
self.ROWS = None
|
||||
|
||||
def cleanup(self):
|
||||
print("[Board][info] Board clean up")
|
||||
|
||||
self.clear()
|
||||
|
||||
def __exit__(self):
|
||||
print("[Board][info] Board exit")
|
||||
|
||||
self.cleanup()
|
||||
@@ -0,0 +1,500 @@
|
||||
from __future__ import print_function
|
||||
from board import Board
|
||||
from apple import Apple
|
||||
from mock import MagicMock, patch
|
||||
from random import randint
|
||||
from snake import Snake
|
||||
from tinydb import TinyDB, Query
|
||||
import math
|
||||
import pygame
|
||||
import sys
|
||||
|
||||
|
||||
try:
|
||||
from sense_hat import SenseHat
|
||||
except ImportError:
|
||||
print("[Game][error] An error occurred importing 'sense_hat.SenseHat'")
|
||||
mock = MagicMock()
|
||||
mock.clear.return_value = True
|
||||
mock.set_pixel.return_value = True
|
||||
mock.show_message.return_value = True
|
||||
|
||||
with patch.dict('sys.modules', {'sense_hat': mock, 'sense_hat.SenseHat': mock.RTIMU}):
|
||||
from sense_hat import SenseHat
|
||||
|
||||
|
||||
class Game:
|
||||
BLOCK_SIZE = 36
|
||||
|
||||
COLUMNS = 8
|
||||
ROWS = 8
|
||||
|
||||
FPS = None
|
||||
|
||||
COLORS = [
|
||||
# 0 - Black
|
||||
(0, 0, 0),
|
||||
# 1 - Green
|
||||
(0, 255, 0),
|
||||
# 2 - Red
|
||||
(255, 0, 0),
|
||||
# 3 - Purple
|
||||
(128, 0, 128),
|
||||
# 4 - Blue
|
||||
(0, 0, 255),
|
||||
# 5 - Orange
|
||||
(255, 165, 0),
|
||||
# 6 - Cyan
|
||||
(0, 255, 255),
|
||||
# 7 - Yellow
|
||||
(255, 255, 0),
|
||||
# 8 - Dark Grey
|
||||
(35, 35, 35),
|
||||
# 9 - White
|
||||
(255, 255, 255)
|
||||
]
|
||||
|
||||
SHAPES = [
|
||||
# Snake
|
||||
[
|
||||
[1],
|
||||
],
|
||||
|
||||
# Apple
|
||||
[
|
||||
[2]
|
||||
],
|
||||
]
|
||||
|
||||
WIDTH = None
|
||||
HEIGHT = None
|
||||
|
||||
COUNTDOWN = None
|
||||
|
||||
SCORE_INCREMENT = None
|
||||
|
||||
SCORE = 0
|
||||
APPLES = 0
|
||||
|
||||
INTERVAL = None
|
||||
INTERVAL_INCREMENT = None
|
||||
|
||||
LEVEL = 1
|
||||
LEVEL_INCREMENT = None
|
||||
|
||||
PAUSED = False
|
||||
|
||||
GAMEOVER = False
|
||||
|
||||
BACKGROUND_GRID = None
|
||||
|
||||
SNAKE_MOVED = True
|
||||
|
||||
pygame = None
|
||||
pygame_font = None
|
||||
pygame_screen = None
|
||||
sensehat = None
|
||||
db = None
|
||||
|
||||
board = None
|
||||
snake = None
|
||||
apple = None
|
||||
|
||||
def __init__(self, columns, rows, fps, countdown, interval, score_increment, level_increment, interval_increment, pygame_instance=None):
|
||||
self.COLUMNS = columns
|
||||
self.ROWS = rows
|
||||
self.FPS = fps
|
||||
self.COUNTDOWN = countdown
|
||||
self.INTERVAL = interval
|
||||
self.SCORE_INCREMENT = score_increment
|
||||
self.LEVEL_INCREMENT = level_increment
|
||||
self.INTERVAL_INCREMENT = interval_increment
|
||||
|
||||
if pygame_instance is None:
|
||||
self.pygame = pygame
|
||||
else:
|
||||
self.pygame = pygame_instance
|
||||
|
||||
self.sensehat = SenseHat()
|
||||
self.db = TinyDB('data/database.json')
|
||||
|
||||
try:
|
||||
self.WIDTH = self.BLOCK_SIZE * self.COLUMNS + 150
|
||||
self.HEIGHT = self.BLOCK_SIZE * self.ROWS
|
||||
|
||||
self.BACKGROUND_GRID = [
|
||||
[8 if x % 2 == y % 2 else 0 for x in xrange(self.COLUMNS)]
|
||||
for y in xrange(self.ROWS)
|
||||
]
|
||||
|
||||
self.pygame.init()
|
||||
self.pygame.key.set_repeat(0, 0)
|
||||
self.pygame_font = self.pygame.font.Font(self.pygame.font.get_default_font(), 12)
|
||||
self.pygame_screen = self.pygame.display.set_mode((self.WIDTH, self.HEIGHT), 0, 24)
|
||||
self.pygame.event.set_blocked(self.pygame.MOUSEMOTION)
|
||||
|
||||
self.board = Board(self.COLUMNS, self.ROWS)
|
||||
self.__generate_snake()
|
||||
self.__generate_apple()
|
||||
except AttributeError:
|
||||
print("[Game][error] An error occurred initialising game")
|
||||
|
||||
def start(self, run_once=False):
|
||||
print("[Game][info] Starting game")
|
||||
|
||||
try:
|
||||
pygame_wait = True
|
||||
|
||||
while pygame_wait:
|
||||
for event in self.pygame.event.get():
|
||||
if event.type == self.pygame.KEYDOWN:
|
||||
if event.key == self.pygame.K_RETURN:
|
||||
pygame_wait = False
|
||||
elif event.key == self.pygame.K_ESCAPE:
|
||||
self.quit()
|
||||
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
self.__display_message("Press to start")
|
||||
self.pygame.display.update()
|
||||
|
||||
self.sensehat.clear()
|
||||
except AttributeError:
|
||||
print("[Game][error] An error occurred starting game")
|
||||
|
||||
self.__countdown()
|
||||
self.__loop()
|
||||
self.finish()
|
||||
|
||||
if run_once is not True:
|
||||
self.start()
|
||||
|
||||
def __countdown(self):
|
||||
print("[Game][info] Starting game countdown")
|
||||
|
||||
try:
|
||||
seconds = 0
|
||||
|
||||
while True:
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
remaining = (self.COUNTDOWN - seconds)
|
||||
|
||||
if seconds > self.COUNTDOWN:
|
||||
break
|
||||
|
||||
if seconds == self.COUNTDOWN:
|
||||
self.__display_message("Go!")
|
||||
else:
|
||||
self.__display_message("%d!" % remaining)
|
||||
|
||||
seconds += 1
|
||||
self.sensehat.clear()
|
||||
self.pygame.display.update()
|
||||
self.pygame.time.wait(1000)
|
||||
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
self.sensehat.clear()
|
||||
except AttributeError:
|
||||
print("[Game][error] An error occurred starting game countdown")
|
||||
|
||||
def __loop(self):
|
||||
print("[Game][info] Starting game loop")
|
||||
|
||||
try:
|
||||
self.pygame.time.set_timer(pygame.USEREVENT + 1, self.INTERVAL)
|
||||
|
||||
key_actions = {
|
||||
'ESCAPE': lambda: self.quit(),
|
||||
'LEFT': lambda: self.__direction_left(),
|
||||
'RIGHT': lambda: self.__direction_right(),
|
||||
'DOWN': lambda: self.__direction_down(),
|
||||
'UP': lambda: self.__direction_up(),
|
||||
'p': lambda: self.toggle_pause(),
|
||||
}
|
||||
|
||||
pygame_clock = self.pygame.time.Clock()
|
||||
|
||||
while not self.GAMEOVER:
|
||||
self.sensehat.clear()
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
|
||||
if self.PAUSED:
|
||||
self.__display_message("Paused")
|
||||
else:
|
||||
self.__draw_line(
|
||||
((self.BLOCK_SIZE * self.COLUMNS) + 1, 0),
|
||||
((self.BLOCK_SIZE * self.COLUMNS) + 1, (self.HEIGHT - 1)),
|
||||
self.COLORS[9]
|
||||
)
|
||||
|
||||
self.__display_message(
|
||||
"Score: %d\n\nLevel: %d\n\nApples: %d" % (self.SCORE, self.LEVEL, self.APPLES),
|
||||
((self.BLOCK_SIZE * self.COLUMNS) + self.BLOCK_SIZE, 2),
|
||||
self.COLORS[9],
|
||||
self.COLORS[0],
|
||||
False
|
||||
)
|
||||
|
||||
self.__draw_matrix(self.BACKGROUND_GRID, (0, 0), None, False)
|
||||
self.__draw_matrix(self.board.coordinates(), (0, 0), None, False)
|
||||
self.__draw_matrix(self.snake.coordinates(self.COLUMNS, self.ROWS), (0, 0))
|
||||
self.__draw_matrix(self.apple.coordinates(), (self.apple.x(), self.apple.y()))
|
||||
|
||||
self.pygame.display.update()
|
||||
|
||||
for event in self.pygame.event.get():
|
||||
if event.type == self.pygame.USEREVENT + 1:
|
||||
self.__move()
|
||||
elif event.type == self.pygame.QUIT:
|
||||
self.quit()
|
||||
elif event.type == self.pygame.KEYDOWN:
|
||||
for key in key_actions:
|
||||
if event.key == eval("self.pygame.K_" + key):
|
||||
key_actions[key]()
|
||||
|
||||
pygame_clock.tick(self.FPS)
|
||||
except AttributeError:
|
||||
print("[Game][error] An error occurred during game loop")
|
||||
|
||||
def __generate_snake(self):
|
||||
print("[Game][info] Generating snake")
|
||||
|
||||
self.snake = Snake(self.SHAPES[0], int(math.floor(self.COLUMNS / 2)), int(math.floor(self.ROWS / 2)))
|
||||
|
||||
def __generate_apple(self):
|
||||
print("[Game][info] Generating apple")
|
||||
|
||||
apple_collision = True
|
||||
offset_x, offset_y = (0, 0)
|
||||
|
||||
while apple_collision:
|
||||
offset_x = randint(0, self.ROWS - 1)
|
||||
offset_y = randint(0, self.COLUMNS - 1)
|
||||
|
||||
apple_collision = self.board.check_collision(self.snake, (offset_x, offset_y))
|
||||
|
||||
self.apple = Apple(self.SHAPES[1], offset_x, offset_y)
|
||||
|
||||
def __display_message(self, message, coordinates=None, color=COLORS[9], background_color=COLORS[0], sensehat=True):
|
||||
print("[Game][info] Displaying message")
|
||||
|
||||
if sensehat:
|
||||
self.sensehat.show_message(message, text_colour=color, scroll_speed=0.05)
|
||||
|
||||
for i, line in enumerate(message.splitlines()):
|
||||
message_image = self.pygame_font.render(line, False, color, background_color)
|
||||
|
||||
if coordinates is not None:
|
||||
position_x, position_y = coordinates
|
||||
else:
|
||||
message_image_center_x, message_image_center_y = message_image.get_size()
|
||||
message_image_center_x //= 2
|
||||
message_image_center_y //= 2
|
||||
position_x = self.WIDTH // 2 - message_image_center_x
|
||||
position_y = self.HEIGHT // 2 - message_image_center_y
|
||||
|
||||
self.pygame_screen.blit(
|
||||
message_image,
|
||||
(position_x, position_y + i * 22)
|
||||
)
|
||||
|
||||
def __draw_line(self, start_position, end_position, color=COLORS[9], sensehat=True):
|
||||
print("[Game][info] Drawing line")
|
||||
|
||||
if sensehat:
|
||||
start_x, start_y = start_position
|
||||
end_x, end_y = start_position
|
||||
|
||||
for i in xrange(start_y, end_y):
|
||||
for j in xrange(start_x, end_x):
|
||||
if start_x == end_x or start_y == end_y:
|
||||
self.sensehat.set_pixel(j, i, color)
|
||||
|
||||
self.pygame.draw.line(self.pygame_screen, color, start_position, end_position)
|
||||
|
||||
def __draw_matrix(self, matrix, offset, color=None, sensehat=True):
|
||||
print("[Game][info] Drawing matrix")
|
||||
|
||||
offset_x, offset_y = offset
|
||||
|
||||
for y, row in enumerate(matrix):
|
||||
for x, val in enumerate(row):
|
||||
if val:
|
||||
if color is None:
|
||||
shape_color = self.COLORS[val]
|
||||
else:
|
||||
shape_color = color
|
||||
|
||||
if sensehat:
|
||||
self.sensehat.set_pixel((offset_x + x), (offset_y + y), shape_color)
|
||||
|
||||
self.pygame.draw.rect(
|
||||
self.pygame_screen,
|
||||
shape_color,
|
||||
self.pygame.Rect((offset_x + x) * self.BLOCK_SIZE, (offset_y + y) * self.BLOCK_SIZE, self.BLOCK_SIZE, self.BLOCK_SIZE),
|
||||
0
|
||||
)
|
||||
|
||||
def __count_clear_apples(self, apples):
|
||||
print("[Game][info] Counting cleared apples")
|
||||
|
||||
if apples > 0:
|
||||
self.APPLES += apples
|
||||
self.SCORE += self.SCORE_INCREMENT * self.LEVEL
|
||||
|
||||
if self.APPLES >= self.LEVEL * self.LEVEL_INCREMENT:
|
||||
self.LEVEL += 1
|
||||
delay = self.INTERVAL - self.INTERVAL_INCREMENT * (self.LEVEL - 1)
|
||||
delay = 100 if delay < 100 else delay
|
||||
self.pygame.time.set_timer(self.pygame.USEREVENT + 1, delay)
|
||||
|
||||
def __move(self):
|
||||
print("[Game][info] Moving snake %s" % (self.snake.direction()))
|
||||
|
||||
if not self.GAMEOVER and not self.PAUSED:
|
||||
new_x, new_y = self.snake.head()
|
||||
|
||||
if self.snake.direction() == self.snake.DIRECTION_UP:
|
||||
new_y = self.snake.y() - 1
|
||||
elif self.snake.direction() == self.snake.DIRECTION_DOWN:
|
||||
new_y = self.snake.y() + 1
|
||||
elif self.snake.direction() == self.snake.DIRECTION_LEFT:
|
||||
new_x = self.snake.x() - 1
|
||||
elif self.snake.direction() == self.snake.DIRECTION_RIGHT:
|
||||
new_x = self.snake.x() + 1
|
||||
|
||||
if self.board.check_collision(self.snake, (new_x, new_y)):
|
||||
self.GAMEOVER = True
|
||||
|
||||
return False
|
||||
|
||||
tail_x, tail_y = self.snake.tail()
|
||||
self.snake.set_position((new_x, new_y))
|
||||
cleared_apples = 0
|
||||
|
||||
if new_x == self.apple.x() and new_y == self.apple.y():
|
||||
self.snake.add_segment((tail_x, tail_y))
|
||||
cleared_apples += 1
|
||||
self.__generate_apple()
|
||||
|
||||
self.__count_clear_apples(cleared_apples)
|
||||
self.SNAKE_MOVED = True
|
||||
|
||||
return True
|
||||
|
||||
def __direction_up(self):
|
||||
print("[Game][info] Event direction up")
|
||||
|
||||
if self.snake.direction() != self.snake.DIRECTION_DOWN and self.SNAKE_MOVED is True:
|
||||
self.snake.set_direction(self.snake.DIRECTION_UP)
|
||||
self.SNAKE_MOVED = False
|
||||
|
||||
def __direction_down(self):
|
||||
print("[Game][info] Event direction down")
|
||||
|
||||
if self.snake.direction() != self.snake.DIRECTION_UP and self.SNAKE_MOVED is True:
|
||||
self.snake.set_direction(self.snake.DIRECTION_DOWN)
|
||||
self.SNAKE_MOVED = False
|
||||
|
||||
def __direction_left(self):
|
||||
print("[Game][info] Event direction left")
|
||||
|
||||
if self.snake.direction() != self.snake.DIRECTION_RIGHT and self.SNAKE_MOVED is True:
|
||||
self.snake.set_direction(self.snake.DIRECTION_LEFT)
|
||||
self.SNAKE_MOVED = False
|
||||
|
||||
def __direction_right(self):
|
||||
print("[Game][info] Event direction right")
|
||||
|
||||
if self.snake.direction() != self.snake.DIRECTION_LEFT and self.SNAKE_MOVED is True:
|
||||
self.snake.set_direction(self.snake.DIRECTION_RIGHT)
|
||||
self.SNAKE_MOVED = False
|
||||
|
||||
def toggle_pause(self):
|
||||
print("[Game][info] Toggling paused state")
|
||||
|
||||
self.PAUSED = not self.PAUSED
|
||||
|
||||
def get_score(self):
|
||||
print("[Game][info] Calculating score")
|
||||
|
||||
return self.SCORE
|
||||
|
||||
def print_score(self, high_score=False):
|
||||
print("[Game][info] Printing score")
|
||||
|
||||
score = self.get_score()
|
||||
|
||||
try:
|
||||
self.sensehat.clear()
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
|
||||
if high_score:
|
||||
self.__display_message("Game Over!\n\nHigh score: %d" % score)
|
||||
self.pygame.display.update()
|
||||
else:
|
||||
self.__display_message("Game Over!\n\nYour score: %d!" % self.get_score())
|
||||
self.pygame.display.update()
|
||||
|
||||
self.pygame.time.wait(3000)
|
||||
except AttributeError:
|
||||
print("[Game][error] An error occurred printing score")
|
||||
|
||||
def finish(self):
|
||||
print("[Game][info] Finishing game")
|
||||
|
||||
score = self.get_score()
|
||||
|
||||
self.pygame.display.update()
|
||||
|
||||
if self.db.contains(Query().score >= score):
|
||||
self.print_score()
|
||||
else:
|
||||
self.print_score(True)
|
||||
|
||||
self.db.insert({'score': score})
|
||||
self.reset()
|
||||
|
||||
def quit(self):
|
||||
print("[Game][info] Quitting game")
|
||||
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
self.sensehat.clear()
|
||||
self.__display_message("Exiting...")
|
||||
self.pygame.display.update()
|
||||
sys.exit()
|
||||
|
||||
def reset(self):
|
||||
print("[Game][info] Resetting game")
|
||||
|
||||
self.PAUSED = False
|
||||
self.GAMEOVER = False
|
||||
self.SCORE = 0
|
||||
self.APPLES = 0
|
||||
self.LEVEL = 1
|
||||
self.SNAKE_MOVED = True
|
||||
|
||||
self.board = Board(self.COLUMNS, self.ROWS)
|
||||
self.snake = None
|
||||
self.apple = None
|
||||
self.__generate_snake()
|
||||
self.__generate_apple()
|
||||
|
||||
self.sensehat.clear()
|
||||
self.pygame.time.set_timer(pygame.USEREVENT + 1, 0)
|
||||
self.pygame.display.update()
|
||||
|
||||
def cleanup(self):
|
||||
print("[Game][info] Game clean up")
|
||||
|
||||
try:
|
||||
self.sensehat.clear()
|
||||
self.pygame_screen.fill(self.COLORS[0])
|
||||
self.pygame.display.update()
|
||||
self.pygame.quit()
|
||||
except AttributeError:
|
||||
print("[Game][error] An error occurred cleaning up")
|
||||
|
||||
def __exit__(self):
|
||||
print("[Game][info] Game exit")
|
||||
|
||||
self.cleanup()
|
||||
@@ -0,0 +1,165 @@
|
||||
|
||||
|
||||
class Snake:
|
||||
COORDINATES = None
|
||||
|
||||
SEGMENTS = None
|
||||
|
||||
X = None
|
||||
Y = None
|
||||
|
||||
DIRECTION = None
|
||||
|
||||
DIRECTION_UP = 'up'
|
||||
DIRECTION_DOWN = 'down'
|
||||
DIRECTION_LEFT = 'left'
|
||||
DIRECTION_RIGHT = 'right'
|
||||
|
||||
def __init__(self, coordinates, x=0, y=0, direction=DIRECTION_LEFT):
|
||||
print("[Snake][info] Initialising Snake")
|
||||
|
||||
self.COORDINATES = coordinates
|
||||
self.SEGMENTS = []
|
||||
self.add_segment((x, y))
|
||||
self.X = x
|
||||
self.Y = y
|
||||
self.DIRECTION = direction
|
||||
|
||||
def set_coordinates(self, coordinates):
|
||||
print("[Snake][info] Setting Snake coordinates")
|
||||
|
||||
self.COORDINATES = coordinates
|
||||
|
||||
def set_direction(self, direction):
|
||||
print("[Snake][info] Setting Snake direction")
|
||||
|
||||
self.DIRECTION = direction
|
||||
|
||||
def set_x(self, x):
|
||||
print("[Snake][info] Setting Snake X position")
|
||||
|
||||
if self.length() > 1:
|
||||
self.remove_segment(-1)
|
||||
self.add_segment((x, self.SEGMENTS[0][1]), 0)
|
||||
else:
|
||||
self.SEGMENTS[0] = (x, self.SEGMENTS[0][1])
|
||||
|
||||
self.X = x
|
||||
|
||||
def set_y(self, y):
|
||||
print("[Snake][info] Setting Snake Y position")
|
||||
|
||||
if self.length() > 1:
|
||||
self.remove_segment(-1)
|
||||
self.add_segment((self.SEGMENTS[0][0], y), 0)
|
||||
else:
|
||||
self.SEGMENTS[0] = (self.SEGMENTS[0][0], y)
|
||||
|
||||
self.Y = y
|
||||
|
||||
def set_position(self, coordinates):
|
||||
print("[Snake][info] Setting Snake X, Y position")
|
||||
|
||||
x, y = coordinates
|
||||
|
||||
if self.length() > 1:
|
||||
self.remove_segment(-1)
|
||||
self.add_segment(coordinates, 0)
|
||||
else:
|
||||
self.SEGMENTS[0] = coordinates
|
||||
|
||||
self.X = x
|
||||
self.Y = y
|
||||
|
||||
def add_segment(self, coordinates, position=-1):
|
||||
print("[Snake][info] Creating Snake segment at index %d" % (position))
|
||||
|
||||
self.SEGMENTS.insert(position if position != -1 else len(self.SEGMENTS), coordinates)
|
||||
|
||||
def remove_segment(self, position=-1):
|
||||
print("[Snake][info] Removing Snake segment at index %d" % (position))
|
||||
|
||||
self.SEGMENTS.pop(position)
|
||||
|
||||
def coordinates(self, columns=0, rows=0):
|
||||
print("[Snake][info] Getting Snake coordinates")
|
||||
|
||||
if columns == 0 and rows == 0:
|
||||
return self.COORDINATES
|
||||
|
||||
coordinates = [
|
||||
[False for x in xrange(columns)]
|
||||
for y in xrange(rows)
|
||||
]
|
||||
|
||||
for segment in self.SEGMENTS:
|
||||
coordinates[segment[1]][segment[0]] = self.COORDINATES[0][0]
|
||||
|
||||
return coordinates
|
||||
|
||||
def direction(self):
|
||||
print("[Snake][info] Getting Snake direction")
|
||||
|
||||
return self.DIRECTION
|
||||
|
||||
def x(self):
|
||||
print("[Snake][info] Getting Snake X position")
|
||||
|
||||
return self.X
|
||||
|
||||
def y(self):
|
||||
print("[Snake][info] Getting Snake Y position")
|
||||
|
||||
return self.Y
|
||||
|
||||
def position(self):
|
||||
print("[Snake][info] Getting Snake X,Y position")
|
||||
|
||||
return (self.X, self.Y)
|
||||
|
||||
def segments(self):
|
||||
print("[Snake][info] Getting all Snake segments")
|
||||
|
||||
return self.SEGMENTS
|
||||
|
||||
def head(self):
|
||||
print("[Snake][info] Getting Snake head")
|
||||
|
||||
return self.SEGMENTS[0]
|
||||
|
||||
def body(self):
|
||||
print("[Snake][info] Getting Snake body")
|
||||
|
||||
if len(self.SEGMENTS) < 2:
|
||||
return None
|
||||
|
||||
return self.SEGMENTS[1:]
|
||||
|
||||
def tail(self):
|
||||
print("[Snake][info] Getting Snake tail")
|
||||
|
||||
return self.SEGMENTS[-1]
|
||||
|
||||
def length(self):
|
||||
print("[Snake][info] Getting Snake length")
|
||||
|
||||
return len(self.SEGMENTS)
|
||||
|
||||
def clear(self):
|
||||
print("[Snake][info] Clearing Snake")
|
||||
|
||||
self.COORDINATES = None
|
||||
self.SEGMENTS = None
|
||||
self.X = None
|
||||
self.Y = None
|
||||
self.DIRECTION = None
|
||||
|
||||
def cleanup(self):
|
||||
print("[Snake][info] Snake clean up")
|
||||
|
||||
self.clear()
|
||||
|
||||
def __exit__(self):
|
||||
print("[Snake][info] Snake exit")
|
||||
|
||||
self.cleanup()
|
||||
@@ -0,0 +1,25 @@
|
||||
from __future__ import print_function
|
||||
from lib.game import Game
|
||||
import atexit
|
||||
import config
|
||||
|
||||
|
||||
def main():
|
||||
game = Game(
|
||||
config.game['columns'],
|
||||
config.game['rows'],
|
||||
config.game['fps'],
|
||||
config.game['countdown'],
|
||||
config.game['interval'],
|
||||
config.game['score_increment'],
|
||||
config.game['level_increment'],
|
||||
config.game['interval_increment'],
|
||||
)
|
||||
|
||||
game.start()
|
||||
|
||||
atexit.register(game.__exit__)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,60 @@
|
||||
from setuptools import setup, find_packages
|
||||
from codecs import open
|
||||
from os import path
|
||||
|
||||
here = path.abspath(path.dirname(__file__))
|
||||
|
||||
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name='sense-hat-snake',
|
||||
|
||||
version='1.0.0',
|
||||
|
||||
description='Raspberry Pi Python sense hat snake',
|
||||
long_description=long_description,
|
||||
|
||||
url='https://github.com/bradcornford/Sense-Hat-Snake',
|
||||
|
||||
author='Bradley Cornford',
|
||||
author_email='me@bradleycornford.co.uk',
|
||||
|
||||
license='MIT',
|
||||
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
|
||||
'Intended Audience :: Developers',
|
||||
|
||||
'Topic :: Software Development :: Build Tools',
|
||||
|
||||
'License :: OSI Approved :: MIT License',
|
||||
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
],
|
||||
|
||||
keywords='sense hat snake raspberry pi',
|
||||
|
||||
packages=find_packages(exclude=['contrib', 'docs', 'test']),
|
||||
|
||||
install_requires=['pygame', 'sense-hat', 'tinydb', 'mock'],
|
||||
|
||||
extras_require={
|
||||
'dev': ['check-manifest'],
|
||||
'test': ['coverage'],
|
||||
},
|
||||
|
||||
package_data={},
|
||||
|
||||
data_files=[],
|
||||
|
||||
entry_points={
|
||||
'console_scripts': []
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,56 @@
|
||||
from __future__ import print_function
|
||||
from sensehatsnake.lib.apple import Apple
|
||||
import unittest
|
||||
|
||||
|
||||
class AppleTestCase(unittest.TestCase):
|
||||
COORDINATES = [
|
||||
[1]
|
||||
]
|
||||
|
||||
apple = None
|
||||
|
||||
def setUp(self):
|
||||
self.apple = Apple(self.COORDINATES)
|
||||
|
||||
def test__init__(self):
|
||||
self.assertIsInstance(self.apple, Apple)
|
||||
|
||||
def test_set_coordinates(self):
|
||||
self.assertIs(self.apple.set_coordinates([[0]]), None)
|
||||
self.assertEquals(self.apple.coordinates(), [[0]])
|
||||
|
||||
def test_set_x(self):
|
||||
self.assertIs(self.apple.set_x(1), None)
|
||||
self.assertEquals(self.apple.x(), 1)
|
||||
|
||||
def test_set_y(self):
|
||||
self.assertIs(self.apple.set_y(1), None)
|
||||
self.assertEquals(self.apple.y(), 1)
|
||||
|
||||
def test_set_position(self):
|
||||
self.assertIs(self.apple.set_position((1, 1)), None)
|
||||
self.assertEquals(self.apple.x(), 1)
|
||||
self.assertEquals(self.apple.y(), 1)
|
||||
|
||||
def test_coordinates(self):
|
||||
self.assertEquals(self.apple.coordinates(), self.COORDINATES)
|
||||
|
||||
def test_x(self):
|
||||
self.assertEquals(self.apple.x(), 0)
|
||||
|
||||
def test_y(self):
|
||||
self.assertEquals(self.apple.y(), 0)
|
||||
|
||||
def test_position(self):
|
||||
self.assertEquals(self.apple.position(), (0, 0))
|
||||
|
||||
def test_cleanup(self):
|
||||
self.assertIs(self.apple.cleanup(), None)
|
||||
|
||||
def test__exit__(self):
|
||||
self.assertIs(self.apple.__exit__(), None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,43 @@
|
||||
from __future__ import print_function
|
||||
from sensehatsnake.lib.board import Board
|
||||
from sensehatsnake.lib.snake import Snake
|
||||
import unittest
|
||||
|
||||
|
||||
class BoardTestCase(unittest.TestCase):
|
||||
COLUMNS = 2
|
||||
ROWS = 2
|
||||
|
||||
board = None
|
||||
|
||||
def setUp(self):
|
||||
self.board = Board(self.COLUMNS, self.ROWS)
|
||||
|
||||
def test__init__(self):
|
||||
self.assertIsInstance(self.board, Board)
|
||||
|
||||
def test_columns(self):
|
||||
self.assertEquals(self.board.columns(), self.COLUMNS)
|
||||
|
||||
def test_rows(self):
|
||||
self.assertEquals(self.board.rows(), self.ROWS)
|
||||
|
||||
def test_coordinates(self):
|
||||
self.assertEquals(self.board.coordinates(), [[0, 0], [0, 0]])
|
||||
|
||||
def test_check_collision(self):
|
||||
snake = Snake([[1]])
|
||||
self.assertIs(self.board.check_collision(snake, (snake.x(), snake.y())), True)
|
||||
|
||||
def test_clear(self):
|
||||
self.assertIs(self.board.clear(), None)
|
||||
|
||||
def test_cleanup(self):
|
||||
self.assertIs(self.board.cleanup(), None)
|
||||
|
||||
def test__exit__(self):
|
||||
self.assertIs(self.board.__exit__(), None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,93 @@
|
||||
from __future__ import print_function
|
||||
from sensehatsnake.lib.game import Game
|
||||
from mock import MagicMock
|
||||
import unittest
|
||||
|
||||
|
||||
class GameTestCase(unittest.TestCase):
|
||||
COLUMNS = 2
|
||||
ROWS = 2
|
||||
FPS = 1
|
||||
COUNTDOWN = 0
|
||||
INTERVAL = 0
|
||||
SCORE_INCREMENT = 1
|
||||
LEVEL_INCREMENT = 1
|
||||
INTERVAL_INCREMENT = 0
|
||||
|
||||
game = None
|
||||
|
||||
def setUp(self):
|
||||
mock = MagicMock()
|
||||
|
||||
keypress_event_mock = MagicMock()
|
||||
keypress_event_mock.type = 1
|
||||
keypress_event_mock.key = 1
|
||||
|
||||
userevent_event_mock = MagicMock()
|
||||
userevent_event_mock.type = 3
|
||||
|
||||
mock.get_size.return_value = (0, 0)
|
||||
mock.get.return_value = [keypress_event_mock, userevent_event_mock]
|
||||
mock.get_ticks.side_effect = [0, 6000, 0, 5000]
|
||||
|
||||
mock.Font.return_value = mock
|
||||
mock.render.return_value = mock
|
||||
|
||||
mock.KEYDOWN = 1
|
||||
mock.K_RETURN = 1
|
||||
mock.USEREVENT = 2
|
||||
|
||||
mock.font = mock
|
||||
mock.display = mock
|
||||
mock.event = mock
|
||||
mock.time = mock
|
||||
|
||||
self.game = Game(
|
||||
self.COLUMNS,
|
||||
self.ROWS,
|
||||
self.FPS,
|
||||
self.COUNTDOWN,
|
||||
self.INTERVAL,
|
||||
self.SCORE_INCREMENT,
|
||||
self.LEVEL_INCREMENT,
|
||||
self.INTERVAL_INCREMENT,
|
||||
mock
|
||||
)
|
||||
|
||||
def test__init__(self):
|
||||
self.assertIsInstance(self.game, Game)
|
||||
|
||||
def test_start(self):
|
||||
self.assertIs(self.game.start(True), None)
|
||||
|
||||
def test_toggle_pause(self):
|
||||
self.assertIs(self.game.toggle_pause(), None)
|
||||
self.assertEqual(self.game.PAUSED, True)
|
||||
self.assertIs(self.game.toggle_pause(), None)
|
||||
self.assertEqual(self.game.PAUSED, False)
|
||||
|
||||
def test_get_score(self):
|
||||
self.assertIs(self.game.get_score(), 0)
|
||||
|
||||
def test_print_score(self):
|
||||
self.assertIs(self.game.print_score(), None)
|
||||
|
||||
def test_finish(self):
|
||||
self.assertIs(self.game.finish(), None)
|
||||
|
||||
def test_quit(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
self.assertIs(self.game.quit(), None)
|
||||
|
||||
def test_reset(self):
|
||||
self.assertIs(self.game.reset(), None)
|
||||
|
||||
def test_cleanup(self):
|
||||
self.assertIs(self.game.cleanup(), None)
|
||||
|
||||
def test__exit__(self):
|
||||
self.assertIs(self.game.__exit__(), None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,90 @@
|
||||
from __future__ import print_function
|
||||
from sensehatsnake.lib.snake import Snake
|
||||
import unittest
|
||||
|
||||
|
||||
class SnakeTestCase(unittest.TestCase):
|
||||
COORDINATES = [
|
||||
[1]
|
||||
]
|
||||
|
||||
COLUMNS = 2
|
||||
ROWS = 2
|
||||
|
||||
snake = None
|
||||
|
||||
def setUp(self):
|
||||
self.snake = Snake(self.COORDINATES)
|
||||
|
||||
def test__init__(self):
|
||||
self.assertIsInstance(self.snake, Snake)
|
||||
|
||||
def test_set_coordinates(self):
|
||||
self.assertIs(self.snake.set_coordinates([[0]]), None)
|
||||
self.assertEquals(self.snake.coordinates(), [[0]])
|
||||
|
||||
def test_set_direction(self):
|
||||
self.assertIs(self.snake.set_direction(self.snake.DIRECTION_UP), None)
|
||||
self.assertEquals(self.snake.direction(), self.snake.DIRECTION_UP)
|
||||
|
||||
def test_set_x(self):
|
||||
self.assertIs(self.snake.set_x(1), None)
|
||||
self.assertEquals(self.snake.x(), 1)
|
||||
|
||||
def test_set_y(self):
|
||||
self.assertIs(self.snake.set_y(1), None)
|
||||
self.assertEquals(self.snake.y(), 1)
|
||||
|
||||
def test_set_position(self):
|
||||
self.assertIs(self.snake.set_position((1, 1)), None)
|
||||
self.assertEquals(self.snake.x(), 1)
|
||||
self.assertEquals(self.snake.y(), 1)
|
||||
|
||||
def test_add_segment(self):
|
||||
self.assertIs(self.snake.add_segment((1, 1)), None)
|
||||
self.assertEquals(self.snake.length(), 2)
|
||||
|
||||
def test_remove_segment(self):
|
||||
self.assertIs(self.snake.remove_segment(), None)
|
||||
self.assertEquals(self.snake.length(), 0)
|
||||
|
||||
def test_coordinates(self):
|
||||
self.assertEquals(self.snake.coordinates(), self.COORDINATES)
|
||||
self.assertEquals(self.snake.coordinates(self.COLUMNS, self.ROWS), [[self.COORDINATES[0][0], False], [False, False]])
|
||||
|
||||
def test_direction(self):
|
||||
self.assertEquals(self.snake.direction(), self.snake.DIRECTION_LEFT)
|
||||
|
||||
def test_x(self):
|
||||
self.assertEquals(self.snake.x(), 0)
|
||||
|
||||
def test_y(self):
|
||||
self.assertEquals(self.snake.y(), 0)
|
||||
|
||||
def test_position(self):
|
||||
self.assertEquals(self.snake.position(), (0, 0))
|
||||
|
||||
def test_segments(self):
|
||||
self.assertEquals(self.snake.segments(), [(0, 0)])
|
||||
|
||||
def test_head(self):
|
||||
self.assertEquals(self.snake.head(), (0, 0))
|
||||
|
||||
def test_body(self):
|
||||
self.assertEquals(self.snake.body(), None)
|
||||
|
||||
def test_tail(self):
|
||||
self.assertEquals(self.snake.tail(), (0, 0))
|
||||
|
||||
def test_length(self):
|
||||
self.assertEquals(self.snake.length(), 1)
|
||||
|
||||
def test_cleanup(self):
|
||||
self.assertIs(self.snake.cleanup(), None)
|
||||
|
||||
def test__exit__(self):
|
||||
self.assertIs(self.snake.__exit__(), None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,8 @@
|
||||
from __future__ import print_function
|
||||
import sensehatsnake.main
|
||||
import unittest
|
||||
|
||||
|
||||
class MainTestCase(unittest.TestCase):
|
||||
def test__init__(self):
|
||||
self.assertTrue("main" in dir(sensehatsnake.main))
|
||||
@@ -0,0 +1,21 @@
|
||||
[tox]
|
||||
envlist = py{27,33,34}
|
||||
|
||||
[testenv]
|
||||
basepython =
|
||||
py27: python2.7
|
||||
py33: python3.3
|
||||
py34: python3.4
|
||||
deps =
|
||||
check-manifest
|
||||
readme_renderer
|
||||
flake8
|
||||
pytest
|
||||
commands =
|
||||
check-manifest --ignore tox.ini,tests*
|
||||
python setup.py check -m -r -s
|
||||
flake8 .
|
||||
py.test tests
|
||||
[flake8]
|
||||
exclude = .tox,*.egg,build,data
|
||||
select = E,W,F
|
||||
Reference in New Issue
Block a user