Find the closest enemy I'm currently creating a 2d game in python. I have to calculate the closest enemy so the player knows which one to target. I have the following function to accomplish this task:

import math

def find_closest(character: tuple, enemies: list) -&gt; (int, int):
    """
    Finds the closest enemy in enemies

    :param character: An (x, y) representing the position of the character\n
    :param enemies: A list of tuples (x, y) that represent enemies

    :return: A tuple (x, y) of the closest enemy
    """
    closest_enemy = None
    smallest_distance = 100_000_000 # Set to large number to ensure values can be less #
    for enemy in enemies:
        distance = math.sqrt(math.pow(character[0] - enemy[0], 2) + (math.pow(character[1] - enemy[1], 2)))
        if distance &lt; smallest_distance:
            closest_enemy = (enemy[0], enemy[1])
            smallest_distance = distance
    return closest_enemy

if __name__ == "__main__":

    # Test Case #

    character = (5, 6)
    enemies = [(1, 2), (3, 4), (7, 6), (11, 4)]
    closest = find_closest(character, enemies)
    print(closest)


I would like to know if there's any better way to go about doing this. While it is a simple function, I'm sure there's some built in python function that can accomplish this even shorter. I use tuples because, in my opinion, they're the best way to represent coordinates. Any and all suggestions and/or improvements are welcome and appreciated.
For small lists of enemies, linearly scanning all of them and computing the distance to the character is sufficient. However, if you have many enemies, a more efficient data structure is needed.

If your list of enemies does not change (or changes less often than you need to find the closest enemy), I would use scipy.spatial.cKDTree. kd-trees take \$\mathcal{O}(n\log n)\$ time to build, but afterwards each query only takes \$\mathcal{O}(\log n)\$.

from scipy.spatial import cKDTree as KDTree

def find_closest_kdtree(character: tuple, enemies: KDTree) -&gt; (int, int):
    """
    Finds the closest enemy in enemies

    :param character: An (x, y) representing the position of the character\n
    :param enemies: A KDTree that represent enemies

    :return: A tuple (x, y) of the closest enemy
    """
    _, i = enemies.query([character], 1)
    return i[0]


if __name__ == "__main__":

    # Test Case #

    character = (5, 6)
    enemies = [(1, 2), (3, 4), (7, 6), (11, 4)]
    enemies_tree = KDTree(enemies)
    closest = enemies[find_closest_kdtree(character, enemies_tree)]
    print(closest)


If you do regularly need to update the list of enemies (because they are spawned, get killed, move off-screen, etc), you might be able to use a R* tree instead:

from rtree.index import Rtree


def find_closest_rtree(character: tuple, enemies) -&gt; (int, int):
    """
    Finds the closest enemy in enemies

    :param character: An (x, y) representing the position of the character\n
    :param enemies: A KDTree that represent enemies

    :return: A tuple (x, y) of the closest enemy
    """
    return next(enemies.nearest(character, 1, objects='raw'))


if __name__ == "__main__":

    # Test Case #

    character = (5, 6)
    enemies = [(1, 2), (3, 4), (7, 6), (11, 4)]
    enemies_tree = Rtree()
    for i, p in enumerate(enemies):
        enemies_tree.insert(i, p+p, p)

    closest = find_closest_rtree(character, enemies_tree)
    print(closest)


Here is how the different methods compare performance wise, including the implementation using min and your original implementation:



Note that building all the R* trees for this took multiple minutes, while building all the KDTrees took only a couple of seconds. So you would probably have to rebuild the KDTree quite often for it to be worth it to switch to the R* tree. 



In case you are interested, this is how I generated that graph:

import numpy as np
import pandas as pd
from functools import partial
import timeit
from scipy.spatial import cKDTree as KDTree
from rtree.index import Rtree

def get_time(func, *x):
    timer = timeit.Timer(partial(func, *x))
    t = timer.repeat(repeat=5, number=1)
    return np.min(t), np.std(t) / np.sqrt(len(t))

def get_times(func, inputs):
    return np.array(list(map(partial(get_time, func), inputs))

def find_closest(character: tuple, enemies: list) -&gt; (int, int):
    closest_enemy = None
    smallest_distance = 100_000_000 # Set to large number to ensure values can be less #
    for enemy in enemies:
        distance = math.sqrt(math.pow(character[0] - enemy[0], 2) + (math.pow(character[1] - enemy[1], 2)))
        if distance &lt; smallest_distance:
            closest_enemy = (enemy[0], enemy[1])
            smallest_distance = distance
    return closest_enemy

def find_closest_min(character: tuple, enemies: list) -&gt; (int, int):
    def find_enemy_distance(enemy):
        return math.hypot((character[0] - enemy[0]), (character[1] - enemy[1]))
    return min(enemies, key=find_enemy_distance)

def find_closest_kdtree(character: tuple, enemies: KDTree) -&gt; (int, int):
    _, i = enemies.query([character], 1)
    return i[0]

def find_closest_rtree(character: tuple, enemies) -&gt; (int, int):
    return next(enemies.nearest(character, 1, objects='raw'))

def find_closest_kdtree_with_build(character: tuple, enemies) -&gt; (int, int):
    enemies_tree = KDTree(enemies)
    _, i = enemies_tree.query([character], 1)
    return enemies[i[0]]

if __name__ == "__main__":
    character = 5, 6
    x = [list(map(tuple, np.random.randint(-n, n, (n, 2))))
         for n in np.logspace(1, 6, dtype=int)]
    kdtrees = [KDTree(v) for v in x]
    rtrees = []
    for y in x:
        rtrees.append(Rtree())
        for i, v in enumerate(y):
            rtrees[-1].insert(i, v+v, v)

    df = pd.DataFrame(list(map(len, x)), columns=["x"])
    df["find_closest"], df["find_closest_err"] = get_times(partial(find_closest, character), x).T
    df["find_closest_min"], df["find_closest_min_err"] = get_times(partial(find_closest_min, character), x).T
    df["find_closest_kdtree"], df["find_closest_kdtree_err"] = get_times(partial(find_closest_kdtree, character), kdtrees).T
    df["find_closest_rtree"], df["find_closest_rtree_err"] = get_times(partial(find_closest_rtree, character), rtrees).T
    df["find_closest_kdtree_with_build"], df["find_closest_kdtree_with_build_err"] = get_times(partial(find_closest_kdtree_with_build, character), x).T

    for label in df.columns[1::2]:
        plt.errorbar(df["x"], df[label], yerr=df[label + "_err"], fmt='o-', label=label)
    plt.xlabel("Number of enemies")
    plt.ylabel("Time [s]")
    plt.xscale("log")
    plt.yscale("log")
    plt.legend()
    plt.show()