From 67334784344d3eb8d4de518184aef7ebc47d6606 Mon Sep 17 00:00:00 2001 From: Ben Hindman Date: Wed, 19 Apr 2023 11:28:38 -0500 Subject: [PATCH 1/2] Endpoint for win probablities for each player --- ng_model/src/lib.rs | 83 ++++++++++++++++++++++++++++++++++++++++++- ng_server/src/main.rs | 9 +++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/ng_model/src/lib.rs b/ng_model/src/lib.rs index 4e88844..0a5cd27 100644 --- a/ng_model/src/lib.rs +++ b/ng_model/src/lib.rs @@ -4,6 +4,10 @@ pub use serde; pub use serde_json; pub use serde_with; +const NONCE_MIN: u32 = 0; +const NONCE_MAX: u32 = 0xFFFFFFFF; +const NONCE_SET_SPACE: u32 = NONCE_MAX - NONCE_MIN; + /// The target block that players are trying to guess the nonce for. #[derive(Serialize, Deserialize, Default, Debug, Clone, Eq, PartialEq)] pub struct Target { @@ -12,7 +16,7 @@ pub struct Target { } /// A players guess for a target block nonce. -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq,)] pub struct Guess { pub block: Option, pub name: String, @@ -26,6 +30,36 @@ pub struct Block { pub height: u32, pub nonce: u32, } +/* +This function calculates the chance each guess has of winning by dividing +the NONCE_SET_SPACE into ranges determined by proximity +to each guess. The win probability for a uniformly distributed +nonce is the ratio of a guess' range to the total set space +*/ +pub fn get_guess_probabilities(guesses: &mut [Guess]) -> Vec<(String, f32)> { + sort_guesses_by_nonce(guesses); + + let mut result = Vec::new(); + let mut lower_bound = NONCE_MIN; + + let mut guess_iter = guesses.iter(); + if let Some(mut current_guess) = guess_iter.next() { + while let Some(next_guess) = guess_iter.next() { + let upper_bound = current_guess.nonce + ((next_guess.nonce - current_guess.nonce) / 2); + result.push((current_guess.name.clone(), range_probability(lower_bound, upper_bound))); + lower_bound = upper_bound + 1; + current_guess = next_guess; + } + let upper_bound = NONCE_MAX; + result.push((current_guess.name.clone(), range_probability(lower_bound, upper_bound))); + } + + result +} + +fn range_probability(lower: u32, upper: u32) -> f32 { + ((upper - lower) as f32) / (NONCE_SET_SPACE as f32) +} pub fn sort_guesses_by_target_diff(guesses: &mut [Guess], target_nonce: u32) { guesses.sort_by(|a, b| { @@ -38,3 +72,50 @@ pub fn sort_guesses_by_target_diff(guesses: &mut [Guess], target_nonce: u32) { pub fn sort_guesses_by_nonce(guesses: &mut [Guess]) { guesses.sort_by_key(|g| g.nonce) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn probabilities_for_empty_guesses() { + let mut input = vec![]; + let output = get_guess_probabilities(input.as_mut_slice()); + assert_eq!(output.len(), 0); + } + + #[test] + fn probabilities_for_one_guess() { + let mut input = vec![ + Guess { name: String::from("b"), nonce: 0xBBBBBBBB, block: None } + ]; + let output = get_guess_probabilities(input.as_mut_slice()); + assert_eq!(output[0], (String::from("b"), 1.0)); + } + + #[test] + fn probabilities_for_guesses() { + let mut input = vec![ + Guess { name: String::from("ben"), nonce: 0xDEADBEEF, block: None }, + Guess { name: String::from("carol"), nonce: 0xBEEFDEAD, block: None } + ]; + let output = get_guess_probabilities(input.as_mut_slice()); + + dbg!(&output); + assert_eq!(output[0], (String::from("carol"), 0.80784315)); + assert_eq!(output[1], (String::from("ben"), 0.19215687)); + } + + #[test] + fn probabilities_for_boundary_guesses() { + let mut input = vec![ + Guess { name: String::from("Max"), nonce: NONCE_MAX, block: None }, + Guess { name: String::from("Min"), nonce: NONCE_MIN, block: None } + ]; + let output = get_guess_probabilities(input.as_mut_slice()); + + assert_eq!(output[0], (String::from("Min"), 0.5)); + assert_eq!(output[1], (String::from("Max"), 0.5)); + } +} + diff --git a/ng_server/src/main.rs b/ng_server/src/main.rs index 7179a0a..3f3e992 100644 --- a/ng_server/src/main.rs +++ b/ng_server/src/main.rs @@ -81,6 +81,7 @@ async fn main() { .route("/target/nonce", get(get_target_nonce)) .route("/guesses", get(get_guesses)) .route("/guesses", on(MethodFilter::POST, post_guess)) + .route("/guesses/odds", get(get_guess_probabalities)) .route("/guesses/:block", get(get_block_guesses)) .layer(Extension(shared_state)); @@ -151,6 +152,7 @@ async fn not_found() -> Response { .unwrap() } +/// The target block that players are trying to guess the nonce for. async fn get_current_target( Extension(state): Extension>, ) -> Result, Error> { @@ -212,6 +214,13 @@ async fn get_target_nonce(Extension(state): Extension>) -> Result>) -> Result>, Error> { + let mut tx = state.pool.begin().await?; + let mut guesses = tx.select_guesses().await.unwrap_or_default(); + tx.commit().await?; + Ok(Json(ng_model::get_guess_probabilities(guesses.as_mut_slice()))) +} + async fn get_guesses(Extension(state): Extension>) -> Result>, Error> { let mut tx = state.pool.begin().await?; let guesses = tx.select_guesses().await.unwrap_or_default(); From 88a55e57333c37d4242c7bbb9ebc5d4e0d3464a6 Mon Sep 17 00:00:00 2001 From: Ben Hindman Date: Thu, 20 Apr 2023 07:50:47 -0500 Subject: [PATCH 2/2] Clean PR --- ng_model/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ng_model/src/lib.rs b/ng_model/src/lib.rs index 0a5cd27..2bcfd9f 100644 --- a/ng_model/src/lib.rs +++ b/ng_model/src/lib.rs @@ -16,7 +16,7 @@ pub struct Target { } /// A players guess for a target block nonce. -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq,)] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct Guess { pub block: Option, pub name: String, @@ -101,7 +101,6 @@ mod tests { ]; let output = get_guess_probabilities(input.as_mut_slice()); - dbg!(&output); assert_eq!(output[0], (String::from("carol"), 0.80784315)); assert_eq!(output[1], (String::from("ben"), 0.19215687)); }