def __init__(self, viewer): BasePlayer.__init__(self, viewer) self.button_events = ButtonEvents() self.selected = (2, 2) # short timeout after a game starts, it is increased on the first input self.timeout = 30 self.return_position = (None, None)
def __init__(self): self.button_events = ButtonEvents() self.cube = Cube() self.cube_buffer = get_empty_cube_buffer() self.theta = 0.0 self.phi = 0.0 self.wheel_offset = 0.0 self.theta_d = 0.0 self.phi_d = 0.0 self.wheel_offset_d = 0.0
class Selector: """ App selector to show previews and run apps """ def __init__(self): self.cube = Cube() self.button_events = ButtonEvents() self.apps = [Rainbow(), Random(), Connect4Demo(), Connect4Human(), SnakeGame()] self.selected = 0 self.show_preview() def run(self): event = self.button_events.get_event() if event == EventEnum.UP_PRESSED or event == EventEnum.LEFT_PRESSED: self.selected = (self.selected - 1) % len(self.apps) self.show_preview() elif event == EventEnum.DOWN_PRESSED or event == EventEnum.RIGHT_PRESSED: self.selected = (self.selected + 1) % len(self.apps) self.show_preview() elif event == EventEnum.A_PRESSED: # create new instance self.apps[self.selected] = type(self.apps[self.selected])() self.apps[self.selected].run() self.show_preview() def show_preview(self): preview = self.apps[self.selected].get_preview() self.cube.draw(preview) self.cube.show()
class Random(App): """ fill the cube with random stuff and change it slowly """ def __init__(self): self.button_events = ButtonEvents() self.cube = Cube() self.cube_buffer = get_empty_cube_buffer() self.wheel_start = self.get_random_wheel() self.wheel_stop = self.get_random_wheel() def run(self): steps = 100 try: while True: self.wheel_stop = self.get_random_wheel() for i in range(steps): if self.button_events.get_event(block=False): LOG.debug("button pressed, interrupting rainbow") raise RandomInterrupted() self.step(self.wheel_start, self.wheel_stop, float(i) / steps) self.cube.draw(self.cube_buffer) self.cube.show() if not is_a_raspberry(): sleep(0.02) self.wheel_start = copy.deepcopy(self.wheel_stop) except RandomInterrupted: return def step(self, start, stop, step): for x in range(5): for y in range(5): for z in range(5): color = wheel(int(start[x][y][z] + (stop[x][y][z] - start[x][y][z]) * step) % 256) self.cube_buffer[x][y][z] = color def get_random_wheel(self): random_wheel = [[[randint(0, 255) for _ in range(5)] for _ in range(5)] for _ in range(5)] return random_wheel def get_random_cube(self, cube_buffer): random_wheel = self.get_random_wheel() for x in range(5): for y in range(5): for z in range(5): cube_buffer[x][y][z] = wheel(random_wheel[x][y][z]) def get_preview(self): preview = get_empty_cube_buffer() self.get_random_cube(preview) return preview def get_description(self) -> str: return "random"
def __init__(self): self.cube = Cube() self.button_events = ButtonEvents() self.apps = [Rainbow(), Random(), Connect4Demo(), Connect4Human(), SnakeGame()] self.selected = 0 self.show_preview()
def __init__(self): self.button_events = ButtonEvents() self.cube = Cube() self.cube_buffer = get_empty_cube_buffer() self.wheel_start = self.get_random_wheel() self.wheel_stop = self.get_random_wheel()
def __init__(self): self.button_events = ButtonEvents() self.cube = Cube() self.snake = Snake() self.apple = Apple()
class SnakeGame(App): def __init__(self): self.button_events = ButtonEvents() self.cube = Cube() self.snake = Snake() self.apple = Apple() def run(self) -> None: try: last_time = monotonic() self.apple.set_random_position(self.snake.snake) while True: self.handle_events() current_time = monotonic() if (current_time - last_time) > DELAY: last_time = current_time self.snake.move(self.apple) cube_buffer = get_empty_cube_buffer() cube_buffer = self.apple.draw(cube_buffer) cube_buffer = self.snake.draw(cube_buffer) self.cube.draw(cube_buffer) self.cube.show() if not is_a_raspberry(): sleep(0.01) except SnakeCollision: LOG.debug("collision") self.death_animation() return except SnakeInterrupted: return def death_animation(self) -> None: cube_buffer = get_empty_cube_buffer() for x in range(5): for y in range(5): for z in range(5): cube_buffer[x][y][z] = wheel(85) self.cube.draw(cube_buffer) self.cube.show() sleep(1) def handle_events(self) -> None: """ get user input to move snake """ event = self.button_events.get_event(block=False) if event: if event == EventEnum.UP_PRESSED: self.snake.set_direction(Direction.FORWARD.value) elif event == EventEnum.DOWN_PRESSED: self.snake.set_direction(Direction.BACKWARD.value) elif event == EventEnum.LEFT_PRESSED: self.snake.set_direction(Direction.LEFT.value) elif event == EventEnum.RIGHT_PRESSED: self.snake.set_direction(Direction.RIGHT.value) elif event == EventEnum.A_PRESSED: self.snake.set_direction(Direction.UP.value) elif event == EventEnum.A_REPEATED: LOG.debug("interrupting snake") raise SnakeInterrupted() elif event == EventEnum.B_PRESSED: self.snake.set_direction(Direction.DOWN.value) def get_preview(self) -> CubeType: snake = Snake([(4, 4, 0), (4, 3, 0), (4, 2, 0), (4, 2, 1), (4, 2, 2), (3, 2, 2)]) apple = Apple() apple.set_random_position(snake.snake) cube_buffer = get_empty_cube_buffer() cube_buffer = apple.draw(cube_buffer) cube_buffer = snake.draw(cube_buffer) return cube_buffer def get_description(self) -> str: return "snake"
class Rainbow(App): def __init__(self): self.button_events = ButtonEvents() self.cube = Cube() self.cube_buffer = get_empty_cube_buffer() self.theta = 0.0 self.phi = 0.0 self.wheel_offset = 0.0 self.theta_d = 0.0 self.phi_d = 0.0 self.wheel_offset_d = 0.0 def run(self): try: while True: self.handle_events() v = [ sin(self.theta) * cos(self.phi), sin(self.theta) * sin(self.phi), cos(self.theta) ] self.rainbow(self.cube_buffer, v, self.wheel_offset) self.cube.draw(self.cube_buffer) self.cube.show() # Rotate vector self.theta += self.theta_d self.phi += self.phi_d # Cycle through all colors so the rainbow not only rotates, but also # moves through the cube. self.wheel_offset += self.wheel_offset_d self.wheel_offset %= 256 if not is_a_raspberry(): sleep(0.02) except RainbowInterrupted: return def handle_events(self): """ adjust the rotation settings of the rainbow """ event = self.button_events.get_event(block=False) if event: if event == EventEnum.UP_PRESSED or event == EventEnum.UP_REPEATED: self.theta_d += THETA_D elif event == EventEnum.DOWN_PRESSED or event == EventEnum.DOWN_REPEATED: self.theta_d -= THETA_D elif event == EventEnum.LEFT_PRESSED or event == EventEnum.LEFT_REPEATED: self.phi_d -= PHI_D elif event == EventEnum.RIGHT_PRESSED or event == EventEnum.RIGHT_REPEATED: self.phi_d += PHI_D elif event == EventEnum.A_PRESSED: self.wheel_offset_d -= WHEEL_OFFSET_D elif event == EventEnum.A_REPEATED: LOG.debug("interrupting rainbow") raise RainbowInterrupted() elif event == EventEnum.B_PRESSED: self.wheel_offset_d += 0.5 elif event == EventEnum.B_REPEATED: self.theta = 0.0 self.phi = 0.0 self.wheel_offset = 0.0 self.theta_d = 0.0 self.phi_d = 0.0 self.wheel_offset_d = 0.0 def rainbow(self, cube_buffer, v, wheel_offset): """ draw a rainbow across the cube, oriented according to the given vector :param cube_buffer: cube buffer to modify :param v: vector for rainbow orientation and scaling (unit vector results in one rainbow across the cube) :param wheel_offset: color wheel offset """ for x in range(5): for y in range(5): for z in range(5): dot_product = ((x - 2) * v[0] + (y - 2) * v[1] + (z - 2) * v[2]) # 37 is the number to have the full rainbow range from [-2, -2, -2] to [2, 2, 2] # if v is a unit vector. cube_buffer[x][y][z] = wheel( int(dot_product * 37 + wheel_offset)) def get_preview(self): preview = get_empty_cube_buffer() self.rainbow(preview, [0.5, 0.5, 0.5], 0) return preview def get_description(self) -> str: return "rainbow"
class GpioPlayer(BasePlayer): """ A binary-joystick controlled player using RasPi GPIOs https://gpiozero.readthedocs.io/en/stable/ """ def __init__(self, viewer): BasePlayer.__init__(self, viewer) self.button_events = ButtonEvents() self.selected = (2, 2) # short timeout after a game starts, it is increased on the first input self.timeout = 30 self.return_position = (None, None) def axis_pressed(self, dx, dy): LOG.debug("axis button pressed: {} {}".format(dx, dy)) x, y = self.selected x += dx y += dy if not (0 <= x < 5 and 0 <= y < 5): LOG.debug("out of bounds: ignoring {},{}".format(x, y)) return self.selected = (x, y) LOG.debug("selected {},{}".format(x, y)) self.do_select(x, y) def drop_pressed(self): LOG.debug("drop button pressed") if self.board.field(*self.selected, 4) != EMPTY: LOG.debug("non playable location, ignoring") return self.return_position = self.selected def undo_pressed(self): LOG.debug("undo button pressed") self.return_position = (-1, -1) def reset_pressed(self): LOG.debug("reset button pressed") raise PlayerResetError() def exit_pressed(self): LOG.debug("exit button pressed") raise PlayerExitError() def do_play(self) -> tuple: if self.selected == (-1, -1): self.selected = (2, 2) self.do_select(*self.selected) # first show the last selected location self.button_events.clear() self.return_position = (None, None) event_functions = { EventEnum.UP_PRESSED: lambda: self.axis_pressed(-1, 0), EventEnum.UP_REPEATED: lambda: self.axis_pressed(-1, 0), EventEnum.DOWN_PRESSED: lambda: self.axis_pressed(1, 0), EventEnum.DOWN_REPEATED: lambda: self.axis_pressed(1, 0), EventEnum.LEFT_PRESSED: lambda: self.axis_pressed(0, -1), EventEnum.LEFT_REPEATED: lambda: self.axis_pressed(0, -1), EventEnum.RIGHT_PRESSED: lambda: self.axis_pressed(0, 1), EventEnum.RIGHT_REPEATED: lambda: self.axis_pressed(0, 1), EventEnum.A_PRESSED: self.drop_pressed, EventEnum.A_REPEATED: self.exit_pressed, EventEnum.B_PRESSED: self.undo_pressed, EventEnum.B_REPEATED: self.reset_pressed, } while self.return_position == (None, None): event = self.button_events.get_event(timeout=self.timeout) if event: event_function = event_functions[event] if event_function is not None: event_functions[event]() else: LOG.warning("player idle for too long") raise PlayerExitError() # increase timeout after first event self.timeout = 200 return self.return_position