From d2ab1b9a2dd138621d354674962d6bce15de3207 Mon Sep 17 00:00:00 2001 From: Matt Strapp Date: Thu, 12 Dec 2024 11:09:20 -0600 Subject: Initial commit Signed-off-by: Matt Strapp --- .gitignore | 21 ++++++++ Cargo.toml | 9 ++++ rustfmt.toml | 0 wwb/Cargo.toml | 11 +++++ wwb/report.json | 5 ++ wwb/src/lib.rs | 20 ++++++++ wwb/src/main.rs | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 213 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 rustfmt.toml create mode 100644 wwb/Cargo.toml create mode 100644 wwb/report.json create mode 100644 wwb/src/lib.rs create mode 100644 wwb/src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d01bd1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# 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 + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f9af5c7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +resolver = "2" + +members = ["wwb"] + +[profile.release] +strip = true +lto = true +panic = "abort" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..e69de29 diff --git a/wwb/Cargo.toml b/wwb/Cargo.toml new file mode 100644 index 0000000..810f96b --- /dev/null +++ b/wwb/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wwb" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = "0.4.39" +ctrlc = "3.4.5" +env_logger = "0.11.5" +fastrand = "2.3.0" +log = "0.4.22" diff --git a/wwb/report.json b/wwb/report.json new file mode 100644 index 0000000..4896cc3 --- /dev/null +++ b/wwb/report.json @@ -0,0 +1,5 @@ +{ + "version": 0, + "root_name": "wwb", + "workspace_crates": {} +} \ No newline at end of file diff --git a/wwb/src/lib.rs b/wwb/src/lib.rs new file mode 100644 index 0000000..84838c6 --- /dev/null +++ b/wwb/src/lib.rs @@ -0,0 +1,20 @@ +/// WWB is a 2-player game at heart. Hardcode 2 players only so no one else has to suffer +pub const PLAYER_COUNT: usize = 2; +pub const BOARD_SIZE: u16 = 100 ; + +#[derive(Debug, Default)] +pub struct Game { + /// We're going to need an insanely large number for the turn count. Previous attempts crashed at trillions of turns. + pub turn_count: u128, + /// The players of this "game". + pub players: [Player; PLAYER_COUNT], +} + +#[derive(Debug, Default)] +pub struct Player { + // name: String, + /// The current space the player is on. The default game board is a 1D array of 100 spaces. + pub current_space: u16, + /// The best space they have ever been on. + pub high_score: u16, +} diff --git a/wwb/src/main.rs b/wwb/src/main.rs new file mode 100644 index 0000000..b09c7c4 --- /dev/null +++ b/wwb/src/main.rs @@ -0,0 +1,147 @@ +use chrono::Local; +use env_logger::{Builder, Env, Target}; +use std::io::Write; +use std::process::exit; +use std::sync::{Arc, Mutex}; +use wwb::*; + +/// Roll a d6 and return the result. +fn roll_d6() -> u8 { + fastrand::u8(1..=6) +} + +fn current_space_pretty(current_space: u16) -> String { + if current_space == 0 { + "Start".to_string() + } else if current_space == BOARD_SIZE + 1 { + "Finish".to_string() + } else { + current_space.to_string() + } +} + +fn main() { + let env = Env::new().filter_or("RUST_LOG", "info"); + // Create logger + Builder::from_env(env) + .format(|buf, record| { + writeln!( + buf, + "{} - {}", + Local::now().format("%Y-%m-%dT%H:%M:%S"), + record.args() + ) + }) + .target(Target::Stdout) + .init(); + + let game = Arc::new(Mutex::new(Game::default())); + + // Set up Ctrl-C handler to print the game state before exiting. + let game_clone = Arc::clone(&game); + ctrlc::set_handler(move || { + let game = game_clone.lock().unwrap(); + log::error!("{:#?}", *game); + exit(0); + }) + .expect("Error setting Ctrl-C handler"); + + loop { + let mut game = game.lock().unwrap(); + game.turn_count += 1; + // Game loop: + // Player rolls d6 + // If not 5, next player's turn + // If 5, roll d6 again + // If not 5, go back to space 0 + // If 5 again, go ahead a space + // If players are on the same space that is NOT 0, move BOTH back to space 0 + // Next player's turn + // Roll the dice + + // This will probably not work properly when the turn count goes above 2^32 on a 32-bit machine and 3^64 on a 64-bit machine. + let current_player_number = game.turn_count as usize % PLAYER_COUNT; + let mut current_space = game.players[current_player_number].current_space; + let current_turn_pretty = game + .turn_count + .to_string() + .as_bytes() + .rchunks(3) + .rev() + .map(std::str::from_utf8) + .collect::, _>>() + .unwrap() + .join(","); + + let roll = roll_d6(); + log::debug!( + "Player {} rolled a {} on turn {} from space {}", + current_player_number, + roll, + current_turn_pretty, + current_space_pretty(current_space) + ); + + if roll != 5 { + continue; + } + current_space += 1; + // Check if the player has won by reaching the space after the last space. + if current_space == BOARD_SIZE + 1 { + println!("Player {} has reached the Finish space and won on turn {}!", current_player_number, current_turn_pretty); + break; + } + + if current_space > game.players[current_player_number].high_score { + log::info!( + "Player {} has a new high score of {} on turn {}", + current_player_number, + current_space_pretty(current_space), + current_turn_pretty + ); + game.players[current_player_number].high_score = current_space; + } + + let mut collision: bool = false; + + for player in game.players.iter_mut() { + if player.current_space == current_space && player.current_space != 0 { + log::debug!( + "Two players collided on space {} on turn {}", + current_space_pretty(current_space), + current_turn_pretty + ); + player.current_space = 0; + current_space = 0; + collision = true; + } + } + + // No need to run this logic if there was a collision and the current player is already at 0. + if !collision { + if roll_d6() == 5 { + log::debug!( + "Player {} rolled a 5 again on turn {} gets to stay on space {}", + current_player_number, + current_turn_pretty, + current_space_pretty(current_space) + ); + } else { + log::debug!( + "Player {} rolled a non-5 after rolling a 5 on turn {} and goes back to Start", + current_player_number, + current_turn_pretty + ); + current_space = 0; + } + } + + game.players[current_player_number].current_space = current_space; + log::debug!( + "Player {} ends turn {} on space {}", + current_player_number, + current_turn_pretty, + current_space_pretty(current_space) + ); + } +} -- cgit v1.2.3