Skip to main content
edited tags
Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237
Removed dead link and opinion-based question regarding open-source
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

Self playing tetris-playing Tetris game

I have got an extremely simple self playing tetris-playing Tetris game which I coded up and I am looking to see how it could be improved.

It It would also be a learning curve for me to see how those much better than I am would go about improving the code.

I would also like to opensource this project. Where would be a good place to get some attention to this trivial project?

BTW I have the code already hosted on bitbucket and the link is https://bitbucket.org/c4obi/tetris

###############################################################################
## an implementation of a ***very*** basic tetrisTetris game in pythonPython using pygamePygame
###############################################################################
'''rotate --- r
   pause ---- p
   direction buttons for movement'''

import sys
import copy
import pygame
import random

size = width, height = 200, 400
sqrsize, pen_size = 20, 1
occupied_squares = []
top_of_screen = (0, 0)
color = {'white':(255, 255, 255)}
top_x, top_y = top_of_screen[0], top_of_screen[1]

pygame.init()
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((color['white']))
screen.blit(background, top_of_screen)
pygame.display.flip()

################################################################################
#constructors and selectors for a tetrominoe shape
################################################################################
def make_tetrominoe(block1, block2, block3, block4, name):
    """Inputs<- 4 constituent blocks that make up a tetrominoe shape and name
       of tetrominoe shape.
       This returns a tetrominoe shape."""
    return [block1, block2, block3, block4, name]
 
def get_tetname(tetrominoe):
    """returns the name of a tetrominoe shape"""
    return tetrominoe[4]
    
def get_blocks(tetrominoe):
    """returns a list of blocks that make up a tetrominoe piece"""
    return tetrominoe[:4]
    
def get_refblock(tetrominoe):
    """gets reference block.Reference block is one around which other
    blocks are drawn"""
    return tetrominoe[3]

def block_points(tetronimoe):
    """gets the coordinates of the individual blocks that make up a tetrominoe
    piece"""
    blocks = get_blocks(tetronimoe) 
    return [get_point(block) for block in blocks]
    
    
###############################################################################
#constructors and selectors for a tetrominoe shape block
###############################################################################
def make_block(point, breadth, length):
    """This returns a block. A block is one of the constituent parts of a
    tetrominoe shape and is made up of a start coordinate,the breadth of the
    block and the lenght"""
    return [point, breadth, length]

def get_point(a_block):
    """returns the coordinate start point of block"""
    return a_block[0]

def block_width(a_block):
    """Returns the width of a block"""
    return a_block[1]
    
def block_height(a_block):
    """Returns the height of a block"""
    return a_block[2]


#############################################################################    
#constructors and selectors for coordinate points
##############################################################################
def make_point(x_coord, y_coord, colour):
    """Input<-coordinate of a point, color
       returns a point object with the coordinates of the point and color"""
    return [x_coord, y_coord, colour]
    
def point_x(a_point):
    """Returns the xcoordinate of a point structure"""
    return a_point[0]
    
def point_y(a_point):
    """Returns the ycoordinate of a point structure"""
    return a_point[1]
    
def point_color(a_point):
    """Returns the color of a point structure"""
    return a_point[2]


###############################################################################
###############################################################################
def delta_point(a_block, delta_x, delta_y):
    """input<- a block(constituent of a tetrominoe shape), integer, integer
       output->a block
       function which takes a block and increments its POINT"""
    point = get_point(a_block)
    return (make_block(make_point(point_x(point)+delta_x,
                                  point_y(point)+delta_y, point_color(point)),
                       block_width(a_block), block_height(a_block)))


###############################################################################
## game controller
###############################################################################
def tetris():
    """Sets up the whole game play and handles event handling"""
    mov_delay = 150
    events = {276: 'left', 275: 'right', 112: 'pause'}
    
    while True:
        move_dir = 'down' #default move direction
        game = 'playing'  #default game state play:- is game paused or playing?
        
        tet_shape = random_shape()
        
        if legal(tet_shape):
            draw_shape(tet_shape)
        else:
            break  #game over

        dets = find_column(get_tetname(tet_shape))
        
        sel_col = dets[0]
        rotate_count = dets[-1]

        mov_cnt = (sel_col - 80) / sqrsize

        if mov_cnt < 0:
            move_dir = 'left'
        elif mov_cnt > 0:
            move_dir = 'right'
        elif mov_cnt == 0:
            move_dir = 'down'

        mov_cnt = abs(mov_cnt)

        while rotate_count > 0:
            new_tet_shape = rotate(tet_shape)
            if legal(new_tet_shape):
                prev_tet, tet_shape = tet_shape, new_tet_shape
                draw_and_clear(tet_shape, prev_tet, mov_delay)
            rotate_count = rotate_count - 1

        while mov_cnt > 0:
            new_tet_shape = move(tet_shape, move_dir)
            if legal(new_tet_shape):
                prev_tet, tet_shape = tet_shape, new_tet_shape
                draw_and_clear(tet_shape, prev_tet, mov_delay)
            mov_cnt = mov_cnt - 1


        while True:
            if game == 'paused':
                for event in pygame.event.get((pygame.KEYDOWN, pygame.KEYUP)):
                    if event.key == pygame.K_p:
                        game, move_dir = 'playing', 'down'  
            else:
                for event in pygame.event.get((pygame.KEYDOWN, pygame.KEYUP)):
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_p:
                            game, move_dir = 'paused', 'pause'
                            break

                    elif event.type == pygame.KEYUP:
                        mov_delay, move_dir = mov_delay, 'down'


                move_dir = 'down'
                new_tet_shape = move(tet_shape, move_dir)
                if legal(new_tet_shape):
                    prev_tet, tet_shape = tet_shape, new_tet_shape
                    draw_and_clear(tet_shape, prev_tet, mov_delay) 
                else:
                    #If shape didn't move and direction of movement is down
                    #then shape has come to rest so we can check for a full row
                    #which we delete before exiting loop and generating a new
                    #tetrominoe. if direction for movement is sideways
                    #and   block did not move it should be moved down rather
                    if move_dir == 'down':
                        occupied_squares.extend(block_points(tet_shape))
                        for row_no in range(height, -sqrsize, -sqrsize):
                            while row_filled(row_no):
                                delete_row(row_no)
                                background.fill(color['white'])
                                for point in occupied_squares:
                                    draw_block(point)
                        break
                    else:
                        draw_shape(tet_shape)
                        pygame.time.delay(mov_delay)


###########################################################################
###########################################################################                      
def draw_and_clear(tetrominoe, prev_tet, delay):
    """input<-two tetrominoe shapes
       clear the previously drawn tetrominoe first and then draw a new
       tetrominoe"""
    for point in block_points(prev_tet):
        background.fill((color['white']), (point_x(point), point_y(point),
                                           sqrsize, sqrsize))
        screen.blit(background, top_of_screen)
        pygame.display.update()
    draw_shape(tetrominoe)
    pygame.time.delay(delay)
    
    
############################################################################
############################################################################
def draw_shape(tetrominoe):
    """input<-tetriminoe shape
       This draws a tetrominoe shape to game board"""
    for point in block_points(tetrominoe):
        draw_block(point)
    screen.blit(background, top_of_screen)
    pygame.display.update()
    
 
#############################################################################
#############################################################################
def draw_block(a_point):
    """draws a basic shape to screen"""
    pygame.draw.rect(background, point_color(a_point), (point_x(a_point),
                     point_y(a_point), sqrsize, sqrsize), 1)
    
    
############################################################################
############################################################################
def row_filled(row_no, board=None):
    """input<-tetriminoe shape
       checks if a row on game board is fully occupied by a shape block"""
    if board:
        filled_coords = [(point_x(point), point_y(point)) for point in board]
        for col in range(0, width, sqrsize):
            if (col, row_no) in filled_coords:
                continue
            else:
                return False
        return True 

    else:
        filled_coords = [[point_x(pt), point_y(pt)] for pt in occupied_squares]
        for col in range(0, width, sqrsize):
            if [col, row_no] in filled_coords:
                continue
            else:
                return False
        return True 


##############################################################################
##############################################################################
def delete_row(row_no):
    """input<-integer(a row number)
       output->list of points
       removes all squares on a row from the occupied_squares list and then
       moves all square positions which have y-axis coord less than row_no down
       board"""
    global occupied_squares
    occupied_squares = [point for point in occupied_squares
                        if point_y(point) != row_no]
    for index in range(len(occupied_squares)):
        if point_y(occupied_squares[index]) < row_no:
            occupied_squares[index] = make_point(point_x(occupied_squares[index]),
                                    point_y(occupied_squares[index]) + sqrsize,
                                    point_color(occupied_squares[index]))
        
        
##############################################################################
##############################################################################
def legal(tet_shape):
    """input<-tetrominoe piece
       output->bool
       checks that a tetromone is in a legal portion of the board"""
    tet_block_points = block_points(tet_shape)
    filled_coords = [(point_x(pt), point_y(pt)) for pt in occupied_squares]
    for point in tet_block_points:
        new_x, new_y = point_x(point), point_y(point)
        if ((new_x, new_y) in filled_coords or (new_y >= height or
                                        (new_x >= width or new_x < top_x))):
            return False
    return True


##############################################################################
##############################################################################
def move(shape, direction, undo=False):
    """input<- a tetrominoe shape
       output<- a terominoe shape
       function moves a tetrominoe shape by moving all constituent blocks
       by a fixed amount in a direction given by 'direction' argument"""
    no_move = 0
    directions = {'down':(no_move, sqrsize), 'left':(-sqrsize, no_move),
        'right':(sqrsize, no_move), 'pause': (no_move, no_move)}
    delta_x, delta_y = directions[direction]
    if undo:
        delta_x, delta_y = -delta_x, -delta_y
    new_blocks = [delta_point (block, delta_x, delta_y)
                  for block in get_blocks(shape)]
    return (make_tetrominoe(new_blocks[0], new_blocks[1],
                            new_blocks[2], new_blocks[3], get_tetname(shape)))
       
       
##############################################################################
##############################################################################
def tetrominoe_shape(shape, start_x=80, start_y=0):
    """function returns a random tetrominoe piece"""
    shapes = {'S': make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                         start_y + 2*sqrsize,
                                                         (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x, start_y,
                                                        (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x,
                                                        start_y + 1*sqrsize,
                                                        (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x + 1*sqrsize,
                                                    start_y + 1*sqrsize,
                                                    (0, 0, 0)),
                                             sqrsize, sqrsize)
                                  , 'S'),
        
        'O': make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                (200, 200, 200)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x + 1*sqrsize, start_y,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            'O'),
                
        'I': make_tetrominoe(make_block(make_point(start_x, start_y + 3*sqrsize,
                                                   (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 2*sqrsize,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            'I'),
                
        'L':make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 2*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (0, 0, 255)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 2*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize), 'L'),
                
        'T':make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                  (255, 0, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (255, 0, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x - 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                  (255, 0, 0)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x,
                                                        start_y + 1*sqrsize,
                                                        (255, 0, 0)),
                                        sqrsize, sqrsize), 'T')
        }
    return shapes[shape]


#####
#####
def random_shape(start_x=80, start_y=0):
    """return a random tetrominoe shape"""
    tets = ['S', 'O', 'I', 'L', 'T']
    return tetrominoe_shape(tets[random.randint(0, 4)], start_x, start_y)

    
##############################################################################
##############################################################################
def rotate(tetrominoe):
    """input<- tetrominoe shape
       ouput-> tetrominoe shape
       rotates a tetrominoe shape if possible about a reference block."""
    #global occupied_squares
    if get_tetname(tetrominoe) == 'O':
        return tetrominoe
    else:
        ref_point = get_point(get_refblock(tetrominoe)) 
        x_coord = point_x(ref_point)
        y_coord = point_y(ref_point)
        
        tetblock_coords = block_points(tetrominoe) 
        
        new_tet = make_tetrominoe(make_block(make_point(x_coord +
                                            y_coord-point_y(tetblock_coords[0]),
                            y_coord - (x_coord - point_x(tetblock_coords[0])),
                            point_color(ref_point)), sqrsize, sqrsize,
                                             ),
                        
                        make_block(make_point(x_coord + y_coord -
                                              point_y(tetblock_coords[1]),
                            y_coord - (x_coord - point_x(tetblock_coords[1])),
                            point_color(ref_point)), sqrsize, sqrsize),
                        
                        make_block(make_point(x_coord + y_coord -
                                              point_y(tetblock_coords[2]),
                            y_coord - (x_coord - point_x(tetblock_coords[2])),
                            point_color(ref_point)), sqrsize, sqrsize),
                        
                        make_block(make_point(x_coord, y_coord,
                                              point_color(ref_point)),
                                   sqrsize, sqrsize),
                    get_tetname(tetrominoe))
    
        #if legal(new_tet):
        return new_tet
        #else:
         #   return tetrominoe
####
####
def drop_shape(shape):
    """drop a shape into postion on a column"""
    new_shape = move(shape, 'down')
    prev_shape, new_shape = shape, new_shape
    while legal(new_shape):
        prev_shape, new_shape = new_shape, move(new_shape, 'down')
    return prev_shape


####
####
def bubble_count(shape):
    """returns number of new empty spots generated when a shape is placed at a 
    legal point"""
    count = 0
    points = [(point_x(pt), point_y(pt)) for pt in block_points(shape)]
    board = [(point_x(pt), point_y(pt)) for pt in occupied_squares]
    for pt in points:
        for i in range(point_y(pt) + sqrsize, height, sqrsize):
            if (pt[0], i) in board or (pt[0], i) in points:
                break
            else:
                count += 1
    return count

####
###
def shape_lowest_row(shape):
    """return the lowest row of a shape"""
    points = [(point_x(pt), point_y(pt)) for pt in block_points(shape)]
    points = sorted(points, key=lambda point: point[1], reverse=True)
    return points[0]

#####
#####
def row_filln_column(shape):
    """return a list of columns, rows filled tuple for each column on the 
    board if there are n columns for which a shape dropped in column fills
    a row"""
    rows_filled = []
    shape_rotates = {'S':2, 'I':1, 'O':0, 'L':3, 'T':3}
    rotate_count = shape_rotates[shape]
    curr_cnt = 0
    while True:
        for col in range(0, width, sqrsize):
            board = copy.deepcopy(occupied_squares)
            tet_shape = tetrominoe_shape(shape, start_x=col, start_y=0)
            cnt = curr_cnt
            while cnt > 0:
                tet_shape = rotate(tet_shape)
                cnt -= 1

            if not legal(tet_shape): # check shape is in board sideways
                continue
            tet_shape = drop_shape(tet_shape)
            board.extend(block_points(tet_shape))
            rows = 0
            for row in range(height, 0, -sqrsize):
                if row_filled(row, board=board):
                    rows += 1
            if rows > 0:
                rows_filled.append((col, rows, curr_cnt))
            tet_shape = rotate(tet_shape)

        if rotate_count == curr_cnt:
            break
        curr_cnt += 1
    if rows_filled:
        return rows_filled
    return None

####
####
def next_best_columns(shape):
    """return list of columns which a shape can go into if the shape cannot
    fill any rows"""
    next_best = []
    shape_rotates = {'S':2, 'I':1, 'O':0, 'L':3, 'T':3}
    rotate_count = shape_rotates[shape]
    curr_cnt = 0
    while True:
        for col in range(0, width, sqrsize):
            board = copy.deepcopy(occupied_squares)
            tet_shape = tetrominoe_shape(shape, start_x=col, start_y=0)
            cnt = curr_cnt
            while cnt > 0:
                tet_shape = rotate(tet_shape)
                cnt -= 1

            if not legal(tet_shape):
                continue
            tet_shape = drop_shape(tet_shape)
            board.extend(block_points(tet_shape))
            bubble_cnt = bubble_count(tet_shape)
            next_best.append((col, bubble_cnt, shape_lowest_row(tet_shape)[1],
                             curr_cnt))
            #print col, bubble_cnt
        if rotate_count == curr_cnt:
            break
        curr_cnt += 1
    return next_best

#####
#####
def find_column(shape):
    """find column of best fit to drop down a tetrominoe shape from"""
    # search for if any rows can be filled up by shape
    rows_filled = row_filln_column(shape)
    if not rows_filled:
        next_best = next_best_columns(shape)
        next_best =  sorted(next_best, key=lambda col: col[2], reverse=True)
        cols = sorted(next_best, key=lambda col: col[1])
        return cols[0]
    rows_filled = sorted(rows_filled, key=lambda row_filled: row_filled[1])
    return rows_filled[-1] #col with most rows filled


if __name__ == '__main__':
    tetris()

Self playing tetris

I have got an extremely simple self playing tetris which I coded up and I am looking to see how it could be improved.

It would also be a learning curve for me to see how those much better than I am would go about improving the code.

I would also like to opensource this project. Where would be a good place to get some attention to this trivial project?

BTW I have the code already hosted on bitbucket and the link is https://bitbucket.org/c4obi/tetris

###############################################################################
## an implementation of a ***very*** basic tetris game in python using pygame
###############################################################################
'''rotate --- r
   pause ---- p
   direction buttons for movement'''

import sys
import copy
import pygame
import random

size = width, height = 200, 400
sqrsize, pen_size = 20, 1
occupied_squares = []
top_of_screen = (0, 0)
color = {'white':(255, 255, 255)}
top_x, top_y = top_of_screen[0], top_of_screen[1]

pygame.init()
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((color['white']))
screen.blit(background, top_of_screen)
pygame.display.flip()

################################################################################
#constructors and selectors for a tetrominoe shape
################################################################################
def make_tetrominoe(block1, block2, block3, block4, name):
    """Inputs<- 4 constituent blocks that make up a tetrominoe shape and name
       of tetrominoe shape.
       This returns a tetrominoe shape."""
    return [block1, block2, block3, block4, name]
 
def get_tetname(tetrominoe):
    """returns the name of a tetrominoe shape"""
    return tetrominoe[4]
    
def get_blocks(tetrominoe):
    """returns a list of blocks that make up a tetrominoe piece"""
    return tetrominoe[:4]
    
def get_refblock(tetrominoe):
    """gets reference block.Reference block is one around which other
    blocks are drawn"""
    return tetrominoe[3]

def block_points(tetronimoe):
    """gets the coordinates of the individual blocks that make up a tetrominoe
    piece"""
    blocks = get_blocks(tetronimoe) 
    return [get_point(block) for block in blocks]
    
    
###############################################################################
#constructors and selectors for a tetrominoe shape block
###############################################################################
def make_block(point, breadth, length):
    """This returns a block. A block is one of the constituent parts of a
    tetrominoe shape and is made up of a start coordinate,the breadth of the
    block and the lenght"""
    return [point, breadth, length]

def get_point(a_block):
    """returns the coordinate start point of block"""
    return a_block[0]

def block_width(a_block):
    """Returns the width of a block"""
    return a_block[1]
    
def block_height(a_block):
    """Returns the height of a block"""
    return a_block[2]


#############################################################################    
#constructors and selectors for coordinate points
##############################################################################
def make_point(x_coord, y_coord, colour):
    """Input<-coordinate of a point, color
       returns a point object with the coordinates of the point and color"""
    return [x_coord, y_coord, colour]
    
def point_x(a_point):
    """Returns the xcoordinate of a point structure"""
    return a_point[0]
    
def point_y(a_point):
    """Returns the ycoordinate of a point structure"""
    return a_point[1]
    
def point_color(a_point):
    """Returns the color of a point structure"""
    return a_point[2]


###############################################################################
###############################################################################
def delta_point(a_block, delta_x, delta_y):
    """input<- a block(constituent of a tetrominoe shape), integer, integer
       output->a block
       function which takes a block and increments its POINT"""
    point = get_point(a_block)
    return (make_block(make_point(point_x(point)+delta_x,
                                  point_y(point)+delta_y, point_color(point)),
                       block_width(a_block), block_height(a_block)))


###############################################################################
## game controller
###############################################################################
def tetris():
    """Sets up the whole game play and handles event handling"""
    mov_delay = 150
    events = {276: 'left', 275: 'right', 112: 'pause'}
    
    while True:
        move_dir = 'down' #default move direction
        game = 'playing'  #default game state play:- is game paused or playing?
        
        tet_shape = random_shape()
        
        if legal(tet_shape):
            draw_shape(tet_shape)
        else:
            break  #game over

        dets = find_column(get_tetname(tet_shape))
        
        sel_col = dets[0]
        rotate_count = dets[-1]

        mov_cnt = (sel_col - 80) / sqrsize

        if mov_cnt < 0:
            move_dir = 'left'
        elif mov_cnt > 0:
            move_dir = 'right'
        elif mov_cnt == 0:
            move_dir = 'down'

        mov_cnt = abs(mov_cnt)

        while rotate_count > 0:
            new_tet_shape = rotate(tet_shape)
            if legal(new_tet_shape):
                prev_tet, tet_shape = tet_shape, new_tet_shape
                draw_and_clear(tet_shape, prev_tet, mov_delay)
            rotate_count = rotate_count - 1

        while mov_cnt > 0:
            new_tet_shape = move(tet_shape, move_dir)
            if legal(new_tet_shape):
                prev_tet, tet_shape = tet_shape, new_tet_shape
                draw_and_clear(tet_shape, prev_tet, mov_delay)
            mov_cnt = mov_cnt - 1


        while True:
            if game == 'paused':
                for event in pygame.event.get((pygame.KEYDOWN, pygame.KEYUP)):
                    if event.key == pygame.K_p:
                        game, move_dir = 'playing', 'down'  
            else:
                for event in pygame.event.get((pygame.KEYDOWN, pygame.KEYUP)):
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_p:
                            game, move_dir = 'paused', 'pause'
                            break

                    elif event.type == pygame.KEYUP:
                        mov_delay, move_dir = mov_delay, 'down'


                move_dir = 'down'
                new_tet_shape = move(tet_shape, move_dir)
                if legal(new_tet_shape):
                    prev_tet, tet_shape = tet_shape, new_tet_shape
                    draw_and_clear(tet_shape, prev_tet, mov_delay) 
                else:
                    #If shape didn't move and direction of movement is down
                    #then shape has come to rest so we can check for a full row
                    #which we delete before exiting loop and generating a new
                    #tetrominoe. if direction for movement is sideways
                    #and   block did not move it should be moved down rather
                    if move_dir == 'down':
                        occupied_squares.extend(block_points(tet_shape))
                        for row_no in range(height, -sqrsize, -sqrsize):
                            while row_filled(row_no):
                                delete_row(row_no)
                                background.fill(color['white'])
                                for point in occupied_squares:
                                    draw_block(point)
                        break
                    else:
                        draw_shape(tet_shape)
                        pygame.time.delay(mov_delay)


###########################################################################
###########################################################################                      
def draw_and_clear(tetrominoe, prev_tet, delay):
    """input<-two tetrominoe shapes
       clear the previously drawn tetrominoe first and then draw a new
       tetrominoe"""
    for point in block_points(prev_tet):
        background.fill((color['white']), (point_x(point), point_y(point),
                                           sqrsize, sqrsize))
        screen.blit(background, top_of_screen)
        pygame.display.update()
    draw_shape(tetrominoe)
    pygame.time.delay(delay)
    
    
############################################################################
############################################################################
def draw_shape(tetrominoe):
    """input<-tetriminoe shape
       This draws a tetrominoe shape to game board"""
    for point in block_points(tetrominoe):
        draw_block(point)
    screen.blit(background, top_of_screen)
    pygame.display.update()
    
 
#############################################################################
#############################################################################
def draw_block(a_point):
    """draws a basic shape to screen"""
    pygame.draw.rect(background, point_color(a_point), (point_x(a_point),
                     point_y(a_point), sqrsize, sqrsize), 1)
    
    
############################################################################
############################################################################
def row_filled(row_no, board=None):
    """input<-tetriminoe shape
       checks if a row on game board is fully occupied by a shape block"""
    if board:
        filled_coords = [(point_x(point), point_y(point)) for point in board]
        for col in range(0, width, sqrsize):
            if (col, row_no) in filled_coords:
                continue
            else:
                return False
        return True 

    else:
        filled_coords = [[point_x(pt), point_y(pt)] for pt in occupied_squares]
        for col in range(0, width, sqrsize):
            if [col, row_no] in filled_coords:
                continue
            else:
                return False
        return True 


##############################################################################
##############################################################################
def delete_row(row_no):
    """input<-integer(a row number)
       output->list of points
       removes all squares on a row from the occupied_squares list and then
       moves all square positions which have y-axis coord less than row_no down
       board"""
    global occupied_squares
    occupied_squares = [point for point in occupied_squares
                        if point_y(point) != row_no]
    for index in range(len(occupied_squares)):
        if point_y(occupied_squares[index]) < row_no:
            occupied_squares[index] = make_point(point_x(occupied_squares[index]),
                                    point_y(occupied_squares[index]) + sqrsize,
                                    point_color(occupied_squares[index]))
        
        
##############################################################################
##############################################################################
def legal(tet_shape):
    """input<-tetrominoe piece
       output->bool
       checks that a tetromone is in a legal portion of the board"""
    tet_block_points = block_points(tet_shape)
    filled_coords = [(point_x(pt), point_y(pt)) for pt in occupied_squares]
    for point in tet_block_points:
        new_x, new_y = point_x(point), point_y(point)
        if ((new_x, new_y) in filled_coords or (new_y >= height or
                                        (new_x >= width or new_x < top_x))):
            return False
    return True


##############################################################################
##############################################################################
def move(shape, direction, undo=False):
    """input<- a tetrominoe shape
       output<- a terominoe shape
       function moves a tetrominoe shape by moving all constituent blocks
       by a fixed amount in a direction given by 'direction' argument"""
    no_move = 0
    directions = {'down':(no_move, sqrsize), 'left':(-sqrsize, no_move),
        'right':(sqrsize, no_move), 'pause': (no_move, no_move)}
    delta_x, delta_y = directions[direction]
    if undo:
        delta_x, delta_y = -delta_x, -delta_y
    new_blocks = [delta_point (block, delta_x, delta_y)
                  for block in get_blocks(shape)]
    return (make_tetrominoe(new_blocks[0], new_blocks[1],
                            new_blocks[2], new_blocks[3], get_tetname(shape)))
       
       
##############################################################################
##############################################################################
def tetrominoe_shape(shape, start_x=80, start_y=0):
    """function returns a random tetrominoe piece"""
    shapes = {'S': make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                         start_y + 2*sqrsize,
                                                         (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x, start_y,
                                                        (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x,
                                                        start_y + 1*sqrsize,
                                                        (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x + 1*sqrsize,
                                                    start_y + 1*sqrsize,
                                                    (0, 0, 0)),
                                             sqrsize, sqrsize)
                                  , 'S'),
        
        'O': make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                (200, 200, 200)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x + 1*sqrsize, start_y,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            'O'),
                
        'I': make_tetrominoe(make_block(make_point(start_x, start_y + 3*sqrsize,
                                                   (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 2*sqrsize,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            'I'),
                
        'L':make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 2*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (0, 0, 255)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 2*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize), 'L'),
                
        'T':make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                  (255, 0, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (255, 0, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x - 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                  (255, 0, 0)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x,
                                                        start_y + 1*sqrsize,
                                                        (255, 0, 0)),
                                        sqrsize, sqrsize), 'T')
        }
    return shapes[shape]


#####
#####
def random_shape(start_x=80, start_y=0):
    """return a random tetrominoe shape"""
    tets = ['S', 'O', 'I', 'L', 'T']
    return tetrominoe_shape(tets[random.randint(0, 4)], start_x, start_y)

    
##############################################################################
##############################################################################
def rotate(tetrominoe):
    """input<- tetrominoe shape
       ouput-> tetrominoe shape
       rotates a tetrominoe shape if possible about a reference block."""
    #global occupied_squares
    if get_tetname(tetrominoe) == 'O':
        return tetrominoe
    else:
        ref_point = get_point(get_refblock(tetrominoe)) 
        x_coord = point_x(ref_point)
        y_coord = point_y(ref_point)
        
        tetblock_coords = block_points(tetrominoe) 
        
        new_tet = make_tetrominoe(make_block(make_point(x_coord +
                                            y_coord-point_y(tetblock_coords[0]),
                            y_coord - (x_coord - point_x(tetblock_coords[0])),
                            point_color(ref_point)), sqrsize, sqrsize,
                                             ),
                        
                        make_block(make_point(x_coord + y_coord -
                                              point_y(tetblock_coords[1]),
                            y_coord - (x_coord - point_x(tetblock_coords[1])),
                            point_color(ref_point)), sqrsize, sqrsize),
                        
                        make_block(make_point(x_coord + y_coord -
                                              point_y(tetblock_coords[2]),
                            y_coord - (x_coord - point_x(tetblock_coords[2])),
                            point_color(ref_point)), sqrsize, sqrsize),
                        
                        make_block(make_point(x_coord, y_coord,
                                              point_color(ref_point)),
                                   sqrsize, sqrsize),
                    get_tetname(tetrominoe))
    
        #if legal(new_tet):
        return new_tet
        #else:
         #   return tetrominoe
####
####
def drop_shape(shape):
    """drop a shape into postion on a column"""
    new_shape = move(shape, 'down')
    prev_shape, new_shape = shape, new_shape
    while legal(new_shape):
        prev_shape, new_shape = new_shape, move(new_shape, 'down')
    return prev_shape


####
####
def bubble_count(shape):
    """returns number of new empty spots generated when a shape is placed at a 
    legal point"""
    count = 0
    points = [(point_x(pt), point_y(pt)) for pt in block_points(shape)]
    board = [(point_x(pt), point_y(pt)) for pt in occupied_squares]
    for pt in points:
        for i in range(point_y(pt) + sqrsize, height, sqrsize):
            if (pt[0], i) in board or (pt[0], i) in points:
                break
            else:
                count += 1
    return count

####
###
def shape_lowest_row(shape):
    """return the lowest row of a shape"""
    points = [(point_x(pt), point_y(pt)) for pt in block_points(shape)]
    points = sorted(points, key=lambda point: point[1], reverse=True)
    return points[0]

#####
#####
def row_filln_column(shape):
    """return a list of columns, rows filled tuple for each column on the 
    board if there are n columns for which a shape dropped in column fills
    a row"""
    rows_filled = []
    shape_rotates = {'S':2, 'I':1, 'O':0, 'L':3, 'T':3}
    rotate_count = shape_rotates[shape]
    curr_cnt = 0
    while True:
        for col in range(0, width, sqrsize):
            board = copy.deepcopy(occupied_squares)
            tet_shape = tetrominoe_shape(shape, start_x=col, start_y=0)
            cnt = curr_cnt
            while cnt > 0:
                tet_shape = rotate(tet_shape)
                cnt -= 1

            if not legal(tet_shape): # check shape is in board sideways
                continue
            tet_shape = drop_shape(tet_shape)
            board.extend(block_points(tet_shape))
            rows = 0
            for row in range(height, 0, -sqrsize):
                if row_filled(row, board=board):
                    rows += 1
            if rows > 0:
                rows_filled.append((col, rows, curr_cnt))
            tet_shape = rotate(tet_shape)

        if rotate_count == curr_cnt:
            break
        curr_cnt += 1
    if rows_filled:
        return rows_filled
    return None

####
####
def next_best_columns(shape):
    """return list of columns which a shape can go into if the shape cannot
    fill any rows"""
    next_best = []
    shape_rotates = {'S':2, 'I':1, 'O':0, 'L':3, 'T':3}
    rotate_count = shape_rotates[shape]
    curr_cnt = 0
    while True:
        for col in range(0, width, sqrsize):
            board = copy.deepcopy(occupied_squares)
            tet_shape = tetrominoe_shape(shape, start_x=col, start_y=0)
            cnt = curr_cnt
            while cnt > 0:
                tet_shape = rotate(tet_shape)
                cnt -= 1

            if not legal(tet_shape):
                continue
            tet_shape = drop_shape(tet_shape)
            board.extend(block_points(tet_shape))
            bubble_cnt = bubble_count(tet_shape)
            next_best.append((col, bubble_cnt, shape_lowest_row(tet_shape)[1],
                             curr_cnt))
            #print col, bubble_cnt
        if rotate_count == curr_cnt:
            break
        curr_cnt += 1
    return next_best

#####
#####
def find_column(shape):
    """find column of best fit to drop down a tetrominoe shape from"""
    # search for if any rows can be filled up by shape
    rows_filled = row_filln_column(shape)
    if not rows_filled:
        next_best = next_best_columns(shape)
        next_best =  sorted(next_best, key=lambda col: col[2], reverse=True)
        cols = sorted(next_best, key=lambda col: col[1])
        return cols[0]
    rows_filled = sorted(rows_filled, key=lambda row_filled: row_filled[1])
    return rows_filled[-1] #col with most rows filled


if __name__ == '__main__':
    tetris()

Self-playing Tetris game

I have an extremely simple self-playing Tetris game which I coded up and I am looking to see how it could be improved. It would also be a learning curve for me to see how those much better than I am would go about improving the code.

###############################################################################
## an implementation of a ***very*** basic Tetris game in Python using Pygame
###############################################################################
'''rotate --- r
   pause ---- p
   direction buttons for movement'''

import sys
import copy
import pygame
import random

size = width, height = 200, 400
sqrsize, pen_size = 20, 1
occupied_squares = []
top_of_screen = (0, 0)
color = {'white':(255, 255, 255)}
top_x, top_y = top_of_screen[0], top_of_screen[1]

pygame.init()
screen = pygame.display.set_mode(size)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((color['white']))
screen.blit(background, top_of_screen)
pygame.display.flip()

################################################################################
#constructors and selectors for a tetrominoe shape
################################################################################
def make_tetrominoe(block1, block2, block3, block4, name):
    """Inputs<- 4 constituent blocks that make up a tetrominoe shape and name
       of tetrominoe shape.
       This returns a tetrominoe shape."""
    return [block1, block2, block3, block4, name]
 
def get_tetname(tetrominoe):
    """returns the name of a tetrominoe shape"""
    return tetrominoe[4]
    
def get_blocks(tetrominoe):
    """returns a list of blocks that make up a tetrominoe piece"""
    return tetrominoe[:4]
    
def get_refblock(tetrominoe):
    """gets reference block.Reference block is one around which other
    blocks are drawn"""
    return tetrominoe[3]

def block_points(tetronimoe):
    """gets the coordinates of the individual blocks that make up a tetrominoe
    piece"""
    blocks = get_blocks(tetronimoe) 
    return [get_point(block) for block in blocks]
    
    
###############################################################################
#constructors and selectors for a tetrominoe shape block
###############################################################################
def make_block(point, breadth, length):
    """This returns a block. A block is one of the constituent parts of a
    tetrominoe shape and is made up of a start coordinate,the breadth of the
    block and the lenght"""
    return [point, breadth, length]

def get_point(a_block):
    """returns the coordinate start point of block"""
    return a_block[0]

def block_width(a_block):
    """Returns the width of a block"""
    return a_block[1]
    
def block_height(a_block):
    """Returns the height of a block"""
    return a_block[2]


#############################################################################    
#constructors and selectors for coordinate points
##############################################################################
def make_point(x_coord, y_coord, colour):
    """Input<-coordinate of a point, color
       returns a point object with the coordinates of the point and color"""
    return [x_coord, y_coord, colour]
    
def point_x(a_point):
    """Returns the xcoordinate of a point structure"""
    return a_point[0]
    
def point_y(a_point):
    """Returns the ycoordinate of a point structure"""
    return a_point[1]
    
def point_color(a_point):
    """Returns the color of a point structure"""
    return a_point[2]


###############################################################################
###############################################################################
def delta_point(a_block, delta_x, delta_y):
    """input<- a block(constituent of a tetrominoe shape), integer, integer
       output->a block
       function which takes a block and increments its POINT"""
    point = get_point(a_block)
    return (make_block(make_point(point_x(point)+delta_x,
                                  point_y(point)+delta_y, point_color(point)),
                       block_width(a_block), block_height(a_block)))


###############################################################################
## game controller
###############################################################################
def tetris():
    """Sets up the whole game play and handles event handling"""
    mov_delay = 150
    events = {276: 'left', 275: 'right', 112: 'pause'}
    
    while True:
        move_dir = 'down' #default move direction
        game = 'playing'  #default game state play:- is game paused or playing?
        
        tet_shape = random_shape()
        
        if legal(tet_shape):
            draw_shape(tet_shape)
        else:
            break  #game over

        dets = find_column(get_tetname(tet_shape))
        
        sel_col = dets[0]
        rotate_count = dets[-1]

        mov_cnt = (sel_col - 80) / sqrsize

        if mov_cnt < 0:
            move_dir = 'left'
        elif mov_cnt > 0:
            move_dir = 'right'
        elif mov_cnt == 0:
            move_dir = 'down'

        mov_cnt = abs(mov_cnt)

        while rotate_count > 0:
            new_tet_shape = rotate(tet_shape)
            if legal(new_tet_shape):
                prev_tet, tet_shape = tet_shape, new_tet_shape
                draw_and_clear(tet_shape, prev_tet, mov_delay)
            rotate_count = rotate_count - 1

        while mov_cnt > 0:
            new_tet_shape = move(tet_shape, move_dir)
            if legal(new_tet_shape):
                prev_tet, tet_shape = tet_shape, new_tet_shape
                draw_and_clear(tet_shape, prev_tet, mov_delay)
            mov_cnt = mov_cnt - 1


        while True:
            if game == 'paused':
                for event in pygame.event.get((pygame.KEYDOWN, pygame.KEYUP)):
                    if event.key == pygame.K_p:
                        game, move_dir = 'playing', 'down'  
            else:
                for event in pygame.event.get((pygame.KEYDOWN, pygame.KEYUP)):
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_p:
                            game, move_dir = 'paused', 'pause'
                            break

                    elif event.type == pygame.KEYUP:
                        mov_delay, move_dir = mov_delay, 'down'


                move_dir = 'down'
                new_tet_shape = move(tet_shape, move_dir)
                if legal(new_tet_shape):
                    prev_tet, tet_shape = tet_shape, new_tet_shape
                    draw_and_clear(tet_shape, prev_tet, mov_delay) 
                else:
                    #If shape didn't move and direction of movement is down
                    #then shape has come to rest so we can check for a full row
                    #which we delete before exiting loop and generating a new
                    #tetrominoe. if direction for movement is sideways
                    #and   block did not move it should be moved down rather
                    if move_dir == 'down':
                        occupied_squares.extend(block_points(tet_shape))
                        for row_no in range(height, -sqrsize, -sqrsize):
                            while row_filled(row_no):
                                delete_row(row_no)
                                background.fill(color['white'])
                                for point in occupied_squares:
                                    draw_block(point)
                        break
                    else:
                        draw_shape(tet_shape)
                        pygame.time.delay(mov_delay)


###########################################################################
###########################################################################                      
def draw_and_clear(tetrominoe, prev_tet, delay):
    """input<-two tetrominoe shapes
       clear the previously drawn tetrominoe first and then draw a new
       tetrominoe"""
    for point in block_points(prev_tet):
        background.fill((color['white']), (point_x(point), point_y(point),
                                           sqrsize, sqrsize))
        screen.blit(background, top_of_screen)
        pygame.display.update()
    draw_shape(tetrominoe)
    pygame.time.delay(delay)
    
    
############################################################################
############################################################################
def draw_shape(tetrominoe):
    """input<-tetriminoe shape
       This draws a tetrominoe shape to game board"""
    for point in block_points(tetrominoe):
        draw_block(point)
    screen.blit(background, top_of_screen)
    pygame.display.update()
    
 
#############################################################################
#############################################################################
def draw_block(a_point):
    """draws a basic shape to screen"""
    pygame.draw.rect(background, point_color(a_point), (point_x(a_point),
                     point_y(a_point), sqrsize, sqrsize), 1)
    
    
############################################################################
############################################################################
def row_filled(row_no, board=None):
    """input<-tetriminoe shape
       checks if a row on game board is fully occupied by a shape block"""
    if board:
        filled_coords = [(point_x(point), point_y(point)) for point in board]
        for col in range(0, width, sqrsize):
            if (col, row_no) in filled_coords:
                continue
            else:
                return False
        return True 

    else:
        filled_coords = [[point_x(pt), point_y(pt)] for pt in occupied_squares]
        for col in range(0, width, sqrsize):
            if [col, row_no] in filled_coords:
                continue
            else:
                return False
        return True 


##############################################################################
##############################################################################
def delete_row(row_no):
    """input<-integer(a row number)
       output->list of points
       removes all squares on a row from the occupied_squares list and then
       moves all square positions which have y-axis coord less than row_no down
       board"""
    global occupied_squares
    occupied_squares = [point for point in occupied_squares
                        if point_y(point) != row_no]
    for index in range(len(occupied_squares)):
        if point_y(occupied_squares[index]) < row_no:
            occupied_squares[index] = make_point(point_x(occupied_squares[index]),
                                    point_y(occupied_squares[index]) + sqrsize,
                                    point_color(occupied_squares[index]))
        
        
##############################################################################
##############################################################################
def legal(tet_shape):
    """input<-tetrominoe piece
       output->bool
       checks that a tetromone is in a legal portion of the board"""
    tet_block_points = block_points(tet_shape)
    filled_coords = [(point_x(pt), point_y(pt)) for pt in occupied_squares]
    for point in tet_block_points:
        new_x, new_y = point_x(point), point_y(point)
        if ((new_x, new_y) in filled_coords or (new_y >= height or
                                        (new_x >= width or new_x < top_x))):
            return False
    return True


##############################################################################
##############################################################################
def move(shape, direction, undo=False):
    """input<- a tetrominoe shape
       output<- a terominoe shape
       function moves a tetrominoe shape by moving all constituent blocks
       by a fixed amount in a direction given by 'direction' argument"""
    no_move = 0
    directions = {'down':(no_move, sqrsize), 'left':(-sqrsize, no_move),
        'right':(sqrsize, no_move), 'pause': (no_move, no_move)}
    delta_x, delta_y = directions[direction]
    if undo:
        delta_x, delta_y = -delta_x, -delta_y
    new_blocks = [delta_point (block, delta_x, delta_y)
                  for block in get_blocks(shape)]
    return (make_tetrominoe(new_blocks[0], new_blocks[1],
                            new_blocks[2], new_blocks[3], get_tetname(shape)))
       
       
##############################################################################
##############################################################################
def tetrominoe_shape(shape, start_x=80, start_y=0):
    """function returns a random tetrominoe piece"""
    shapes = {'S': make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                         start_y + 2*sqrsize,
                                                         (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x, start_y,
                                                        (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x,
                                                        start_y + 1*sqrsize,
                                                        (0, 0, 0)),
                                             sqrsize, sqrsize),
                                  make_block(make_point(start_x + 1*sqrsize,
                                                    start_y + 1*sqrsize,
                                                    (0, 0, 0)),
                                             sqrsize, sqrsize)
                                  , 'S'),
        
        'O': make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                (200, 200, 200)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x + 1*sqrsize, start_y,
                                                  (200, 200, 200)),
                                       sqrsize, sqrsize),
                            'O'),
                
        'I': make_tetrominoe(make_block(make_point(start_x, start_y + 3*sqrsize,
                                                   (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 2*sqrsize,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (0, 255, 0)),
                                       sqrsize, sqrsize),
                            'I'),
                
        'L':make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 2*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (0, 0, 255)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 2*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y + 1*sqrsize,
                                                  (0, 0, 255)),
                                       sqrsize, sqrsize), 'L'),
                
        'T':make_tetrominoe(make_block(make_point(start_x + 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                  (255, 0, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x, start_y,
                                                  (255, 0, 0)),
                                       sqrsize, sqrsize),
                            make_block(make_point(start_x - 1*sqrsize,
                                                  start_y + 1*sqrsize,
                                                  (255, 0, 0)),
                                        sqrsize, sqrsize),
                            make_block(make_point(start_x,
                                                        start_y + 1*sqrsize,
                                                        (255, 0, 0)),
                                        sqrsize, sqrsize), 'T')
        }
    return shapes[shape]


#####
#####
def random_shape(start_x=80, start_y=0):
    """return a random tetrominoe shape"""
    tets = ['S', 'O', 'I', 'L', 'T']
    return tetrominoe_shape(tets[random.randint(0, 4)], start_x, start_y)

    
##############################################################################
##############################################################################
def rotate(tetrominoe):
    """input<- tetrominoe shape
       ouput-> tetrominoe shape
       rotates a tetrominoe shape if possible about a reference block."""
    #global occupied_squares
    if get_tetname(tetrominoe) == 'O':
        return tetrominoe
    else:
        ref_point = get_point(get_refblock(tetrominoe)) 
        x_coord = point_x(ref_point)
        y_coord = point_y(ref_point)
        
        tetblock_coords = block_points(tetrominoe) 
        
        new_tet = make_tetrominoe(make_block(make_point(x_coord +
                                            y_coord-point_y(tetblock_coords[0]),
                            y_coord - (x_coord - point_x(tetblock_coords[0])),
                            point_color(ref_point)), sqrsize, sqrsize,
                                             ),
                        
                        make_block(make_point(x_coord + y_coord -
                                              point_y(tetblock_coords[1]),
                            y_coord - (x_coord - point_x(tetblock_coords[1])),
                            point_color(ref_point)), sqrsize, sqrsize),
                        
                        make_block(make_point(x_coord + y_coord -
                                              point_y(tetblock_coords[2]),
                            y_coord - (x_coord - point_x(tetblock_coords[2])),
                            point_color(ref_point)), sqrsize, sqrsize),
                        
                        make_block(make_point(x_coord, y_coord,
                                              point_color(ref_point)),
                                   sqrsize, sqrsize),
                    get_tetname(tetrominoe))
    
        #if legal(new_tet):
        return new_tet
        #else:
         #   return tetrominoe
####
####
def drop_shape(shape):
    """drop a shape into postion on a column"""
    new_shape = move(shape, 'down')
    prev_shape, new_shape = shape, new_shape
    while legal(new_shape):
        prev_shape, new_shape = new_shape, move(new_shape, 'down')
    return prev_shape


####
####
def bubble_count(shape):
    """returns number of new empty spots generated when a shape is placed at a 
    legal point"""
    count = 0
    points = [(point_x(pt), point_y(pt)) for pt in block_points(shape)]
    board = [(point_x(pt), point_y(pt)) for pt in occupied_squares]
    for pt in points:
        for i in range(point_y(pt) + sqrsize, height, sqrsize):
            if (pt[0], i) in board or (pt[0], i) in points:
                break
            else:
                count += 1
    return count

####
###
def shape_lowest_row(shape):
    """return the lowest row of a shape"""
    points = [(point_x(pt), point_y(pt)) for pt in block_points(shape)]
    points = sorted(points, key=lambda point: point[1], reverse=True)
    return points[0]

#####
#####
def row_filln_column(shape):
    """return a list of columns, rows filled tuple for each column on the 
    board if there are n columns for which a shape dropped in column fills
    a row"""
    rows_filled = []
    shape_rotates = {'S':2, 'I':1, 'O':0, 'L':3, 'T':3}
    rotate_count = shape_rotates[shape]
    curr_cnt = 0
    while True:
        for col in range(0, width, sqrsize):
            board = copy.deepcopy(occupied_squares)
            tet_shape = tetrominoe_shape(shape, start_x=col, start_y=0)
            cnt = curr_cnt
            while cnt > 0:
                tet_shape = rotate(tet_shape)
                cnt -= 1

            if not legal(tet_shape): # check shape is in board sideways
                continue
            tet_shape = drop_shape(tet_shape)
            board.extend(block_points(tet_shape))
            rows = 0
            for row in range(height, 0, -sqrsize):
                if row_filled(row, board=board):
                    rows += 1
            if rows > 0:
                rows_filled.append((col, rows, curr_cnt))
            tet_shape = rotate(tet_shape)

        if rotate_count == curr_cnt:
            break
        curr_cnt += 1
    if rows_filled:
        return rows_filled
    return None

####
####
def next_best_columns(shape):
    """return list of columns which a shape can go into if the shape cannot
    fill any rows"""
    next_best = []
    shape_rotates = {'S':2, 'I':1, 'O':0, 'L':3, 'T':3}
    rotate_count = shape_rotates[shape]
    curr_cnt = 0
    while True:
        for col in range(0, width, sqrsize):
            board = copy.deepcopy(occupied_squares)
            tet_shape = tetrominoe_shape(shape, start_x=col, start_y=0)
            cnt = curr_cnt
            while cnt > 0:
                tet_shape = rotate(tet_shape)
                cnt -= 1

            if not legal(tet_shape):
                continue
            tet_shape = drop_shape(tet_shape)
            board.extend(block_points(tet_shape))
            bubble_cnt = bubble_count(tet_shape)
            next_best.append((col, bubble_cnt, shape_lowest_row(tet_shape)[1],
                             curr_cnt))
            #print col, bubble_cnt
        if rotate_count == curr_cnt:
            break
        curr_cnt += 1
    return next_best

#####
#####
def find_column(shape):
    """find column of best fit to drop down a tetrominoe shape from"""
    # search for if any rows can be filled up by shape
    rows_filled = row_filln_column(shape)
    if not rows_filled:
        next_best = next_best_columns(shape)
        next_best =  sorted(next_best, key=lambda col: col[2], reverse=True)
        cols = sorted(next_best, key=lambda col: col[1])
        return cols[0]
    rows_filled = sorted(rows_filled, key=lambda row_filled: row_filled[1])
    return rows_filled[-1] #col with most rows filled


if __name__ == '__main__':
    tetris()
Tweeted twitter.com/#!/StackCodeReview/status/207928239073673216
improved title
Link
palacsint
  • 29.9k
  • 9
  • 81
  • 156

Request for improvements to python Self playing tetris clone.

added 50 characters in body
Source Link
cobie
  • 457
  • 3
  • 13
Loading
Source Link
cobie
  • 457
  • 3
  • 13
Loading