perf: improve top-20 leaderboard algorithm
This commit is contained in:
+34
-10
@@ -48,12 +48,14 @@ enum LeaderboardUpdateType {
|
|||||||
Increment { loscore: u32 },
|
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_HISCORES: &str = "hiscores.json";
|
||||||
const PATH_LOSCORES: &str = "loscores.json";
|
const PATH_LOSCORES: &str = "loscores.json";
|
||||||
const PATH_PINGSCORES: &str = "pingscores.json";
|
const PATH_PINGSCORES: &str = "pingscores.json";
|
||||||
|
|
||||||
|
const MAX_LEADERBOARD: usize = 20;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
fn read_file<T: for<'de> de::Deserialize<'de>>(file_path: &str) -> Arc<Mutex<T>> {
|
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()))
|
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);
|
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);
|
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 pingscores: Arc<Mutex<HashMap<String, (u64, u32)>>> = read_file(PATH_PINGSCORES);
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel::<LeaderboardUpdate>(1024);
|
let (tx, rx) = mpsc::channel::<LeaderboardUpdate>(1024);
|
||||||
@@ -129,22 +140,29 @@ async fn handle_hiscores(
|
|||||||
) {
|
) {
|
||||||
fn update_scoretable<G: Deref<Target = Vec<Entry>> + DerefMut>(
|
fn update_scoretable<G: Deref<Target = Vec<Entry>> + DerefMut>(
|
||||||
score_name: &str,
|
score_name: &str,
|
||||||
mut scoretable: G,
|
mut scoretable_lock: G,
|
||||||
name: &str,
|
name: &str,
|
||||||
score: u32,
|
score: u32,
|
||||||
file_path: &str,
|
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}");
|
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,
|
score,
|
||||||
person: name.to_string(),
|
person: name.to_string(),
|
||||||
});
|
},
|
||||||
scoretable.sort();
|
);
|
||||||
scoretable.reverse();
|
if scoretable.len() < MAX_LEADERBOARD {
|
||||||
scoretable.truncate(20);
|
scoretable.push(push_out);
|
||||||
let file_contents: String = serde_json::to_string(&*scoretable).unwrap();
|
}
|
||||||
drop(scoretable);
|
|
||||||
|
let file_contents: String = serde_json::to_string(&*scoretable_lock).unwrap();
|
||||||
|
drop(scoretable_lock);
|
||||||
let mut file = fs::OpenOptions::new()
|
let mut file = fs::OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
@@ -152,6 +170,12 @@ async fn handle_hiscores(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
file.write_all(file_contents.as_bytes()).unwrap();
|
file.write_all(file_contents.as_bytes()).unwrap();
|
||||||
file.flush().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