forked from deadvey/button
perf: improve top-20 leaderboard algorithm
This commit is contained in:
+34
-10
@@ -48,12 +48,14 @@ enum LeaderboardUpdateType {
|
||||
Increment { loscore: u32 },
|
||||
}
|
||||
|
||||
static CHANCE: f64 = 1.0 / 3.0;
|
||||
const CHANCE: f64 = 1.0 / 3.0;
|
||||
|
||||
const PATH_HISCORES: &str = "hiscores.json";
|
||||
const PATH_LOSCORES: &str = "loscores.json";
|
||||
const PATH_PINGSCORES: &str = "pingscores.json";
|
||||
|
||||
const MAX_LEADERBOARD: usize = 20;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
fn read_file<T: for<'de> de::Deserialize<'de>>(file_path: &str) -> Arc<Mutex<T>> {
|
||||
@@ -61,8 +63,17 @@ async fn main() {
|
||||
Arc::new(Mutex::new(serde_json::from_str(&file_contents).unwrap()))
|
||||
}
|
||||
|
||||
/// Makes the vector at `vec` one with a capacity of exactly [`MAX_LEADERBOARD`] if `vec` is
|
||||
/// smaller or equal.
|
||||
fn exact_leaderboard<T>(mut vec: impl DerefMut<Target = Vec<T>>) {
|
||||
let mut old_vec = std::mem::replace(&mut *vec, Vec::with_capacity(MAX_LEADERBOARD));
|
||||
old_vec.drain(..).for_each(|e| vec.push(e));
|
||||
}
|
||||
|
||||
let hiscores: Arc<Mutex<Vec<Entry>>> = read_file(PATH_HISCORES);
|
||||
exact_leaderboard(hiscores.lock().await);
|
||||
let loscores: Arc<Mutex<Vec<Entry>>> = read_file(PATH_LOSCORES);
|
||||
exact_leaderboard(loscores.lock().await);
|
||||
let pingscores: Arc<Mutex<HashMap<String, (u64, u32)>>> = read_file(PATH_PINGSCORES);
|
||||
|
||||
let (tx, rx) = mpsc::channel::<LeaderboardUpdate>(1024);
|
||||
@@ -129,22 +140,29 @@ async fn handle_hiscores(
|
||||
) {
|
||||
fn update_scoretable<G: Deref<Target = Vec<Entry>> + DerefMut>(
|
||||
score_name: &str,
|
||||
mut scoretable: G,
|
||||
mut scoretable_lock: G,
|
||||
name: &str,
|
||||
score: u32,
|
||||
file_path: &str,
|
||||
) {
|
||||
if scoretable.last().is_none_or(|e| score > e.score) {
|
||||
let scoretable = &mut *scoretable_lock;
|
||||
|
||||
if let Some(index_to_insert_at) = scoretable.iter().position(|e| score > e.score) {
|
||||
println!("New {score_name} {score} by {name}");
|
||||
scoretable.push(Entry {
|
||||
scoretable[index_to_insert_at..].rotate_right(1);
|
||||
let push_out = std::mem::replace(
|
||||
&mut scoretable[index_to_insert_at],
|
||||
Entry {
|
||||
score,
|
||||
person: name.to_string(),
|
||||
});
|
||||
scoretable.sort();
|
||||
scoretable.reverse();
|
||||
scoretable.truncate(20);
|
||||
let file_contents: String = serde_json::to_string(&*scoretable).unwrap();
|
||||
drop(scoretable);
|
||||
},
|
||||
);
|
||||
if scoretable.len() < MAX_LEADERBOARD {
|
||||
scoretable.push(push_out);
|
||||
}
|
||||
|
||||
let file_contents: String = serde_json::to_string(&*scoretable_lock).unwrap();
|
||||
drop(scoretable_lock);
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
@@ -152,6 +170,12 @@ async fn handle_hiscores(
|
||||
.unwrap();
|
||||
file.write_all(file_contents.as_bytes()).unwrap();
|
||||
file.flush().unwrap();
|
||||
} else if scoretable.len() < MAX_LEADERBOARD {
|
||||
println!("New {score_name} {score} by {name}");
|
||||
scoretable.push(Entry {
|
||||
score,
|
||||
person: name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user