Files @ 835bac5d3b3c
Branch filter:

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

matthewreed
Added new demos and handle splash screens and controller input
### 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)