blah-rs/src/main.rs

115 lines
3.5 KiB
Rust

use anyhow::Result;
use clap::Parser;
use rand::{seq::SliceRandom, Rng};
mod cards;
mod files;
mod price;
const IDENTIFIER_URL: &str = "https://mtgjson.com/api/v5/AllIdentifiers.json";
const PRICES_URL: &str = "https://mtgjson.com/api/v5/AllPricesToday.json";
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Identifier file (AllIdentifiers.json), if not found it will be downloaded
#[arg(short = 'i', long, default_value = "AllIdentifiers.json")]
identifier_file: String,
/// Prices file (AllPricesToday.json), if not found it will be downloaded
#[arg(short = 'p', long, default_value = "AllPricesToday.json")]
prices_file: String,
/// Maximum card price allowed (in whole units of any currency)
#[arg(long = "price", default_value_t = 0.04f64)]
price_filter: f64,
/// Comma-separated list of card types to remove
#[arg(long, default_value = "token,emblem,sticker,card,hero,plane,scheme")]
remove_types: String,
/// How many cards to put in each "dollar store" pack
#[arg(short = 'c', long = "pack-size", default_value_t = 100)]
cards_per_draft: usize,
/// How many "dollar store" pack to generate
#[arg(short = 'n', long, default_value_t = 2)]
pack_count: usize,
/// Limit how many lands to put in each pack (0 to disable)
#[arg(short = 'l', long, default_value_t = 5)]
land_limit: usize,
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
let prices_file = files::open_or_download(PRICES_URL, &args.prices_file).await?;
let cards_file = files::open_or_download(IDENTIFIER_URL, &args.identifier_file).await?;
let prices = price::get_cards_in_budget(prices_file, args.price_filter)?;
let card_db = cards::CardDB::load(cards_file)?;
let remove_types: Vec<_> = args
.remove_types
.split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.collect();
let cards: Vec<_> = prices
.iter()
// Map UUIDs to cards
.filter_map(|uuid| card_db.get_by_uuid(uuid))
// Remove types we don't care about
.filter(|card| !remove_types.iter().any(|t| card.is_type(t)))
.collect();
// Print stats
println!("Found {} cards", cards.len());
// Generate packs of cards
for index in 0..args.pack_count {
let decklist = generate_pack(&cards, args.cards_per_draft, args.land_limit);
// Save decklist to file
files::save_decklist(&format!("decklist-{}.txt", index), decklist).await?;
}
Ok(())
}
fn generate_pack(
card_list: &Vec<&cards::Card>,
cards_per_draft: usize,
land_limit: usize,
) -> Vec<String> {
let mut rng = rand::thread_rng();
// Generate a bunch of random cards
let mut picked: Vec<_> = (0..cards_per_draft)
.map(|_| card_list[rng.gen_range(0..card_list.len())])
.collect();
// Check for too many lands
if land_limit > 0 {
let non_lands: Vec<_> = card_list.iter().filter(|c| !c.is_type("land")).collect();
let mut num_lands = picked.iter().filter(|c| c.is_type("land")).count();
picked = picked
.into_iter()
.map(|c| {
if c.is_type("land") && num_lands > land_limit {
num_lands -= 1;
non_lands.choose(&mut rng).unwrap()
} else {
c
}
})
.collect();
}
// Return the picked card names
picked.iter().map(|c| c.name.clone()).collect()
}