"""
Code providing various functionalities using the archery handicap equations.
Makes use of the basic handicap equations in handicaps.handicap_equations to do
more elaborate things such as reverse calculation of handicap from score,
generation of handicap tables, etc.
Extended Summary
----------------
Code to add functionality to the basic handicap equations code
in handicap_equations.py including inverse function and display.
Routine Listings
----------------
- handicap_scheme()
- arrow_score()
- score_for_passes()
- score_for_round()
- handicap_from_score()
"""
import numpy as np
import numpy.typing as npt
from archeryutils import rounds, targets
from .handicap_scheme import HandicapScheme
from .handicap_scheme_aa import HandicapAA, HandicapAA2
from .handicap_scheme_agb import HandicapAGB, HandicapAGBold
_CLASSES = {
"AGB": HandicapAGB,
"AGBold": HandicapAGBold,
"AA": HandicapAA,
"AA2": HandicapAA2,
}
[docs]
def handicap_scheme( # noqa: D417 - Missing argument in docstring (**kwargs)
handicap_sys: str | HandicapScheme,
**kwargs: float,
) -> HandicapScheme:
r"""
Create a HandicapScheme subclass for a requested handicap scheme.
Parameters
----------
handicap_sys : str | HandicapScheme
String identifying the handicap scheme class to be generated, or an existing
HandicapScheme
\\**kwargs : float
Various kwargs defining custom values of parameters as appropriate for hc_sys
Returns
-------
HandicapScheme
a subclass of HandicapScheme as appropriate for the inputs
Raises
------
ValueError
If a handicap scheme is requested for which there is no defined class.
See Also
--------
handicap_scheme.HandicapScheme : The HandicapScheme class
handicap_scheme_agb.HandicapAGB :
The AGB HandicapScheme subclass and associated \\**kwargs
handicap_scheme_agb.HandicapAGBold :
The AGBold HandicapScheme subclass and associated \\**kwargs
handicap_scheme_aa.HandicapAA :
The AA HandicapScheme subclass and associated \\**kwargs
handicap_scheme_aa.HandicapAA2 :
The AA2 HandicapScheme subclass and associated \\**kwargs
Examples
--------
To generate a HandicapScheme class using the Archery GB (AGB) handicap scheme:
>>> from archeryutils import handicaps as hc
>>> agb_scheme = hc.handicap_scheme("AGB")
>>> agb_scheme
HandicapScheme('AGB')
"""
if isinstance(handicap_sys, HandicapScheme):
return handicap_sys
try:
return _CLASSES[handicap_sys](**kwargs)
except KeyError as exc:
msg = (
f"{handicap_sys} is not a recognised handicap system.\n"
f"""Please select from '{"', '".join(_CLASSES.keys())}'."""
)
raise ValueError(msg) from exc
[docs]
def arrow_score(
handicap: npt.ArrayLike,
target: targets.Target,
handicap_sys: str | HandicapScheme,
arw_d: float | None = None,
) -> npt.NDArray[np.float64]:
"""
Calculate the average arrow score for a given target and handicap rating.
Parameters
----------
handicap : ArrayLike
handicap(s) to calculate score for
target : targets.Target
A Target class specifying the target to be used
handicap_sys : str | HandicapScheme
identifier for the handicap system to use
arw_d : float | None, default=None
user-specified arrow diameter in [metres]
Returns
-------
NDArray[np.float64]
average expected arrow score for this handicap and target
Warnings
--------
Using a custom value for arrow diameter may give values that differ
from the official handicap scheme values.
Examples
--------
Expected arrow score on a WA720 70m target, using the AGB handicap system at a
handicap of 10 can be calculated with:
>>> import archeryutils as au
>>> from archeryutils import handicaps as hc
>>> my720target = au.Target("10_zone", 122, 70.0)
>>> hc.arrow_score(10.0, my720target, "AGB")
9.401182682963338
It can also be passed an array of handicaps:
>>> hc.arrow_score(np.array([10.0, 50.0, 100.0]), my720target, "AGB")
array([9.40118268, 6.05227962, 0.46412515])
"""
hc_sys = handicap_scheme(handicap_sys)
return hc_sys.arrow_score(handicap, target, arw_d=arw_d)
[docs]
def score_for_passes(
handicap: npt.ArrayLike,
rnd: rounds.Round,
handicap_sys: str | HandicapScheme,
arw_d: float | None = None,
rounded_score: bool = True,
) -> npt.NDArray[np.float64]:
"""
Calculate the expected score for all passes in a round at a given handicap.
Parameters
----------
handicap : ArrayLike
handicap value(s) to calculate score for
rnd : rounds.Round
A Round class specifying the round being shot
handicap_sys : str | HandicapScheme
identifier for the handicap system to use
arw_d : float | None, default=None
user-specified arrow diameter in [metres]
rounded_score : bool, default=True
round score to integer value?
Note the sum of rounded passes may not be the same as the rounded round score
Returns
-------
NDArray
average score for each pass in the round
Examples
--------
Expected score for each pass on a WA1440 90m, using the AGB handicap system at a
handicap of 10 can be calculated with the code below which returns an array with
one score for each pass that makes up the round:
>>> import archeryutils as au
>>> from archeryutils import handicaps as hc
>>> wa_outdoor = au.load_rounds.WA_outdoor
>>> hc.score_for_passes(10.0, wa_outdoor.wa1440_90, "AGB")
array([322.84091528, 338.44257659, 338.66395001, 355.87959411])
It can also be passed an array of handicaps:
>>> hc.score_for_passes(
... np.array([10.0, 50.0, 100.0]),
... wa_outdoor.wa1440_90,
... "AGB",
... )
array([[322.84091528, 162.76200686, 8.90456718],
[338.44257659, 217.88206641, 16.70850537],
[338.66395001, 216.74407488, 16.41855209],
[355.87959411, 288.77185611, 48.47897177]])
"""
hc_sys = handicap_scheme(handicap_sys)
return hc_sys.score_for_passes(
handicap,
rnd,
arw_d=arw_d,
rounded_score=rounded_score,
)
[docs]
def score_for_round(
handicap: npt.ArrayLike,
rnd: rounds.Round,
handicap_sys: str | HandicapScheme,
arw_d: float | None = None,
rounded_score: bool = True,
):
"""
Calculate the expected score for a round at a given handicap.
Parameters
----------
handicap : ArrayLike
handicap(s) to calculate score for
rnd : rounds.Round
A Round class specifying the round being shot
handicap_sys : str | HandicapScheme
identifier for the handicap system to use
arw_d : float | None, default=None
user-specified arrow diameter in [metres]
rounded_score : bool, default=True
round score to integer value?
Returns
-------
round_score : NDArray[np.float64]
average score of the round for this set of parameters
Examples
--------
Expected score for a WA1440 90m, using the AGB handicap system at a
handicap of 10 can be calculated with:
>>> import archeryutils as au
>>> from archeryutils import handicaps as hc
>>> wa_outdoor = au.load_rounds.WA_outdoor
>>> hc.score_for_round(10.0, wa_outdoor.wa1440_90, "AGB")
1356.0
>>> au.handicap_equations.score_for_round(
... wa_outdoor.wa1440_90, 10.0, rounded_score=False
... )
1355.8270359849505
It can also be passed an array of handicaps:
>>> hc.score_for_round(
... np.array([10.0, 50.0, 100.0]),
... wa_outdoor.wa1440_90,
... )
array([1356., 887., 91.])
"""
hc_sys = handicap_scheme(handicap_sys)
return hc_sys.score_for_round(
handicap,
rnd,
arw_d=arw_d,
rounded_score=rounded_score,
)
[docs]
def handicap_from_score(
score: float,
rnd: rounds.Round,
handicap_sys: str | HandicapScheme,
arw_d: float | None = None,
int_prec: bool = False,
) -> int | float:
"""
Calculate the handicap of a given score on a given round.
Parameters
----------
score : float
score achieved on the round
rnd : rounds.Round
the rounds.Round object to calculate the handicap for
handicap_sys : str | HandicapScheme
identifier for the handicap system to use
arw_d : float | None, default=None
user-specified arrow diameter in [metres]
int_prec : bool, default=False
display results as integers? default = False
if True decimal results accurate to 2dp
Returns
-------
int | float
Handicap for score. Has type int if int_prec is True, else float.
Examples
--------
Handicap for a score of 999 on a WA 1440 (90m) using the AGB scheme can be
calculated as:
>>> import archeryutils as au
>>> wa_outdoor = au.load_rounds.WA_outdoor
>>> hc.handicap_from_score(999, wa_outdoor.wa1440_90, "AGB")
43.999964586102706
To get an integer value as would appear in the handicap tables use
``int_prec=True``:
>>> au.handicap_functions.handicap_from_score(
... 999, wa_outdoor.wa1440_90, "AGB", hc_params, int_prec=True
... )
44.0
"""
hc_sys = handicap_scheme(handicap_sys)
return hc_sys.handicap_from_score(score, rnd, arw_d=arw_d, int_prec=int_prec)