Add player matchup command
This commit is contained in:
parent
c1604772a2
commit
5543f0a6b6
129
src/main.rs
129
src/main.rs
|
@ -105,6 +105,8 @@ enum DatasetSC {
|
||||||
enum PlayerSC {
|
enum PlayerSC {
|
||||||
#[command(about = "Get info about a player")]
|
#[command(about = "Get info about a player")]
|
||||||
Info { player: String },
|
Info { player: String },
|
||||||
|
#[command(about = "Matchup data between two players")]
|
||||||
|
Matchup { player1: String, player2: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -521,18 +523,125 @@ fn player_info(connection: &Connection, dataset: Option<String>, player: String)
|
||||||
println!("\x1b[1mVolatility:\x1b[0m {}", volatility);
|
println!("\x1b[1mVolatility:\x1b[0m {}", volatility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Finish
|
||||||
|
fn player_matchup(
|
||||||
|
connection: &Connection,
|
||||||
|
dataset: Option<String>,
|
||||||
|
player1: String,
|
||||||
|
player2: String,
|
||||||
|
) {
|
||||||
|
let dataset = dataset.unwrap_or_else(|| String::from("default"));
|
||||||
|
|
||||||
|
let PlayerData {
|
||||||
|
id: player1,
|
||||||
|
name: name1,
|
||||||
|
prefix: prefix1,
|
||||||
|
discrim: discrim1,
|
||||||
|
} = get_player_from_input(connection, player1)
|
||||||
|
.unwrap_or_else(|_| error("Could not find player", 1));
|
||||||
|
|
||||||
|
let (deviation1, _, _) = get_player_rating_data(connection, &dataset, player1)
|
||||||
|
.unwrap_or_else(|_| error("Could not find player", 1));
|
||||||
|
|
||||||
|
let PlayerData {
|
||||||
|
id: player2,
|
||||||
|
name: name2,
|
||||||
|
prefix: prefix2,
|
||||||
|
discrim: discrim2,
|
||||||
|
} = get_player_from_input(connection, player2)
|
||||||
|
.unwrap_or_else(|_| error("Could not find player", 1));
|
||||||
|
|
||||||
|
let (deviation2, _, _) = get_player_rating_data(connection, &dataset, player2)
|
||||||
|
.unwrap_or_else(|_| error("Could not find player", 1));
|
||||||
|
|
||||||
|
let (hypothetical, advantage) = get_advantage(connection, &dataset, player1, player2)
|
||||||
|
.expect("Error communicating with SQLite")
|
||||||
|
.map(|x| (false, x))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let metadata = get_metadata(connection, &dataset)
|
||||||
|
.expect("Error communicating with SQLite")
|
||||||
|
.unwrap_or_else(|| error("Dataset not found", 1));
|
||||||
|
(
|
||||||
|
true,
|
||||||
|
hypothetical_advantage(
|
||||||
|
connection,
|
||||||
|
&dataset,
|
||||||
|
player1,
|
||||||
|
player2,
|
||||||
|
metadata.set_limit,
|
||||||
|
metadata.decay_rate,
|
||||||
|
metadata.adj_decay_rate,
|
||||||
|
)
|
||||||
|
.expect("Error communicating with SQLite"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let probability = 1.0
|
||||||
|
/ (1.0
|
||||||
|
+ f64::exp(
|
||||||
|
g_func((deviation1 * deviation1 + deviation2 * deviation2).sqrt()) * advantage,
|
||||||
|
));
|
||||||
|
|
||||||
|
let color = ansi_num_color(advantage, 0.2, 2.0);
|
||||||
|
let other_color = ansi_num_color(-advantage, 0.2, 2.0);
|
||||||
|
|
||||||
|
let len1 = prefix1.as_deref().map(|s| s.len() + 1).unwrap_or(0) + name1.len();
|
||||||
|
let len2 = prefix2.as_deref().map(|s| s.len() + 1).unwrap_or(0) + name2.len();
|
||||||
|
|
||||||
|
println!();
|
||||||
|
if let Some(pre) = prefix1 {
|
||||||
|
print!("\x1b[2m{}\x1b[22m ", pre);
|
||||||
|
}
|
||||||
|
print!(
|
||||||
|
"\x1b[4m\x1b]8;;https://www.start.gg/user/{}\x1b\\\
|
||||||
|
\x1b[1m{}\x1b[22m\x1b]8;;\x1b\\\x1b[0m - ",
|
||||||
|
discrim1, name1
|
||||||
|
);
|
||||||
|
if let Some(pre) = prefix2 {
|
||||||
|
print!("\x1b[2m{}\x1b[22m ", pre);
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"\x1b[4m\x1b]8;;https://www.start.gg/user/{}\x1b\\\
|
||||||
|
\x1b[1m{}\x1b[22m\x1b]8;;\x1b\\\x1b[0m",
|
||||||
|
discrim2, name2
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"\x1b[1m\x1b[{4}m{0:>2$}\x1b[0m - \x1b[1m\x1b[{5}m{1:<3$}\x1b[0m",
|
||||||
|
format!("{:.1}%", probability * 100.0),
|
||||||
|
format!("{:.1}%", (1.0 - probability) * 100.0),
|
||||||
|
len1,
|
||||||
|
len2,
|
||||||
|
other_color,
|
||||||
|
color
|
||||||
|
);
|
||||||
|
|
||||||
|
if hypothetical {
|
||||||
|
println!(
|
||||||
|
"\n\x1b[1mHypothetical Advantage: \x1b[{1}m{0:+.4}\x1b[0m",
|
||||||
|
advantage, color
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"\n\x1b[1mAdvantage: \x1b[{1}m{0:+.4}\x1b[0m",
|
||||||
|
advantage, color
|
||||||
|
);
|
||||||
|
|
||||||
|
let (a, b) = get_matchup_set_counts(connection, &dataset, player1, player2)
|
||||||
|
.expect("Error communicating with SQLite");
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"\n\x1b[1mSet Count:\x1b[0m {} - {} ({:.3}% - {:.3}%)",
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
(a as f64 / (a + b) as f64) * 100.0,
|
||||||
|
(b as f64 / (a + b) as f64) * 100.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
|
|
||||||
fn sync(datasets: Vec<String>, all: bool, auth_token: Option<String>) {
|
|
||||||
let config_dir = dirs::config_dir().unwrap();
|
|
||||||
|
|
||||||
let auth = auth_token
|
|
||||||
.or_else(|| get_auth_token(&config_dir))
|
|
||||||
.unwrap_or_else(|| error("Access token not provided", 1));
|
|
||||||
|
|
||||||
let connection =
|
|
||||||
open_datasets(&config_dir).unwrap_or_else(|_| error("Could not open datasets file", 2));
|
|
||||||
|
|
||||||
fn sync(connection: &Connection, auth: String, datasets: Vec<String>, all: bool) {
|
fn sync(connection: &Connection, auth: String, datasets: Vec<String>, all: bool) {
|
||||||
let all_datasets = list_dataset_names(connection).unwrap();
|
let all_datasets = list_dataset_names(connection).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,10 @@ use sqlite::*;
|
||||||
|
|
||||||
// Glicko-2 system calculation
|
// Glicko-2 system calculation
|
||||||
|
|
||||||
|
pub fn g_func(dev: f64) -> f64 {
|
||||||
|
1.0 / (1.0 + 3.0 * dev * dev / PI / PI).sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
fn time_adjust(periods: f64, old_dev_sq: f64, volatility: f64) -> f64 {
|
fn time_adjust(periods: f64, old_dev_sq: f64, volatility: f64) -> f64 {
|
||||||
(old_dev_sq + periods * volatility * volatility).sqrt()
|
(old_dev_sq + periods * volatility * volatility).sqrt()
|
||||||
}
|
}
|
||||||
|
@ -45,7 +49,7 @@ fn glicko_adjust(
|
||||||
let period = metadata.period;
|
let period = metadata.period;
|
||||||
let tau = metadata.tau;
|
let tau = metadata.tau;
|
||||||
|
|
||||||
let g_val = 1.0 / (1.0 + 3.0 * other_deviation * other_deviation / PI / PI).sqrt();
|
let g_val = g_func(other_deviation);
|
||||||
let exp_val = 1.0 / (1.0 + f64::exp(-g_val * advantage));
|
let exp_val = 1.0 / (1.0 + f64::exp(-g_val * advantage));
|
||||||
|
|
||||||
let variance = 1.0 / (g_val * g_val * exp_val * (1.0 - exp_val));
|
let variance = 1.0 / (g_val * g_val * exp_val * (1.0 - exp_val));
|
||||||
|
|
20
src/util.rs
20
src/util.rs
|
@ -40,6 +40,26 @@ pub fn read_string() -> String {
|
||||||
line.trim().to_owned()
|
line.trim().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ansi_num_color(num: f64, threshold1: f64, threshold2: f64) -> &'static str {
|
||||||
|
let sign = num > 0.0;
|
||||||
|
let num_abs = num.abs();
|
||||||
|
let severity = if num_abs < threshold1 {
|
||||||
|
0
|
||||||
|
} else if num_abs < threshold2 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
|
match (sign, severity) {
|
||||||
|
(false, 1) => "31",
|
||||||
|
(true, 1) => "32",
|
||||||
|
(false, 2) => "91",
|
||||||
|
(true, 2) => "92",
|
||||||
|
_ => "39",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Player Input
|
// Player Input
|
||||||
|
|
||||||
pub enum PlayerInput {
|
pub enum PlayerInput {
|
||||||
|
|
Loading…
Reference in a new issue