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
Replay
s: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
Loadable
s:load_info()
- loads the info of aReplayContainer
.ReplayDir()
- creates a newReplayDir
and loads its info.
- 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. IfNone
, no replays will be cached or loaded from cache.slider_dir (str or
os.PathLike
) – The path to the directory used byslider.library.Library
to store beatmaps. IfNone
, a temporary directory will be created forslider.library.Library
and subsequently destroyed when thisCircleguard
object is garbage collected.loader (
Loader
) – An instance ofLoader
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
andreplay2
.- Parameters:
replay1 (
Replay
) – The replay to compare againstreplay2
.replay2 (
Replay
) – The replay to compare againstreplay1
.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. SeeSIM_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 of1
. The higher correlation, the more correlated (or similar) the two replays are. SeeCORR_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
iscorrelation
. Note that runtime increases linearly with the number of chunks.mods_unknown ({"best", "both"}) – What to do if one or both of
replay1
andreplay2
do not know what mods they were played with. In this case, the similarity will be computed twice, both with no modifications and withMod.HR
applied toreplay1
.
Ifbest
is passed, the best (that is, lowest ifmethod
issimilarity
and highest ifmethod
iscorrelation
) similarity of these two calculations is returned.
Ifboth
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 withMod.HR
applied toreplay1
.
- Returns:
float – If
method
issimilarity
, this is the similarity of the two replays. Ifmethod
iscorrelation
, this is the correlation between the two replays.(float, float) – If
mods_unknown
isboth
, a tuple with two floats is returned. The first element is the similarity with no modifications, and the second element is the similarity withMod.HR
applied toreplay1
. See the documentation for themods_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 thereplay
, 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:
- 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 thereplay
, instead of retrieving a beatmap from the replay itself. This is only used whenonly_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:
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, andcv
isTrue
.
Ifraise
, a ValueError will be raised.
Ifdt
, the frametime will be converted as if the replay was played withMod.DT
.
Ifnm
, the frametime will be converted as if the replay was played withMod.NM
(that is, not converted at all).
Ifht
, the frametime will be converted as if the replay was played withMod.HT
.
- Returns:
The median frametime (in ms) of the replay.
- Return type:
- 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, andcv
isTrue
.
Ifraise
, a ValueError will be raised.
Ifdt
, the frametime swill be converted as if the replay was played withMod.DT
.
Ifnm
, the frametimes will be converted as if the replay was played withMod.NM
(that is, not converted at all).
Ifht
, the frametimes will be converted as if the replay was played withMod.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 thereplay
, 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 thereplay
, 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.
Iffigure
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 usingCircleguard
is to want to instantiate aMap
and immediately iterate over it to operate on its replays. However, thisMap
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 usingCircleguard
is to want to instantiate aUser
and immediately iterate over it to operate on its replays. However, thisUser
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 usingCircleguard
is to want to instantiate aMapUser
and immediately iterate over it to operate on its replays. However, thisMapUser
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 usingCircleguard
is to want to instantiate aReplayDir
and immediately iterate over it to operate on its replays. However, thisReplayDir
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 loadedLoadable
s 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 ``ReplayPath
s andReplayString
s, 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 byslider.library.Library
to store beatmaps. If None, a temporary directory will be created forslider.library.Library
and subsequently destroyed when thisCircleguard
object is garbage collected.loader (
Loader
) – An instance ofLoader
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 caseconcrete
should beTrue
), or just an approximation of the version of osu! the replay was played on (in which caseconcrete
should beFalse
).
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 (asconcrete
will beFalse
), 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 providefrom_datetime()
as a convenience for when you have adatetime.datetime
object representing the day an osu! version was released, and want to create aGameVersion
from that.
This class subclassesint
so consumers don’t need to know or expect a special class when they accessreplay.game_version
. For instance, to get the numeric value of the game version, they would have to doreplay.game_version.version
as opposed toreplay.game_version
here.- static from_datetime(datetime, concrete)[source]#
Provided as a convenience for converting a
datetime.datetime
object to aGameVersion
object.- Parameters:
datetime (
datetime.datetime
) – The datetime to convert to aGameVersion
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 aGameVersion
.- Return type:
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
, acircleguard.loadables.Replay
, that the hitobject was hit on, and aslider.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.
Investigations#
Judgment#
- 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:
hitobject (
slider.beatmap.HitObject
) – The hitobject being judged. This is converted to acircleguard.hitobjects.Hitobject
.replay (
circleguard.loadables.Replay
) – The replay this judgment was made on.beatmap (
slider.beatmap.Beatmap
) – The beatmap this judgment was made on.type (
JudgmentType
) – The type of this judgment (either Hit300, Hit100, or Hit50, or Miss).
- 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:
hitobject (
slider.beatmap.HitObject
) – The hitobject that was hit. This is converted to acircleguard.hitobjects.Hitobject
.t (float) – The time the hit occured.
xy (list[float, float]) – The x and y position where the hit occured.
replay (
circleguard.loadables.Replay
) – The replay this hit was made on.beatmap (
slider.beatmap.Beatmap
) – The beatmap this hit was made on.type (
JudgmentType
) – The type of this hit (either Hit300, Hit100, or Hit50).
- 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. Ifedge
, 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:
- 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:
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.
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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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
ReplayContainer
s orReplay
s.- Parameters:
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
Replay
s 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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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 whenload()
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.
- abstract all_replays()[source]#
Returns all the
Replay
s in this loadable container.Warning
If you want an accurate list of
Replay
s in this instance, you must callload_info()
on this instance beforeall_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 beforespan
. That is, ifspan="1-2"
andmods=Mod.HD
, the top twoHD
plays on the map are represented.cache (bool) – Whether to cache the replays once they are loaded.
- all_replays()[source]#
Returns all the
Replay
s in this loadable container.Warning
If you want an accurate list of
Replay
s in this instance, you must callload_info()
on this instance beforeall_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 beforespan
. That is, ifspan="1-2"
andmods=Mod.HD
, the user’s top twoHD
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
andspan
are applied. True by default.
- all_replays()[source]#
Returns all the
Replay
s in this loadable container.Warning
If you want an accurate list of
Replay
s in this instance, you must callload_info()
on this instance beforeall_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
Replay
s in this loadable container.Warning
If you want an accurate list of
Replay
s in this instance, you must callload_info()
on this instance beforeall_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:
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
Replay
s in this loadable container.Warning
If you want an accurate list of
Replay
s in this instance, you must callload_info()
on this instance beforeall_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
Replay
s in this loadable container.Warning
If you want an accurate list of
Replay
s in this instance, you must callload_info()
on this instance beforeall_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:
- timestamp#
When the replay was played.
- Type:
- map_id#
The id of the map the replay was played on, or 0 if unknown or on an unsubmitted map.
- Type:
- 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:
- mods#
The mods the replay was played with.
- Type:
ModCombination
- 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
andy
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:
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 callingCircleguard
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 keysK1
andf2
has keysK1 + K2
, thenkeydowns[1]
isK2
.
- 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. IfNone
, the highest scoring replay ofuser_id
onmap_id
will be loaded, regardless of mod combination. Otherwise, the replay withmods
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
- 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 callingCircleguard
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
) – TheLoader
to load this replay with.
IfNone
, this replay will be unable to retrieve itsmap_id
oruser_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 cacheReplayPath
regardless of this parameter.
- 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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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:
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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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 aLoader
to load themselves (if they don’t load anything from the osu api, for instance), a loader is still passed regardless. Note:loader
may beNone
. 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 nullloader
. If your loadable can load itself without aloader
, proceed as planned and ignore the nullloader
.cache (bool) – Whether to cache the replay data once loaded. This argument comes from a parent—either a
ReplayContainer
orCircleguard
itself. Should the loadable already have a setcache
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#
- circleguard.loader.check_cache(function)[source]#
A decorator that checks if the passed
ReplayInfo
has its replay cached. If so, returns aReplay
instance from the cached data. Otherwise, calls and returns the function as normal.- Parameters:
function (callable) – The function to wrap.
Notes
self
andreplay_info
MUST be the first and second arguments to the function, respectively.
- 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.
IfNone
, 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. Seelimit
.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. Iflimit
isTrue
, will only return the top scoring replay info byuser_id
. IfFalse
, will return all scores byuser_id
.
- Returns:
list[
ReplayInfo
] – The replay infos representing the map’s leaderboard.ReplayInfo
– Iflimit
isTrue
anduser_id
is passed.
Notes
One of
user_id
orspan
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:
- 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:
- 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
, or0
ifmap_hash
doesn’t mach any map.- Return type:
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
, or0
ifmap_hash
doesn’t mach any map.- Return type:
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
, or0
ifusername
doesn’t match any user.- Return type:
Notes
The api redirects name changes to the current username. For instance,
user_id("cookiezi")
will return124493
, despite shige’s current osu! username beingchocomint
. 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 ifuser_id
doesn’t match any user.- Return type:
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
.
Mod#
Span#
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 butget_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.
- 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
andMod.HT
will affect the statistic conversion.to ({"cv", "ucv"}) –
cv
if the statistic should be converted to its converted form, anducv
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
orMod.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.
- 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/orreplays2
.If
replays2
is not passed (the default), this is a list of 2-tuples which are pairs of replays inreplays
, 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 fromreplays
, the other is fromreplays2
, and every replay inreplays
is paired against every replay inreplays2
(but not against other replays inreplays
).- Returns:
The first element is the earlier replay, and the second element is the later replay.
- Return type:
Notes
This is equivalent to
itertools.combinations(replays, 2)
ifreplays2
isNone
or the empty list, anditertools.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 yourrequired_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
- 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.
- class circleguard.utils.ColoredFormatter(patern)[source]#
A subclass of
logging.Formatter
that uses ANSI escape codes to color different parts of thelogging.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.