Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed PlayerPosition to tuple[float, float, float] #270

Merged
merged 2 commits into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion awpy/analytics/map_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def _extract_team_metadata(
"""
coords = ("x", "y", "z")
alive_players: list[PlayerPosition] = [
[player[dim] for dim in coords]
tuple(player[dim] for dim in coords)
for player in side_data["players"] or []
if player["isAlive"]
]
Expand Down
51 changes: 34 additions & 17 deletions awpy/analytics/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from collections import defaultdict
from itertools import pairwise
from statistics import mean, median
from typing import Literal, get_args
from typing import Literal, get_args, overload

import networkx as nx
import numpy as np
Expand All @@ -56,6 +56,8 @@
DistanceType,
GameFrame,
PlaceMatrix,
PlayerPosition,
PlayerPosition2D,
TileId,
Token,
)
Expand Down Expand Up @@ -104,16 +106,30 @@ def point_in_area(map_name: str, area_id: int, point: list[float]) -> bool:
return contains_x and contains_y


@overload
def find_closest_area(
map_name: str, point: list[float], *, flat: bool = False
map_name: str, point: PlayerPosition, *, flat: Literal[False] = ...
) -> ClosestArea:
...


@overload
def find_closest_area(
map_name: str, point: PlayerPosition2D, *, flat: Literal[True] = ...
) -> ClosestArea:
...


def find_closest_area(
map_name: str, point: PlayerPosition | PlayerPosition2D, *, flat: bool = False
) -> ClosestArea:
"""Finds the closest area in the nav mesh.

Searches through all the areas by comparing point to area centerpoint.

Args:
map_name (string): Map to search
point (list): Point as a list [x,y,z]
point (tuple): Point as a tuple (x,y,z) or (x, y)
flat (Boolean): Whether z should be ignored.

Returns:
Expand All @@ -128,10 +144,10 @@ def find_closest_area(
raise ValueError(msg)
if flat:
if len(point) != 2: # noqa: PLR2004
msg = "Point must be a list [X,Y] when flat is True"
msg = "Point must be a tuple (X,Y) when flat is True"
raise ValueError(msg)
elif len(point) != 3: # noqa: PLR2004
msg = "Point must be a list [X,Y,Z]"
msg = "Point must be a tuple (X,Y,Z)"
raise ValueError(msg)
closest_area: ClosestArea = {
"mapName": map_name,
Expand All @@ -148,7 +164,8 @@ def find_closest_area(
dist = np.sqrt(
(point[0] - avg_x) ** 2
+ (point[1] - avg_y) ** 2
+ (point[2] - avg_z) ** 2
# If flat is false we have a 3D PlayerPosition
+ (point[2] - avg_z) ** 2 # pyright: ignore [reportGeneralTypeIssues]
)
if dist < closest_area["distance"]:
closest_area["areaId"] = area
Expand Down Expand Up @@ -184,7 +201,7 @@ def _check_arguments_area_distance(
if map_name not in NAV:
msg = "Map not found."
raise ValueError(msg)
if (area_a not in NAV[map_name].keys()) or (area_b not in NAV[map_name].keys()):
if (area_a not in NAV[map_name]) or (area_b not in NAV[map_name]):
msg = "Area ID not found."
raise ValueError(msg)
if dist_type not in get_args(DistanceType):
Expand Down Expand Up @@ -342,8 +359,8 @@ def area_distance(

def point_distance(
map_name: str,
point_a: list[float],
point_b: list[float],
point_a: PlayerPosition,
point_b: PlayerPosition,
dist_type: PointDistanceType = "graph",
) -> DistanceObject:
"""Returns the distance between two points.
Expand Down Expand Up @@ -442,7 +459,7 @@ def generate_position_token(map_name: str, frame: GameFrame) -> Token:
for player in ct_players:
if player["isAlive"]:
closest_area = find_closest_area(
map_name, [player["x"], player["y"], player["z"]]
map_name, (player["x"], player["y"], player["z"])
)
ct_token[
map_area_names.index(NAV[map_name][closest_area["areaId"]]["areaName"])
Expand All @@ -452,7 +469,7 @@ def generate_position_token(map_name: str, frame: GameFrame) -> Token:
for player in t_players:
if player["isAlive"]:
closest_area = find_closest_area(
map_name, [player["x"], player["y"], player["z"]]
map_name, (player["x"], player["y"], player["z"])
)
t_token[
map_area_names.index(NAV[map_name][closest_area["areaId"]]["areaName"])
Expand Down Expand Up @@ -784,20 +801,20 @@ def generate_centroids(
# Get the centroids and rep. point of the hull
try:
my_polygon = Polygon(hull)
my_centroid = [
my_centroid = (
*list(np.array(my_polygon.centroid.coords)[0]),
mean(z_s[area_name]),
]
rep_point = [
)
rep_point = (
*list(np.array(my_polygon.representative_point().coords)[0]),
mean(z_s[area_name]),
]
)
except ValueError: # A LinearRing must have at least 3 coordinate tuples
my_centroid = [
my_centroid = (
mean([x for (x, _) in hull]),
mean([y for (_, y) in hull]),
mean(z_s[area_name]),
]
)
rep_point = my_centroid

# Find the closest tile for these points
Expand Down
7 changes: 5 additions & 2 deletions awpy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,8 +708,11 @@ class RoundStatistics(TypedDict):
# Type to represent tile id for navigation tiles.
TileId: TypeAlias = int

# Type to represent player position (list of floats [x, y, z])
PlayerPosition: TypeAlias = list[float]
# Type to represent player position (tuple of floats [x, y, z])
PlayerPosition: TypeAlias = tuple[float, float, float]

# Type to represent player position (tuple of floats [x, y, z])
PlayerPosition2D: TypeAlias = tuple[float, float]

# Return type for awpy.analytics.map_control._bfs_helper.
# Contains map control values for one team.
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ extension-pkg-allow-list = ["cv2"]

[tool.pylint.basic]
# Good variable names which should always be accepted, separated by a comma.
good-names = ["i", "j", "k", "ex", "Run", "_", "x", "y", "z", "e"]
good-names = ["i", "j", "k", "ex", "Run", "_", "x", "y", "z", "e", "PlayerPosition2D"]
include-naming-hint = true

[tool.pylint.design]
# Maximum number of arguments for function / method.
Expand Down
8 changes: 5 additions & 3 deletions tests/test_nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,13 @@ def test_point_in_area(self):
def test_find_area(self):
"""Tests find_area."""
with pytest.raises(ValueError, match="Map not found."):
find_closest_area(map_name="test", point=[0, 0, 0])
with pytest.raises(ValueError, match=re.escape("Point must be a list [X,Y,Z]")):
find_closest_area(map_name="test", point=(0, 0, 0))
with pytest.raises(
ValueError, match=re.escape("Point must be a tuple (X,Y,Z)")
):
find_closest_area(map_name="de_dust2", point=[0, 0])
with pytest.raises(
ValueError, match=re.escape("Point must be a list [X,Y] when flat is True")
ValueError, match=re.escape("Point must be a tuple (X,Y) when flat is True")
):
find_closest_area(map_name="de_dust2", point=[0, 0, 0], flat=True)
example_area = NAV["de_dust2"][152]
Expand Down
Loading