1
\$\begingroup\$

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 [0] to have current position in points
DIRECTION_OFFSETS = (
    [0] + [round(math.cos(2 * math.pi / DIRECTIONS_AMOUNT * p), 15) for p in range(DIRECTIONS_AMOUNT)],
    [0] + [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[0][p], position.y + DIRECTION_OFFSETS[1][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))
\$\endgroup\$
  • \$\begingroup\$ 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 \$\endgroup\$ – kushj Nov 9 '18 at 7:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.