import collections import numpy as np import tetrominos FIELD_WIDTH = 10 FIELD_HEIGHT = 20 COMMAND_NONE = 0 COMMAND_LEFT = 1 COMMAND_RIGHT = 2 COMMAND_ROTATE = 3 def tetromino_to_field_at(tetromino, row, col): col_offset = col + tetromino.col_offset row_offset = row + tetromino.row_offset if col_offset < 0 or row_offset < 0: return None if col_offset + tetromino.data.shape[1] > FIELD_WIDTH: return None if row_offset + tetromino.data.shape[0] > FIELD_HEIGHT: return None array = tetromino.data left_zeros = np.zeros(shape=(array.shape[0], col_offset), dtype=np.int32, order="C") right_zeros = np.zeros(shape=(array.shape[0], FIELD_WIDTH-col_offset-array.shape[1]), dtype=np.int32, order="C") array = np.concatenate((left_zeros, array, right_zeros), axis=1) top_zeros = np.zeros(shape=(row_offset,FIELD_WIDTH), dtype=np.int32, order="C") bottom_zeros = np.zeros(shape=(FIELD_HEIGHT - row_offset - array.shape[0], FIELD_WIDTH), dtype=np.int32, order="C") array = np.concatenate((top_zeros, array, bottom_zeros), axis=0) return array GameState = collections.namedtuple('GameState', ['grid', 'current_piece', 'next_piece', 'rotation', 'row', 'col', 'score']) def initial_game_state(current_piece, next_piece): return GameState(grid=np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH), dtype=np.int32, order="C"), current_piece=current_piece, next_piece=next_piece, rotation=0, row=0, col=0, score=0) def is_game_state_valid(game_state): tetromino_array = tetromino_to_field_at( tetrominos.SHAPES[game_state.current_piece][game_state.rotation], game_state.row, game_state.col) if tetromino_array is None: return False total_field = np.add(game_state.grid, tetromino_array) if np.any(np.vectorize(lambda x: x > 1)(total_field)): return False return True def get_game_state_field(game_state): tetromino_array = tetromino_to_field_at( tetrominos.SHAPES[game_state.current_piece][game_state.rotation], game_state.row, game_state.col) total_field = np.add(game_state.grid, tetromino_array) total_field = np.add(total_field, tetromino_array) return total_field def game_state_shift(game_state, col_offset): next_game_state = GameState(grid=game_state.grid, current_piece=game_state.current_piece, next_piece=game_state.next_piece, rotation=game_state.rotation, row=game_state.row, col=game_state.col + col_offset, score=game_state.score) if is_game_state_valid(next_game_state): return next_game_state return game_state def game_state_land(game_state): while True: next_game_state = GameState(grid=game_state.grid, current_piece=game_state.current_piece, next_piece=game_state.next_piece, rotation=game_state.rotation, row=game_state.row + 1, col=game_state.col, score=game_state.score) if is_game_state_valid(next_game_state): game_state = next_game_state else: break return game_state def game_state_rotate(game_state): next_game_state = GameState(grid=game_state.grid, current_piece=game_state.current_piece, next_piece=game_state.next_piece, rotation=(game_state.rotation + 1) % 4, row=game_state.row, col=game_state.col, score=game_state.score) if is_game_state_valid(next_game_state): return next_game_state return game_state def advance_game_state_down(game_state): next_game_state = GameState(grid=game_state.grid, current_piece=game_state.current_piece, next_piece=game_state.next_piece, rotation=game_state.rotation, row=game_state.row + 1, col=game_state.col, score=game_state.score) if is_game_state_valid(next_game_state): return next_game_state return None def advance_game_state_piece(game_state, next_piece): tetromino_array = tetromino_to_field_at( tetrominos.SHAPES[game_state.current_piece][game_state.rotation], game_state.row, game_state.col) next_grid = np.add(game_state.grid, tetromino_array) next_game_state = GameState(grid=next_grid, current_piece=game_state.next_piece, next_piece=next_piece, rotation=0, row=0, col=0, score=game_state.score) return next_game_state def advance_game_state_purge_lines(game_state): next_grid_row = FIELD_HEIGHT-1 next_grid = np.zeros(shape=(FIELD_HEIGHT,FIELD_WIDTH), dtype=np.int32, order="C") next_score = game_state.score for row in range(FIELD_HEIGHT-1, -1,-1): if np.all(game_state.grid[row,:]): next_score += 1 else: next_grid[next_grid_row,:] = game_state.grid[row,:] next_grid_row -= 1 next_game_state = GameState(grid=next_grid, current_piece=game_state.current_piece, next_piece=game_state.next_piece, rotation=game_state.rotation, row=game_state.row, col=game_state.col, score=next_score) return next_game_state def advance_game_state(game_state, next_piece_func): next_game_state = advance_game_state_down(game_state) is_game_running = True if next_game_state is None: next_game_state = advance_game_state_piece(game_state, next_piece_func()) next_game_state = advance_game_state_purge_lines(next_game_state) if not is_game_state_valid(next_game_state): is_game_running = False if not is_game_running: return game_state, False return next_game_state, True