예제 #1
0
 def __init__(self):
     self._screen = Screen()
     self._matrix = Matrix()
     self._row_time = 0
     self._curr_tetrimino = Tetrimino()
     self._next_tetrimino = Tetrimino()
     self._last_score = 0
     self._total_score = 0
     self._exit = False
     self._game_paused = False
예제 #2
0
    def _process_input(self, actions):
        for action in actions:
            if not self._game_paused and action in [
                    InputAction.DOWN, InputAction.LEFT, InputAction.RIGHT
            ]:
                success = self._matrix.move_tetrimino(self._curr_tetrimino,
                                                      action)

                if not success and action == InputAction.DOWN:
                    # The tetrimino could not be moved down, which means it reached the bottom
                    num_removed_rows = self._matrix.remove_completed_rows(
                        self._curr_tetrimino)

                    curr_score = _SCORES[num_removed_rows]
                    if curr_score > 0:
                        self._last_score = curr_score
                        self._total_score += curr_score

                    self._curr_tetrimino = self._next_tetrimino
                    self._next_tetrimino = Tetrimino()

            elif not self._game_paused and action == InputAction.ROTATE:
                self._matrix.rotate_tetrimino(self._curr_tetrimino)

            elif action == InputAction.QUIT:
                self._exit = True
                break

            elif action == InputAction.PAUSE:
                self._game_paused = True

            elif action == InputAction.CONTINUE:
                self._game_paused = False
예제 #3
0
    def get_next_tetrimino(self):
        """Retrieve the next random tetrimino using the "bag" system."""
        t = Tetrimino(self.tetrimino_bag[self.t_index], self.grid, self.level)
        self.t_index += 1
        if self.t_index == len(Shape):
            self.t_index = 0
            self.tetrimino_bag = random.sample(list(Shape), len(Shape))

        self.next_queue.update_next_queue(self.tetrimino_bag[self.t_index])
        return t
예제 #4
0
    def spawn(self):
        '''
        -initializes a tetrimino
        -sets it to the first type in the queue
        -spawns the block on the board
        -adds the block to the list of existing blocks
        -deletes the first type and appends it to the queue

        '''
        self.block = Tetrimino()

        current_type = self.queue[0]

        self.block.set_type(current_type)

        self.block.spawn()
        self.existing_blocks.append(self.block)

        del self.queue[0]
        self.queue.append(current_type)
예제 #5
0
def main():
    # argv = [width, height, queue, first]
    grid_width, grid_height, queue_size, first = process_args()

    grid = Grid(grid_width, grid_height)
    current = Tetrimino(first, Coord(grid_width // 2, 1))
    grid.set_current(current)
    tqueue = TQueue(queue_size)
    hold = None

    grid.refresh()
    print(grid)

    while True:
        cmd = input()
        move = Coord(0, 0)

        if cmd == 'a':
            move = Coord(-1, 0)
        elif cmd == 'd':
            move = Coord(1, 0)
        elif cmd == 's':
            move = Coord(0, 1)
        elif cmd == 'e':
            grid.curr.rotate(1)
            move = grid.rotate_kick()
        elif cmd == 'q':
            grid.curr.rotate(0)
            move = grid.rotate_kick()
        elif cmd == 'x':
            break

        can_move = grid.piece_can_move(move)
        if can_move:
            grid.curr.move(move)
        elif not can_move and move.equals(Coord(0, 1)):
            grid.place_current(tqueue.get())

            # move_in_bounds(grid, grid.curr)

        grid.clear_lines()
        grid.refresh()
        print(grid)
예제 #6
0
 def update(self, subject: Tetrimino) -> None:
     for x, y in subject.get_prev_positions():
         self._empty_square(x, y)
     for x, y in subject.get_positions():
         self._fill_square(x, y, subject.kind)
예제 #7
0
    def __init__(self, size):
        super().__init__((size))

        # Fill queue up initially
        for i in range(size):
            super().put(Tetrimino(random.choice(list(TPiece))))
예제 #8
0
    def get(self):
        # get next tetrimino and push a random one to the queue
        result = super().get()
        super().put(Tetrimino(random.choice(list(TPiece))))

        return result
예제 #9
0
class Tetris():
    def __init__(self):
        self._screen = Screen()
        self._matrix = Matrix()
        self._row_time = 0
        self._curr_tetrimino = Tetrimino()
        self._next_tetrimino = Tetrimino()
        self._last_score = 0
        self._total_score = 0
        self._exit = False
        self._game_paused = False

    def main(self, stdscr):
        self._screen.init_tetris(stdscr)

        while not self._exit:
            tick_start_time = Tetris._get_curr_time()
            actions = self._get_input(stdscr.getch())

            if not self._curr_tetrimino.in_matrix():
                game_over = self._matrix.insert_new_tetrimino(
                    self._curr_tetrimino)
                if game_over:
                    self._exit = True
            else:
                self._process_input(actions)
                self._sleep_until_end_of_tick(tick_start_time)

            self._screen.render_tetris(self._matrix.get_matrix(),
                                       self._total_score, self._last_score,
                                       self._next_tetrimino, self._game_paused,
                                       stdscr)

    def get_score(self):
        return self._total_score

    def _get_input(self, input_char):
        actions = []

        user_action = InputAction.get_action(input_char)
        if user_action is not None:
            actions.append(user_action)

        if self._tetrimino_should_go_down():
            actions.append(InputAction.DOWN)

        return actions

    def _process_input(self, actions):
        for action in actions:
            if not self._game_paused and action in [
                    InputAction.DOWN, InputAction.LEFT, InputAction.RIGHT
            ]:
                success = self._matrix.move_tetrimino(self._curr_tetrimino,
                                                      action)

                if not success and action == InputAction.DOWN:
                    # The tetrimino could not be moved down, which means it reached the bottom
                    num_removed_rows = self._matrix.remove_completed_rows(
                        self._curr_tetrimino)

                    curr_score = _SCORES[num_removed_rows]
                    if curr_score > 0:
                        self._last_score = curr_score
                        self._total_score += curr_score

                    self._curr_tetrimino = self._next_tetrimino
                    self._next_tetrimino = Tetrimino()

            elif not self._game_paused and action == InputAction.ROTATE:
                self._matrix.rotate_tetrimino(self._curr_tetrimino)

            elif action == InputAction.QUIT:
                self._exit = True
                break

            elif action == InputAction.PAUSE:
                self._game_paused = True

            elif action == InputAction.CONTINUE:
                self._game_paused = False

    def _tetrimino_should_go_down(self):
        if self._game_paused:
            return False

        if self._row_time >= GAME_SPEED:
            self._row_time = 0  # reset row time counter
            return True
        else:
            self._row_time += TICK_DURATION
            return False

    def _sleep_until_end_of_tick(self, tick_start_time):
        elapsed_time = self._get_curr_time() - tick_start_time
        if elapsed_time < TICK_DURATION:
            time.sleep((TICK_DURATION - elapsed_time) / 1000)

    @staticmethod
    def _get_curr_time():
        return time.time() * 1000
예제 #10
0
 def test_spawn(self, type_str):
     #initializes custom Tetrimino object
     self.block = Tetrimino()
     self.block.set_type(type_str)
     self.block.spawn()
     self.existing_blocks.append(self.block)
예제 #11
0
class GameState:
    ROWS = 22
    COLUMNS = 10

    def __init__(self):
        self.board = [[' * ' for i in range(self.COLUMNS)]
                      for j in range(self.ROWS)]
        self.block = None
        self.existing_blocks = []  #a list of tetromino objects

        self.block_types = ['I', 'O', 'T', 'S', 'Z', 'J', 'L']
        shuffle(self.block_types)
        self.queue = self.block_types[:]

        self.hold_queue = []  #this should be empty most of the time...

        self.player_data = {
            'score': 0,
            'level': 1,
            'time': 0,
            'lines': 0,
            't-spins': 0,
        }

    def block_pieces(self) -> {str: [int, int]}:
        '''
        returns a dict of the coordinates for easy access
        '''
        assert type(
            self.block
        ) == Tetrimino, 'GameState.block_pieces: self.block is not a Tetrimino object'
        return self.block.blocks

    def existing_blocks_list(self) -> [str]:
        return [str(i) for i in self.existing_blocks]

    def block_coords_deleter(self, specific_block: [int, int]):
        for tetrimino in self.existing_blocks:
            blocks_to_delete = []
            for block in tetrimino.blocks:
                if tetrimino.blocks[block] == specific_block:
                    blocks_to_delete.append(block)
            for i in blocks_to_delete:
                del tetrimino.blocks[i]

    def block_deleter(self):
        '''
        makes sure that if a block doesn't exist (no more coordinates), it just deletes the block object completely from the game
        '''
        self.existing_blocks = [
            block for block in self.existing_blocks if len(block.blocks) != 0
        ]

    def printout(self) -> str:
        '''
        creates a string representation of the board

        keep in mind that this still shows the buffer zone... 
        
        '''
        return '\n'.join([
            ''.join([self.board[i][j] for j in range(len(self.board[i]))])
            for i in range(len(self.board))
        ])

    def ghost_printout(self):
        field = [[self.board[i][j] for j in range(len(self.board[i]))]
                 for i in range(len(self.board))]

        print(self.hard_drop_coords())

        for j, k in self.hard_drop_coords().values():
            if [j, k] not in self.block.blocks.values(
            ):  #basically we should see the parts of the ghost block that are not currently occupied by the current block
                field[j][k] = '({})'.format(self.block.block_type)

        return '\n'.join([
            ''.join([field[i][j] for j in range(len(field[i]))])
            for i in range(len(field))
        ])

    def spawn(self):
        '''
        -initializes a tetrimino
        -sets it to the first type in the queue
        -spawns the block on the board
        -adds the block to the list of existing blocks
        -deletes the first type and appends it to the queue

        '''
        self.block = Tetrimino()

        current_type = self.queue[0]

        self.block.set_type(current_type)

        self.block.spawn()
        self.existing_blocks.append(self.block)

        del self.queue[0]
        self.queue.append(current_type)

    def hold(self):
        '''
        We check first to see if we have a current block and the hold_queue is empty and that the block we want to hold is not frozen

        If true, we do the rest of this method...
        We add the current block type to the the hold queue and delete the current block from the existing blocks
        
        '''
        print(self.queue)

        if self.block and len(self.hold_queue) == 0 and not self.block.frozen:
            self.hold_queue.append(self.block.block_type)

            for i in range(len(self.existing_blocks[:])):
                if self.block == self.existing_blocks[i]:
                    del self.existing_blocks[i]

        elif self.block.block_type not in self.hold_queue and len(
                self.hold_queue) == 1 and not self.block.frozen:
            #we add the new value to the hold queue

            self.hold_queue.append(self.block.block_type)

            #we make sure the block with the new value is deleted...
            for i in range(len(self.existing_blocks[:])):
                if self.block == self.existing_blocks[i]:
                    del self.existing_blocks[i]

            self.queue.remove(self.hold_queue[0])
            #we remove the first occurence of the old value held from the queue to put it at the beginning of the queue
            #without creating a duplicate

            self.queue.insert(0, self.hold_queue[0])

            del self.hold_queue[0]

    def clear_hold_queue(self):
        '''
        Automatically clears hold_queue when a block is spawned
        '''
        if self.block.block_type in self.hold_queue:
            del self.hold_queue[0]

    def print_next_block(self) -> str:
        '''
        prints ths first block in the queue
        '''
        return self.queue[0]

    def print_queue(self) -> [str]:
        '''
        prints the six blocks after the very next block
        '''
        return self.queue[1:7]

    def print_hold_queue(self) -> list:
        '''
        prints the hold queue
        '''
        return self.hold_queue

    def test_spawn(self, type_str):
        #initializes custom Tetrimino object
        self.block = Tetrimino()
        self.block.set_type(type_str)
        self.block.spawn()
        self.existing_blocks.append(self.block)

    def board_update(self):
        '''
        when a tetrinimo object is active and another one spawns, the first one disappears...
        '''
        for i in range(self.ROWS):
            for j in range(self.COLUMNS):
                self.board[i][j] = ' * '

        if self.block:
            for i in self.existing_blocks:
                for j, k in i.blocks.values():
                    if i.landed == False and i.frozen == False:
                        self.board[j][k] = '|{}|'.format(i.block_type)

                    elif i.landed == True and i.frozen == False:
                        self.board[j][k] = '[{}]'.format(i.block_type)

                    elif i.landed == True and i.frozen == True:
                        self.board[j][k] = ' {} '.format(i.block_type)

    def move_right(self):
        '''
        Checks whether all blocks can be moved to the right

        possible is a bool that determines whether the move stays within the board...

        Eventually, we're going to have to figure out how this can work when blocks are in the squares you attempt to move to...
        Perhaps we can mark them as a gray block.

        '''

        dct = self.block_pieces()

        possible = all(
            map(lambda x: 0 <= x[1] < 9, [dct[i] for i in sorted(dct)]))

        if self.block.frozen == False and self.nothing_right() and possible:
            self.block.move_right()

    def move_left(self):
        '''
        Checks whether all blocks can be moved to the left

        possible is a bool that determines whether the move stays within the board...

        Eventually, we're going to have to figure out how this can work when blocks are in the squares you attempt to move to...
        Perhaps we can mark them as a gray block.

        '''
        dct = self.block_pieces()

        possible = all(
            map(lambda x: 0 < x[1] <= 9, [dct[i] for i in sorted(dct)]))

        if self.block.frozen == False and self.nothing_left() and possible:
            self.block.move_left()

    def gravity(self):
        '''
        Moves blocks down each tick
        possible is a bool that determines whether the move stays within the board...

        We will eventually need to check what will happen if a block is right below another...
        '''
        dct = self.block_pieces()

        possible = all(
            map(lambda x: 1 <= x[0] + 1 < 21, [dct[i] for i in sorted(dct)]))
        #print([self.block.block1, self.block.block2, self.block.block3, self.block.block4])
        #print(self.nothing_below())

        if possible and self.nothing_below():
            self.block.fall_down()

        else:
            if self.block.landed == True:
                self.block.frozen = True

            elif self.block.landed == False:
                self.block.landed = True

                if self.nothing_below():
                    self.block.fall_down()

    def hard_drop_coords(self):
        block_coords = self.block.blocks.copy()
        bottom_value = max([i[0] for i in block_coords.values()])

        hard_drop = {
            block: [21, block_coords[block][1]]
            for block in block_coords
        }

        for i in range(1, 22 - bottom_value):
            hard_drop = {
                block: [block_coords[block][0] + i, block_coords[block][1]]
                for block in block_coords
            }
            current_pos = {
                i: [hard_drop[i][0] - 1, hard_drop[i][1]]
                for i in hard_drop
            }

            if any([
                    self.board[x][y] != ' * ' for x, y in hard_drop.values()
                    if [x, y] not in current_pos.values()
            ]):
                return current_pos

        return hard_drop

    def hard_drop(self):
        '''
        In Tetris, we need to make blocks immediately fall down...

        To hard drop... we get the current bottom... At most, all four blocks. 
        Then, we use a for loop to test each row and column value below the 'bottom' to see how many we must move down
        Then we check to see if we can use the spaces for the 'top'
        If this doesn't work, we take the next value...
        '''
        block_coords = self.block.blocks.copy()
        bottom_value = max([i[0] for i in block_coords.values()])

        for i in range(1, 22 - bottom_value):

            hard_drop = {
                block: [block_coords[block][0] + i, block_coords[block][1]]
                for block in block_coords
            }
            current_pos = {
                i: [hard_drop[i][0] - 1, hard_drop[i][1]]
                for i in hard_drop
            }

            if any([
                    self.board[x][y] != ' * ' for x, y in hard_drop.values()
                    if [x, y] not in current_pos.values()
            ]):
                self.block.landed = True
                self.block.frozen = True
                self.block.blocks = current_pos
                break
        else:
            self.block.landed = True
            self.block.frozen = True
            self.block.blocks = hard_drop

    def nothing_below(self) -> bool:
        '''
        determines whether there is a block that is in the way of the current block falling down...

        the nothing_below method will work like this...

        it will take a list of current block coordinates and add 1 to the row values..
        if one of the coordinates in the resultant list is in the list of current block coordinates, those values are deleted...
        this keeps this method flexible, especially for when blocks rotate
        
        o-block
        [ [1,0], [1,1], [2,0], [2,1] ] -> [ [2,0], [2, 1], [3, 0], [3,1] ] -> [ [3,0], [3,1] ]

        if there is, it returns True, else False
        '''

        for x, y in self.block.indexes_below():
            if x != ROWS and self.board[x][y] != ' * ':
                return False
        else:
            return True

    def nothing_right(self) -> bool:
        '''
        should work similarily to nothing_below
        '''

        for x, y in self.block.indexes_right():
            if y != COLUMNS and self.board[x][y] != ' * ':
                return False
        else:
            return True

    def nothing_left(self) -> bool:
        '''
        should work similarily to nothing_below
        '''

        for x, y in self.block.indexes_left():
            if y != COLUMNS and self.board[x][y] != ' * ':
                return False
        else:
            return True

    def rotation(self, value=0) -> {str: [int, int]}:
        '''
        1. We find the current orientation and coordinates
        2. We rotate the block
        2. We get the ideal new orientation and coordinates
        3. We rotate the block back
        4. We check the new coordinates depending on which type of block it is...
        5. 
        '''
        current_phase = self.block.orientation(
        )  #we need to keep track of the current phase and original coords
        current_coords = self.block.blocks.copy()

        self.block.rotate(value)

        new_phase = self.block.orientation(
        )  #we need to keep track of the new value
        rotation_coords = self.block.blocks.copy()

        self.block.rotate(-value)

        print('Current Phase\n', current_phase)
        print('Current coordinates\n', current_coords, '\n')

        print('New Phase\n', new_phase)
        print('Rotation coordinates\n', rotation_coords, '\n')

        #each list in the board is each row (first index)
        #each element in the list is each column (second index)
        '''
        [X, X, A, X]
        [X, A, A, X]
        [X, X, A, X]
        '''
        def valid_rotation(rotation_coords: dict) -> bool:
            '''
            The valid rotation checks whether the coordinates that a block will rotate into are valid...

            1. First, it filters coordinates to check only the "new" spots; basically to prevent from checking itself...
            2. We check to see if the coordinates are even inside the bounds of the board
            3. We then check if the new spot is "unoccupied"
            '''
            filtered_rotation_coords = [
                i for i in rotation_coords.values()
                if i not in current_coords.values()
            ]

            print(filtered_rotation_coords)

            for i in filtered_rotation_coords:
                row = i[0]
                column = i[1]

                if row not in range(ROWS):
                    return False
                if column not in range(COLUMNS):
                    return False

                if self.board[row][column] != ' * ':
                    return False
            else:
                return True

        def final_coords_generator(rotation_coords: dict, y_value: int,
                                   x_value: int) -> {
                                       str: [int, int]
                                   }:
            return {
                i: [
                    rotation_coords[i][0] + y_value,
                    rotation_coords[i][1] + x_value
                ]
                for i in rotation_coords
            }

        if valid_rotation(rotation_coords):  #Check 1
            self.block.index_changer(value)
            self.block.blocks = rotation_coords  #the problem here is that the phase isn't changing...

        elif self.block.block_type in ['J', 'L', 'S', 'T', 'Z']:

            if (current_phase, new_phase) in [('R', '0'), ('R', '2')]:
                if valid_rotation(final_coords_generator(
                        rotation_coords, 0, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -1, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -1, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 2, 0)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 2, 0)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 2, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 2, 1)

            elif (current_phase, new_phase) in [('0', 'R'), ('2', 'R')]:
                if valid_rotation(
                        final_coords_generator(rotation_coords, 0, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 1, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 1, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -2, 0)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -2, 0)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -2, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -2, -1)

            elif (current_phase, new_phase) in [('2', 'L'), ('0', 'L')]:
                if valid_rotation(final_coords_generator(
                        rotation_coords, 0, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 1, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 1, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -2, 0)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -2, 0)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -2, 1)):
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -2, 1)

            elif (current_phase, new_phase) in [('L', '2'), ('L', '0')]:
                if valid_rotation(
                        final_coords_generator(rotation_coords, 0, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -1, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -1, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 2, 0)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 2, 0)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 2, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 2, -1)

        elif self.block.block_type == 'I':
            if (current_phase, new_phase) in [('R', '0'), ('2', 'L')]:
                if valid_rotation(final_coords_generator(
                        rotation_coords, 0, 2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, 2)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 0, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 1, 2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 1, 2)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -2, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -2, -1)

            elif (current_phase, new_phase) in [('0', 'R'), ('L', '2')]:
                if valid_rotation(
                        final_coords_generator(rotation_coords, 0, -2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, -2)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 0, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -1, -2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -1, -2)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 2, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 2, 1)

            elif (current_phase, new_phase) in [('R', '2'), ('0', 'L')]:
                if valid_rotation(
                        final_coords_generator(rotation_coords, 0, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 0, 2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, 2)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 2, -1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 2, -1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -1, 2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -1, 2)

            elif (current_phase, new_phase) in [('2', 'R'), ('L', '0')]:
                if valid_rotation(final_coords_generator(
                        rotation_coords, 0, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 0, -2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 0, -2)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, -2, 1)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, -2, 1)

                elif valid_rotation(
                        final_coords_generator(rotation_coords, 1, -2)):
                    self.block.index_changer(value)
                    self.block.blocks = final_coords_generator(
                        rotation_coords, 1, -2)

    def mark_lines(self):
        '''
        REQUIRES TESTING!!!!!!
        replaces any rows full of only letters (nested lists) stored in self.board with rows of clear_strings

        -I'm assuming all I have to do is check if a row or a list is full
        -Then, I show the matching.
        -Then, I empty the row.
        -Then I push the rows down. Hopefully, I don't do this recursively.
        -I'll do more research on this, but this seems rather straightforward to be honest.
        '''
        indexes = []

        for i in range(len(self.board)):
            if ' * ' not in self.board[i]:
                self.board[i] = [CLEAR_STRING for i in range(COLUMNS)]
                indexes.append(i)

    def line_clear(self):
        #print('line_clear')

        coords = []

        #collects the coordinates
        for row in range(len(self.board)):
            if self.board[row] == [CLEAR_STRING for i in range(COLUMNS)]:
                for column in range(COLUMNS):
                    coords.append([row, column])

        #deletes the coordinates from the blocks because the update function relies primarily on existing block coordinates
        for coord in coords:
            self.block_coords_deleter(coord)

        #deletes any block objects with no coordinates...
        self.block_deleter()

        #the blocks above cleared lines end up getting hard dropped...
        #collect the indexes of the rows cleared
        #compare the row index of any block above that row
        #if four lines are cleared under a block, we add four to the row index of that block

        #print(self.existing_blocks_list())
        #print()

        for block in self.existing_blocks:
            for coord in block.blocks:
                block.blocks[coord][0] += len({
                    i[0]
                    for i in coords if i[0] > block.blocks[coord][0]
                })  #rows 19 and 20 are cleared; thus 18 goes down two rows,

        #print(self.existing_blocks_list())

    def lock_out(self):
        '''
        triggers when a whole tetrimino locks down above the sky line

        the sky line i assume is the first two rows... so rows 1,2 or indexes 0,1
        if the block is frozen and all coordinates are inside.. trigger gameover
        '''

        if self.block.frozen and all(
            [i[0] in [0, 1] for i in self.block.blocks.values()]):
            print("LOCK OUT")
            print("GAME OVER")
            print("end rewards...")
            print("high score table...")

            raise Exception

    def block_out(self):
        '''
        triggers when part of a newly-generated tetrimino is blocked due to an existing block in the matrix

        first, we check that a newly generated tetrimino will not be blocked,
            the new block's coordinates will be checked

            if any of those coordinates are occupied already, end game
                otherwise handle the block like normal
        '''
        block_coords = self.block.blocks.values()

        for j, k in block_coords:
            if self.board[j][k] != ' * ':
                print("BLOCK OUT")
                print("GAME OVER")
                print("end rewards...")
                print("high score table...")

                raise Exception