Files @ 835bac5d3b3c
Branch filter:

Location: led-matrix-software/demos/tetris.py - annotation

matthewreed
Added new demos and handle splash screens and controller input
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
835bac5d3b3c
### tetris.py
### Author: Matthew Reed
### Game of tetris, uses the D-Pad
### Adapted from https://gist.github.com/silvasur/565419/d9de6a84e7da000797ac681976442073045c74a4

import sys
import time
import signal
import logging
import configparser
from enum import Enum

import math
from random import randrange as rand
import matrix

class Tetris:

    # Define the shapes of the single parts
    tetris_shapes = [
        [[1, 1, 1],
         [0, 1, 0]],

        [[0, 2, 2],
         [2, 2, 0]],

        [[3, 3, 0],
         [0, 3, 3]],

        [[4, 0, 0],
         [4, 4, 4]],

        [[0, 0, 5],
         [5, 5, 5]],

        [[6, 6, 6, 6]],

        [[7, 7],
         [7, 7]]
    ]
    
    colors = [
        matrix.Colors.OFF.value,
        matrix.Colors.RED.value,
        matrix.Colors.GREEN.value,
        matrix.Colors.BLUE.value,
        matrix.Colors.ORANGE.value,
        matrix.Colors.YELLOW.value,
        matrix.Colors.MAGENTA.value,
        matrix.Colors.LIGHT_BLUE.value
    ]
    
    def rotate_clockwise(self, shape):
        return [[ shape[y][x] for y in range(len(shape)) ] for x in range(len(shape[0]) - 1, -1, -1)]

    def check_collision(self, board, shape, offset):
        off_x, off_y = offset
        for cy, row in enumerate(shape):
            for cx, cell in enumerate(row):
                try:
                    if cell and board[ cy + off_y ][ cx + off_x ]:
                        return True
                except IndexError:
                    return True
        return False

    def remove_row(self, board, row):
        del board[row]
        self.score += 1
        return [[0 for i in range(self.width)]] + board

    def join_matrixes(self, mat1, mat2, mat2_off):
        off_x, off_y = mat2_off
        for cy, row in enumerate(mat2):
            for cx, val in enumerate(row):
                mat1[cy+off_y-1	][cx+off_x] += val
        return mat1

    def new_board(self):
        board = [[0 for x in range(self.width)] for y in range(self.height)]
        board += [[1 for x in range(self.width)]]
        return board
        
    def new_stone(self):
        self.stone = self.tetris_shapes[rand(len(self.tetris_shapes))]
        self.stone_x = int(self.width / 2 - len(self.stone[0])/2)
        self.stone_y = 0
        
        if self.check_collision(self.board, self.stone, (self.stone_x, self.stone_y)):
            self.gameover = True

    def draw(self, matrix, board, offset):
        off_x, off_y  = offset
        for y, row in enumerate(board):
            for x, val in enumerate(row):
                if val and y < self.height:
                    matrix.set_pixel(off_x + x, off_y + y, self.colors[val])

    def move(self, delta_x):
        if not self.gameover and not self.paused:
            new_x = self.stone_x + delta_x
            if new_x < 0:
                new_x = 0
            if new_x > self.width - len(self.stone[0]):
                new_x = self.width - len(self.stone[0])
            if not self.check_collision(self.board, self.stone, (new_x, self.stone_y)):
                self.stone_x = new_x

    def drop(self):
        if not self.gameover and not self.paused:
            self.stone_y += 1
            if self.check_collision(self.board, self.stone, (self.stone_x, self.stone_y)):
                self.board = self.join_matrixes(self.board, self.stone, (self.stone_x, self.stone_y))
                self.new_stone()
                while True:
                    for i, row in enumerate(self.board[:-1]):
                        if 0 not in row:
                            self.board = self.remove_row(self.board, i)
                            break
                    else:
                        break

    def rotate_stone(self):
        if not self.gameover and not self.paused:
            new_stone = self.rotate_clockwise(self.stone)
            if not self.check_collision(self.board, new_stone, (self.stone_x, self.stone_y)):
                self.stone = new_stone

    def toggle_pause(self):
        self.paused = not self.paused

    def __init__(self, config, parent, matrix, controller):
        self.logger = logging.getLogger('snake')
        self.config = config
        self.parent = parent
        self.matrix = matrix
        self.controller = controller
        
    def reset(self):
        pass
        
    def splash(self):
    
        w = matrix.Colors.WHITE.value
        r = matrix.Colors.RED.value
        g = matrix.Colors.GREEN.value
        b = matrix.Colors.BLUE.value
        l = matrix.Colors.LIGHT_BLUE.value
        m = matrix.Colors.MAGENTA.value
        O = matrix.Colors.ORANGE.value
        y = matrix.Colors.YELLOW.value
        o = matrix.Colors.OFF.value
        
        splash = [
            [o, o, o, o, o, o, y, y],
            [o, g, g, g, g, g, o, y],
            [o, g, o, g, o, g, o, y],
            [o, o, o, g, o, o, l, l],
            [o, o, o, g, o, o, l, l],
            [r, o, o, g, o, o, b, O],
            [r, r, g, g, g, b, b, O],
            [r, m, m, m, m, b, O, O],
        ]
        
        for x in range(0, self.matrix.WIDTH):
            for y in range(0, self.matrix.HEIGHT):
                self.matrix.set_pixel(x, y, splash[y][x])
        
        self.matrix.update()
        
    def run(self):
    
        self.width = 8
        self.height = 8
        
        self.board = self.new_board()
        self.new_stone()
    
        #start timers and counters
        self.start_time = time.time()
        last_time = time.time()
        delay_time = 0.1
        
        led_iteration_count = 0
        frame_count = 0
        
        self.updateCountMax = 15
        self.updateCount = 0
        
        self.score = 0
        
        self.gameover = False
        self.paused = False
        
        while not self.gameover:
            
            for event in self.controller.read_input():
                if event.code == 313 and event.value == 1:
                    #start button
                    self.gameover = True
                if event.code == 305 and event.value == 1:
                    #A button
                    self.toggle_pause()
                elif event.code == 16:
                    if event.value == 1:
                        #dpad right
                        self.move(+1)
                    if event.value == 0:
                        #dpad none
                        pass
                    if event.value == -1:
                        #dpad left
                        self.move(-1)
                elif event.code == 17:
                    if event.value == 1:
                        #dpad down
                        self.drop()
                    if event.value == 0:
                        #dpad none
                        pass
                    if event.value == -1:
                        #dpad up
                        self.rotate_stone()
            
            if time.time() > last_time + delay_time:
                last_time = time.time()
                
                self.updateCount = self.updateCount + 1
                if self.updateCount >= self.updateCountMax:
                    self.drop()
                    self.updateCount = 0
                
                #update display
                self.matrix.set_matrix(matrix.Colors.OFF.value)
                self.draw(self.matrix, self.board, (0,0))
                self.draw(self.matrix, self.stone, (self.stone_x, self.stone_y))
                self.matrix.update()
                
                led_iteration_count = (led_iteration_count + 1) % self.matrix.NUM_LEDS
                frame_count = frame_count + 1
                
            time.sleep(0.01)
        
        #display score before exiting
        self.matrix.set_matrix(matrix.Colors.OFF.value)
        if self.score > 750:
            for i in range(0, 64):
                self.matrix.set_pixel(i % self.matrix.WIDTH, math.floor(i / self.matrix.HEIGHT), matrix.Colors.YELLOW.value)
        else:
            multiples = math.floor(self.score / 50)
            self.score = self.score % 50
            for i in range(0, multiples):
                self.matrix.set_pixel(i % self.matrix.WIDTH, math.floor(i / self.matrix.HEIGHT), matrix.Colors.YELLOW.value)
            for i in range(multiples, self.score):
                self.matrix.set_pixel(i % self.matrix.WIDTH, math.floor(i / self.matrix.HEIGHT), matrix.Colors.WHITE.value)
        self.matrix.update()
        time.sleep(2)