From 9a00eb67fd03d0d852f12b7546df3ef4e0c2e498 Mon Sep 17 00:00:00 2001 From: Leonard Kugis Date: Mon, 10 Oct 2022 02:09:57 +0200 Subject: Initial commit, base functionality --- .gitignore | 61 +++++++++++++ Cargo.toml | 14 +++ src/main.glade | 268 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 173 +++++++++++++++++++++++++++++++++++++ 4 files changed, 516 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/main.glade create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf1fae1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,61 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust,windows,linux +# Edit at https://www.toptal.com/developers/gitignore?templates=rust,windows,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/rust,windows,linux diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..88b5045 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rraffle" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +csv = "1.1" +clap = { version = "4.0.10", features = ["derive"] } +glib = "0.15.12" +gtk = "0.15.5" +rand = "0.8.5" +text_io = "0.1.12" diff --git a/src/main.glade b/src/main.glade new file mode 100644 index 0000000..4d28906 --- /dev/null +++ b/src/main.glade @@ -0,0 +1,268 @@ + + + + + + 100 + 1 + 10 + + + 10000 + 1 + 10 + + + + + + + + + + + False + RRaffle + False + + + True + False + + + + True + False + + + True + False + 5 + 5 + 5 + 5 + Choose CSV + + + 0 + 0 + + + + + True + False + 5 + 5 + 5 + 5 + + + + 1 + 0 + + + + + Load + True + True + True + 5 + 5 + 5 + 5 + + + 2 + 0 + + + + + True + False + 5 + 5 + 5 + 5 + Max Wins + + + 0 + 1 + + + + + True + True + 5 + 5 + 5 + 5 + adjDraws + 1 + + + 1 + 1 + 2 + + + + + Draw Single + True + True + True + 5 + 5 + 5 + 5 + + + 0 + 2 + 3 + + + + + Reset Draws + True + True + True + 5 + 5 + 5 + 5 + + + 0 + 4 + 3 + + + + + True + False + 5 + 5 + 5 + 5 + Draws + + + 0 + 5 + + + + + Draw Multiple + True + True + True + 5 + 5 + 5 + 5 + + + 0 + 6 + 3 + + + + + True + True + 5 + 5 + 5 + 5 + adjDraws + + + 1 + 5 + 2 + + + + + True + False + start + 5 + 5 + 5 + 5 + Draw Round: 1 + + + 0 + 3 + 3 + + + + + False + True + 0 + + + + + True + True + in + 200 + + + True + False + + + True + True + ts + + + + + + Name + + + + + + + + Lots + + + + + + + + + + + + False + True + 1 + + + + + + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b92c136 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,173 @@ +use gtk::prelude::*; +use clap::Parser; +use csv; +use rand::Rng; +use text_io; + +const APP_ID: &str = "com.encrox.rraffle"; + +#[derive(Parser, Debug)] +struct Cli { + /// CSV file with names and number of lots + #[arg(short, long)] + file: Option, + + /// Show GUI + #[arg(short, long)] + gui: bool, + + /// Shuffle animation + #[arg(short, long)] + animation: Option, + + /// Number of draws + #[arg(short, long, default_value_t = 5)] + draws: u32, + + /// Number of wins per entry + #[arg(short, long, default_value_t = 2)] + wins: u32, + + /// Interactive when in CLI mode + #[arg(short, long)] + interactive: bool, +} + +#[derive(Debug)] +struct Entry { + name: String, + lots: u32, + wins: u32, + odds: f64 +} + +fn load_csv(entries : &mut Vec, file: std::path::PathBuf) { + let reader = csv::Reader::from_path(file); + for record in reader.unwrap().records() { + //dbg!(record.unwrap().get(0).unwrap()); + let rec = record.unwrap(); + entries.push(Entry { + name: rec.get(0).unwrap().to_string(), + lots: rec.get(1).unwrap().parse::().unwrap(), + wins: 2, + odds: 0.0 + }); + } +} + +fn max_wins(entries : &mut Vec, wins : u32) { + for entry in entries { + entry.wins = wins; + } +} + +fn calc_odds(entries : &mut Vec) { + let mut sum : u32 = 0; + for entry in &mut *entries { + sum += if entry.wins > 0 { entry.lots } else { 0 }; + } + for entry in entries { + entry.odds = if entry.wins > 0 { entry.lots as f64 / sum as f64 } else { 0.0 }; + } +} + +fn get_winner(entries : &mut Vec) -> &Entry { + let mut rng = rand::thread_rng(); + let rn = rng.gen::(); + let mut sum : f64 = 0.0; + for entry in entries { + if rn >= sum && rn < sum + entry.odds { + entry.lots -= 1; + entry.wins -= 1; + return entry; + } + sum += entry.odds; + } + unreachable!(); +} + +fn main() { + let args = Cli::parse(); + //dbg!(args); + + if args.gui { + // Create a new application + let app = gtk::Application::builder().application_id(APP_ID).build(); + + // Connect to "activate" signal of `app` + app.connect_activate(build_ui); + + // Run the application + app.run_with_args(&[""]); + } else { + if !args.file.is_none() { + let mut entries = Vec::::new(); + load_csv(&mut entries, args.file.unwrap()); + max_wins(&mut entries, args.wins); + let mut round = 1; + 'outer: loop { + calc_odds(&mut entries); + println!("Draw round: {}", round); + println!("Entries: {:?}", &entries); + if args.interactive { + print!("Option (s = start, a = abort) [s/a]: "); + loop { + let option : char = text_io::read!(); + match option { + 'a' => break 'outer, + 's' => break, + _ => println!("Unknown option: {}", option), + } + } + } + let winner = get_winner(&mut entries); + println!("Winner is: {}", winner.name); + if args.interactive { + print!("Option (n = next, a = abort) [n/a]: "); + loop { + let option : char = text_io::read!(); + match option { + 'a' => break 'outer, + 'n' => break, + _ => println!("Unknown option: {}", option), + } + } + } + if round == args.draws && !args.interactive { + println!("Number of draws reached."); + break; + } + round += 1; + } + } + } +} + +fn build_ui(app: >k::Application) { + let glade_src = include_str!("main.glade"); + let builder = gtk::Builder::from_string(glade_src); + + let window: gtk::ApplicationWindow = builder.object("window").expect("Couldn't get window"); + let tv : gtk::TreeView = builder.object("tv").expect("Couldn't get tree view"); + let btn_draw_single : gtk::Button = builder.object("btn_draw_single").expect("Couldn't get button btn_draw_single"); + let btn_draw_multiple : gtk::Button = builder.object("btn_draw_multiple").expect("Couldn't get button btn_draw_multiple"); + let file_choose_csv : gtk::FileChooserButton = builder.object("file_choose_csv").expect("Couldn't get button file_choose_csv"); + let btn_load : gtk::Button = builder.object("btn_load").expect("Couldn't get button btn_load"); + let lbl_round : gtk::Label = builder.object("lbl_round").expect("Couldn't get lbl_round"); + let btn_reset : gtk::Button = builder.object("btn_reset").expect("Couldn't get btn_reset"); + let sp_num_draws : gtk::SpinButton = builder.object("sp_num_draws").expect("Couldn't get sp_num_draws"); + let ts : gtk::TreeStore = builder.object("ts").expect("Couldn't get tree store"); + + window.set_application(Some(app)); + + btn_load.connect_clicked(glib::clone!(@weak file_choose_csv, @weak ts => move |button| { + //let data = load_csv(file_choose_csv.filename().unwrap()); + //for entry in data { + let iter = ts.insert(None, -1); + ts.set(&iter, &[(0, &glib::Value::from("Eva")), (1, &0.to_value())]); + //} + })); + + // Present window + window.show_all(); +} \ No newline at end of file -- cgit v1.2.1