def create_brick(self):
     """
 Create a new dropping brick.
 """
     # Create a dropping brick pair
     self.db = DoubleBrick(self.next_color, self.next_breaker,
                           (self.gen_col * self.bw + self.left, self.top),
                           self.brick_size)
     # Determine next brick colors and breaker status
     self.gen_next()
    def draw_bricks(self, surface):
        """
    Draw all bricks (stacked and dropping) to the passed Pygame Surface.
    """
        if self.db:
            # Dropping brick
            surface.blit(self.b1().image, self.b1().rect)
            surface.blit(self.b2().image, self.b2().rect)

        # Draw "Next" label
        font_obj = pygame.font.Font(self._font_dir + "OpenSans-Regular.ttf",
                                    20)
        self.print_surface("Next", surface,
                           (self.max_pos_x + int(self.bw * 3 / 2),
                            self.min_pos_y + 3 * self.bh - 10), font_obj)
        # Draw next brick
        next_db = DoubleBrick(self.next_color, self.next_breaker,
                              self.next_topleft, self.brick_size)
        surface.blit(next_db.brick1.image, next_db.brick1.rect)
        surface.blit(next_db.brick2.image, next_db.brick2.rect)
        # Draw rectangle around next brick
        pygame.draw.rect(
            surface, white,
            pygame.rect.Rect((self.max_pos_x + int(self.bw * 7 / 4),
                              self.min_pos_y + int(self.bh * 15 / 4)),
                             (int(self.bw * 3 / 2), int(self.bh * 5 / 2))), 2)

        # Stacked bricks
        for col in range(self.num_cols):
            for row in range(self.num_rows):
                surface.blit(self.stacked_bricks[col][row].image,
                             self.stacked_bricks[col][row].rect)
 def create_brick(self):
   """
   Create a new dropping brick.
   """
   # Create a dropping brick pair
   self.db = DoubleBrick(self.next_color, self.next_breaker, 
                         (self.gen_col*self.bw + self.left, self.top), self.brick_size)
   # Determine next brick colors and breaker status
   self.gen_next()
class Board:
  """
  Class for play area (stacked bricks).
  """
  
  # Possible brick colors (R, G, B) triplets
  _brick_colors = [red, green, blue, yellow]
  
  # Brick fallspeed (pixels per loop iteration)
  _fallspeed_slow = 1
  _fallspeed_fast = 10
  
  # High score filename
  _highscore_filename = "highscore.txt"
  
  # Key for hashing score
  _hash_key = "SomethingSomethingBricks"
      
  # Probability of getting a breaker brick
  _breaker_prob = 0.20
  
  # Time to show break graphic before continuing on (in milliseconds) 
  _break_time = 500
  
  ## Wall images
  _img_dir = "images/"
  # Left wall segment
  _wall_l = pygame.sprite.Sprite()
  _wall_l.image = pygame.image.load(_img_dir + "wall_left.png")
  _wall_l.rect = _wall_l.image.get_rect()
  # Right wall segment
  _wall_r = pygame.sprite.Sprite()
  _wall_r.image = pygame.image.load(_img_dir + "wall_right.png")
  _wall_r.rect = _wall_r.image.get_rect()
  # Bottom wall segment
  _wall_b = pygame.sprite.Sprite()
  _wall_b.image = pygame.image.load(_img_dir + "wall_bottom.png")
  _wall_b.rect = _wall_b.image.get_rect()
  # Bottom right corner
  _wall_br = pygame.sprite.Sprite()
  _wall_br.image = pygame.image.load(_img_dir + "wall_br.png")
  _wall_br.rect = _wall_br.image.get_rect()
  # Bottom left corner
  _wall_bl = pygame.sprite.Sprite()
  _wall_bl.image = pygame.image.load(_img_dir + "wall_bl.png")
  _wall_bl.rect = _wall_bl.image.get_rect()
  
  # Fonts directory
  _font_dir = "fonts/"
    
  def __init__(self, board_size, brick_size, mixer):
    """
    Default constructor.
    Arguments:
      board_size    number of (columns, rows)
      brick_size    (x, y) pixel size of each brick
      mixer         Pygame mixer object for sound output.
    """
    # Save parameters
    self.num_cols, self.num_rows = self.board_size = board_size
    self.bw, self.bh = self.brick_size = brick_size
    # Column where bricks are spawned (0 indexed)
    self.gen_col = int(math.ceil(self.num_cols/2))
    # Top left corner (pixels) of game board
    # Indent in by one block on each side
    self.left, self.top = self.topleft = (self.bw, -2*self.bh)
    # Min, max possible values for brick topleft corner
    self.min_pos_x = self.left
    self.max_pos_x = self.left + self.bw * (self.num_cols - 1)
    self.min_pos_y = self.top
    self.max_pos_y = self.top + self.bh * (self.num_rows - 1)
    
    # Create array of stacked bricks
    self.clear_board()
      
    # Current dropping brick pair
    self.db = None
    # Position to display next brick
    self.next_topleft = (self.max_pos_x + 2*self.bw, self.min_pos_y + 4*self.bh)
    # Generate next brick placeholder
    self.gen_next()
    
    ## Sounds
    # Directory where sounds are kept
    snd_dir = "sounds/"
    # Sound for key press
    self.snd_key = mixer.Sound(snd_dir + "select.wav")
    self.snd_key.set_volume(0.1)
    # Sound for bricks broken
    self.snd_break = mixer.Sound(snd_dir + "break.wav")
    self.snd_break.set_volume(0.2)
    # Sound when brick reaches bottom
    self.snd_drop = mixer.Sound(snd_dir + "drop.wav")
    self.snd_drop.set_volume(0.5)
    
    # Get high score
    self.read_highscore()    
  
  def gen_breaker(self):
    """
    Return true with probability _breaker_prob to determine whether
    a breaker brick is generated.
    """
    if random.random() < self._breaker_prob:
      return True
    else:
      return False
    
  def gen_next(self):
    """
    Generate parameters for next brick.
    """
    # Determine next brick colors and breaker status
    self.next_color = (random.choice(self._brick_colors),random.choice(self._brick_colors)) 
    self.next_breaker = (self.gen_breaker(), self.gen_breaker()) 
  
  def start(self):
    """
    Reset game state and start new game.
    """
    # Reset game board
    self.clear_board()
    # Drop a new brick
    self.create_brick()    
      
  def clear_board(self):
    """
    Reset game board to all blank bricks and reset all state.
    """
    # Blank all brick spaces
    self.stacked_bricks = [0]*self.num_cols
    for col in range(self.num_cols):
      self.stacked_bricks[col] = [Brick()]*self.num_rows
    # Reset fallspeed
    self._fallspeed_slow = 1
    self.fallspeed = self._fallspeed_slow
    # Reset score
    self.score = 0
    # Start state is falling brick
    self.state = "fall"
      
  def create_brick(self):
    """
    Create a new dropping brick.
    """
    # Create a dropping brick pair
    self.db = DoubleBrick(self.next_color, self.next_breaker, 
                          (self.gen_col*self.bw + self.left, self.top), self.brick_size)
    # Determine next brick colors and breaker status
    self.gen_next()
  
  def col_top(self, col):
    """
    Return the top brick in column col that is not empty.
    Arguments:
      col    column to return top brick of.
    """
    for row in range(self.num_rows):
      if not self.stacked_bricks[col][row].empty():
        return self.stacked_bricks[col][row]
    # If no brick's found
    return None
      
  def col_pix_top(self, col):
    """
    Return top pixel of first (bottom) brick in column col that is empty.
    Arguments:
      col    column to return top pixel of.
    """
    if self.col_top(col):
      return self.col_top(col).rect.top - self.bh
    # No bricks found
    else:
      # Return bottom edge
      return self.max_pos_y
      
  def b1(self):
    """
    Return first (originally top) brick.
    """
    if self.db:
      return self.db.brick1
    else:
      return None
  
  def b2(self):
    """
    Return second (originally bottom) brick.
    """
    if self.db:
      return self.db.brick2
    else:
      return None
  
  def b1_col(self):
    """
    Column of first (originally top) brick.
    """
    return (self.b1().rect.left - self.left)/self.bw

  def b2_col(self):
    """
    Column of second (originally bottom) brick.
    """
    return (self.b2().rect.left - self.left)/self.bw
  
  def move_left(self):
    """
    Move dropping brick to the left.
    """
    if self.db and not self.game_over():
      # Check that there is space on the left
      if self.db.left_edge() > self.min_pos_x and \
        self.db.bottom_edge() < self.col_pix_top(self.b1_col() - 1) and \
        self.db.top_edge() < self.col_pix_top(self.b2_col() - 1):
        # Move both bricks to the left
        self.b1().rect.left -= self.bw
        self.b2().rect.left -= self.bw
        self.snd_key.play()
        
  def move_right(self):
    """
    Move dropping brick to the right.
    """
    if self.db and not self.game_over():
      # Check that there is space on the right
      if self.db.right_edge() < self.max_pos_x and \
        self.db.bottom_edge() < self.col_pix_top(self.b1_col() + 1) and \
        self.db.top_edge() < self.col_pix_top(self.b2_col() + 1):
        self.b1().rect.left += self.bw
        self.b2().rect.left += self.bw
        self.snd_key.play()
  
  def rotate_cw(self):
    """
    Rotate first (originally top) brick clockwise around second brick.
    """
    if self.db and not self.game_over():
      # If first brick above second
      if self.b1().rect.top < self.b2().rect.top and \
        self.db.right_edge() < self.max_pos_x and self.db.bottom_edge() < self.col_pix_top(self.b1_col() + 1):
        self.b1().rect.top += self.bh
        self.b1().rect.left += self.bw
        self.snd_key.play()
      # first brick below second
      elif self.b1().rect.top > self.b2().rect.top and \
        self.db.left_edge() > self.min_pos_x and self.db.top_edge() < self.col_pix_top(self.b1_col() - 1):
        self.b1().rect.top -= self.bh
        self.b1().rect.left -= self.bw
        self.snd_key.play()  
      # first brick to the right of the second
      elif self.b1().rect.left > self.b2().rect.left and \
        self.db.bottom_edge() + self.bh < self.col_pix_top(self.b1_col() - 1):
        self.b1().rect.top += self.bh
        self.b1().rect.left -= self.bw
        self.snd_key.play()
      # first brick to the left of the second
      elif self.b1().rect.left < self.b2().rect.left:
        self.b1().rect.top -= self.bh
        self.b1().rect.left += self.bw
        self.snd_key.play()
  
  def rotate_ccw(self):
    """
    Rotate first (originally top) brick counter-clockwise around second brick.
    """
    if self.db and not self.game_over():
      # If first brick above second
      if self.b1().rect.top < self.b2().rect.top and \
        self.db.left_edge() > self.min_pos_x and self.db.bottom_edge() < self.col_pix_top(self.b1_col() - 1):
        self.b1().rect.top += self.bh
        self.b1().rect.left -= self.bw
        self.snd_key.play()
      # first brick below second
      elif self.b1().rect.top > self.b2().rect.top and \
        self.db.right_edge() < self.max_pos_x and self.db.top_edge() < self.col_pix_top(self.b1_col() + 1):
        self.b1().rect.top -= self.bh
        self.b1().rect.left += self.bw
        self.snd_key.play()
      # first brick to the right of the second
      elif self.b1().rect.left > self.b2().rect.left:
        self.b1().rect.top -= self.bh
        self.b1().rect.left -= self.bw
        self.snd_key.play()
      # first brick to the left of the second
      elif self.b1().rect.left < self.b2().rect.left and \
        self.db.bottom_edge() + self.bh < self.col_pix_top(self.b1_col() + 1):
        self.b1().rect.top += self.bh
        self.b1().rect.left += self.bw
        self.snd_key.play() 
  
  def speed_up(self):
    """
    Increase fallspeed to fast.
    """
    if not self.game_over():
      self.fallspeed = self._fallspeed_fast
  
  def slow_down(self):
    """
    Decrease fallspeed to slow.
    """
    if not self.game_over():
      self.fallspeed = self._fallspeed_slow
    
  def update(self):
    """
    Drop brick by fallspeed. If the bottom is reached, then handle breaking
    and spawn a new brick.
    """
    # If handling breaking
    if self.state == "break":
      # Break bricks given current stacked bricks
      broken = self.break_bricks()              
      # Collapse bricks from above if any bricks were broken
      if broken:
        self.state = "drop"
      # Otherwise, continue on
      else:
        # Fall speed increases as points are scored
        self._fallspeed_slow = int(self.score/200) + 1
        self.fallspeed = self._fallspeed_slow
        # Create a new brick after all breaks have occurred
        self.state = "new_brick"
        
    # Drop bricks to fill holes after breaking bricks
    elif self.state == "drop":
      pygame.time.wait(self._break_time)    
      self.drop_bricks()
      # Rerun breaking to find combos
      self.state = "break"
      
    # Create new brick after breaking
    elif self.state == "new_brick":
      # Create new brick if the game is not over
      if (self.stacked_bricks[self.gen_col][1].empty() and \
      self.stacked_bricks[self.gen_col][2].empty()):
        self.create_brick()
        # Go back to dropping brick
        self.state = "fall"
      else:
        # If the game is over because another spawned brick cannot fall,
        # still create a new one
        if self.stacked_bricks[self.gen_col][1].empty():
          self.create_brick()
        # Game state is set to game over
        self.state = "game_over"
      
    # Update falling brick
    elif self.state == "fall":
      # If the brick pair hasn't reached the bottom,
      if self.b1().rect.top < self.col_pix_top(self.b1_col()) - self.fallspeed and \
        self.b2().rect.top < self.col_pix_top(self.b2_col()) - self.fallspeed:
        # Drop bricks
        self.b1().rect.top += self.fallspeed
        self.b2().rect.top += self.fallspeed
      # Else one of the bricks has reached the top of a column
      # In this case, both bricks are dropped
      else:
        # Sound for brick reaching bottom
        self.snd_drop.play()
            
        # Brick that hits a surface first needs to handled first (in case stacked)
        # Set the one that hits first as b1 and it's column as c1
        # The other one is b2 in column c2
        if self.b2().rect.top >= self.col_pix_top(self.b2_col()) - self.fallspeed:
          b2, b1 = self.b1(), self.b2()
          c2, c1 = self.b1_col(), self.b2_col()
        else:
          b1, b2 = self.b1(), self.b2()
          c1, c2 = self.b1_col(), self.b2_col()
        # Drop bottom brick to bottom
        b1.rect.top = self.col_pix_top(c1)
        # Save to array
        self.stacked_bricks[b1.get_col_index()][b1.get_row_index()] = b1
        # Now drop the other brick to the bototm
        b2.rect.top = self.col_pix_top(c2)
        # Save to array
        self.stacked_bricks[b2.get_col_index()][b2.get_row_index()] = b2
        # Remove dropping brick while handling break
        self.db = None
  
        # Next, handle any breaking of bricks
        self.state = "break"

    elif self.state == "game_over":
      pass
    else:
      raise Exception("Unknown state in game board.")
      
  def break_bricks(self):
    """
    Remove any bricks marked as broken.
    """ 
    # If any bricks were broken
    broken = False
    # Scan through array
    for col in range(self.num_cols):
      for row in range(self.num_rows):
        # If breaker is touching a brick with the same color
        if self.stacked_bricks[col][row].breaker:
          breaker_color = self.stacked_bricks[col][row].color
          if (col > 0 and self.stacked_bricks[col-1][row].color == breaker_color) or \
            (col < self.num_cols-1 and self.stacked_bricks[col+1][row].color == breaker_color) or \
            (row > 0 and self.stacked_bricks[col][row-1].color == breaker_color) or \
            (row < self.num_rows-1 and self.stacked_bricks[col][row+1].color == breaker_color):
            # Use spread_break to recursively break proper bricks
            self.spread_break(col, row, breaker_color)
            # Play sound for breaking bricks
            self.snd_break.play()
            # Bricks have been broken
            broken = True
            
    return broken
        
  def drop_bricks(self):
    """
    Fill in any empty spaces by moving bricks above down.
    """
    # Handle each column individual
    for col in range(self.num_cols):
      # If top row is broken
      if self.stacked_bricks[col][0].broken:
        # Replace with an empty brick
        self.stacked_bricks[col][0] = Brick()
      # For each row, starting from the top
      for row in range(self.num_rows - 1):
        # If the next spot is a broken brick
        if self.stacked_bricks[col][row+1].broken:
          # Move all bricks above down a space, starting from the bottom
          for drop_row in range(row, -1, -1):
            self.stacked_bricks[col][drop_row].rect.top += self.bh
            self.stacked_bricks[col][drop_row + 1] = self.stacked_bricks[col][drop_row]
          # Create an empty brick at the top
          self.stacked_bricks[col][0] = Brick()

  def spread_break(self, col, row, breaker_color):
    """
    Break the brick at col, row and recursively call on surrounding bricks.
    Arguments:
      col               Column index to spread break
      row               Row index to spread break
      breaker_color     Color of break to spread
    """
    # Replace image with a broken brick
    self.stacked_bricks[col][row].break_brick()  
    # Increment score for each brick destroyed
    self.score += 10
  
    # Spread to surrounding blue bricks
    if col > 0 and self.stacked_bricks[col-1][row].color == breaker_color:
      self.spread_break(col-1, row, breaker_color)
    if col < self.num_cols-1 and self.stacked_bricks[col+1][row].color == breaker_color:
      self.spread_break(col+1, row, breaker_color)
    if row > 0 and self.stacked_bricks[col][row-1].color == breaker_color:
      self.spread_break(col, row-1, breaker_color)
    if row < self.num_rows-1 and self.stacked_bricks[col][row+1].color == breaker_color:
      self.spread_break(col, row+1, breaker_color)
    
  def draw_bricks(self, surface):
    """
    Draw all bricks (stacked and dropping) to the passed Pygame Surface.
    """
    if self.db:
      # Dropping brick
      surface.blit(self.b1().image, self.b1().rect)
      surface.blit(self.b2().image, self.b2().rect)
      
    # Draw "Next" label
    font_obj = pygame.font.Font(self._font_dir + "OpenSans-Regular.ttf", 20)
    self.print_surface("Next", surface, (self.max_pos_x + int(self.bw*3/2), self.min_pos_y + 3*self.bh - 10), font_obj)
    # Draw next brick
    next_db = DoubleBrick(self.next_color, self.next_breaker, self.next_topleft, self.brick_size)
    surface.blit(next_db.brick1.image, next_db.brick1.rect)
    surface.blit(next_db.brick2.image, next_db.brick2.rect)
    # Draw rectangle around next brick
    pygame.draw.rect(surface, white, pygame.rect.Rect((self.max_pos_x + int(self.bw*7/4), self.min_pos_y + int(self.bh*15/4)), (int(self.bw*3/2), int(self.bh*5/2))), 2)
    
    # Stacked bricks
    for col in range(self.num_cols):
      for row in range(self.num_rows):
        surface.blit(self.stacked_bricks[col][row].image, self.stacked_bricks[col][row].rect)

  def draw_walls(self, surface):
    """
    Draw walls of board area to the passed Pygame surface.
    """   
    # Draw walls
    # Left and right walls
    for row in range(self.num_rows):
      self._wall_l.rect.topleft = (self.left - self.bw, self.bh*row + self.top)
      surface.blit(self._wall_l.image, self._wall_l.rect)
      self._wall_r.rect.topleft = (self.left + self.num_cols*self.bw, self.top + self.bh*row)
      surface.blit(self._wall_r.image, self._wall_r.rect)
    # Bottom wall
    for col in range(self.num_cols):
      self._wall_b.rect.topleft = ((self.left + col*self.bw, self.top + self.bh*self.num_rows))
      surface.blit(self._wall_b.image, self._wall_b.rect)
    # Bottom corners
    self._wall_br.rect.topleft = (self.left + self.num_cols*self.bw, self.top + self.bh*self.num_rows)
    self._wall_bl.rect.topleft = (self.left - self.bw, self.top + self.bh*self.num_rows) 
    surface.blit(self._wall_br.image, self._wall_br.rect)      
    surface.blit(self._wall_bl.image, self._wall_bl.rect)
  
  def draw_score(self, surface):
    """
    Write the current score to the passed surface. Also note whether game is over.
    """
    # Font object for rendering text
    font_obj = pygame.font.Font(self._font_dir + "OpenSans-Regular.ttf", 20)
    # Location to print score
    topleft = [self.max_pos_x + 2*self.bw, self.min_pos_y + 11*self.bh]
    # Print score to screen
    self.print_surface("Score: %d" % self.score, surface, topleft, font_obj)
    # Print highscore to screen
    topleft[1] += self.bh
    self.print_surface("High score: %d" % self.highscore, surface, topleft, font_obj)
    
    # If game is over
    if self.game_over():
      # Location to print to
      center = (int((self.min_pos_x + self.max_pos_x + self.bw)/2), 
                 int((self.min_pos_y + self.max_pos_y + self.bh)/2))
      # Print Game Over
      self.print_surface_center("Game Over", surface, center, font_obj, white, dark_gray)
      
      # If we have a new high score,
      if self.score > self.highscore:
        # Update high score
        self.highscore = self.score
        # Save it to file 
        self.write_highscore()
    
  def print_surface(self, msg, surface, topleft, font_obj):
    """
    Print the passed msg string to the surface at location specified by topleft.
    Arguments
      msg        String to be displayed.
      surface    Pygame surface to print string to.
      topleft    (x, y) top left corner of location to print to.
      font_obj   Pygame font object for text rendering.
    """
    # Surface containing game over text
    # Do not anti-alias, render in white
    msg_surface = font_obj.render(msg, True, white)
    # Create rect object to specify where to place text
    msg_rect = msg_surface.get_rect()
    msg_rect.topleft = topleft
    surface.blit(msg_surface, msg_rect)
  
  def print_surface_center(self, msg, surface, center, font_obj, color=white, bg_color=None):
    """
    Print the passed msg string to the surface at location specified by center.
    Arguments
      msg        String to be displayed.
      surface    Pygame surface to print string to.
      center     (x, y) center location of where to print to
      font_obj   Pygame font object for text rendering.
      color      Color to use for printing.
      bg_color   Background color for text box. Default is no box.
    """
    # Surface containing game over text
    # Do not anti-alias, render in white
    msg_surface = font_obj.render(msg, True, color)
    # Create rect object to specify where to place text
    msg_rect = msg_surface.get_rect()
    msg_rect.center = center
    
    # Draw background box if there is one
    if bg_color:
      bg_surface = msg_surface.copy()
      bg_surface.fill(bg_color)
      bg_rect = bg_surface.get_rect()
      bg_rect.center = center
 
    # Draw background box to surface first
    surface.blit(bg_surface, bg_rect)
    # Then, draw text to surface
    surface.blit(msg_surface, msg_rect)
        
  def game_over(self):
    """
    Return whether the game is over.
    """
    return self.state == "game_over"
    #return not (self.stacked_bricks[self.gen_col][1].empty() and \
    #  self.stacked_bricks[self.gen_col][2].empty())
  
  def read_highscore(self):
    """
    Read high score from file.
    """
    # Read highscore from file
    try:
      # Open high score file
      f = open(self._highscore_filename, 'r')
      # Read first line of file
      line = f.readline()
      # Close file
      f.close()
      
      # Separate score and hash
      highscore, hs_hash = line.split(",")
      # Convert highscore to integer
      highscore = int(highscore)
      
      # Check hash
      if hs_hash == self.hash_score(highscore):
        # If hash matches, use highscore
        self.highscore = highscore
      else:
        # Otherwise, raise exception
        raise Exception("High score hash check failed.")
    # If getting high score fails, then reset to high score of 0
    except:
      self.highscore = 0
      # Write high score of 0 to file
      self.write_highscore()
    
  def write_highscore(self):
    """
    Write current highscore to highscore file.
    """
    # Write high score and hash to file    
    f = open(self._highscore_filename, 'w')
    f.write("%d,%s" % (self.highscore, self.hash_score(self.highscore)))
    f.close()
    
  def hash_score(self, score):
    """
    Return SHA-256 hash of passed score.
      score   integer score
    """
    # Create hash object
    h = hashlib.new("sha256")
    # Add score string
    h.update("%d%s" % (score, self._hash_key))
    # Return hash of score
    return h.hexdigest()
    
class Board:
    """
  Class for play area (stacked bricks).
  """

    # Possible brick colors (R, G, B) triplets
    _brick_colors = [red, green, blue, yellow]

    # Brick fallspeed (pixels per loop iteration)
    _fallspeed_slow = 1
    _fallspeed_fast = 10

    # High score filename
    _highscore_filename = "highscore.txt"

    # Key for hashing score
    _hash_key = "SomethingSomethingBricks"

    # Probability of getting a breaker brick
    _breaker_prob = 0.20

    # Time to show break graphic before continuing on (in milliseconds)
    _break_time = 500

    ## Wall images
    _img_dir = "images/"
    # Left wall segment
    _wall_l = pygame.sprite.Sprite()
    _wall_l.image = pygame.image.load(_img_dir + "wall_left.png")
    _wall_l.rect = _wall_l.image.get_rect()
    # Right wall segment
    _wall_r = pygame.sprite.Sprite()
    _wall_r.image = pygame.image.load(_img_dir + "wall_right.png")
    _wall_r.rect = _wall_r.image.get_rect()
    # Bottom wall segment
    _wall_b = pygame.sprite.Sprite()
    _wall_b.image = pygame.image.load(_img_dir + "wall_bottom.png")
    _wall_b.rect = _wall_b.image.get_rect()
    # Bottom right corner
    _wall_br = pygame.sprite.Sprite()
    _wall_br.image = pygame.image.load(_img_dir + "wall_br.png")
    _wall_br.rect = _wall_br.image.get_rect()
    # Bottom left corner
    _wall_bl = pygame.sprite.Sprite()
    _wall_bl.image = pygame.image.load(_img_dir + "wall_bl.png")
    _wall_bl.rect = _wall_bl.image.get_rect()

    # Fonts directory
    _font_dir = "fonts/"

    def __init__(self, board_size, brick_size, mixer):
        """
    Default constructor.
    Arguments:
      board_size    number of (columns, rows)
      brick_size    (x, y) pixel size of each brick
      mixer         Pygame mixer object for sound output.
    """
        # Save parameters
        self.num_cols, self.num_rows = self.board_size = board_size
        self.bw, self.bh = self.brick_size = brick_size
        # Column where bricks are spawned (0 indexed)
        self.gen_col = int(math.ceil(self.num_cols / 2))
        # Top left corner (pixels) of game board
        # Indent in by one block on each side
        self.left, self.top = self.topleft = (self.bw, -2 * self.bh)
        # Min, max possible values for brick topleft corner
        self.min_pos_x = self.left
        self.max_pos_x = self.left + self.bw * (self.num_cols - 1)
        self.min_pos_y = self.top
        self.max_pos_y = self.top + self.bh * (self.num_rows - 1)

        # Create array of stacked bricks
        self.clear_board()

        # Current dropping brick pair
        self.db = None
        # Position to display next brick
        self.next_topleft = (self.max_pos_x + 2 * self.bw,
                             self.min_pos_y + 4 * self.bh)
        # Generate next brick placeholder
        self.gen_next()

        ## Sounds
        # Directory where sounds are kept
        snd_dir = "sounds/"
        # Sound for key press
        self.snd_key = mixer.Sound(snd_dir + "select.wav")
        self.snd_key.set_volume(0.1)
        # Sound for bricks broken
        self.snd_break = mixer.Sound(snd_dir + "break.wav")
        self.snd_break.set_volume(0.2)
        # Sound when brick reaches bottom
        self.snd_drop = mixer.Sound(snd_dir + "drop.wav")
        self.snd_drop.set_volume(0.5)

        # Get high score
        self.read_highscore()

    def gen_breaker(self):
        """
    Return true with probability _breaker_prob to determine whether
    a breaker brick is generated.
    """
        if random.random() < self._breaker_prob:
            return True
        else:
            return False

    def gen_next(self):
        """
    Generate parameters for next brick.
    """
        # Determine next brick colors and breaker status
        self.next_color = (random.choice(self._brick_colors),
                           random.choice(self._brick_colors))
        self.next_breaker = (self.gen_breaker(), self.gen_breaker())

    def start(self):
        """
    Reset game state and start new game.
    """
        # Reset game board
        self.clear_board()
        # Drop a new brick
        self.create_brick()

    def clear_board(self):
        """
    Reset game board to all blank bricks and reset all state.
    """
        # Blank all brick spaces
        self.stacked_bricks = [0] * self.num_cols
        for col in range(self.num_cols):
            self.stacked_bricks[col] = [Brick()] * self.num_rows
        # Reset fallspeed
        self._fallspeed_slow = 1
        self.fallspeed = self._fallspeed_slow
        # Reset score
        self.score = 0
        # Start state is falling brick
        self.state = "fall"

    def create_brick(self):
        """
    Create a new dropping brick.
    """
        # Create a dropping brick pair
        self.db = DoubleBrick(self.next_color, self.next_breaker,
                              (self.gen_col * self.bw + self.left, self.top),
                              self.brick_size)
        # Determine next brick colors and breaker status
        self.gen_next()

    def col_top(self, col):
        """
    Return the top brick in column col that is not empty.
    Arguments:
      col    column to return top brick of.
    """
        for row in range(self.num_rows):
            if not self.stacked_bricks[col][row].empty():
                return self.stacked_bricks[col][row]
        # If no brick's found
        return None

    def col_pix_top(self, col):
        """
    Return top pixel of first (bottom) brick in column col that is empty.
    Arguments:
      col    column to return top pixel of.
    """
        if self.col_top(col):
            return self.col_top(col).rect.top - self.bh
        # No bricks found
        else:
            # Return bottom edge
            return self.max_pos_y

    def b1(self):
        """
    Return first (originally top) brick.
    """
        if self.db:
            return self.db.brick1
        else:
            return None

    def b2(self):
        """
    Return second (originally bottom) brick.
    """
        if self.db:
            return self.db.brick2
        else:
            return None

    def b1_col(self):
        """
    Column of first (originally top) brick.
    """
        return (self.b1().rect.left - self.left) / self.bw

    def b2_col(self):
        """
    Column of second (originally bottom) brick.
    """
        return (self.b2().rect.left - self.left) / self.bw

    def move_left(self):
        """
    Move dropping brick to the left.
    """
        if self.db and not self.game_over():
            # Check that there is space on the left
            if self.db.left_edge() > self.min_pos_x and \
              self.db.bottom_edge() < self.col_pix_top(self.b1_col() - 1) and \
              self.db.top_edge() < self.col_pix_top(self.b2_col() - 1):
                # Move both bricks to the left
                self.b1().rect.left -= self.bw
                self.b2().rect.left -= self.bw
                self.snd_key.play()

    def move_right(self):
        """
    Move dropping brick to the right.
    """
        if self.db and not self.game_over():
            # Check that there is space on the right
            if self.db.right_edge() < self.max_pos_x and \
              self.db.bottom_edge() < self.col_pix_top(self.b1_col() + 1) and \
              self.db.top_edge() < self.col_pix_top(self.b2_col() + 1):
                self.b1().rect.left += self.bw
                self.b2().rect.left += self.bw
                self.snd_key.play()

    def rotate_cw(self):
        """
    Rotate first (originally top) brick clockwise around second brick.
    """
        if self.db and not self.game_over():
            # If first brick above second
            if self.b1().rect.top < self.b2().rect.top and \
              self.db.right_edge() < self.max_pos_x and self.db.bottom_edge() < self.col_pix_top(self.b1_col() + 1):
                self.b1().rect.top += self.bh
                self.b1().rect.left += self.bw
                self.snd_key.play()
            # first brick below second
            elif self.b1().rect.top > self.b2().rect.top and \
              self.db.left_edge() > self.min_pos_x and self.db.top_edge() < self.col_pix_top(self.b1_col() - 1):
                self.b1().rect.top -= self.bh
                self.b1().rect.left -= self.bw
                self.snd_key.play()
            # first brick to the right of the second
            elif self.b1().rect.left > self.b2().rect.left and \
              self.db.bottom_edge() + self.bh < self.col_pix_top(self.b1_col() - 1):
                self.b1().rect.top += self.bh
                self.b1().rect.left -= self.bw
                self.snd_key.play()
            # first brick to the left of the second
            elif self.b1().rect.left < self.b2().rect.left:
                self.b1().rect.top -= self.bh
                self.b1().rect.left += self.bw
                self.snd_key.play()

    def rotate_ccw(self):
        """
    Rotate first (originally top) brick counter-clockwise around second brick.
    """
        if self.db and not self.game_over():
            # If first brick above second
            if self.b1().rect.top < self.b2().rect.top and \
              self.db.left_edge() > self.min_pos_x and self.db.bottom_edge() < self.col_pix_top(self.b1_col() - 1):
                self.b1().rect.top += self.bh
                self.b1().rect.left -= self.bw
                self.snd_key.play()
            # first brick below second
            elif self.b1().rect.top > self.b2().rect.top and \
              self.db.right_edge() < self.max_pos_x and self.db.top_edge() < self.col_pix_top(self.b1_col() + 1):
                self.b1().rect.top -= self.bh
                self.b1().rect.left += self.bw
                self.snd_key.play()
            # first brick to the right of the second
            elif self.b1().rect.left > self.b2().rect.left:
                self.b1().rect.top -= self.bh
                self.b1().rect.left -= self.bw
                self.snd_key.play()
            # first brick to the left of the second
            elif self.b1().rect.left < self.b2().rect.left and \
              self.db.bottom_edge() + self.bh < self.col_pix_top(self.b1_col() + 1):
                self.b1().rect.top += self.bh
                self.b1().rect.left += self.bw
                self.snd_key.play()

    def speed_up(self):
        """
    Increase fallspeed to fast.
    """
        if not self.game_over():
            self.fallspeed = self._fallspeed_fast

    def slow_down(self):
        """
    Decrease fallspeed to slow.
    """
        if not self.game_over():
            self.fallspeed = self._fallspeed_slow

    def update(self):
        """
    Drop brick by fallspeed. If the bottom is reached, then handle breaking
    and spawn a new brick.
    """
        # If handling breaking
        if self.state == "break":
            # Break bricks given current stacked bricks
            broken = self.break_bricks()
            # Collapse bricks from above if any bricks were broken
            if broken:
                self.state = "drop"
            # Otherwise, continue on
            else:
                # Fall speed increases as points are scored
                self._fallspeed_slow = int(self.score / 200) + 1
                self.fallspeed = self._fallspeed_slow
                # Create a new brick after all breaks have occurred
                self.state = "new_brick"

        # Drop bricks to fill holes after breaking bricks
        elif self.state == "drop":
            pygame.time.wait(self._break_time)
            self.drop_bricks()
            # Rerun breaking to find combos
            self.state = "break"

        # Create new brick after breaking
        elif self.state == "new_brick":
            # Create new brick if the game is not over
            if (self.stacked_bricks[self.gen_col][1].empty() and \
            self.stacked_bricks[self.gen_col][2].empty()):
                self.create_brick()
                # Go back to dropping brick
                self.state = "fall"
            else:
                # If the game is over because another spawned brick cannot fall,
                # still create a new one
                if self.stacked_bricks[self.gen_col][1].empty():
                    self.create_brick()
                # Game state is set to game over
                self.state = "game_over"

        # Update falling brick
        elif self.state == "fall":
            # If the brick pair hasn't reached the bottom,
            if self.b1().rect.top < self.col_pix_top(self.b1_col()) - self.fallspeed and \
              self.b2().rect.top < self.col_pix_top(self.b2_col()) - self.fallspeed:
                # Drop bricks
                self.b1().rect.top += self.fallspeed
                self.b2().rect.top += self.fallspeed
            # Else one of the bricks has reached the top of a column
            # In this case, both bricks are dropped
            else:
                # Sound for brick reaching bottom
                self.snd_drop.play()

                # Brick that hits a surface first needs to handled first (in case stacked)
                # Set the one that hits first as b1 and it's column as c1
                # The other one is b2 in column c2
                if self.b2().rect.top >= self.col_pix_top(
                        self.b2_col()) - self.fallspeed:
                    b2, b1 = self.b1(), self.b2()
                    c2, c1 = self.b1_col(), self.b2_col()
                else:
                    b1, b2 = self.b1(), self.b2()
                    c1, c2 = self.b1_col(), self.b2_col()
                # Drop bottom brick to bottom
                b1.rect.top = self.col_pix_top(c1)
                # Save to array
                self.stacked_bricks[b1.get_col_index()][
                    b1.get_row_index()] = b1
                # Now drop the other brick to the bototm
                b2.rect.top = self.col_pix_top(c2)
                # Save to array
                self.stacked_bricks[b2.get_col_index()][
                    b2.get_row_index()] = b2
                # Remove dropping brick while handling break
                self.db = None

                # Next, handle any breaking of bricks
                self.state = "break"

        elif self.state == "game_over":
            pass
        else:
            raise Exception("Unknown state in game board.")

    def break_bricks(self):
        """
    Remove any bricks marked as broken.
    """
        # If any bricks were broken
        broken = False
        # Scan through array
        for col in range(self.num_cols):
            for row in range(self.num_rows):
                # If breaker is touching a brick with the same color
                if self.stacked_bricks[col][row].breaker:
                    breaker_color = self.stacked_bricks[col][row].color
                    if (col > 0 and self.stacked_bricks[col-1][row].color == breaker_color) or \
                      (col < self.num_cols-1 and self.stacked_bricks[col+1][row].color == breaker_color) or \
                      (row > 0 and self.stacked_bricks[col][row-1].color == breaker_color) or \
                      (row < self.num_rows-1 and self.stacked_bricks[col][row+1].color == breaker_color):
                        # Use spread_break to recursively break proper bricks
                        self.spread_break(col, row, breaker_color)
                        # Play sound for breaking bricks
                        self.snd_break.play()
                        # Bricks have been broken
                        broken = True

        return broken

    def drop_bricks(self):
        """
    Fill in any empty spaces by moving bricks above down.
    """
        # Handle each column individual
        for col in range(self.num_cols):
            # If top row is broken
            if self.stacked_bricks[col][0].broken:
                # Replace with an empty brick
                self.stacked_bricks[col][0] = Brick()
            # For each row, starting from the top
            for row in range(self.num_rows - 1):
                # If the next spot is a broken brick
                if self.stacked_bricks[col][row + 1].broken:
                    # Move all bricks above down a space, starting from the bottom
                    for drop_row in range(row, -1, -1):
                        self.stacked_bricks[col][drop_row].rect.top += self.bh
                        self.stacked_bricks[col][
                            drop_row + 1] = self.stacked_bricks[col][drop_row]
                    # Create an empty brick at the top
                    self.stacked_bricks[col][0] = Brick()

    def spread_break(self, col, row, breaker_color):
        """
    Break the brick at col, row and recursively call on surrounding bricks.
    Arguments:
      col               Column index to spread break
      row               Row index to spread break
      breaker_color     Color of break to spread
    """
        # Replace image with a broken brick
        self.stacked_bricks[col][row].break_brick()
        # Increment score for each brick destroyed
        self.score += 10

        # Spread to surrounding blue bricks
        if col > 0 and self.stacked_bricks[col -
                                           1][row].color == breaker_color:
            self.spread_break(col - 1, row, breaker_color)
        if col < self.num_cols - 1 and self.stacked_bricks[
                col + 1][row].color == breaker_color:
            self.spread_break(col + 1, row, breaker_color)
        if row > 0 and self.stacked_bricks[col][row -
                                                1].color == breaker_color:
            self.spread_break(col, row - 1, breaker_color)
        if row < self.num_rows - 1 and self.stacked_bricks[col][
                row + 1].color == breaker_color:
            self.spread_break(col, row + 1, breaker_color)

    def draw_bricks(self, surface):
        """
    Draw all bricks (stacked and dropping) to the passed Pygame Surface.
    """
        if self.db:
            # Dropping brick
            surface.blit(self.b1().image, self.b1().rect)
            surface.blit(self.b2().image, self.b2().rect)

        # Draw "Next" label
        font_obj = pygame.font.Font(self._font_dir + "OpenSans-Regular.ttf",
                                    20)
        self.print_surface("Next", surface,
                           (self.max_pos_x + int(self.bw * 3 / 2),
                            self.min_pos_y + 3 * self.bh - 10), font_obj)
        # Draw next brick
        next_db = DoubleBrick(self.next_color, self.next_breaker,
                              self.next_topleft, self.brick_size)
        surface.blit(next_db.brick1.image, next_db.brick1.rect)
        surface.blit(next_db.brick2.image, next_db.brick2.rect)
        # Draw rectangle around next brick
        pygame.draw.rect(
            surface, white,
            pygame.rect.Rect((self.max_pos_x + int(self.bw * 7 / 4),
                              self.min_pos_y + int(self.bh * 15 / 4)),
                             (int(self.bw * 3 / 2), int(self.bh * 5 / 2))), 2)

        # Stacked bricks
        for col in range(self.num_cols):
            for row in range(self.num_rows):
                surface.blit(self.stacked_bricks[col][row].image,
                             self.stacked_bricks[col][row].rect)

    def draw_walls(self, surface):
        """
    Draw walls of board area to the passed Pygame surface.
    """
        # Draw walls
        # Left and right walls
        for row in range(self.num_rows):
            self._wall_l.rect.topleft = (self.left - self.bw,
                                         self.bh * row + self.top)
            surface.blit(self._wall_l.image, self._wall_l.rect)
            self._wall_r.rect.topleft = (self.left + self.num_cols * self.bw,
                                         self.top + self.bh * row)
            surface.blit(self._wall_r.image, self._wall_r.rect)
        # Bottom wall
        for col in range(self.num_cols):
            self._wall_b.rect.topleft = ((self.left + col * self.bw,
                                          self.top + self.bh * self.num_rows))
            surface.blit(self._wall_b.image, self._wall_b.rect)
        # Bottom corners
        self._wall_br.rect.topleft = (self.left + self.num_cols * self.bw,
                                      self.top + self.bh * self.num_rows)
        self._wall_bl.rect.topleft = (self.left - self.bw,
                                      self.top + self.bh * self.num_rows)
        surface.blit(self._wall_br.image, self._wall_br.rect)
        surface.blit(self._wall_bl.image, self._wall_bl.rect)

    def draw_score(self, surface):
        """
    Write the current score to the passed surface. Also note whether game is over.
    """
        # Font object for rendering text
        font_obj = pygame.font.Font(self._font_dir + "OpenSans-Regular.ttf",
                                    20)
        # Location to print score
        topleft = [self.max_pos_x + 2 * self.bw, self.min_pos_y + 11 * self.bh]
        # Print score to screen
        self.print_surface("Score: %d" % self.score, surface, topleft,
                           font_obj)
        # Print highscore to screen
        topleft[1] += self.bh
        self.print_surface("High score: %d" % self.highscore, surface, topleft,
                           font_obj)

        # If game is over
        if self.game_over():
            # Location to print to
            center = (int((self.min_pos_x + self.max_pos_x + self.bw) / 2),
                      int((self.min_pos_y + self.max_pos_y + self.bh) / 2))
            # Print Game Over
            self.print_surface_center("Game Over", surface, center, font_obj,
                                      white, dark_gray)

            # If we have a new high score,
            if self.score > self.highscore:
                # Update high score
                self.highscore = self.score
                # Save it to file
                self.write_highscore()

    def print_surface(self, msg, surface, topleft, font_obj):
        """
    Print the passed msg string to the surface at location specified by topleft.
    Arguments
      msg        String to be displayed.
      surface    Pygame surface to print string to.
      topleft    (x, y) top left corner of location to print to.
      font_obj   Pygame font object for text rendering.
    """
        # Surface containing game over text
        # Do not anti-alias, render in white
        msg_surface = font_obj.render(msg, True, white)
        # Create rect object to specify where to place text
        msg_rect = msg_surface.get_rect()
        msg_rect.topleft = topleft
        surface.blit(msg_surface, msg_rect)

    def print_surface_center(self,
                             msg,
                             surface,
                             center,
                             font_obj,
                             color=white,
                             bg_color=None):
        """
    Print the passed msg string to the surface at location specified by center.
    Arguments
      msg        String to be displayed.
      surface    Pygame surface to print string to.
      center     (x, y) center location of where to print to
      font_obj   Pygame font object for text rendering.
      color      Color to use for printing.
      bg_color   Background color for text box. Default is no box.
    """
        # Surface containing game over text
        # Do not anti-alias, render in white
        msg_surface = font_obj.render(msg, True, color)
        # Create rect object to specify where to place text
        msg_rect = msg_surface.get_rect()
        msg_rect.center = center

        # Draw background box if there is one
        if bg_color:
            bg_surface = msg_surface.copy()
            bg_surface.fill(bg_color)
            bg_rect = bg_surface.get_rect()
            bg_rect.center = center

        # Draw background box to surface first
        surface.blit(bg_surface, bg_rect)
        # Then, draw text to surface
        surface.blit(msg_surface, msg_rect)

    def game_over(self):
        """
    Return whether the game is over.
    """
        return self.state == "game_over"
        #return not (self.stacked_bricks[self.gen_col][1].empty() and \
        #  self.stacked_bricks[self.gen_col][2].empty())

    def read_highscore(self):
        """
    Read high score from file.
    """
        # Read highscore from file
        try:
            # Open high score file
            f = open(self._highscore_filename, 'r')
            # Read first line of file
            line = f.readline()
            # Close file
            f.close()

            # Separate score and hash
            highscore, hs_hash = line.split(",")
            # Convert highscore to integer
            highscore = int(highscore)

            # Check hash
            if hs_hash == self.hash_score(highscore):
                # If hash matches, use highscore
                self.highscore = highscore
            else:
                # Otherwise, raise exception
                raise Exception("High score hash check failed.")
        # If getting high score fails, then reset to high score of 0
        except:
            self.highscore = 0
            # Write high score of 0 to file
            self.write_highscore()

    def write_highscore(self):
        """
    Write current highscore to highscore file.
    """
        # Write high score and hash to file
        f = open(self._highscore_filename, 'w')
        f.write("%d,%s" % (self.highscore, self.hash_score(self.highscore)))
        f.close()

    def hash_score(self, score):
        """
    Return SHA-256 hash of passed score.
      score   integer score
    """
        # Create hash object
        h = hashlib.new("sha256")
        # Add score string
        h.update("%d%s" % (score, self._hash_key))
        # Return hash of score
        return h.hexdigest()