All Projects → maxim → cx_leaderboard

maxim / cx_leaderboard

Licence: Apache-2.0 license
Elixir library for fast, customizable leaderboards

Programming Languages

elixir
2628 projects

Projects that are alternatives of or similar to cx leaderboard

Beacon
A simple UPnP Assistant that is capable of opening arbitrary ports.
Stars: ✭ 34 (+88.89%)
Mutual labels:  games
SwitchGamesDownloader
Python script designed to search and fetch direct download links for games roms
Stars: ✭ 88 (+388.89%)
Mutual labels:  games
mankind
CDGD01 - Community Driven Game Development
Stars: ✭ 18 (+0%)
Mutual labels:  games
CelesteSpeedrunTool
A Celelste Mod for Speedrun Practice
Stars: ✭ 21 (+16.67%)
Mutual labels:  games
AMP-dockerized
CubeCoders AMP in a Docker Image. Easily create game servers for games like Minecraft, GMod, TF2, Factorio, and StarBound!
Stars: ✭ 54 (+200%)
Mutual labels:  games
Fractal Engine
WIP 3D game engine with editor and other stuff
Stars: ✭ 152 (+744.44%)
Mutual labels:  games
rsc-client
🎮 runescape classic web client
Stars: ✭ 45 (+150%)
Mutual labels:  games
HexBot
A Relatively Simply Awesome Discord bot with Music, Games, Comics, Memes and other cool features. This bot is made in Python 3.8 using discord.py
Stars: ✭ 109 (+505.56%)
Mutual labels:  games
boxgame
A sample project for following along a tutorial found on jap.alekhin.io.
Stars: ✭ 32 (+77.78%)
Mutual labels:  games
humble-bundle-key-redeemer
Bulk redemption/activation of Humble Bundle Steam keys.
Stars: ✭ 21 (+16.67%)
Mutual labels:  games
RiptideSampleFPS
Sample FPS project using RiptideNetworking, and source code for the Riptide tutorials.
Stars: ✭ 80 (+344.44%)
Mutual labels:  games
Game-Assets-And-Resources
Free and paid game assets and game resources for 2D games, 3D games, mobile games, Steam games, Unity games, and other games.
Stars: ✭ 164 (+811.11%)
Mutual labels:  games
fight-game
战斗小游戏-Java实现-设计模式
Stars: ✭ 26 (+44.44%)
Mutual labels:  games
gamesearch
A Simple Search Engine to help you find FREE Download Links to your Favourite Games
Stars: ✭ 30 (+66.67%)
Mutual labels:  games
sdk-for-unity
SpatialOS SDK for Unity
Stars: ✭ 49 (+172.22%)
Mutual labels:  games
UnityTutorials-RTS
The code for my series of tutorials on how to make a real-time stategy (RTS) game in the well-know Unity game engine (with C# scripting)!
Stars: ✭ 256 (+1322.22%)
Mutual labels:  games
malib
A parallel framework for population-based multi-agent reinforcement learning.
Stars: ✭ 341 (+1794.44%)
Mutual labels:  games
BoilR
Synchronize games from other platforms into your Steam library
Stars: ✭ 664 (+3588.89%)
Mutual labels:  games
Shadowcol
A python control interface to play games and control your PC using voice commands
Stars: ✭ 16 (-11.11%)
Mutual labels:  games
PySimpleGUI
Launched in 2018. It's 2022 and PySimpleGUI is actively developed & supported. Create complex windows simply. Supports tkinter, Qt, WxPython, Remi (in browser). Create GUI applications trivially with a full set of widgets. Multi-Window applications are also simple. 3.4 to 3.11 supported. 325+ Demo programs & Cookbook for rapid start. Extensive d…
Stars: ✭ 10,846 (+60155.56%)
Mutual labels:  games

CxLeaderboard

Travis Hex.pm

A featureful, fast leaderboard based on ets store. Can carry payloads, calculate custom stats, provide nearby entries around any entry, and do many other fun things.

alias CxLeaderboard.Leaderboard

board =
  Leaderboard.create!(name: :global_lb)
  |> Leaderboard.populate!([
    {{-23, :id1}, :user1},
    {{-65, :id2}, :user2},
    {{-24, :id3}, :user3},
    {{-23, :id4}, :user4},
    {{-34, :id5}, :user5}
  ])

records =
  board
  |> Leaderboard.top()
  |> Enum.to_list()

# Returned records (explained):
#   {{score, id}, payload, {index, {rank, percentile}}}
# [ {{-65, :id2}, :user2,  {0,     {1,    99.0}}},
#   {{-65, :id3}, :user3,  {1,     {1,    99.0}}},
#   {{-34, :id5}, :user5,  {2,     {3,    59.8}}},
#   {{-23, :id1}, :user1,  {3,     {4,    40.2}}},
#   {{-23, :id4}, :user4,  {4,     {4,    40.2}}} ]

Features

  • Ranks, percentiles, any custom stats of your choice
  • Concurrent reads, sequential writes
  • Stream API access to records from the top and the bottom
  • O(1) querying of any record by id
  • Auto-populating data on leaderboard startup
  • Adding, updating, removing, upserting of individual entries in live leaderboard
  • Fetching a range of records around a given id (contextual leaderboard)
  • Pluggable data stores: EtsStore for big boards, TermStore for dynamic mini boards
  • Atomic full repopulation in O(2n log n) time
  • Multi-node support
  • Extensibility for storage engines (CxLeaderboard.Storage behaviour)

Installation

The package can be installed by adding cx_leaderboard to your list of dependencies in mix.exs:

def deps do
  [
    {:cx_leaderboard, "~> 0.1.0"}
  ]
end

Documentation

https://hexdocs.pm/cx_leaderboard/CxLeaderboard.Leaderboard.html

Global Leaderboards

If you want to have a global leaderboard starting at the same time as your application, and running alongside it, all you need to do is declare a as follows:

defmodule Foo.Application do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec

    children = [
      # This is where you provide a data enumerable (e.g. a stream of paginated 
      # Postgres results) for leaderboard to auto-populate itself on startup.
      # It's best if this is implemented as a Stream to avoid consuming more
      # RAM than necessary.
      worker(CxLeaderboard.Leaderboard, [:global, [data: Foo.MyData.load()]])
    ]

    opts = [strategy: :one_for_one, name: Foo.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Then you can interact with it anywhere in your app like this:

alias CxLeaderboard.Leaderboard

global_lb = Leaderboard.client_for(:global)
global_lb
|> Leaderboard.top()
|> Enum.take(10)

Fetching ranges

If you want to get a record and its context (nearby records), you can use a range.

Leaderboard.get(board, :id3, -1..1)
# [
#   {{-34, :id5}, :user5, {1, {2, 79.4}}},
#   {{-24, :id3}, :user3, {2, {3, 59.8}}},
#   {{-23, :id1}, :user1, {3, {4, 40.2}}}
# ]

Different ranking flavors

To use different ranking you can just create your own indexer. Here's an example of the above leaderboard only in this case we want sequential ranks.

alias CxLeaderboard.{Leaderboard, Indexer}

my_indexer = Indexer.new(on_rank:
  &Indexer.Stats.sequential_rank_1_99_less_or_equal_percentile/1)

board =
  Leaderboard.create!(name: :global_lb, indexer: my_indexer)
  |> Leaderboard.populate!([
    {{-23, :id1}, :user1},
    {{-65, :id2}, :user2},
    {{-65, :id3}, :user3},
    {{-23, :id4}, :user4},
    {{-34, :id5}, :user5}
  ])

records =
  board
  |> Leaderboard.top()
  |> Enum.to_list()

# Returned records (explained):
# [ {{-65, :id2}, :user2, {0, {1, 99.0}}},
#   {{-65, :id3}, :user3, {1, {1, 99.0}}},
#   {{-34, :id5}, :user5, {2, {2, 59.8}}},
#   {{-23, :id1}, :user1, {3, {3, 40.2}}},
#   {{-23, :id4}, :user4, {4, {3, 40.2}}} ]

Notice how the resulting ranks are not offset like 1,1,3,4,4 but are sequential like 1,1,2,3,3.

See docs for CxLeaderboard.Indexer.Stats for various pre-packaged functions you can plug into the indexer, or write your own.

Mini-leaderboards

Sometimes all you need is to render a quick one-off leaderboard with just a few entries in it. For this you don't have to run a persistent ets, instead you can use TermStore.

miniboard =
  Leaderboard.create!(store: CxLeaderboard.TermStore)
  |> Leaderboard.populate!(
    [
      {23, 1},
      {65, 2},
      {24, 3},
      {23, 4},
      {34, 5}
    ]
  )

miniboard
|> Leaderboard.top()
|> Enum.take(3)
# [
#   {{23, 1}, 1, {0, {1, 99.0}}},
#   {{23, 4}, 4, {1, {1, 99.0}}},
#   {{24, 3}, 3, {2, {3, 59.8}}}
# ]

This would produce a complete full-featured leaderboard that's entirely stored in the miniboard variable. All the same API functions work on it.

Note: It is not recommended to use TermStore for big leaderboards (as evident from the benchmarks below). A typical use case for it would be to dynamically render a single-page leaderboard among a small group of users.

Benchmark

These benchmarks use 1 million randomly generated records, however, the same set of records is used for both ets and term leaderboard within each benchmark.

Operating System: macOS
CPU Information: Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz
Number of Available Cores: 8
Available memory: 16 GB
Elixir 1.6.2
Erlang 20.2.4
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
parallel: 1

Populating the leaderboard with 1mil entries

Script: benchmark/populate.exs

Name           ips        average  deviation         median         99th %
ets           0.21         4.76 s     ±0.95%         4.76 s         4.81 s
term         0.169         5.91 s     ±0.00%         5.91 s         5.91 s

Comparison:
ets           0.21
term         0.169 - 1.24x slower

Summary:

  • It takes ~4.76s to populate ets leaderboard with 1 million random scores.
  • It takes ~5.91s to populate term leaderboard with 1 million random scores (but you shouldn't).

The leaderboard is fully sorted and indexed at the end.

Adding an entry to 1mil leaderboard

Script: benchmark/add_entry.exs

Name           ips        average  deviation         median         99th %
ets       148.95 K      0.00001 s    ±88.34%      0.00001 s      0.00002 s
term     0.00034 K         2.92 s     ±0.56%         2.92 s         2.94 s

Comparison:
ets       148.95 K
term     0.00034 K - 435227.97x slower

As you can see, you should not create a TermStore leaderboard with a million entries.

Getting a -10..10 range from 1mil leaderboard

Script: benchmark/range.exs

Name           ips        average  deviation         median         99th %
ets        17.84 K      0.0560 ms    ±20.66%      0.0530 ms       0.101 ms
term     0.00290 K      345.13 ms     ±3.83%      345.04 ms      374.28 ms

Comparison:
ets        17.84 K
term     0.00290 K - 6158.09x slower

Another example of how the TermStore is not intended for big number of entries.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].