# Get directions for unit movement with conditions

I am developing a bot for Starcraft II. In my code for unit movement, I often find myself doing this:

• Calculate points around the current position of the unit
• Check if these points are valid and can be reached
• Get the best n points that fit condition A (for example, the n points that are furthest away from the closest enemy unit)
• Of these n points, get the best point that fits condition B (for example, the point that is closest to the closest friendly unit)

I already made a numpy version to speed it up a bit, but would like this to run even faster. Maybe there is a way to combine the two conditions somehow.

import heapq
import math
import timeit

import numpy as np

# Point2 objects are needed here for the starcraft bot, they have .x and .y properties
# positions have 15 decimal places
from sc2.position import Point2

DIRECTIONS_AMOUNT = 16
# add  to have current position in points
DIRECTION_OFFSETS = (
 + [round(math.cos(2 * math.pi / DIRECTIONS_AMOUNT * p), 15) for p in range(DIRECTIONS_AMOUNT)],
 + [round(math.sin(2 * math.pi / DIRECTIONS_AMOUNT * p), 15) for p in range(DIRECTIONS_AMOUNT)],
)
# the current position of the unit
position = Point2((65.11906509381345, 127.15779037493655))
#  urrent position of closest enemy
closestEnemy = Point2((74.11906509381345, 112.15779037493655))
# current position of closest friend
closestFriend = Point2((68.11906509381345, 131.15312313133655))

# directions + original position for the case that standing still is the best option
retreatPoints = [
Point2((position.x + DIRECTION_OFFSETS[p], position.y + DIRECTION_OFFSETS[p]))
for p in range(DIRECTIONS_AMOUNT + 1)
]

# select some points which are far away from the enemy
retreatPointsChoice = heapq.nlargest(3, retreatPoints, key=lambda p: p.distance_to(closestEnemy))
# final point where the unit moves to based on distance to closest friend
retreatPoint = min(retreatPointsChoice, key=lambda p: p.distance_to(closestFriend))

# with numpy, i improved it to this:
np_retreatPoints = np.array(retreatPoints)

def numpy_distance_sort(direction_points, target_point, amount=6, sort="min"):
if sort == "max":
results = np.add(
np.square(np.subtract(target_point.x, direction_points[:, 0])),
np.square(np.subtract(target_point.y, direction_points[:, 1])),
)
if sort == "min":
results = -1 * np.add(
np.square(np.subtract(target_point.x, direction_points[:, 0])),
np.square(np.subtract(target_point.y, direction_points[:, 1])),
)
return [np_retreatPoints[i] for i in np.argpartition(-results, amount)[:amount]]

np_retreatPointsChoice = numpy_distance_sort(np_retreatPoints, closestEnemy, 3, "max")
np_retreatPoint = min(retreatPointsChoice, key=lambda p: p.distance_to(closestFriend))

• Assuming you have enemy unit A at 3 distance away, and B at 4 distance away. According to your logic, I can move 2 distance away from A and towards B, so that I am now 2 distance away from B. (Which results in a worse position than original). Notwithstanding long calculation you are already doing, I think you might need to do even more, or approach this problem from a different perspective – kushj Nov 9 '18 at 7:13