Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Player Odds Endpoint #9

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions ng_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ pub use serde;
pub use serde_json;
pub use serde_with;

const NONCE_MIN: u32 = 0;
Copy link
Owner

@notmandatory notmandatory May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need constants for these, can create a type alias for u32:

Suggested change
const NONCE_MIN: u32 = 0;
type Nonce = u32;

And replace: NONCE_MIN with Nonce::MIN, NONCE_MAX with Nonce::MAX, and NONCE_SET_SPACE is always going to be Nonce::MAX.

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 {
Expand All @@ -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| {
Expand All @@ -38,3 +72,49 @@ 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());

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));
}
}

9 changes: 9 additions & 0 deletions ng_server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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<Arc<State>>,
) -> Result<Json<Target>, Error> {
Expand Down Expand Up @@ -212,6 +214,13 @@ async fn get_target_nonce(Extension(state): Extension<Arc<State>>) -> Result<Str
Ok(String::default())
}

async fn get_guess_probabalities(Extension(state): Extension<Arc<State>>) -> Result<Json<Vec<(String, f32)>>, Error> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little spelling typo:

Suggested change
async fn get_guess_probabalities(Extension(state): Extension<Arc<State>>) -> Result<Json<Vec<(String, f32)>>, Error> {
async fn get_guess_probabilities(Extension(state): Extension<Arc<State>>) -> Result<Json<Vec<(String, f32)>>, 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<Arc<State>>) -> Result<Json<Vec<Guess>>, Error> {
let mut tx = state.pool.begin().await?;
let guesses = tx.select_guesses().await.unwrap_or_default();
Expand Down