def __init__(self, stdscr, start_pos, dimensions): # Very clean and totally maintainable code height, width = dimensions start_y, start_x = start_pos self.win = curses.newwin(height, width, start_y, start_x) self.win.box() stdscr.refresh() self.win.refresh() self.dim = [height, width] self.start_pos = [start_y, start_x] self.status = GameStatus() self.shapes = [ [[1, 1, 1], [0, 0, 1]], [[0, 1], [1, 1], [0, 1]], [[1, 1], [1, 1]], [[0, 0, 1], [1, 1, 1]], [[1, 1, 1, 1]], [[1, 1, 0], [0, 1, 1]] ] self.active_block = self.spawn_block() self.heap = Heap(1, 1, height - 2, width - 2) self.draw()
class Board(object): """ The game board. The central object which controls spawning and movement of blocks as well as the heap of block remnants. """ def __init__(self, stdscr, start_pos, dimensions): # Very clean and totally maintainable code height, width = dimensions start_y, start_x = start_pos self.win = curses.newwin(height, width, start_y, start_x) self.win.box() stdscr.refresh() self.win.refresh() self.dim = [height, width] self.start_pos = [start_y, start_x] self.status = GameStatus() self.shapes = [ [[1, 1, 1], [0, 0, 1]], [[0, 1], [1, 1], [0, 1]], [[1, 1], [1, 1]], [[0, 0, 1], [1, 1, 1]], [[1, 1, 1, 1]], [[1, 1, 0], [0, 1, 1]] ] self.active_block = self.spawn_block() self.heap = Heap(1, 1, height - 2, width - 2) self.draw() def random_shape(self): """Pick a shape from the presets at random.""" shape_index = randint(0, len(self.shapes) - 1) return self.shapes[shape_index] def spawn_block(self): """Create and return a new bock with random shape.""" spawn_y = self.start_pos[0] + 1 spawn_x = self.start_pos[1] + (self.dim[1] / 2) block = Block(self.random_shape(), random_color(), spawn_y, spawn_x) return block def out_of_bounds(self, coord_y, coord_x): """Check if the given location is outside the game board""" min_y = self.start_pos[0] max_y = self.start_pos[0] + self.dim[0] - 1 min_x = self.start_pos[1] max_x = self.start_pos[1] + self.dim[1] - 1 return coord_y <= min_y or coord_y >= max_y \ or coord_x <= min_x or coord_x >= max_x def block_movable(self, coord_y, coord_x): """ Return if the active block can be safely moved into the specified location. """ for new_y, new_x in self.active_block.gen_coords(coord_y, coord_x): if self.out_of_bounds(new_y, new_x): return False # Blue 'ghost' block moves through the heap if self.active_block.color != 3 \ and self.heap.collision(new_y, new_x): return False return True def update_game_status(self): """Increase the score and check if the game should end.""" self.status.add_score(self.heap.get_removed()) coord_y, coord_x = self.active_block.position if not self.block_movable(coord_y + 1, coord_x): self.status.over = True def advance_block(self): """ Move the active block block one square down. If the block cannot be moved, it is added to the heap and a new block is spawned. The function returns True if the block was moved, False otherwise. """ coord_y, coord_x = self.active_block.position if self.block_movable(coord_y + 1, coord_x): self.active_block.set_position(coord_y + 1, coord_x) return True self.heap.add(self.active_block) self.active_block = self.spawn_block() self.update_game_status() return False def lshift_block(self): """Move the active block on square to the left, if possible.""" coord_y, coord_x = self.active_block.position if self.block_movable(coord_y, coord_x - 1): self.active_block.set_position(coord_y, coord_x - 1) def rshift_block(self): """Move the active block on square to the right, if possible.""" coord_y, coord_x = self.active_block.position if self.block_movable(coord_y, coord_x + 1): self.active_block.set_position(coord_y, coord_x + 1) def land_block(self): """Move the active block all the way down.""" while self.advance_block(): pass # wat def rotate_block(self): """Rotate the active block 90 degrees right""" coord_y, coord_x = self.active_block.position rotation = self.active_block.gen_rotation() for new_y, new_x in \ self.active_block.gen_coords(coord_y, coord_x, shape=rotation): if self.out_of_bounds(new_y, new_x) or \ (self.active_block.color != 3 and \ self.heap.collision(new_y, new_x)): return self.active_block.rotate(rot=rotation) def draw_active_block(self): """Draw the active block on the curses display.""" color = self.active_block.color for coord_y, coord_x in self.active_block.coords(): curses_draw_spot(self.win, coord_y, coord_x, color) def draw_heap(self): """Draw the block remnant heap on the curses display.""" for coord_y, coord_x, color in self.heap.contents(): curses_draw_spot(self.win, coord_y, coord_x, color) def draw(self): """Draw all the game elements and refresh the curses window.""" self.draw_heap() self.draw_active_block() self.win.refresh() def game_over(self): """Return the game status, True if game should end, False otherwise.""" return self.status.over def game_speed(self): """Return the game step time in ms""" return self.status.game_speed()