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()
def __init__(self, mode=CYCLE): super().__init__() self.cube = Cube() self.queue = Queue() self.mode = mode self.animation_thread = StoppableThread(target=self.animation) self.animation_thread.setDaemon(True) self.animation_thread.start()
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 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"
class TextWriter: def __init__(self): self.cube = Cube() def draw_character(self, char, pos, color): bitmap = CHARS.get(char.lower(), None) if bitmap: for y in range(len(bitmap[0])): for z in range(len(bitmap) - 1, -1, -1): if bitmap[4 - z][y] == 0: continue if y + pos < 0 or y + pos >= 5: continue self.cube.set_color(4, y + pos, z, *color) def clear(self): for y in range(5): for z in range(5): self.cube.set_color(4, y, z, 0, 0, 0) def draw_string(self, string, delay): for pos in range(-1, len(string)): for i in range(4): self.clear() if pos >= 0: self.draw_character(string[pos], -i, (255, 255, 255)) if pos + 1 < len(string): self.draw_character(string[pos + 1], 4 - i, (255, 255, 255)) self.cube.show() sleep(delay)
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"
def __init__(self): self.cube = Cube()
class LedViewer(BoardViewer): def __init__(self, mode=CYCLE): super().__init__() self.cube = Cube() self.queue = Queue() self.mode = mode self.animation_thread = StoppableThread(target=self.animation) self.animation_thread.setDaemon(True) self.animation_thread.start() def player_plays(self, x, y): self.queue.put((PLAY, x, y)) def player_undoes(self): last_move = self.board.get_last() self.board.undo() if last_move != (None, None): self.queue.put((UNDO, *last_move)) def player_selects(self, x, y): self.queue.put((SELECT, x, y)) def finish(self, winning_coords): self.queue.put((FINISH, winning_coords)) def close(self): self.animation_thread.stop() self.animation_thread.join() def animation(self): animation_state = AnimationState(self.mode) animation_list = [FieldColorsAnimation(animation_state, self.board.field)] while not self.animation_thread.stopped() or animation_list[-1].is_blocking() or not self.queue.empty(): if not animation_list[-1].is_blocking(): try: event = self.queue.get_nowait() # some animations have to stop when a new one is started animation_list[-1].new_animation_available() if event[0] == PLAY: super().player_plays(*event[1:3]) animation_list.append(PlayAnimation(animation_state)) elif event[0] == UNDO: x = event[1] y = event[2] z = self.get_z(x, y) c = self.board.next_color animation_list.append(UndoAnimation(animation_state, x, y, z, c)) elif event[0] == SELECT: x = event[1] y = event[2] z = self.get_z(x, y) c = self.board.next_color top = self.board.field(x, y, z) is not EMPTY animation_list.append(SelectAnimation(animation_state, x, y, z, c, top)) elif event[0] == FINISH: c = RED if self.board.next_color == RED: c = BLUE animation_list.append(FinishAnimation(animation_state, event[1], c)) except Empty: pass cube_buffer = get_empty_cube_buffer() for a in animation_list: a.animate(cube_buffer) # remove completed animations from list animation_list[:] = [a for a in animation_list if not a.is_done()] # draw the completed cube self.cube.draw(cube_buffer) self.cube.show() animation_state.update() if not is_a_raspberry(): sleep(0.01) def get_z(self, x, y): z = 4 while z > 0 and self.board.field(x, y, z - 1) == EMPTY: z -= 1 return z