I have got an extremely simple self playing tetris which I coded up and I am looking to see how the code would evolve if it was open sourced for everyone to add some modification. It would also be a learning curve for me to see how those much better than I am would go about improving the code. 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()