Appendix#

Circleguard#

class circleguard.circleguard.Circleguard(key, db_path=None, slider_dir=None, loader=None, cache=True)[source]#

Circleguard is the main entry point for using circlecore. In particular, we provide methods to calculate various statistics about Replays:

  • similarity() - the similarity between two replays.

  • ur() - the unstable rate of a replay.

  • snaps() - any unusual snaps in the cursor movement of a replay.

  • frametime() - the average frametime of a replay.

  • frametimes() - a list of the time between frames of a replay.

  • hits() - the locations in a replay where a hitobject is hit.

We also provide methods to interact with Loadables:

Parameters:
  • key (str) – A valid api key. Can be retrieved from https://osu.ppy.sh/p/api/.

  • db_path (str or os.PathLike) – The path to the database file to read and write cached replays. If the path does not exist, a fresh database will be created there. If None, no replays will be cached or loaded from cache.

  • slider_dir (str or os.PathLike) – The path to the directory used by slider.library.Library to store beatmaps. If None, a temporary directory will be created for slider.library.Library and subsequently destroyed when this Circleguard object is garbage collected.

  • loader (Loader) – An instance of Loader or a subclass thereof, which will be used instead of creating a loader if passed.

similarity(replay1, replay2, method='similarity', num_chunks=5, mods_unknown='best') float | Tuple[float][source]#

The similarity between replay1 and replay2.

Parameters:
  • replay1 (Replay) – The replay to compare against replay2.

  • replay2 (Replay) – The replay to compare against replay1.

  • method ({"similarity", "correlation"}) – What method to use to calculate the similarity between the replays.
    similarity is (roughly speaking) the average distance between the two replays in pixels. A replay compared to itself (or an exact copy) has a similarity of 0. See SIM_LIMIT for a suggested number where similarities below this number indicate a stolen replay.
    correlation is a signal-processing metric which measures how similar two signals (or replays, in our case) are. Correlation also takes into account time shifts, so a replay which is a perfect copy of another replay but consistently lags 10 ms behind will still have a perfect correltion of 1. The higher correlation, the more correlated (or similar) the two replays are. See CORR_LIMIT for a suggested number where correlations above this number indicate a stolen replay.

  • num_chunks (int) – How many chunks to split the replay into when comparing. This parameter only has an affect if method is correlation. Note that runtime increases linearly with the number of chunks.

  • mods_unknown ({"best", "both"}) – What to do if one or both of replay1 and replay2 do not know what mods they were played with. In this case, the similarity will be computed twice, both with no modifications and with Mod.HR applied to replay1.
    If best is passed, the best (that is, lowest if method is similarity and highest if method is correlation) similarity of these two calculations is returned.
    If both is passed, a tuple with two floats is returned. The first element is the similarity with no modifications, and the second element is the similarity with Mod.HR applied to replay1.

Returns:

  • float – If method is similarity, this is the similarity of the two replays. If method is correlation, this is the correlation between the two replays.

  • (float, float) – If mods_unknown is both, a tuple with two floats is returned. The first element is the similarity with no modifications, and the second element is the similarity with Mod.HR applied to replay1. See the documentation for the mods_unknown parameter for more information.

ur(replay, cv=True, beatmap=None, adjusted=False) float[source]#

The unstable rate of replay.

Parameters:
  • replay (Replay) – The replay to calculate the ur of.

  • cv (bool) – Whether to return the converted or unconverted ur. The converted ur is returned by default.

  • beatmap (slider.beatmap.Beatmap) – The beatmap to use to calculate ur for the replay, instead of retrieving a beatmap from the replay itself.
    This parameter is provided primarily as an optimization for when you already have the replay’s beatmap, to avoid re-retrieving it in this method.

  • adjusted (boolean) – Whether to calculate “adjusted” ur. Adjusted ur filters outlier hits before calculating ur and can result in a more accurate ur for some replays.

Returns:

The ur of the replay.

Return type:

float

snaps(replay, max_angle=10, min_distance=8, only_on_hitobjs=True, beatmap=None) Iterable[Snap][source]#

Finds any snaps (sudden, jerky movement) in replay.

Parameters:
  • replay (Replay) – The replay to find snaps in.

  • max_angle (float) – Consider only (a,b,c) where ∠abc < max_angle

  • min_distance (float) – Consider only (a,b,c) where |ab| > min_distance and |bc| > min_distance.

  • only_on_hitobjs (bool) – Whether to only return snaps that occur on a hitobject.

  • beatmap (slider.beatmap.Beatmap) – The beatmap to use to calculate snaps for the replay, instead of retrieving a beatmap from the replay itself. This is only used when only_on_hitobjs is true, since the beatmap is not necessary otherwise.
    This parameter is provided primarily as an optimization for when you already have the replay’s beatmap, to avoid re-retrieving it in this method.

Returns:

The snaps of the replay.

Return type:

list[Snap]

Notes

Specifically, this function calculates the angle between each set of three points (a,b,c) and finds points where this angle is extremely acute and neither |ab| or |bc| are small.

By default, only snaps which occur on a hitobject are returned. This is to reduce false positives from spinners, driver issues, or lifting the pen off the tablet and back on again.

frametime(replay, cv=True, mods_unknown='raise') float[source]#

The median frametime (in ms) of replay.

Parameters:
  • replay (Replay) – The replay to calculate the median frametime of.

  • cv (bool) – Whether to return the converted or unconverted frametime. The converted frametime is returned by default.

  • mods_unknown ({"raise", "dt", "nm", "ht"}) – What to do if replay does not know what mods it was played with, and cv is True.
    If raise, a ValueError will be raised.
    If dt, the frametime will be converted as if the replay was played with Mod.DT.
    If nm, the frametime will be converted as if the replay was played with Mod.NM (that is, not converted at all).
    If ht, the frametime will be converted as if the replay was played with Mod.HT.

Returns:

The median frametime (in ms) of the replay.

Return type:

float

frametimes(replay, cv=True, mods_unknown='raise') Iterable[float][source]#

The time (in ms) between each frame in replay.

Parameters:
  • replay (Replay) – The replay to calculate the time between each frame of.

  • cv (bool) – Whether to return the converted or unconverted frametimes. The converted frametimes is returned by default.

  • mods_unknown ({"raise", "dt", "nm", "ht"}) – What to do if replay does not know what mods it was played with, and cv is True.
    If raise, a ValueError will be raised.
    If dt, the frametime swill be converted as if the replay was played with Mod.DT.
    If nm, the frametimes will be converted as if the replay was played with Mod.NM (that is, not converted at all).
    If ht, the frametimes will be converted as if the replay was played with Mod.HT.

Returns:

The time (in ms) between each frame of the replay.
The first element of this array corresponds to the time between the first and second frame, the second element to the time between the second and third frame, etc.

Return type:

[float]

hits(replay, within=None, beatmap=None) Iterable[Hit][source]#

The locations in the replay where a hitobject is hit.

Parameters:
  • replay (Replay) – The replay to calculate the hits of.

  • within (float) – If passed, only the hits which are within pixels or less away from the edge of the hitobject which they hit will be returned. Otherwise, all hits are returned.

  • beatmap (slider.beatmap.Beatmap) – The beatmap to use to calculate hits for the replay, instead of retrieving a beatmap from the replay itself.
    This parameter is provided primarily as an optimization for when you already have the replay’s beatmap, to avoid re-retrieving it in this method.

Returns:

The hits of the replay.

Return type:

list[Judgment]

Notes

In osu!lazer terminology, hits are equivalent to judgements, but without misses.

judgments(replay, beatmap=None) Iterable[Judgment][source]#

The locations in the replay where a hitobject is hit or missed. Judgments are marked as either misses, 50s, 100s, or 300s.

Parameters:
  • replay (Replay) – The replay to calculate the judgments of.

  • beatmap (slider.beatmap.Beatmap) – The beatmap to use to calculate judgments for the replay, instead of retrieving a beatmap from the replay itself.
    This parameter is provided primarily as an optimization for when you already have the replay’s beatmap, to avoid re-retrieving it in this method.

Returns:

The judgments of the replay.

Return type:

list[Judgment]

frametime_graph(replay, cv=True, figure=None, show_expected_frametime=True)[source]#

Uses matplotlib to create a graph of the frametimes of the replay.

Parameters:
  • replay (Replay) – The replay to graph the frametimes of.

  • cv (bool) – Whether the frametimes should be converted before being graphed.

  • figure (matplotlib.figure.Figure) – If passed, this figure will be used instead of creating a new one with pyplot. Using this parameter is not recommended for normal usage. It is exposed to allow circleguard (the gui) to use this method, as matplotlib’s pyqt compatability layer adds some complications which this works around.

  • show_expected_frametime (bool) – Whether to show a vertical line where we expect the average frametime to be.

Returns:

Matplotlib’s pyplot module for ease of use, so you can call matplotlib.pyplot.show() on the return value of this function to display the graph.
If figure is passed, the return value is instead the passed figure after being modified by the frametime graph.

Return type:

:module:`matplotlib.pyplot` or matplotlib.figure.Figure

load(loadable)[source]#

Loads the loadable.

Parameters:

loadable (Loadable) – The loadable to load.

Notes

This is identical to calling loadable.load(cg.loader, cg.cache).

load_info(replay_container)[source]#

Loads the info of the replay_container.

Parameters:

replay_container (ReplayContainer) – The replay container to load.

Notes

This is identical to calling replay_container.load_info(cg.loader).

Map(beatmap_id, span, mods=None, cache=None, load=False) Map[source]#

Returns a new, info-loaded Map.

Notes

This function is provided as a convenience for when you want to create a Map and load its info immediately. A common occurrence in using Circleguard is to want to instantiate a Map and immediately iterate over it to operate on its replays. However, this Map must be info loaded before it can be iterated over, so this function does that info loading for you.

>>> # good
>>> m = cg.Map(221777, "1-2")
>>> for replay in m:
>>>     ...
>>> # bad
>>> m = Map(221777, "1-2")
>>> cg.load_info(m)
>>> for replay in m:
>>>     ...
User(user_id, span, mods=None, cache=None, available_only=True, load=False) User[source]#

Returns a new, info-loaded User.

Notes

This function is provided as a convenience for when you want to create a User and load its info immediately. A common occurrence in using Circleguard is to want to instantiate a User and immediately iterate over it to operate on its replays. However, this User must be info loaded before it can be iterated over, so this function does that info loading for you.

>>> # good
>>> u = cg.User(124493, "1-2")
>>> for replay in u:
>>>     ...
>>> # bad
>>> u = User(124493, "1-2")
>>> cg.load_info(u)
>>> for replay in u:
>>>     ...
MapUser(beatmap_id, user_id, span={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}, cache=None, available_only=True, load=False) MapUser[source]#

Returns a new, info-loaded MapUser.

Notes

This function is provided as a convenience for when you want to create a MapUser and load its info immediately. A common occurrence in using Circleguard is to want to instantiate a MapUser and immediately iterate over it to operate on its replays. However, this MapUser must be info loaded before it can be iterated over, so this function does that info loading for you.

>>> # good
>>> mu = cg.MapUser(124493, 129891)
>>> for replay in mu:
>>>     ...
>>> # bad
>>> mu = MapUser(124493, 129891)
>>> cg.load_info(mu)
>>> for replay in mu:
>>>     ...
ReplayDir(path, cache=None, load=False) ReplayDir[source]#

Returns a new, info-loaded ReplayDir.

Notes

This function is provided as a convenience for when you want to create a ReplayDir and load its info immediately. A common occurrence in using Circleguard is to want to instantiate a ReplayDir and immediately iterate over it to operate on its replays. However, this ReplayDir must be info loaded before it can be iterated over, so this function does that info loading for you.

>>> # bad
>>> r_dir = cg.ReplayDir("/Users/tybug/Desktop/replays")
>>> for replay in r_dir:
>>>     ...
>>> # good
>>> r_dir = ReplayDir("/Users/tybug/Desktop/replays")
>>> cg.load_info(r_dir)
>>> for replay in r_dir:
>>>     ...
ReplayMap(beatmap_id, user_id, mods=None, cache=None, info=None) ReplayMap[source]#

Returns a new, loaded ReplayMap.

Notes

This function is provided as a convenience for when you want to create a ReplayMap and load it immediately. Loading can be an expensive operation which is why this does not occur by default.

ReplayPath(path, cache=None) ReplayPath[source]#

Returns a new, loaded ReplayPath.

Notes

This function is provided as a convenience for when you want to create a ReplayPath and load it immediately. Loading can be an expensive operation which is why this does not occur by default.

ReplayString(replay_data_str, cache=None) ReplayString[source]#

Returns a new, loaded ReplayString.

Notes

This function is provided as a convenience for when you want to create a ReplayString and load it immediately. Loading can be an expensive operation which is why this does not occur by default.

ReplayID(replay_id, cache=None) ReplayID[source]#

Returns a new, loaded ReplayID.

Notes

This function is provided as a convenience for when you want to create a ReplayID and load it immediately. Loading can be an expensive operation which is why this does not occur by default.

beatmap(replay) Beatmap[source]#

The beatmap the replay was played on, or None if the replay doesn’t know what beatmap it was played on.

Returns:

  • slider.beatmap.Beatmap – The beatmap this replay was played on.

  • None – If the replay doesn’t know what beatmap it was played on.

class circleguard.circleguard.KeylessCircleguard(db_path=None, slider_dir=None, cache=True)[source]#

A Circleguard for when you do not have access to an api key, but have loaded Loadables that you want to perform operations on. It should go without saying that instances of this class cannot do anything that requires api access.

KeylessCircleguard``s may also load ``ReplayPaths and ReplayStrings, but some attributes of these replays will not be able to be accessed, as they require api access (such as user id or map id).

Parameters:
  • db_path (str or os.PathLike) – The path to the database file to read and write cached replays. If the path does not exist, a fresh database will be created there. If None, no replays will be cached or loaded from cache.

  • slider_dir (str or os.PathLike) – The path to the directory used by slider.library.Library to store beatmaps. If None, a temporary directory will be created for slider.library.Library and subsequently destroyed when this Circleguard object is garbage collected.

  • loader (Loader) – An instance of Loader or a subclass thereof, which will be used instead of creating a loader if passed.

load(loadable)[source]#

Loads the loadable.

Parameters:

loadable (Loadable) – The loadable to load.

Notes

This is identical to calling loadable.load(cg.loader, cg.cache).

load_info(replay_container)[source]#

Loads the info of the replay_container.

Parameters:

replay_container (ReplayContainer) – The replay container to load.

Notes

This is identical to calling replay_container.load_info(cg.loader).

circleguard.circleguard.set_options(*, loglevel=None)[source]#

Set global options for circlecore.

Parameters:

logevel (int) – What level to log at. Circlecore follows standard python logging levels, with an added level of TRACE with a value of 5 (lower than debug, which is 10). The value passed to loglevel is passed directly to the setLevel function of the circleguard root logger. WARNING by default. For more information on log levels, see the standard python logging lib.

Game Version#

class circleguard.game_version.GameVersion(version, concrete)[source]#

Information about the version of osu! a Replay was played on.

Parameters:
  • version (int) – The version of osu! to represent.

  • concrete (bool) – Whether version is the actual version of osu! the replay was played on (in which case concrete should be True), or just an approximation of the version of osu! the replay was played on (in which case concrete should be False).
    If the version is an approximation, you are not restricted to actual released versions of the game (for instance, osu! has no 20200908 version of the game, only 20200831), but may use any day of any month of any year as your version. Circleguard will see that your version is just an estimate (as concrete will be False), and act accordingly.

Notes

osu!’s versioning scheme uses a variation of Calender Versioning (https://calver.org/), which means that a release’s version is the day that release was released. A new version pushed out on 09/08/2020 (MM/DD/YYYY), for instance, would have a version of 20200809 (YYYYMMDD).
We provide from_datetime() as a convenience for when you have a datetime.datetime object representing the day an osu! version was released, and want to create a GameVersion from that.
This class subclasses int so consumers don’t need to know or expect a special class when they access replay.game_version. For instance, to get the numeric value of the game version, they would have to do replay.game_version.version as opposed to replay.game_version here.

static from_datetime(datetime, concrete)[source]#

Provided as a convenience for converting a datetime.datetime object to a GameVersion object.

Parameters:
  • datetime (datetime.datetime) – The datetime to convert to a GameVersion object.

  • concrete (bool) – Whether this version is concrete (ie, fully accurate) or not (ie, just an estimate of the replay’s actual version).

Returns:

The result of converting datetime to a GameVersion.

Return type:

GameVersion

available()[source]#

Whether we can provide any information about the osu! version a replay was played on, whether that is a fully accurate version or just an estimate of the version.

class circleguard.game_version.NoGameVersion[source]#

Used when a Replay has no information about its version, and cannot even estimate its version.

Hit Objects#

class circleguard.hitobjects.Hitobject(time, xy)[source]#

A Hitobject in osu! gameplay, with a time and a position.

classmethod from_slider_hitobj(hitobj, replay, beatmap, already_converted=False)[source]#

Instantiates a circleguard hitobject from a slider.beatmap.HitObject, a circleguard.loadables.Replay, that the hitobject was hit on, and a slider.beatmap.Beatmap that the hitobject is found in.

The already_converted parameter is only to work around https://github.com/llllllllll/slider/issues/80 and will be removed when it is fixed.

class circleguard.hitobjects.Circle(time, xy, radius)[source]#

A circle in osu! gameplay, with a time, position, and radius.

class circleguard.hitobjects.Slider(time, xy, radius)[source]#

A slider in osu! gameplay, with a time, position, and radius.

class circleguard.hitobjects.Spinner(time, xy)[source]#

A spinner in osu! gameplay, with a time and position.

Investigations#

class circleguard.investigations.Snap(time, angle, distance)[source]#

A suspicious hit in a replay, specifically so because it snaps away from the otherwise normal path. Snaps represent the middle frame in a set of three replay frames (so for example time is the time of the middle frame).

Parameters:
  • time (int) – The time value of the middle datapoint, in ms. 0 represents the beginning of the replay.

  • angle (float) – The angle between the three datapoints.

  • distance (float) – min(dist_a_b, dist_b_c) if a, b, and c are three datapoints with b being the middle one.

Judgment#

class circleguard.judgment.JudgmentType(value)[source]#

An enumeration.

class circleguard.judgment.Judgment(hitobject, replay, beatmap, type_)[source]#

A judgment on a hitobject. A “judgment” is either a hit or a miss, with a hit being further classified as a 300, 100, or 50.

Parameters:
class circleguard.judgment.Miss(hitobject, replay, beatmap)[source]#

A miss on a hitobject when a replay is played against a beatmap.

class circleguard.judgment.Hit(hitobject, t, xy, replay, beatmap, type_)[source]#

A hit on a hitobject when a replay is played against a beatmap.

Parameters:
distance(*, to)[source]#

The distance from this hit to either the center or edge of its hitobject.

Parameters:

to ({"center", "edge"}) – If center, the distance from this hit to the center of its hitobject is calculated. If edge, the distance from this hit to the edge of its hitobject is calculated.

Returns:

The distance from this hit to either the center or edge of its hitobject.

Return type:

float

within(distance)[source]#

Whether the hit was within distance of the edge of its hitobject.

Parameters:

distance (float) – How close, in pixels, to the edge of the hitobject the hit has to be.

Returns:

Whether the hit was within distance of the edge of its hitobject.

Return type:

bool

Notes

The lower the value, the closer to the edge the hit occurred. This value can never be greater than the radius of the hitobject.

error()[source]#

How many milliseconds off this hit was from being a perfectly on time hit. If negative, this was an early hit. If positive, this was a late hit. If 0, this was a perfect hit.

Returns:

How many milliseconds off this hit was from being perfectly on time.

Return type:

float

Loadables#

class circleguard.loadables.Loadable(cache)[source]#

Represents one or multiple replays, which have replay data to be loaded from some additional source - the osu! api, local cache, or some other location.

Parameters:

cache (bool) – Whether to cache the replay data once loaded.

abstract load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

class circleguard.loadables.LoadableContainer(loadables, cache=None)[source]#

A Loadable that holds Loadables, which may be ReplayContainers or Replays.

Parameters:
  • loadables (list[Loadable]) – The loadables to hold.

  • cache (bool) – Whether to cache the loadables once they are loaded. This will be overriden by a cache option set by a Loadable in loadables. This only affects child loadables when they do not have a cache option set.

Notes

This class is intended for situations when you have a list of replays and replay containers, but no way to separate or distinguish them. If you want to get, say, all the replays out of that list (whether they come from replay subclasses already in the list, or the replays held by a replay container in the list), this loadable container class has the logic to do that for you:

>>> lc = LoadableContainer(mixed_loadable_list)
>>> replays = lc.all_replays()

It can also be useful to info load the replay containers in the list, without first filtering the list to remove any replay subclasses:

>>> cg.load_info(lc)
>>> # all loadable containers in the list are now info loaded
>>> cg.load(lc)
>>> # all loadables in the list are now loaded

You are very unlikely to want to subclass this class. If you want to add a new loadable that holds replays, subclass ReplayContainer.

all_replays()[source]#

All the Replays in this loadable container.

Returns:

All the replays in this loadable container.

Return type:

list[Replay]

Warning

This list may be incomplete if you do not call load_info() on this loadable container first, as any replay containers held in this container will likely not have references to their replays yet.

load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

class circleguard.loadables.ReplayContainer(cache)[source]#

A Loadable that holds Replay subclasses, and which has an additional state between “unloaded” and “loaded” called “info loaded”.

ReplayContainers start unloaded and become info loaded when load_info() is called. They become fully loaded when load() is called (and if this is called when the ReplayContainer is in the unloaded state, load() will load info first, then load the replays, effectively skipping the info loaded state).

In the unloaded state, the container has no actual Replay objects. It may have limited knowledge about their number or type.

In the info loaded state, the container has references to Replay objects, but those Replay objects are unloaded.

In the loaded state, the Replay objects in the container are loaded.

load(loader, cache=None)[source]#

Loads all Loadables contained by this loadable container.

Parameters:

loader (Loader) – The loader to load the Loadables with.

abstract all_replays()[source]#

Returns all the Replays in this loadable container.

Warning

If you want an accurate list of Replays in this instance, you must call load_info() on this instance before all_replays(). Otherwise, this instance is not info loaded, and does not have a complete list of replays it represents.

class circleguard.loadables.Map(beatmap_id, span, mods=None, cache=None)[source]#

A map’s top plays (leaderboard), as seen on the website.

Parameters:
  • beatmap_id (int) – The map to represent the top plays for.

  • span (str or Span) – A comma separated list of ranges of top plays to retrieve. span="1-3,6,2-4" -> replays in the range [1,2,3,4,6].

  • mods (ModCombination) – If passed, only represent replays played with this exact mod combination. Due to limitations with the api, fuzzy matching is not implemented.
    This is applied before span. That is, if span="1-2" and mods=Mod.HD, the top two HD plays on the map are represented.

  • cache (bool) – Whether to cache the replays once they are loaded.

all_replays()[source]#

Returns all the Replays in this loadable container.

Warning

If you want an accurate list of Replays in this instance, you must call load_info() on this instance before all_replays(). Otherwise, this instance is not info loaded, and does not have a complete list of replays it represents.

class circleguard.loadables.User(user_id, span, mods=None, cache=None, available_only=True)[source]#

A user’s top plays (pp-wise, as seen on the website).

Parameters:
  • user_id (int) – The user to represent the top plays for.

  • span (str or Span) – A comma separated list of ranges of top plays to retrieve. span="1-3,6,2-4" -> replays in the range [1,2,3,4,6].

  • mods (ModCombination) – If passed, only represent replays played with this exact mod combination. Due to limitations with the api, fuzzy matching is not implemented.
    This is applied before span. That is, if span="1-2" and mods=Mod.HD, the user’s top two HD plays are represented.

  • cache (bool) – Whether to cache the replays once they are loaded.

  • available_only (bool) – Whether to represent only replays that have replay data available. Replays are filtered on this basis after mods and span are applied. True by default.

all_replays()[source]#

Returns all the Replays in this loadable container.

Warning

If you want an accurate list of Replays in this instance, you must call load_info() on this instance before all_replays(). Otherwise, this instance is not info loaded, and does not have a complete list of replays it represents.

class circleguard.loadables.MapUser(beatmap_id, user_id, span={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}, cache=None, available_only=True)[source]#

All replays on a map by a user, not just the top replay.

Parameters:
  • beatmap_id (int) – The beatmap to represent scores by user_id on.

  • user_id (int) – The user to represent scores on beatmap_id for.

  • span (str or Span) – A comma separated list of ranges of plays to retrieve. span="1-3,6,2-4" -> replays in the range [1,2,3,4,6].

  • cache (bool) – Whether to cache the replays once they are loaded.

  • available_only (bool) – Whether to represent only replays that have replay data available. Replays are filtered on this basis after span is applied. True by default.

all_replays()[source]#

Returns all the Replays in this loadable container.

Warning

If you want an accurate list of Replays in this instance, you must call load_info() on this instance before all_replays(). Otherwise, this instance is not info loaded, and does not have a complete list of replays it represents.

class circleguard.loadables.ReplayCache(path, num_maps, num_replays)[source]#

Contains replays represented by a circlecore database. Primarily useful to randomly sample these replays, rather than directly access them.

Parameters:
  • path (string) – The path to the database to load replays from.

  • num_maps (int) – How many (randomly chosen) maps to load replays from.

  • limit (int) – How many replays to load for each map.

Notes

load_info() is an expensive operation for large databases created on circlecore version 4.3.5 or earlier, as they do not have the necessary indexes.
For databases created in later versions, this is a nonissue and the lookup is fast.

all_replays()[source]#

Returns all the Replays in this loadable container.

Warning

If you want an accurate list of Replays in this instance, you must call load_info() on this instance before all_replays(). Otherwise, this instance is not info loaded, and does not have a complete list of replays it represents.

class circleguard.loadables.ReplayDir(dir_path, cache=None)[source]#

A folder with replay files inside it.

Notes

Any files not ending in .osr are ignored.

Warning

Nested directories are not support (yet). Any folders encountered will be ignored.

all_replays()[source]#

Returns all the Replays in this loadable container.

Warning

If you want an accurate list of Replays in this instance, you must call load_info() on this instance before all_replays(). Otherwise, this instance is not info loaded, and does not have a complete list of replays it represents.

class circleguard.loadables.Replay(weight, cache)[source]#

A replay played by a player.

Parameters:
  • weight (RatelimitWeight) – How much it ‘costs’ to load this replay from the api.

  • cache (bool) – Whether to cache this replay once it is loaded.

game_version#

Information about the version of osu! the replay was played on.

Type:

GameVersion

timestamp#

When the replay was played.

Type:

datetime.datetime

map_id#

The id of the map the replay was played on, or 0 if unknown or on an unsubmitted map.

Type:

int

user_id#

The id of the player who played the replay, or 0 if unknown (if the player is restricted, for instance). Note that if the user id is known, even if the user is restricted, it should still be given instead of 0.

Type:

int

username#

The username of the player who played the replay.

Type:

str

mods#

The mods the replay was played with.

Type:

ModCombination

replay_id#

The id of the replay, or 0 if the replay is unsubmitted.

Type:

int

keydowns#

The keydowns for each frame of the replay. Keydowns are the keys pressed in that frame that were not pressed in the previous frame. See keydowns() for more details.

Type:

ndarray[int]

t#

A 1d array containing the timestamp for each frame.
This is only nonnull after the replay has been loaded.

Type:

ndarray[int]

xy#

A 2d, two column array, containing the x and y coordinates of each frame in the first and second column respectively.
This is only nonnull after the replay has been loaded.

Type:

ndarray[float]

k#

A 1d array containing the keys pressed for each frame.
This is only nonnull after the replay has been loaded.

Type:

ndarray[int]

has_data()[source]#

Whether this replay has any replay data.

Returns:

Whether this replay has any replay data.

Return type:

bool

Notes

If this replay is unloaded, it is guaranteed to not have any replay data. But if the replay is loaded, it is not guaranteed to have any replay data. Some replays do not have any replay data available from the api, even after being loaded.

beatmap(library)[source]#

The beatmap this replay was played on.

Parameters:

library (slider.library.Library) – The library used by the calling Circleguard instance. Beatmaps which have already been downloaded and are cached in this library may be returned here instead of redownloading them.
Beatmaps which we download or create in this method, but were not previously stored in the library, may also be stored into the library for future use as a result of calling this method.

Returns:

  • slider.beatmap.Beatmap – The beatmap this replay was played on.

  • None – If we do not know what beatmap this replay was played on.

property keydowns#

A list of the keys pressed for each frame that were not pressed in the previous frame.

Examples

If the first frame (f1) has keys K1 and f2 has keys K1 + K2, then keydowns[1] is K2.

class circleguard.loadables.ReplayMap(beatmap_id, user_id, mods=None, cache=None, info=None)[source]#

A Replay that was submitted to online servers.

Parameters:
  • map_id (int) – The id of the map the replay was played on.

  • user_id (int) – The id of the player who played the replay.

  • mods (ModCombination) – The mods the replay was played with. If None, the highest scoring replay of user_id on map_id will be loaded, regardless of mod combination. Otherwise, the replay with mods will be loaded.

  • cache (bool) – Whether to cache this replay once it is loaded.

Notes

The following replay-related attributes are available (not None) when this replay is unloaded:

  • beatmap_id

  • user_id

  • mods (if passed)

In addition to the above, the following replay-related attributes are available (not None) when this replay is loaded:

  • timestamp

  • username

  • mods

  • replay_id

  • count_300

  • count_100

  • count_50

  • count_geki

  • count_katu

  • count_miss

  • score

  • max_combo

  • is_perfect_combo

  • pp

  • replay_data

load(loader, cache)[source]#

Loads the data for this replay from the api.

Parameters:
  • loader (Loader) – The Loader to load this replay with.

  • cache (bool) – Whether to cache this replay after loading it. This only has an effect if self.cache is unset (None).

class circleguard.loadables.ReplayDataOSR(ratelimit_weight, cache=None)[source]#

A Replay which has been saved in the osr format.

Parameters:
  • weight (RatelimitWeight) – How much it ‘costs’ to load this replay from the api.

  • cache (bool) – Whether to cache this replay once it is loaded.

Notes

ReplayDataStrings have no replay-related attributes available (not None) when they are unloaded.

The following replay-related attributes are available (not None) when this replay is loaded:

  • timestamp

  • beatmap_id

  • username

  • user_id

  • mods

  • replay_id

  • beatmap_hash

  • replay_hash

  • count_300

  • count_100

  • count_50

  • count_geki

  • count_katu

  • count_miss

  • score

  • max_combo

  • is_perfect_combo

  • life_bar_graph (currently unparsed)

  • replay_data

beatmap(library)[source]#

The beatmap this replay was played on.

Parameters:

library (slider.library.Library) – The library used by the calling Circleguard instance. Beatmaps which have already been downloaded and are cached in this library may be returned here instead of redownloading them.
Beatmaps which we download or create in this method, but were not previously stored in the library, may also be stored into the library for future use as a result of calling this method.

Returns:

  • slider.beatmap.Beatmap – The beatmap this replay was played on.

  • None – If we do not know what beatmap this replay was played on.

load_from_osrparse_replay(replay, loader, _cache)[source]#

Loads the data for this replay from the already loaded osrparse replay.

Parameters:
  • loader (Loader) – The Loader to load this replay with.
    If None, this replay will be unable to retrieve its map_id or user_id, but everything else will still be loaded.

  • cache (bool) – Whether to cache this replay after loading it. This only has an effect if self.cache is unset (None). Note that currently we do not cache ReplayPath regardless of this parameter.

can_load_api_attributes()[source]#

Whether we can load attributes that are lazy loaded and require api calls, such as map_id or user_id, if requested.

api_attributes_loaded()[source]#

Whether attributes that are lazy loaded and require api calls, such as map_id or user_id, have already been loaded.

class circleguard.loadables.ReplayPath(path, cache=None)[source]#

A Replay saved locally in a .osr file.

Parameters:
  • path (str or os.PathLike) – The path to the replay file.

  • cache (bool) – Whether to cache this replay once it is loaded. Note that currently we do not cache ReplayPath regardless of this parameter.

Notes

ReplayPaths have no replay-related attributes available (not None) when they are unloaded.

The following replay-related attributes are available (not None) when this replay is loaded:

  • timestamp

  • beatmap_id

  • username

  • user_id

  • mods

  • replay_id

  • beatmap_hash

  • replay_hash

  • count_300

  • count_100

  • count_50

  • count_geki

  • count_katu

  • count_miss

  • score

  • max_combo

  • is_perfect_combo

  • life_bar_graph (currently unparsed)

  • replay_data

load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

class circleguard.loadables.ReplayString(replay_data_str, cache=None)[source]#

A Replay saved locally in a .osr file, when the file has already been read as a string.

Parameters:
  • replay_data_str (str) – The contents of the replay file as a string.

  • cache (bool) – Whether to cache this replay once it is loaded. Note that currently we do not cache ReplayString regardless of this parameter.

Notes

ReplayPaths have no replay-related attributes available (not None) when they are unloaded.

The following replay-related attributes are available (not None) when this replay is loaded:

  • timestamp

  • beatmap_id

  • username

  • user_id

  • mods

  • replay_id

  • beatmap_hash

  • replay_hash

  • count_300

  • count_100

  • count_50

  • count_geki

  • count_katu

  • count_miss

  • score

  • max_combo

  • is_perfect_combo

  • life_bar_graph (currently unparsed)

  • replay_data

Examples

>>> replay_data = open("replay.osr", "rb").read()
>>> r = ReplayString(replay_data)
>>> cg.load(r)
>>> print(cg.ur(r))
load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

class circleguard.loadables.ReplayID(replay_id, cache=None)[source]#

A Replay that was submitted online and is represented by a unique replay id.

Parameters:
  • replay_id (int) – The id of the replay.

  • cache (bool) – Whether to cache this replay once it is loaded. Note that we currently do not cache ReplayIDs.

Notes

The following replay-related attributes are available (not None) when this replay is unloaded:

  • replay_id

In addition to the above, the following replay-related attributes are available (not None) when this replay is loaded:

  • replay_data

load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

class circleguard.loadables.CachedReplay(user_id, beatmap_id, mods, replay_data, replay_id)[source]#

This class is intended to be instantiated from load_info() and should not be instantiated manually.

load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

class circleguard.loadables.ReplayOssapi(ossapi_replay)[source]#

Converts a :module:`ossapi` replay to a circlecore Replay. Requires ossapi to be installed (you can’t get an ossapi replay without having ossapi installed anyway).

load(loader, cache)[source]#

Loads the information this loadable needs to become fully loaded. Details left to the subclass implementation.

Parameters:
  • loader (Loader) – The loader to load this loadable with. Although subclasses may not end up using a Loader to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note: loader may be None. This means that whatever is loading the loadable does not have api access and cannot provide a loader. If your loadable requires a loader to properly load itself, raise an error on a null loader. If your loadable can load itself without a loader, proceed as planned and ignore the null loader.

  • cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a ReplayContainer or Circleguard itself. Should the loadable already have a set cache value, that should take precedence over the option passed in this method, but if the loadable has no preference then it should respect the value passed here.

Loader#

exception circleguard.loader.NoInfoAvailableException[source]#
circleguard.loader.check_cache(function)[source]#

A decorator that checks if the passed ReplayInfo has its replay cached. If so, returns a Replay instance from the cached data. Otherwise, calls and returns the function as normal.

Parameters:

function (callable) – The function to wrap.

Notes

self and replay_info MUST be the first and second arguments to the function, respectively.

Returns:

A Replay instance from the cached data if it was cached, or the return value of the function if not.

Return type:

Replay or Unknown

class circleguard.loader.Loader(key, cache_path=None, write_to_cache=True)[source]#

Manages interactions with the osu api, using the ossapi wrapper.

Parameters:
  • key (str) – A valid api key. Can be retrieved from https://osu.ppy.sh/p/api/.

  • cache_path (str) – The path to the database to use for caching. A new database will be created at this location if one doesn’t exist already.
    If None, no cache will be used or created.

Notes

If the api ratelimits the key, we wait until our ratelimits are refreshed and retry the request. Because the api does not provide the time until the next refresh (and we do not use exponential backoff or another retry strategy), if the key is ratelimited because of an interaction not managed by this class, the class may wait more time than necessary for the key to refresh.

replay_info(beatmap_id, span=None, user_id=None, mods=None, limit=True)[source]#

Retrieves replay infos from a map’s leaderboard.

Parameters:
  • beatmap_id (int) – The map id to retrieve replay info for.

  • span (Span) – A comma separated list of ranges of top replays on the map to retrieve. span="1-3,6,2-4" -> replays in the range [1,2,3,4,6].

  • user_id (int) – If passed, only retrieve replay info on map_id for this user. Note that this is not necessarily limited to just the user’s top score on the map. See limit.

  • mods (ModCombination) – If passed, will only retrieve replay infos for scores that were played with the given mods.

  • limit (bool) – Whether to limit to only one response. Only has an effect if user_id is passed. If limit is True, will only return the top scoring replay info by user_id. If False, will return all scores by user_id.

Returns:

  • list[ReplayInfo] – The replay infos representing the map’s leaderboard.

  • ReplayInfo – If limit is True and user_id is passed.

Notes

One of user_id or span must be passed.

Raises:

NoInfoAvailableException – If there is no info available for the given parameters.

get_user_best(user_id, span, mods=None)[source]#

Retrieves replay infos from a user’s top plays.

Parameters:
  • user_id (int) – The user id to get best plays of.

  • span (Span) – A comma separated list of ranges of top plays to retrieve. span="1-3,6,2-4" -> replays in the range [1,2,3,4,6].

  • mods (ModCombination) – If passed, will only retrieve replay infos for scores that were played with the given mods.

Returns:

The replay infos representing the user’s top plays.

Return type:

list[ReplayInfo]

load_replay_data(beatmap_id, user_id, mods=None)[source]#

Retrieves replay data from the api.

Parameters:
  • beatmap_id (int) – The map the replay was played on.

  • user_id (int) – The user that played the replay.

  • mods (ModCombination) – The mods the replay was played with, or None for the highest scoring replay, regardless of mods.

Returns:

  • str – The lzma-encoded string, decoded from the base 64 api response, representing the replay.

  • None – If no replay data was available.

Notes

This is the low level implementation of replay_data(), handling the actual api request.

replay_data_from_id(replay_id, _cache)[source]#

Retrieves replay data from the api, given a replay id.

Parameters:

replay_id (int) – The id of the replay to retrieve data for.

beatmap_id(beatmap_hash)[source]#

Retrieves a beatmap id from a corresponding beatmap hash through the api.

Parameters:

map_hash (str) – The md5 hash of the map to get the id of.

Returns:

The map id that corresponds to map_hash, or 0 if map_hash doesn’t mach any map.

Return type:

int

Notes

This function is wrapped in a functools.lru_cache() to prevent duplicate api calls.

map_id(beatmap_hash)#

Retrieves a beatmap id from a corresponding beatmap hash through the api.

Parameters:

map_hash (str) – The md5 hash of the map to get the id of.

Returns:

The map id that corresponds to map_hash, or 0 if map_hash doesn’t mach any map.

Return type:

int

Notes

This function is wrapped in a functools.lru_cache() to prevent duplicate api calls.

user_id(username)[source]#

Retrieves a user id from a corresponding username through the api.

Parameters:

username (str) – The username of the user to get the user id of.

Returns:

The user id that corresponds to username, or 0 if username doesn’t match any user.

Return type:

int

Notes

The api redirects name changes to the current username. For instance, user_id("cookiezi") will return 124493, despite shige’s current osu! username being chocomint. However, I am not sure if this behavior is as well defined when someone else takes the previous name of a user.

This function is case insensitive.

This function is wrapped in a functools.lru_cache() to prevent duplicate api calls.

username(user_id)[source]#

Retrieves the username from a corresponding user id through the api.

Parameters:

user_id (int) – The user id of the user to get the username of.

Returns:

The username that corresponds to user_id, or an empty string if user_id doesn’t match any user.

Return type:

str

Notes

This function is the inverse of user_id().

This function is wrapped in a functools.lru_cache() to prevent duplicate api calls.

Map Info#

class circleguard.map_info.MapInfo(*, map_id=None, path=None)[source]#

Represents the information necessary to load a beatmap.

Notes

If multiple ways to load a beatmap are known, all ways should be provided so consumers can choose the order of ways to load the beatmap.

If a way to load a beatmap is not available, it should be left as None.

available()[source]#

Whether this beatmap can be loaded with the information we have or not.

Mod#

Span#

class circleguard.span.Span(data)[source]#

A set of numbers represented by a string, which can include ranges or single numbers, separated by a comma.

Notes

Spans can only range from 1 to 100 inclusive.

Examples

>>> Span("1-3,6,2-4")
{1, 2, 3, 4, 6}

Utils#

class circleguard.utils.RatelimitWeight(value)[source]#

How much it ‘costs’ to load a replay from the api.

NONE if the load method of a replay makes no api calls.

LIGHT if the load method of a replay makes only light api calls (anything but get_replay).

HEAVY if the load method of a replay makes any heavy api calls (get_replay).

Notes

This value currently has no effect in circlecore and is reserved for future functionality.

class circleguard.utils.Key(value)[source]#

An enumeration.

circleguard.utils.convert_statistic(stat, mods, *, to)[source]#

Converts a game statistic to either its unconverted or converted form, depending on to.

Parameters:
  • stat (float) – The statistic to convert.

  • mods (Mod) – The mods the replay was played with. Only Mod.DT and Mod.HT will affect the statistic conversion.

  • to ({"cv", "ucv"}) – cv if the statistic should be converted to its converted form, and ucv if the statistic should be converted to its unconverted form.

Notes

This method is intended for any statistic that is modified from what we expect by Mod.DT or Mod.HT being applied (ie changing the game clock speed). This includes ur (unstable rate) and median frametime (time between frames).

circleguard.utils.order(replay1, replay2)[source]#

An ordered tuple of the given replays. The first element is the earlier replay, and the second element is the later replay.

Parameters:
  • replay1 (Replay) – The first replay to order.

  • replay2 (Replay) – The second replay to order.

Returns:

The first element is the earlier replay, and the second element is the later replay.

Return type:

(Replay, Replay)

circleguard.utils.replay_pairs(replays, replays2=None)[source]#

A list of pairs of replays which can be compared against each other to cover all cases of replay stealing in replays and/or replays2.

If replays2 is not passed (the default), this is a list of 2-tuples which are pairs of replays in replays, where each replay will be paired with every other replay exactly once.

If replays2 is passed, this is a list of 2-tuples which are pairs of replays in where one replay is from replays, the other is from replays2, and every replay in replays is paired against every replay in replays2 (but not against other replays in replays).

Returns:

The first element is the earlier replay, and the second element is the later replay.

Return type:

Iterable[(Replay, Replay)]

Notes

This is equivalent to itertools.combinations(replays, 2) if replays2 is None or the empty list, and itertools.product(replays, replays2) otherwise.

circleguard.utils.fuzzy_mods(required_mod, optional_mods)[source]#

All mod combinations where each mod in optional_mods is allowed to be present or absent.

If you don’t want any mods to be required, pass Mod.NM as your required_mod.

Parameters:
  • required_mod (class:~circleguard.mod.ModCombination) – What mod to require be present for all mods.

  • [class (optional_mods =) – What mods are allowed, but not required, to be present.

Examples

>>> fuzzy_mods(Mod.HD, [Mod.DT])
[HD, HDDT]
>>> fuzzy_mods(Mod.HD, [Mod.EZ, Mod.DT])
[HD, HDDT, HDEZ, HDDTEZ]
>>> fuzzy_mods(Mod.NM, [Mod.EZ, Mod.DT])
[NM, DT, EZ, DTEZ]
circleguard.utils.powerset(iterable)[source]#

The powerset of an iterable.

Examples

>>> powerset([1,2,3])
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

Notes

https://stackoverflow.com/a/1482316

circleguard.utils.hitwindow(OD)[source]#

The number of milliseconds before and after a hitobject’s time where that hitobject can be hit.

circleguard.utils.hitradius(CS)[source]#

The radius, in osu!pixels (?) of where a hitobject can be hit.

circleguard.utils.filter_outliers(arr, bias=1.5)[source]#

Returns ``arr` with outliers removed.

Parameters:
  • arr (list) – List of numbers to filter outliers from.

  • bias (int) – Points in arr which are more than IQR * bias away from the first or third quartile of arr will be removed.

class circleguard.utils.ColoredFormatter(patern)[source]#

A subclass of logging.Formatter that uses ANSI escape codes to color different parts of the logging.LogRecord when printed to the console.

Notes

Adapted from https://stackoverflow.com/a/46482050.

format(record)[source]#

Format the specified record as text.

The record’s attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.