Using Circleguard#

Now that we know how to represent replays, we can start operating on them. We will use the Circleguard class to do so. Circleguard is the main entry point for this library, alongside the replay classes.

To instantiate Circleguard, you will need an api key. If you don’t already have one, visit https://osu.ppy.sh/p/api/ and enter Circleguard for App Name and https://github.com/circleguard/circlecore for App URL. Circleguard needs this key to retrieve replay data and user information, among other things.

Note

Due to a redirection bug on the website, you may need to log in and wait 30 seconds before being able to access the api page through the above link.

After you have your api key, instantiate circleguard as follows:

cg = Circleguard("your-api-key)

Whenever you see cg in any codeblocks in this documentation, it refers to this circleguard instance. We will omit this declaration from examples going forward to avoid duplicating it in every codeblock.

Similarity#

cg.similarity() returns the similarity between two replays. Roughly speaking, this how far apart in pixels the cursors in the replays are on average.

r1 = ReplayMap(221777, 2757689)
r2 = ReplayMap(221777, 3219026)
print(cg.similarity(r1, r2)) # 19.310565461539074

Unstable Rate#

cg.ur() returns the (converted) unstable rate of the replay.

r = ReplayMap(1136506, 846038)
print(cg.ur(r)) # 75.45

You can also get the unconverted unstable rate:

print(cg.ur(r, cv=False)) # 113.18

Snaps#

cg.snaps() returns any unusual, jerky, snappy cursor movement in a replay.

r = ReplayMap(1136506, 6451401)
print(cg.snaps(r)) # [<circleguard.investigator.Snap>]

You can adjust what counts as “snap” with the max_angle and min_distance arguments. See the cg.snaps() documentation for details.

print(cg.snaps(r, max_angle=12, min_distance=6))
# [<circleguard.investigator.Snap>, <circleguard.investigator.Snap>]

This returns a list of Snap objects. See the Snap documentation for details.

Frametime#

cg.frametime() returns the average (converted) frametime of the replay. Frametime is defined as the time, in ms, between each frame and the frame after it. Typical frametime is 16.66. A low frametime is indicative of timewarp (though not necessarily proof of timewarp; see https://github.com/circleguard/circleguard/wiki/Frametime-Tutorial).

r = ReplayMap(1136506, 846038)
print(cg.frametime(r)) # 15.33

You can also get the unconverted frametime:

print(cg.frametime(r, cv=False)) # 23.0

Frametimes#

cg.frametimes() returns the list of (converted) frametimes in the replay. This is useful for performing more advanced analysis on a replay’s frametime, beyond just its average frametime.

r = ReplayMap(1136506, 846038)
print(cg.frametimes(r)) # [16. 8.67 ... 16.67 16.67]

You can also get the unconverted frametime:

print(cg.frametimes(r, cv=False)) # [24 13 ... 25 25]

Judgments#

The locations in a replay where a hitobject is hit or missed. Judgments are marked as either misses, 50s, 100s, or 300s. See cg.judgments().

r = ReplayMap(221777, 2757689)
print(cg.judgments(r)) # a list with lots of elements

This returns a list of Judgment objects. See its documentation for details.

Hits#

The locations in a replay where a hitobject is hit. This is equivalent to calling cg.judgments() and filtering out misses. See cg.hits().

r = ReplayMap(221777, 2757689)
print(cg.hits(r)) # a list with lots of elements

You can also get only the hits which are within a certain number of pixels to the edge of the hitobject:

print(cg.hits(r, within=10)) # a list with fewer elements

This returns a list of Hit objects. See the Hit documentation for details.

Other Replay Classes#

The examples above have been using ReplayMap as their example replay, but you can pass any Replay class to any Circleguard method:

r1 = ReplayPath("/path/to/your/replay.osr")
print(cg.ur(r1))

r2 = ReplayMap(1754777, 2766034)
print(cg.frametime(r2))

r3 = ReplayID(2177560145)
print(cg.snaps(r3))

replay_data = open("/path/to/your/replay.osr", "rb").read()
r4 = ReplayString(replay_data)
print(cg.hits(r4))

# or any combination of the above