#!/usr/bin/python import math import pygame import random from gi.repository import Gtk from sugar3.graphics.style import Color from sugar3 import profile def get_profile_colors(): profile_color = profile.get_color() fill = Color(profile_color.get_fill_color()).get_rgba() stroke = Color(profile_color.get_stroke_color()).get_rgba() fill = [i * 255 for i in fill] stroke = [i * 255 for i in stroke] return fill, stroke BALL_COLOR_FILL, BALL_COLOR_STROKE = get_profile_colors() GROUND_COLOR = 0, 0, 0 BACKGROUND_COLOR = 180, 180, 180 BALL_SIZE = 20 class AccelerometerDevice(object): # http://wiki.laptop.org/go/Accelerometer ACCELEROMETER_DEVICE = '/sys/devices/platform/lis3lv02d/position' SPEED = (0.0, 0.0) FRICTION = 0.993 def __init__(self, acceleration=0.05): self._device = open(self.ACCELEROMETER_DEVICE, 'r') self.acceleration = acceleration def reseek(self): self._device.seek(0) def read(self): self.reseek() x, y, z = self.parse(self._device.read()) return x, y, z def parse(self, data): # discard parentheses from the data data = data[1:-2] x, y, z = map(int, data.split(',')) return x, y, z def close(self): self._device.close() class Ball(object): def __init__(self, initial_x, initial_y): self._x = initial_x self._y = initial_y def get_position(self): return self._x, self._y def set_position(self, new_x, new_y): self._x = new_x self._y = new_y def update(self, vx, vy): self._x += vx self._y += vy def draw(self, surface): pygame.draw.circle(surface, BALL_COLOR_STROKE, (self._x, self._y), BALL_SIZE) pygame.draw.circle(surface, BALL_COLOR_FILL, (self._x, self._y), BALL_SIZE - 3) class Level(object): def __init__(self, hardness, parent_width, parent_height): # FIXME, use the hardness value to make random levels of # different difficulty self._hardness = hardness self._parent_width = parent_width self._parent_height = parent_height self._rects = [] self._ball_start = None self._ball_end = None self.calculate_level() def set_hole(self, x, y): self._ball_end = (x, y) def calculate_level(self): rect = pygame.Rect((0, 0), (self._parent_width, self._parent_height)) self._rects.append(rect) self._ball_start = self._parent_width / 2, self._parent_height / 2 self._ball_end = self._parent_width / 3, self._parent_height / 3 def get_ball_start(self): return self._ball_start def is_on_hole(self, ball_position): """Test if the ball is on the hole.""" distance_x = ball_position[0] - self._ball_end[0] distance_y = ball_position[1] - self._ball_end[1] distance = math.hypot(distance_x, distance_y) return distance < BALL_SIZE / 2 def is_on_ground(self, ball_position): """Test if the ball is on the ground.""" for rect in self._rects: if rect.collidepoint(ball_position): return True return False def draw(self, surface): # draw ground: for rect in self._rects: pygame.draw.rect(surface, GROUND_COLOR, rect) # draw hole: pygame.draw.circle(surface, BALL_COLOR_FILL, self._ball_end, BALL_SIZE, 2) class TiltGame(object): def __init__(self): # Set up a clock for managing the frame rate. self.clock = pygame.time.Clock() self._level = None self._ball = None self._running = False self._paused = False self._screen = None # TODO: add a button to switch this self.show_axis = True self.accelerometer = AccelerometerDevice() def setup(self): self._level = Level(0.5, *self._screen.get_size()) ball_x, ball_y = self._level.get_ball_start() self._ball = Ball(ball_x, ball_y) def set_paused(self, paused): self._paused = paused # Called to save the state of the game to the Journal. def write_file(self, file_path): pass # Called to load the state of the game from the Journal. def read_file(self, file_path): pass # The main game loop. def run(self): self._screen = pygame.display.get_surface() self.setup() self._running = True font = pygame.font.Font(pygame.font.get_default_font(), 22) while self._running: # Pump GTK messages. while Gtk.events_pending(): Gtk.main_iteration() # Pump PyGame messages. for event in pygame.event.get(): if event.type == pygame.QUIT: return elif event.type == pygame.VIDEORESIZE: pygame.display.set_mode(event.size, pygame.RESIZABLE) # Move the ball if not self._paused: # Get the position of the accelerometer x, y, z = self.accelerometer.read() if self.show_axis: surface_x = font.render('X: %s' % x, True, (255, 0, 0)) surface_y = font.render('Y: %s' % y, True, (0, 255, 0)) surface_z = font.render('Z: %s' % z, True, (0, 0, 255)) if self._level.is_on_hole(self._ball.get_position()): # Success! Restart self._ball.set_position(*self._level.get_ball_start()) x, y = self._screen.get_size() self._level.set_hole(random.randint(0 ,x), random.randint(0, y)) if self._level.is_on_ground(self._ball.get_position()): vx = int(x * self.accelerometer.acceleration) vy = int(y * self.accelerometer.acceleration) self._ball.update(vx, vy) else: # Fail! Restart self._ball.set_position(*self._level.get_ball_start()) # Draw the level self._level.draw(self._screen) # Draw the ball self._ball.draw(self._screen) # Draw the accelerometer data self._screen.blit(surface_x, (15, 15)) self._screen.blit(surface_y, (15, surface_x.get_height() + 15)) self._screen.blit(surface_z, (15, surface_x.get_height() * 2 + 15)) # Flip Display pygame.display.flip() # Try to stay at 30 FPS self.clock.tick(30) # Close the device before leave self.accelerometer.close() # This function is called when the game is run directly from the command line: # ./game.py def main(): pygame.init() pygame.display.set_mode((0, 0), pygame.RESIZABLE) game = TiltGame() game.run() if __name__ == '__main__': main()