Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AI Class code review #3

Open
richjoregonstate opened this issue May 17, 2019 · 0 comments
Open

AI Class code review #3

richjoregonstate opened this issue May 17, 2019 · 0 comments
Assignees
Labels
Requirement Required for the class

Comments

@richjoregonstate
Copy link
Collaborator

AI class | lines 4547-4933

start

In the for loop they use a hardcoded set form 0-3 this set could be an iterator to improve memory usage.

line 4613

  def rate_bomb_escape_directions(self, tile_coordinates):
                    #          up       right   down   left 
    axis_directions =          ((0,-1), (1,0),  (0,1), (-1,0))
    perpendicular_directions = ((1,0),  (0,1),  (1,0), (0,1))

    result = [0,0,0,0]
    
    for direction in (0,1,2,3):#TODO: This could be a range iterator
      for i in range(1,self.player.get_flame_length() + 2):
        axis_tile = (tile_coordinates[0] + i * axis_directions[direction][0],tile_coordinates[1] + i * axis_directions[direction][1])
        
        if not self.tile_is_escapable(axis_tile):
          break
        
        perpendicular_tile1 = (axis_tile[0] + perpendicular_directions[direction][0],axis_tile[1] + perpendicular_directions[direction][1])
        perpendicular_tile2 = (axis_tile[0] - perpendicular_directions[direction][0],axis_tile[1] - perpendicular_directions[direction][1])

        if i > self.player.get_flame_length() and self.game_map.get_danger_value(axis_tile) >= GameMap.SAFE_DANGER_VALUE:
          result[direction] += 1
          
        if self.tile_is_escapable(perpendicular_tile1) and self.game_map.get_danger_value(perpendicular_tile1) >= GameMap.SAFE_DANGER_VALUE:
          result[direction] += 1
          
        if self.tile_is_escapable(perpendicular_tile2) and self.game_map.get_danger_value(perpendicular_tile2) >= GameMap.SAFE_DANGER_VALUE:  
          result[direction] += 1
    
    return tuple(result)

The code below is a good example of how this code is well documented but not well commented. There is no explanation as to how the danger score is being used. While I appreciate the headers in cases like this they don't help the reviewer understand why things are the way they are.

line 4613

## Returns an integer score in range 0 - 100 for given file (100 = good, 0 = bad).
    
  def rate_tile(self, tile_coordinates):
    danger = self.game_map.get_danger_value(tile_coordinates)
    
    if danger == 0:
      return 0
    
    score = 0
    
    if danger < 1000:
      score = 20
    elif danger < 2500:
      score = 40
    else:
      score = 60
    
    tile_item = self.game_map.get_tile_at(tile_coordinates).item
    
    if tile_item != None:
      if tile_item != GameMap.ITEM_DISEASE:
        score += 20
      else:
        score -= 10
        
    top = (tile_coordinates[0],tile_coordinates[1] - 1)
    right = (tile_coordinates[0] + 1,tile_coordinates[1])
    down = (tile_coordinates[0],tile_coordinates[1] + 1)
    left = (tile_coordinates[0] - 1,tile_coordinates[1])
    
    if self.game_map.tile_has_lava(top) or self.game_map.tile_has_lava(right) or self.game_map.tile_has_lava(down) or self.game_map.tile_has_lava(left):
      score -= 5    # don't go near lava
    
    if self.game_map.tile_has_bomb(tile_coordinates):
      if not self.player.can_box():
        score -= 5
    
    return score

In the below code a static mapping of cardinal directions appears again. Considering it's a static type and used in a number of locations it should be defined as other elements of the code are.

line 4698

  def number_of_blocks_next_to_tile(self, tile_coordinates):
    count = 0
    
    for tile_offset in ((0,-1),(1,0),(0,1),(-1,0)):  # for each neigbour file
      helper_tile = self.game_map.get_tile_at((tile_coordinates[0] + tile_offset[0],tile_coordinates[1] + tile_offset[1]))
      
      if helper_tile != None and helper_tile.kind == MapTile.TILE_BLOCK:
        count += 1
      
    return count

This is an odd choice rather than deleting the AI player object it continues to live and waste CPU cycles.
line 4738

  def play(self):
    if self.do_nothing or self.player.is_dead():
      return []
........

Again in the below an iterator could be used instead of a static array.

line 4797

      for direction in (0,1,2,3):
        score = self.rate_tile((current_tile[0] + tile_increment[direction][0],current_tile[1] + tile_increment[direction][1]))  
........

At this point, I should mention in the play function each action choice has a section of code associated with it. Each one of these sections could be split off into another function. This isn't necessary but rather a stylistic choice.

In the below code range should be xrange() as to not generate and store a set but to rather use an iterator.
line 4919

      for i in range(multibomb_count):
        if not self.game_map.tile_is_walkable(current_tile) or not self.game_map.tile_is_withing_map(current_tile):
          break
........

Reviewed with minor issues

  • AI class

Recommendations:

  • Refactor as noted in review
  • Move to a AI module to reduce size of main file
  • Replacing static sets with iterators
  • The replacement of range() with xrange()
  • Altering functionality so that AI's can be deleted when dead so they don't waste CPU cycles
  • Adding global definitions for the cardinal direction tuples.
@richjoregonstate richjoregonstate added the Requirement Required for the class label May 17, 2019
@richjoregonstate richjoregonstate self-assigned this May 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Requirement Required for the class
Projects
None yet
Development

No branches or pull requests

1 participant