Compare commits

...

7 commits
v0.1.0 ... main

4 changed files with 29 additions and 25 deletions

2
Cargo.lock generated
View file

@ -507,7 +507,7 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
[[package]]
name = "smogon-stats"
version = "0.1.0"
version = "0.1.2"
dependencies = [
"clap",
"env_logger",

View file

@ -1,6 +1,6 @@
[package]
name = "smogon-stats"
version = "0.1.0"
version = "0.1.2"
edition = "2024"
authors = ["mehbark <terezi@pyrope.net>"]
description = "turn https://smogon.com/stats/ chaos json files into https://sqlite.org databases"

View file

@ -1,9 +1,9 @@
# smogon stats
turn <https://smogon.com/stats/> chaos json files into [SQLite](https://sqlite.org) databases
you can find pre-generated dbs at <https://pyrope.net/mon/stats> (currently only 2025-02), and you can [fiddle](https://sqlite.org/fiddle) with them in your browser or on your own computer.
you can find pre-generated dbs at <https://pyrope.net/mon/stats>, and you can [fiddle](https://sqlite.org/fiddle) with them in your browser or on your own computer.
## examples
## examples (2025-02)
```sh
smogon-stats gen9ou-1500.json -o gen9ou-1500.sqlite
sqlite3 gen9ou-1500.sqlite -markdown "SELECT name, format('%.2f%%', usage * 100) as usage FROM mon WHERE mon.usage > 0.04 ORDER BY mon.usage DESC LIMIT 10"

View file

@ -1,5 +1,3 @@
// https://www.smogon.com/stats/2025-02/chaos/
use std::{
collections::HashMap,
fs::{self, File},
@ -54,10 +52,11 @@ fn run(
}
#[derive(clap::Parser)]
#[command(about)]
#[command(version, about)]
struct Config {
#[arg()]
input_file: PathBuf,
// TODO: non-utf-8 (the problem is saving the database)
#[arg(short, long = "output")]
output_file: PathBuf,
}
@ -82,8 +81,8 @@ fn create_tables(conn: &mut Connection) -> rusqlite::Result<()> {
BEGIN;
CREATE TABLE mon (
name STRING NOT NULL,
usage REAL NOT NULL,
viability_ceiling REAL NOT NULL
usage REAL,
viability_ceiling REAL
);
CREATE TABLE ability (
mon STRING NOT NULL,
@ -125,12 +124,12 @@ fn insert_stats(conn: &mut Connection, stats: &Stats) -> rusqlite::Result<()> {
let mon_count = stats.data.len();
let mon_count_digits = mon_count.to_string().len();
let tx = conn.transaction()?;
for (i, (mon, data)) in stats.data.iter().enumerate() {
log::debug!(
"Processing mon {:mon_count_digits$}/{mon_count} ({mon})",
i + 1
);
let tx = conn.transaction()?;
// normalizing with mon_count gives us data that is much easier to work
// with. for example, if pikachu has 10k count (weighted) and thunderbolt
@ -142,7 +141,11 @@ fn insert_stats(conn: &mut Connection, stats: &Stats) -> rusqlite::Result<()> {
let mon_count: f32 = data.abilities.values().sum();
tx.execute(
"INSERT INTO mon VALUES (?1, ?2, ?3)",
(mon, data.usage, data.viability_ceiling[1]),
(
mon,
data.usage,
data.viability_ceiling.as_ref().map(|x| x[1]),
),
)?;
for (ability, count) in &data.abilities {
@ -166,15 +169,17 @@ fn insert_stats(conn: &mut Connection, stats: &Stats) -> rusqlite::Result<()> {
)?;
}
for (tera, count) in &data.tera {
tx.execute(
"INSERT INTO tera VALUES (?1, ?2, ?3)",
(
mon,
format!("{tera:?}").to_ascii_lowercase(),
count / mon_count,
),
)?;
if let Some(tera) = &data.tera {
for (tera, count) in tera {
tx.execute(
"INSERT INTO tera VALUES (?1, ?2, ?3)",
(
mon,
format!("{tera:?}").to_ascii_lowercase(),
count / mon_count,
),
)?;
}
}
for (mate, count) in &data.teammates {
@ -190,9 +195,8 @@ fn insert_stats(conn: &mut Connection, stats: &Stats) -> rusqlite::Result<()> {
(mon, opp, percentage, stddev),
)?;
}
tx.commit()?;
}
tx.commit()?;
Ok(())
}
@ -242,16 +246,16 @@ type Counts = HashMap<Box<str>, f32>;
#[serde(rename_all = "PascalCase")]
struct Data {
#[serde(rename = "Viability Ceiling")]
viability_ceiling: Box<[u32]>,
viability_ceiling: Option<Box<[u32]>>,
abilities: Counts,
items: Counts,
moves: Counts,
#[serde(rename = "Tera Types")]
tera: HashMap<Type, f32>,
tera: Option<HashMap<Type, f32>>,
// i'm just not going to include happiness sorry
teammates: Counts,
#[serde(rename = "Checks and Counters")]
checks_and_counters: HashMap<Box<str>, (f32, f32, f32)>,
#[serde(rename = "usage")]
usage: f32,
usage: Option<f32>,
}