def test_for_initial_points_get_returns_direction(self): get_direction = DirectionDetector( begin=Point(0, 0), end=Point(1, 0), min_distance=1, ) assert_that(get_direction(), equal_to(Point(1, 0)))
def bresenham(line: Line): x1 = line.begin.x y1 = line.begin.y x2 = line.end.x y2 = line.end.y dx = x2 - x1 dy = y2 - y1 is_steep = abs(dy) > abs(dx) if is_steep: x1, y1 = y1, x1 x2, y2 = y2, x2 swapped = False if x1 > x2: x1, x2 = x2, x1 y1, y2 = y2, y1 swapped = True dx = x2 - x1 dy = y2 - y1 error = int(dx / 2.0) y_step = 1 if y1 < y2 else -1 y = y1 points = [] for x in range(x1, x2 + 1): coord = Point(y, x) if is_steep else Point(x, y) points.append(coord) error -= abs(dy) if error < 0: y += y_step error += dx if swapped: points.reverse() return points
def tiles_at_line(line: Line): tiles = bresenham(line) for i, current in islice(enumerate(tiles), len(tiles) - 1): yield current following = tiles[i + 1] if current.x != following.x and current.y != following.y: yield current + Point(following.x - current.x, 0) yield current + Point(0, following.y - current.y)
def test_for_graph_with_one_node_with_arc_returns_empty(self): result = shortest_path_with_direction( graph={0: Node(position=Point(0, 0), arcs=[Arc(dst=0, weight=1)])}, src=0, dst=0, initial_direction=Point(1, 0), forbidden={}) assert_that(list(result), equal_to([]))
def test_update_by_nearest_than_min_distance_returns_initial(self): get_direction = DirectionDetector( begin=Point(0, 0), end=Point(1, 0), min_distance=1, ) get_direction.update(Point(1, 0.5)) assert_that(get_direction(), equal_to(Point(1, 0)))
def test_update_by_exact_min_distance_returns_updated(self): get_direction = DirectionDetector( begin=Point(0, 0), end=Point(1, 0), min_distance=1, ) get_direction.update(Point(1, 1)) assert_that(get_direction(), equal_to(Point(0, 1)))
def use(car: Car): car_speed = Point(car.speed_x, car.speed_y) return (car.id != context.me.id and (context.speed.norm() > CAR_SPEED_FACTOR * car_speed.norm() or context.speed.norm() > 0 and (car_speed.norm() > 0 and abs(context.speed.cos(car_speed)) < cos(1) or car_speed.norm() == 0)))
def test_for_2_2_left_top_and_any_following_and_shift_1_returns_1_1(self): result = adjust_path_point( previous=None, current=TypedPoint(Point(2, 2), PointType.LEFT_TOP), following=TypedPoint(Point(2, 4), PointType.BOTTOM_TOP), shift=1, tile_size=4, ) assert_that(result, equal_to(Point(1, 1)))
def test_for_2_2_left_right_and_any_left_right_returns_equal(self): result = adjust_path_point(previous=None, current=TypedPoint(Point(2, 2), PointType.LEFT_RIGHT), following=TypedPoint( Point(4, 2), PointType.LEFT_RIGHT), shift=1, tile_size=4) assert_that(result, equal_to(Point(2, 2)))
def test_for_2_2_left_right_and_any_top_left_previous_and_shift_1_returns_2_3( self): result = adjust_path_point( previous=TypedPoint(Point(0, 2), PointType.TOP_RIGHT), current=TypedPoint(Point(2, 2), PointType.LEFT_RIGHT), following=None, shift=1, tile_size=4, ) assert_that(result, equal_to(Point(2, 2)))
def test_for_graph_with_two_disconnected_nodes_returns_empty(self): result = shortest_path_with_direction(graph={ 0: Node(position=Point(0, 0), arcs=[]), 1: Node(position=Point(1, 0), arcs=[]), }, src=0, dst=1, initial_direction=Point(1, 0), forbidden={}) assert_that(list(result), equal_to([]))
def get_target_speed(course: Point, path, angle_to_direct_proportion, max_speed, min_power): direct_factor = 1 / (angle_to_direct_proportion + 1) angle_factor = direct_factor * angle_to_direct_proportion if len(path) > 2: angle_factor *= max(1e-8 - 1, min(1 - 1e-8, cos_product(path, min_power))) if course.norm() > 0: return course * max_speed / course.norm() * (direct_factor + angle_factor) else: return Point(0, 0)
def generate(): l = Point(pos.x - 1, pos.y) if is_valid(l) and tiles[l.x][l.y] in HAS_LEFT_INPUT: yield l r = Point(pos.x + 1, pos.y) if is_valid(r) and tiles[r.x][r.y] in HAS_RIGHT_INPUT: yield r t = Point(pos.x, pos.y - 1) if is_valid(t) and tiles[t.x][t.y] in HAS_TOP_INPUT: yield t b = Point(pos.x, pos.y + 1) if is_valid(b) and tiles[b.x][b.y] in HAS_BOTTOM_INPUT: yield b
def test_from_vertical_to_next_vertical_returns_first_and_second_point( self): result = make_tiles_path( start_tile=Point(0, 1), waypoints=[[0, 1], [0, 2]], tiles=[ [ TileType.EMPTY, TileType.VERTICAL, TileType.VERTICAL, TileType.EMPTY ], ], direction=Point(1, 0), ) assert_that(list(result), equal_to([Point(0, 1), Point(0, 2)]))
def make_unit_barrier(unit): if isinstance(unit, RectangularUnit): return Unit( barrier=Circle(position=Point(unit.x, unit.y), radius=min(unit.width, unit.height) / 2), speed=Point(unit.speed_x, unit.speed_y), ) elif isinstance(unit, CircularUnit): return Unit( barrier=Circle(position=Point(unit.x, unit.y), radius=unit.radius), speed=Point(unit.speed_x, unit.speed_y), ) else: return None
def reset(self): self.__speed = PidController(2, 0, 0) self.__acceleration = PidController(2, 0, 0) self.__angle = PidController(1, 0, 0) self.__angular_speed_angle = PidController(4, 0, 0) self.__previous_speed = Point(0, 0) self.__previous_angular_speed_angle = 0
def make(self, context: Context): waypoints = self._waypoints(context.me.next_waypoint_index, context.world.waypoints, len(context.world.waypoints) * context.game.lap_count) first_unknown = next( (i for i, v in enumerate(waypoints) if context.world.tiles_x_y[v[0]][v[1]] == TileType.UNKNOWN), len(waypoints)) if first_unknown + 1 < len(waypoints): waypoints = waypoints[:first_unknown + 1] path = list(make_tiles_path( start_tile=context.tile, waypoints=waypoints, tiles=context.world.tiles_x_y, direction=context.direction, )) if not path: path = [self.start_tile] elif self.start_tile != path[0]: path = [self.start_tile] + path path = [(x + Point(0.5, 0.5)) * context.game.track_tile_size for x in path] shift = (context.game.track_tile_size / 2 - context.game.track_tile_margin - max(context.me.width, context.me.height) / 2) path = list(adjust_path(path, shift, context.game.track_tile_size)) path = list(shift_on_direct(path)) return path
def test_start_direct_backward_with_initial_speed(self): controller = Controller(distance_to_wheels=1) result = controller( course=Point(-1, 0), angle=0, direct_speed=Point(-1, 0), angular_speed_angle=0, engine_power=0, wheel_turn=0, target_speed=Point(-1, 0), tick=0, backward=False, ) assert_that(result.engine_power, equal_to(-1)) assert_that(result.wheel_turn, equal_to(0)) assert_that(result.brake, equal_to(False))
def test_start_right(self): controller = Controller(distance_to_wheels=1) result = controller( course=Point(0, 1), angle=0, direct_speed=Point(0, 0), angular_speed_angle=0, engine_power=0, wheel_turn=0, target_speed=Point(0, 1), tick=0, backward=False, ) assert_that(result.engine_power, equal_to(1)) assert_that(result.wheel_turn, greater_than(0)) assert_that(result.brake, equal_to(False))
def test_start_direct_backward_with_initial_opposite_speed_turn_left(self): controller = Controller(distance_to_wheels=1) result = controller( course=Point(-1, -0.1), angle=0, direct_speed=Point(1, 0), angular_speed_angle=0, engine_power=0, wheel_turn=0, target_speed=Point(-1, -0.1), tick=0, backward=True, ) assert_that(result.engine_power, equal_to(-1)) assert_that(result.wheel_turn, less_than(0)) assert_that(result.brake, equal_to(True))
def test_over_three_vertical_returns_three_points(self): result = make_tiles_path( start_tile=Point(0, 1), waypoints=[[0, 1], [0, 2], [0, 3]], tiles=[ [ TileType.EMPTY, TileType.VERTICAL, TileType.VERTICAL, TileType.VERTICAL, TileType.EMPTY ], ], direction=Point(1, 0), ) assert_that(list(result), equal_to([Point(0, 1), Point(0, 2), Point(0, 3)]))
def shift_on_direct_x(path): last = next((i for i, p in islice(enumerate(path), 1, len(path)) if p.x != path[i - 1].x), len(path) - 1) x = path[last].x if x != path[0].x: return last, chain([path[0]], (Point(x, p.y) for p in islice(path, 1, last))) return last, (p for p in path)
def shift_on_direct_y(path): last = next((i for i, p in islice(enumerate(path), 1, len(path)) if p.y != path[i - 1].y), len(path) - 1) y = path[last].y if y != path[0].y: return last, chain([path[0]], (Point(p.x, y) for p in islice(path, 1, last))) return last, (p for p in path)
def get_best_bonuses_point(position, bonuses, tile_size, priority_conf): def priority(bonus): type_priority = get_bonus_type_priority(bonus.type, priority_conf) penalty = get_bonus_penalty(bonus, position, tile_size) return (type_priority * BONUS_TYPE_PRIORITY_FACTOR - penalty * BONUS_PENALTY_FACTOR) best = max(bonuses, key=priority) return Point(best.x, best.y) if priority(best) > 0 else position
def test_for_two_double_connected_nodes_returns_with_three_nodes(self): result = split_arcs( graph={ 0: Node(position=Point(0, 0), arcs=[Arc(dst=1, weight=1)]), 1: Node(position=Point(0, 1), arcs=[Arc(dst=0, weight=1)]), }) assert_that( result, equal_to({ 0: Node(position=Point(0, 0), arcs=[Arc(dst=2, weight=0.5)]), 1: Node(position=Point(0, 1), arcs=[Arc(dst=2, weight=0.5)]), 2: Node(position=Point(0, 0.5), arcs=[Arc(dst=0, weight=0.5), Arc(dst=1, weight=0.5)]), }))
def test_for_quadrant_from_left_top_to_right_top_with_direction_to_bottom_returns_path_direct_to_right_top( self): result = shortest_path_with_direction(graph={ 0: Node(position=Point(0, 0), arcs=[Arc(dst=1, weight=1), Arc(dst=2, weight=1)]), 1: Node(position=Point(0, 1), arcs=[Arc(dst=3, weight=1)]), 2: Node(position=Point(1, 0), arcs=[]), 3: Node(position=Point(1, 1), arcs=[Arc(dst=2, weight=1)]), }, src=0, dst=2, initial_direction=Point(0, 1), forbidden={}) assert_that(list(result), equal_to([2]))
def generate(): for x, column in enumerate(tiles): for y, tile in enumerate(column): position = Point(x, y) yield get_point_index(position, row_size), make_tile_barriers( tile_type=tile, position=position, margin=margin, size=size, )
def clip_line(self, line: Line): k1 = self.point_code(line.begin) k2 = self.point_code(line.end) x1 = line.begin.x y1 = line.begin.y x2 = line.end.x y2 = line.end.y left = self.left_top.x top = self.left_top.y right = self.right_bottom.x bottom = self.right_bottom.y accept = False while True: if (k1 | k2) == 0: accept = True break if (k1 & k2) != 0: break opt = k1 or k2 if opt & Rectangle.TOP: x = x1 + (x2 - x1) * (bottom - y1) / (y2 - y1) y = bottom elif opt & Rectangle.BOTTOM: x = x1 + (x2 - x1) * (top - y1) / (y2 - y1) y = top if opt & Rectangle.RIGHT: y = y1 + (y2 - y1) * (right - x1) / (x2 - x1) x = right elif opt & Rectangle.LEFT: y = y1 + (y2 - y1) * (left - x1) / (x2 - x1) x = left if opt == k1: x1, y1 = x, y k1 = self.point_code(Point(x1, y1)) elif opt == k2: x2, y2 = x, y k2 = self.point_code(Point(x2, y2)) if accept: return Line(Point(x1, y1), Point(x2, y2)) else: return line
def make_tiles_path(start_tile, waypoints, tiles, direction): graph = make_graph(tiles) graph = split_arcs(graph) graph = add_diagonal_arcs(graph) row_size = len(tiles[0]) start = get_point_index(start_tile, row_size) waypoints = [get_index(x[0], x[1], row_size) for x in waypoints] if start != waypoints[0] and start in graph: waypoints = [start] + waypoints path = multi_path(graph, waypoints, direction) path = list(graph[x].position + Point(0.5, 0.5) for x in path) path = remove_split(path) return path
def generate(): for car in context.opponents_cars: car_position = Point(car.x, car.y) car_speed = Point(car.speed_x, car.speed_y) car_barriers = list(make_units_barriers([car])) distance = (context.position - car_position).norm() if car_speed.norm() < 1: yield (not has_intersection_with_tiles(distance) and make_has_intersection_with_lane( position=context.position, course=tire_speed * 50, barriers=car_barriers, width=context.game.tire_radius, )(0)) else: car_line = Line(car_position, car_position + car_speed) tire_line = Line(context.position, context.position + tire_speed) intersection = tire_line.intersection(car_line) if intersection is None: continue if not is_in_world(intersection, world_tiles, tile_size): continue if is_in_empty_tile(intersection, world_tiles, tile_size): continue car_dir = intersection - car_position if car_dir.norm() > 0 and car_dir.cos(car_speed) < 0: continue if car_dir.norm() > context.game.tire_initial_speed * 50: continue tire_dir = intersection - context.position if tire_dir.norm() > 0 and tire_dir.cos(tire_speed) < 0: continue if has_intersection_with_tiles(tire_dir.norm()): continue car_time = car_dir.norm() / car_speed.norm() tire_time = tire_dir.norm() / tire_speed.norm() if abs(car_time - tire_time) <= TIRE_INTERVAL: yield True
def generate(): for car in context.opponents_cars: car_position = Point(car.x, car.y) car_speed = Point(car.speed_x, car.speed_y) car_barriers = list(make_units_barriers([car])) if car_speed.norm() < 1: for washer in washers: yield make_has_intersection_with_lane( position=washer.position, course=washer.speed * 150, barriers=car_barriers, width=context.game.washer_radius, )(0) else: car_line = Line(car_position, car_position + car_speed) for washer in washers: washer_line = Line(washer.position, washer.position + washer.speed) intersection = washer_line.intersection(car_line) if intersection is None: continue if not is_in_world(intersection, world_tiles, tile_size): continue if is_in_empty_tile(intersection, world_tiles, tile_size): continue car_dir = intersection - car_position if car_dir.norm() > 0 and car_dir.cos(car_speed) < 0: continue if car_dir.norm() > washer_speed * 150: continue washer_dir = intersection - washer.position if (washer_dir.norm() > 0 and washer_dir.cos(washer.speed) < 0): continue car_time = car_dir.norm() / car_speed.norm() washer_time = washer_dir.norm() / washer.speed.norm() if abs(car_time - washer_time) <= WASHER_INTERVAL: yield True
def make_graph(tiles): column_size = len(tiles) row_size = len(tiles[0]) def left(pos): return get_index(pos.x - 1, pos.y, row_size) def right(pos): return get_index(pos.x + 1, pos.y, row_size) def top(pos): return get_index(pos.x, pos.y - 1, row_size) def bottom(pos): return get_index(pos.x, pos.y + 1, row_size) def tile_arcs(pos, tile_type): if tile_type == TileType.VERTICAL: return top(pos), bottom(pos) elif tile_type == TileType.HORIZONTAL: return left(pos), right(pos) elif tile_type == TileType.LEFT_TOP_CORNER: return right(pos), bottom(pos) elif tile_type == TileType.RIGHT_TOP_CORNER: return left(pos), bottom(pos) elif tile_type == TileType.LEFT_BOTTOM_CORNER: return right(pos), top(pos) elif tile_type == TileType.RIGHT_BOTTOM_CORNER: return left(pos), top(pos) elif tile_type == TileType.LEFT_HEADED_T: return left(pos), top(pos), bottom(pos) elif tile_type == TileType.RIGHT_HEADED_T: return right(pos), top(pos), bottom(pos) elif tile_type == TileType.TOP_HEADED_T: return top(pos), left(pos), right(pos) elif tile_type == TileType.BOTTOM_HEADED_T: return bottom(pos), left(pos), right(pos) elif tile_type == TileType.CROSSROADS: return left(pos), right(pos), top(pos), bottom(pos) elif tile_type == TileType.UNKNOWN: def is_valid(point: Point): return 0 <= point.x < column_size and 0 <= point.y < row_size def generate(): l = Point(pos.x - 1, pos.y) if is_valid(l) and tiles[l.x][l.y] in HAS_LEFT_INPUT: yield l r = Point(pos.x + 1, pos.y) if is_valid(r) and tiles[r.x][r.y] in HAS_RIGHT_INPUT: yield r t = Point(pos.x, pos.y - 1) if is_valid(t) and tiles[t.x][t.y] in HAS_TOP_INPUT: yield t b = Point(pos.x, pos.y + 1) if is_valid(b) and tiles[b.x][b.y] in HAS_BOTTOM_INPUT: yield b return tuple(get_point_index(p, row_size) for p in generate()) else: return tuple() result = {} for x, column in enumerate(tiles): for y, tile in enumerate(column): position = Point(x, y) node = Node(position, []) result[get_index(x, y, row_size)] = node for index in tile_arcs(node.position, tile): neighbor = result.get(index) if neighbor is None: neighbor_position = get_point(index, row_size) neighbor = Node(neighbor_position, []) result[index] = neighbor else: neighbor_position = neighbor.position weight = position.distance(neighbor_position) node.arcs.append(Arc(index, weight)) return result