Advertisement
Guest User

Ai

a guest
Dec 10th, 2019
332
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.62 KB | None | 0 0
  1. import math
  2. import pymunk
  3. from pymunk import Vec2d
  4. import gameobjects
  5. from collections import defaultdict, deque
  6.  
  7.  
  8. # NOTE: use only 'map0' during development!
  9.  
  10. """
  11.   # TODO:
  12.   flag on tank = False när den dör
  13. """
  14.  
  15. MIN_ANGLE_DIF = math.radians(4) # 3 degrees, a bit more than we can turn each tick
  16.  
  17. TILE_SIZE = 1
  18.  
  19. def angle_between_vectors(vec1, vec2):
  20.     """ Since Vec2d operates in a cartesian coordinate space we have to
  21.       convert the resulting vector to get the correct angle for our space.
  22.   """
  23.     vec = vec1 - vec2
  24.     vec = vec.perpendicular()
  25.     return vec.angle
  26.  
  27. def periodic_difference_of_angles(angle1, angle2):
  28.     return  (angle1% (2*math.pi)) - (angle2% (2*math.pi))
  29.  
  30.  
  31. class Ai:
  32.     """ A simple ai that finds the shortest path to the target using
  33.   a breadth first search. Also capable of shooting other tanks and or wooden
  34.   boxes. """
  35.  
  36.     def __init__(self, tank,  game_objects_list, tanks_list, space, currentmap):
  37.         self.tank               = tank
  38.         self.game_objects_list  = game_objects_list
  39.         self.tanks_list         = tanks_list
  40.         self.space              = space
  41.         self.currentmap         = currentmap
  42.         self.flag = None
  43.         self.MAX_X = currentmap.width - 1
  44.         self.MAX_Y = currentmap.height - 1
  45.  
  46.         self.last_distance      = 999
  47.  
  48.         self.path = deque()
  49.         self.move_cycle = self.move_cycle_gen()
  50.         self.update_grid_pos()
  51.  
  52.     def update_grid_pos(self):
  53.         """ This should only be called in the beginning, or at the end of a move_cycle. """
  54.         self.grid_pos = self.get_tile_of_position(self.tank.body.position)
  55.  
  56.     def maybe_shoot(self):
  57.         """ Makes a raycast query in front of the tank. If another tank
  58.           or a wooden box is found, then we shoot.
  59.       """
  60.         ray = self.space.segment_query_first((self.tank.body.position[0] - 0.6 * math.sin(self.tank.body.angle), self.tank.body.position[1] + 0.6 * math.cos(self.tank.body.angle)), (self.tank.body.position[0] - 10*math.sin(self.tank.body.angle),self.tank.body.position[1] + 10*math.cos(self.tank.body.angle)) , 0, pymunk.ShapeFilter())
  61.  
  62.         if ray != None:
  63.             try:
  64.                 if hasattr(ray, 'shape'):
  65.                         if isinstance(ray.shape.parent, gameobjects.Tank):
  66.                             bullet = self.tank.shoot(self.space)
  67.                             if bullet != None:
  68.                                 self.game_objects_list.append(bullet)
  69.                         elif isinstance(ray.shape.parent, gameobjects.Box):
  70.                             if ray.shape.parent.boxmodel.destructable == True:
  71.                                 bullet = self.tank.shoot(self.space)
  72.                                 if bullet != None:
  73.                                     self.game_objects_list.append(bullet)
  74.             except:
  75.                 pass
  76.  
  77.     def decide(self):
  78.         """ Main decision function that gets called on every tick of the game. """
  79.         next(self.move_cycle)
  80.         self.maybe_shoot()
  81.  
  82.     def correct_angle(self, tank_angle, target_angle):
  83.         angle_dif = periodic_difference_of_angles(target_angle, tank_angle)
  84.         if abs(angle_dif) <= MIN_ANGLE_DIF:
  85.             self.tank.stop_turning()
  86.             return True
  87.         else:
  88.             return False
  89.  
  90.     def correct_position(self, target_pos, last_distance):
  91.         tank_pos = Vec2d(self.tank.body.position)
  92.         current_distance = target_pos.get_distance(tank_pos)
  93.         self.last_distance = current_distance
  94.         if last_distance <= current_distance+0.008:
  95.             return True
  96.         else:
  97.             return False
  98.  
  99.     def turn(self, tank_angle, target_angle):
  100.         angle_dif = periodic_difference_of_angles(tank_angle, target_angle)
  101.         #tank_angle = tank_angle % 2*(math.pi)
  102.  
  103.         if (angle_dif+2*math.pi)%2*math.pi >= math.pi and abs(angle_dif) > MIN_ANGLE_DIF:
  104.             self.tank.stop_moving()
  105.             self.tank.turn_left()
  106.         elif (angle_dif+2*math.pi)%2*math.pi < math.pi and abs(angle_dif) > MIN_ANGLE_DIF:
  107.             self.tank.stop_moving()
  108.             self.tank.turn_right()
  109.  
  110.  
  111.     def move_cycle_gen(self):
  112.         """ A generator that iteratively goes through all the required steps
  113.           to move to our goal.
  114.       """
  115.         while True:
  116.             path = self.find_shortest_path()
  117.             if not path:
  118.                 yield
  119.                 continue
  120.  
  121.             target_pos = path.popleft()
  122.             target_pos += Vec2d(0.5, 0.5)
  123.  
  124.             tank_pos = Vec2d(self.tank.body.position)
  125.             yield
  126.  
  127.             tank_angle = self.tank.body.angle
  128.             target_angle = angle_between_vectors(tank_pos, target_pos)
  129.             angle_dif = periodic_difference_of_angles(tank_angle, target_angle)
  130.             last_distance = tank_pos.get_distance(target_pos)
  131.  
  132.             self.turn(tank_angle, target_angle)
  133.             while not self.correct_angle(tank_angle, target_angle):
  134.                 tank_angle = self.tank.body.angle
  135.                 target_angle = angle_between_vectors(tank_pos, target_pos)
  136.                 yield
  137.             self.tank.accelerate()
  138.             while not self.correct_position(target_pos, self.last_distance):
  139.                 yield
  140.             self.update_grid_pos()
  141.             yield
  142.  
  143.  
  144.     def find_shortest_path(self):
  145.         """ A simple Breadth First Search using integer coordinates as our nodes.
  146.           Edges are calculated as we go, using an external function. """
  147.  
  148.         # To be implemented
  149.         self.update_grid_pos()
  150.         searchtree = {}
  151.         queue = deque()
  152.         shortest_path = []
  153.         visited = set()
  154.         starting_position = self.grid_pos
  155.         target = self.get_target_tile()
  156.         queue.append(starting_position)
  157.         visited.add(starting_position.int_tuple)
  158.         while queue:
  159.             node = Vec2d(queue.popleft())
  160.             if node == target.int_tuple:
  161.                 goal_node = node.int_tuple
  162.                 break
  163.             for neighbor in self.get_tile_neighbors(node):
  164.                 neighbor = neighbor.int_tuple
  165.                 if neighbor not in visited:
  166.                     queue.append(neighbor)
  167.                     visited.add(neighbor)
  168.                     searchtree[neighbor] = node.int_tuple
  169.         key = goal_node
  170.         while key != self.grid_pos.int_tuple:
  171.             shortest_path.append(Vec2d(key))
  172.             parent_node = searchtree[key]
  173.             key = parent_node
  174.         shortest_path = shortest_path[::-1]
  175.         return deque(shortest_path)
  176.  
  177.  
  178.     def get_target_tile(self):
  179.         """ Returns position of the flag if we don't have it. If we do have the flag,
  180.           return the position of our home base.
  181.       """
  182.         if self.tank.flag != None:
  183.             x, y = self.tank.start_position
  184.         else:
  185.             self.get_flag() # Ensure that we have initialized it.
  186.             x, y = self.flag.x, self.flag.y
  187.         return Vec2d(int(x), int(y))
  188.  
  189.     def get_flag(self):
  190.         """ This has to be called to get the flag, since we don't know
  191.           where it is when the Ai object is initialized.
  192.       """
  193.         if self.flag == None:
  194.         # Find the flag in the game objects list
  195.             for obj in self.game_objects_list:
  196.                 if isinstance(obj, gameobjects.Flag):
  197.                     self.flag = obj
  198.                     break
  199.         return self.flag
  200.  
  201.     def get_tile_of_position(self, position_vector):
  202.         """ Converts and returns the float position of our tank to an integer position. """
  203.         x, y = position_vector
  204.         return Vec2d(int(x), int(y))
  205.  
  206.     def get_tile_neighbors(self, coord_vec):
  207.         """ Returns all bordering grid squares of the input coordinate.
  208.           A bordering square is only considered accessible if it is grass
  209.           or a wooden box.
  210.       """
  211.  
  212.         #print(coord_vec)
  213.         left = Vec2d(coord_vec[0] - 1 , coord_vec[1])
  214.         right = Vec2d(coord_vec[0] + 1, coord_vec[1])
  215.         down = Vec2d(coord_vec[0], coord_vec[1] + 1)
  216.         up = Vec2d(coord_vec[0], coord_vec[1] - 1)
  217.         neighbors = [left, right, down, up] # Find the coordinates of the tiles' four neighbors
  218.         #print(neighbors)
  219.         return filter(self.filter_tile_neighbors, neighbors)
  220.  
  221.  
  222.     def filter_tile_neighbors (self, coord):
  223.         coord = coord.int_tuple
  224.         if coord[1] <= self.MAX_Y and coord[0] <= self.MAX_X and coord[1] >= 0 and coord[0] >= 0 and (self.currentmap.boxAt(coord[0], coord[1]) == 0 or self.currentmap.boxAt(coord[0], coord[1]) == 2):
  225.             return True
  226.         return False
  227.  
  228.  
  229. SimpleAi = Ai # Legacy
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement