Advertisement
Guest User

Untitled

a guest
Dec 7th, 2019
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.67 KB | None | 0 0
  1. import collections
  2. import heapq
  3. import random
  4.  
  5. import time
  6.  
  7. import math
  8. from collections import deque
  9.  
  10. import numpy as np
  11. import functions as fun
  12.  
  13. # max. amount of tries we iterate until we abort the search
  14. MAX_RUNS = float('inf')
  15. # max. time after we until we abort the search (in seconds)
  16. TIME_LIMIT = float('inf')
  17.  
  18. # used for backtrace of bi-directional A*
  19. BY_START = 1
  20. BY_END = 2
  21.  
  22.  
  23. class Player:
  24.     def __init__(self):
  25.         self.id = "Id1"
  26.         self.name = "Grzegorz Wator"
  27.         self.team = "Pink"
  28.         self.cols_count = 0
  29.         self.rows_count = 0
  30.         self.x = 0
  31.         self.y = 0
  32.         self.tank_direction = fun.S
  33.         self.gun_direction = fun.S
  34.         self.map = ""
  35.  
  36.     # Executed at the beginning of the game.
  37.     def set_init_values(self, player_id, team_name, cols_count, rows_count):
  38.         self.id = player_id
  39.         self.team = team_name
  40.         self.cols_count = cols_count
  41.         self.rows_count = rows_count
  42.  
  43.     # Executed at the beginning to get user name.
  44.     def get_name(self):
  45.         return self.name
  46.  
  47.     # Executed after killing other player.
  48.     def set_information_about_killing(self):
  49.         pass
  50.  
  51.     # Executed after tank death.
  52.     def set_information_about_death(self):
  53.         pass
  54.  
  55.     # Executed when tank had fault. (TANK FAULT RATE is 10% by default)
  56.     def set_information_about_tank_fault(self):
  57.         pass
  58.  
  59.     # Executed when move was wrong, for example when player wants to move out of map.
  60.     def set_information_about_move_fault(self):
  61.         pass
  62.  
  63.     # Executed on the end of the game.
  64.     def set_information_about_game_end(self):
  65.         pass
  66.  
  67.     # Executed at the beginning and after every move. Round kills is list of tuples (killer_name, victim_name).
  68.     def set_map_and_position(self, map, x, y, tank_direction, gun_direction, round, round_kills):
  69.         self.map = map
  70.         self.x = x
  71.         self.y = y
  72.         self.tank_direction = tank_direction
  73.         self.gun_direction = gun_direction
  74.  
  75.     # Executed before function get_next_move at the beginning of every round.
  76.     def get_radio_message(self):
  77.         return None
  78.  
  79.     # Executed at the beginning of every round to get player move.
  80.     def get_next_move(self, radio_messages):
  81.         # self.getRouteToEnemy("Aa")
  82.         players = fun.get_players_from_map(self.map, self.cols_count, self.rows_count)
  83.         if fun.can_shoot_someone(self.map, self.cols_count, self.rows_count, self.x, self.y, self.gun_direction):
  84.             for player in players:
  85.                 if fun.is_the_player_on_line_of_fire(self.map, self.cols_count, self.rows_count, self.x, self.y,
  86.                                                      self.gun_direction, player[0]):
  87.                     return (fun.SHOOT, player.x, player.y)
  88.  
  89.         # No, lets check if I can kill someone after turning the gun.
  90.         # First check turning gun to left.
  91.         new_gun_direction = fun.get_gun_direction_after_gun_rotation(self.gun_direction,
  92.                                                                      fun.TURN_GUN_TO_LEFT)
  93.         if fun.can_shoot_someone(self.map, self.cols_count, self.rows_count, self.x, self.y,
  94.                                  new_gun_direction):
  95.             return (fun.TURN_GUN_TO_LEFT, 0, 0)
  96.         # Now check turning gun to right.
  97.         new_gun_direction = fun.get_gun_direction_after_gun_rotation(self.gun_direction,
  98.                                                                      fun.TURN_GUN_TO_RIGHT)
  99.         if fun.can_shoot_someone(self.map, self.cols_count, self.rows_count, self.x, self.y,
  100.                                  new_gun_direction):
  101.             return (fun.TURN_GUN_TO_RIGHT, 0, 0)
  102.  
  103.  
  104.         # find distances to all tanks
  105.         distances_to_tanks = self.find_distances_to_tanks(players)
  106.         minDistance = min(list(filter(lambda t: t[1] > 0, distances_to_tanks)), key=lambda t:t[1])
  107.         next_squere = self.getRouteToEnemy(*fun.get_position_of_player(self.map, self.cols_count, self.rows_count, minDistance[0]))
  108.         return self.determine_next_move_base_on_next_square(next_squere)
  109.         # return (random.choice([fun.MOVE_FORWARD, fun.TURN_TANK_TO_LEFT]), 0, 0)
  110.  
  111.     def getRouteToEnemy(self, enemy_x_pos, enemy_y_pos):
  112.         newMap = self.convertMapToZerosAndOnes()
  113.         grid = Grid(matrix=newMap)
  114.         start = grid.node(self.x, self.y)
  115.         end = grid.node(enemy_x_pos, enemy_y_pos)
  116.         finder = AStarFinder(diagonal_movement=DiagonalMovement.never)
  117.         path, runs = finder.find_path(start, end, grid)
  118.         return path[1]
  119.  
  120.     def find_distances_to_tanks(self, players):
  121.         return list(map(self.distance_to_tank, players))
  122.  
  123.     def convertMapToZerosAndOnes(self):
  124.         return list(map(self.mapToAStar, self.map))
  125.  
  126.     def convertMapToBFSType(self):
  127.         return list(map(self.mapToBFS, self.map))
  128.  
  129.     def mapToBFS(self, args):
  130.         ALLOWED_CHARS = ["_"]
  131.         return [0 if x in ALLOWED_CHARS or isinstance(x, tuple) else 1 for x in args]
  132.  
  133.     def mapToAStar(self, args):
  134.         ALLOWED_CHARS = ["_"]
  135.         return [1 if x in ALLOWED_CHARS or isinstance(x, tuple)  else 0 for x in args]
  136.  
  137.     def distance_to_tank(self, tank):
  138.         return tank[2], solveBFS(self.convertMapToBFSType(), (self.y, self.x), (tank[1], tank[0]))
  139.  
  140.     def determine_next_move_base_on_next_square(self, next_sqare):
  141.         delta_x, delta_y = next_sqare[0] - self.x, next_sqare[1] - self.y
  142.         if delta_x > 0 and self.tank_direction == fun.E:
  143.             return fun.MOVE_FORWARD, 0, 0
  144.         elif delta_x > 0 and self.tank_direction == fun.W:
  145.             return fun.MOVE_BACK, 0, 0
  146.         elif delta_x < 0 and self.tank_direction == fun.E:
  147.             return fun.MOVE_BACK, 0, 0
  148.         elif delta_x < 0 and self.tank_direction == fun.W:
  149.             return fun.MOVE_FORWARD, 0, 0
  150.         elif delta_x != 0 and self.tank_direction == fun.N or self.tank_direction == fun.S:
  151.             return fun.TURN_TANK_TO_LEFT, 0, 0
  152.         elif delta_y > 0 and self.tank_direction == fun.N:
  153.             return fun.MOVE_FORWARD, 0, 0
  154.         elif delta_y > 0 and self.tank_direction == fun.S:
  155.             return fun.MOVE_BACK, 0, 0
  156.         elif delta_y < 0 and self.tank_direction == fun.N:
  157.             return fun.MOVE_BACK, 0, 0
  158.         elif delta_y < 0 and self.tank_direction == fun.S:
  159.             return fun.MOVE_FORWARD, 0, 0
  160.         elif delta_y != 0 and self.tank_direction == fun.E or self.tank_direction == fun.W:
  161.             return fun.TURN_TANK_TO_LEFT, 0, 0
  162.         return (random.choice([fun.MOVE_FORWARD, fun.TURN_TANK_TO_LEFT]), 0, 0)
  163.  
  164.  
  165. delta_x = [-1, 1, 0, 0]
  166. delta_y = [0, 0, 1, -1]
  167.  
  168.  
  169. def valid(map, x, y):
  170.     if x < 0 or x >= len(map) or y < 0 or y >= len(map[x]):
  171.         return False
  172.     return (map[x][y] != 1)
  173.  
  174.  
  175. def solveBFS(map, start, end):
  176.     Q = deque([start])
  177.     dist = {start: 0}
  178.     while len(Q):
  179.         curPoint = Q.popleft()
  180.         curDist = dist[curPoint]
  181.         if curPoint == end:
  182.             return curDist
  183.         for dx, dy in zip(delta_x, delta_y):
  184.             nextPoint = (curPoint[0] + dx, curPoint[1] + dy)
  185.             if not valid(map, nextPoint[0], nextPoint[1]) or nextPoint in dist.keys():
  186.                 continue
  187.             dist[nextPoint] = curDist + 1
  188.             Q.append(nextPoint)
  189.  
  190.  
  191. USE_NUMPY = False
  192.  
  193. class DiagonalMovement:
  194.     always = 1
  195.     never = 2
  196.     if_at_most_one_obstacle = 3
  197.     only_when_no_obstacle = 4
  198.  
  199. class Node(object):
  200.     def __init__(self, x=0, y=0, walkable=True, weight=1):
  201.         self.x = x
  202.         self.y = y
  203.         self.walkable = walkable
  204.         self.weight = weight
  205.         self.cleanup()
  206.  
  207.     def __lt__(self, other):
  208.         return self.f < other.f
  209.  
  210.     def cleanup(self):
  211.         self.h = 0.0
  212.         self.g = 0.0
  213.         self.f = 0.0
  214.         self.opened = 0
  215.         self.closed = False
  216.         self.parent = None
  217.         self.retain_count = 0
  218.         self.tested = False
  219.  
  220. def build_nodes(width, height, matrix=None, inverse=False):
  221.     nodes = []
  222.     use_matrix = (isinstance(matrix, (tuple, list))) or \
  223.         (USE_NUMPY and isinstance(matrix, np.ndarray) and matrix.size > 0)
  224.  
  225.     for y in range(height):
  226.         nodes.append([])
  227.         for x in range(width):
  228.             # 1, '1', True will be walkable
  229.             # while others will be obstacles
  230.             # if inverse is False, otherwise
  231.             # it changes
  232.             weight = int(matrix[y][x]) if use_matrix else 1
  233.             walkable = weight <= 0 if inverse else weight >= 1
  234.  
  235.             nodes[y].append(Node(x=x, y=y, walkable=walkable, weight=weight))
  236.     return nodes
  237.  
  238.  
  239. class Grid(object):
  240.     def __init__(self, width=0, height=0, matrix=None, inverse=False):
  241.         self.width = width
  242.         self.height = height
  243.         if isinstance(matrix, (tuple, list)) or (
  244.                 USE_NUMPY and isinstance(matrix, np.ndarray) and
  245.                 matrix.size > 0):
  246.             self.height = len(matrix)
  247.             self.width = self.width = len(matrix[0]) if self.height > 0 else 0
  248.         if self.width > 0 and self.height > 0:
  249.             self.nodes = build_nodes(self.width, self.height, matrix, inverse)
  250.         else:
  251.             self.nodes = [[]]
  252.  
  253.     def node(self, x, y):
  254.         return self.nodes[y][x]
  255.  
  256.     def inside(self, x, y):
  257.         return 0 <= x < self.width and 0 <= y < self.height
  258.  
  259.     def walkable(self, x, y):
  260.         return self.inside(x, y) and self.nodes[y][x].walkable
  261.  
  262.     def neighbors(self, node, diagonal_movement=DiagonalMovement.never):
  263.         x = node.x
  264.         y = node.y
  265.         neighbors = []
  266.         s0 = d0 = s1 = d1 = s2 = d2 = s3 = d3 = False
  267.  
  268.         # ↑
  269.         if self.walkable(x, y - 1):
  270.             neighbors.append(self.nodes[y - 1][x])
  271.             s0 = True
  272.         # →
  273.         if self.walkable(x + 1, y):
  274.             neighbors.append(self.nodes[y][x + 1])
  275.             s1 = True
  276.         # ↓
  277.         if self.walkable(x, y + 1):
  278.             neighbors.append(self.nodes[y + 1][x])
  279.             s2 = True
  280.         # ←
  281.         if self.walkable(x - 1, y):
  282.             neighbors.append(self.nodes[y][x - 1])
  283.             s3 = True
  284.  
  285.         if diagonal_movement == DiagonalMovement.never:
  286.             return neighbors
  287.  
  288.         if diagonal_movement == DiagonalMovement.only_when_no_obstacle:
  289.             d0 = s3 and s0
  290.             d1 = s0 and s1
  291.             d2 = s1 and s2
  292.             d3 = s2 and s3
  293.         elif diagonal_movement == DiagonalMovement.if_at_most_one_obstacle:
  294.             d0 = s3 or s0
  295.             d1 = s0 or s1
  296.             d2 = s1 or s2
  297.             d3 = s2 or s3
  298.         elif diagonal_movement == DiagonalMovement.always:
  299.             d0 = d1 = d2 = d3 = True
  300.  
  301.         # ↖
  302.         if d0 and self.walkable(x - 1, y - 1):
  303.             neighbors.append(self.nodes[y - 1][x - 1])
  304.  
  305.         # ↗
  306.         if d1 and self.walkable(x + 1, y - 1):
  307.             neighbors.append(self.nodes[y - 1][x + 1])
  308.  
  309.         # ↘
  310.         if d2 and self.walkable(x + 1, y + 1):
  311.             neighbors.append(self.nodes[y + 1][x + 1])
  312.  
  313.         # ↙
  314.         if d3 and self.walkable(x - 1, y + 1):
  315.             neighbors.append(self.nodes[y + 1][x - 1])
  316.  
  317.         return neighbors
  318.  
  319.     def cleanup(self):
  320.         for y_nodes in self.nodes:
  321.             for node in y_nodes:
  322.                 node.cleanup()
  323.  
  324.     def grid_str(self, path=None, start=None, end=None,
  325.                  border=True, start_chr='s', end_chr='e',
  326.                  path_chr='x', empty_chr=' ', block_chr='#',
  327.                  show_weight=False):
  328.         data = ''
  329.         if border:
  330.             data = '+{}+'.format('-'*len(self.nodes[0]))
  331.         for y in range(len(self.nodes)):
  332.             line = ''
  333.             for x in range(len(self.nodes[y])):
  334.                 node = self.nodes[y][x]
  335.                 if node == start:
  336.                     line += start_chr
  337.                 elif node == end:
  338.                     line += end_chr
  339.                 elif path and ((node.x, node.y) in path or node in path):
  340.                     line += path_chr
  341.                 elif node.walkable:
  342.                     # empty field
  343.                     weight = str(node.weight) if node.weight < 10 else '+'
  344.                     line += weight if show_weight else empty_chr
  345.                 else:
  346.                     line += block_chr  # blocked field
  347.             if border:
  348.                 line = '|'+line+'|'
  349.             if data:
  350.                 data += '\n'
  351.             data += line
  352.         if border:
  353.             data += '\n+{}+'.format('-'*len(self.nodes[0]))
  354.         return data
  355.  
  356.  
  357.  
  358. class Finder(object):
  359.     def __init__(self, heuristic=None, weight=1,
  360.                  diagonal_movement=DiagonalMovement.never,
  361.                  weighted=True,
  362.                  time_limit=TIME_LIMIT,
  363.                  max_runs=MAX_RUNS):
  364.         self.time_limit = time_limit
  365.         self.max_runs = max_runs
  366.         self.weighted = weighted
  367.  
  368.         self.diagonal_movement = diagonal_movement
  369.         self.weight = weight
  370.         self.heuristic = heuristic
  371.  
  372.     def calc_cost(self, node_a, node_b):
  373.         if node_b.x - node_a.x == 0 or node_b.y - node_a.y == 0:
  374.             # direct neighbor - distance is 1
  375.             ng = 1
  376.         else:
  377.             ng = SQRT2
  378.         if self.weighted:
  379.             ng *= node_b.weight
  380.  
  381.         return node_a.g + ng
  382.  
  383.     def apply_heuristic(self, node_a, node_b, heuristic=None):
  384.         if not heuristic:
  385.             heuristic = self.heuristic
  386.         return heuristic(
  387.             abs(node_a.x - node_b.x),
  388.             abs(node_a.y - node_b.y))
  389.  
  390.     def find_neighbors(self, grid, node, diagonal_movement=None):
  391.         if not diagonal_movement:
  392.             diagonal_movement = self.diagonal_movement
  393.         return grid.neighbors(node, diagonal_movement=diagonal_movement)
  394.  
  395.     def keep_running(self):
  396.         if self.runs >= self.max_runs:
  397.             raise ExecutionRunsException(
  398.                 '{} run into barrier of {} iterations without '
  399.                 'finding the destination'.format(
  400.                     self.__class__.__name__, self.max_runs))
  401.  
  402.         if time.time() - self.start_time >= self.time_limit:
  403.             raise ExecutionTimeException(
  404.                 '{} took longer than {} seconds, aborting!'.format(
  405.                     self.__class__.__name__, self.time_limit))
  406.  
  407.     def process_node(self, node, parent, end, open_list, open_value=True):
  408.         ng = self.calc_cost(parent, node)
  409.  
  410.         if not node.opened or ng < node.g:
  411.             node.g = ng
  412.             node.h = node.h or \
  413.                 self.apply_heuristic(node, end) * self.weight
  414.             node.f = node.g + node.h
  415.             node.parent = parent
  416.  
  417.             if not node.opened:
  418.                 heapq.heappush(open_list, node)
  419.                 node.opened = open_value
  420.             else:
  421.                 open_list.remove(node)
  422.                 heapq.heappush(open_list, node)
  423.  
  424.     def find_path(self, start, end, grid):
  425.         self.start_time = time.time()  # execution time limitation
  426.         self.runs = 0  # count number of iterations
  427.         start.opened = True
  428.  
  429.         open_list = [start]
  430.  
  431.         while len(open_list) > 0:
  432.             self.runs += 1
  433.             self.keep_running()
  434.  
  435.             path = self.check_neighbors(start, end, grid, open_list)
  436.             if path:
  437.                 return path, self.runs
  438.         return [], self.runs
  439.  
  440.  
  441. def manhatten(dx, dy):
  442.     return dx + dy
  443.  
  444.  
  445. def octile(dx, dy):
  446.     f = SQRT2 - 1
  447.     if dx < dy:
  448.         return f * dx + dy
  449.     else:
  450.         return f * dy + dx
  451.  
  452.  
  453. class AStarFinder(Finder):
  454.     def __init__(self, heuristic=None, weight=1,
  455.                  diagonal_movement=DiagonalMovement.never,
  456.                  time_limit=TIME_LIMIT,
  457.                  max_runs=MAX_RUNS):
  458.         super(AStarFinder, self).__init__(
  459.             heuristic=heuristic,
  460.             weight=weight,
  461.             diagonal_movement=diagonal_movement,
  462.             time_limit=time_limit,
  463.             max_runs=max_runs)
  464.  
  465.         if not heuristic:
  466.             if diagonal_movement == DiagonalMovement.never:
  467.                 self.heuristic = manhatten
  468.             else:
  469.                 self.heuristic = octile
  470.  
  471.     def check_neighbors(self, start, end, grid, open_list,
  472.                         open_value=True, backtrace_by=None):
  473.         node = heapq.nsmallest(1, open_list)[0]
  474.         open_list.remove(node)
  475.         node.closed = True
  476.         if not backtrace_by and node == end:
  477.             return backtrace(end)
  478.         neighbors = self.find_neighbors(grid, node)
  479.         for neighbor in neighbors:
  480.             if neighbor.closed:
  481.                 continue
  482.             if backtrace_by and neighbor.opened == backtrace_by:
  483.                 if backtrace_by == BY_END:
  484.                     return bi_backtrace(node, neighbor)
  485.                 else:
  486.                     return bi_backtrace(neighbor, node)
  487.             self.process_node(neighbor, node, end, open_list, open_value)
  488.         return None
  489.  
  490.     def find_path(self, start, end, grid):
  491.         start.g = 0
  492.         start.f = 0
  493.         return super(AStarFinder, self).find_path(start, end, grid)
  494.  
  495.  
  496. class ExecutionTimeException(Exception):
  497.     def __init__(self, message):
  498.         super(ExecutionTimeException, self).__init__(message)
  499.  
  500.  
  501. class ExecutionRunsException(Exception):
  502.     def __init__(self, message):
  503.         super(ExecutionRunsException, self).__init__(message)
  504.  
  505. SQRT2 = math.sqrt(2)
  506.  
  507. def backtrace(node):
  508.     path = [(node.x, node.y)]
  509.     while node.parent:
  510.         node = node.parent
  511.         path.append((node.x, node.y))
  512.     path.reverse()
  513.     return path
  514.  
  515.  
  516. def bi_backtrace(node_a, node_b):
  517.     path_a = backtrace(node_a)
  518.     path_b = backtrace(node_b)
  519.     path_b.reverse()
  520.     return path_a + path_b
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement