aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Strapp <matt@mattstrapp.net>2024-12-12 11:09:20 -0600
committerMatt Strapp <matt@mattstrapp.net>2024-12-12 11:09:20 -0600
commitd2ab1b9a2dd138621d354674962d6bce15de3207 (patch)
treeaac913b672ace3e5482d736165f632c0e865fb95
downloadwwb-d2ab1b9a2dd138621d354674962d6bce15de3207.tar
wwb-d2ab1b9a2dd138621d354674962d6bce15de3207.tar.gz
wwb-d2ab1b9a2dd138621d354674962d6bce15de3207.tar.bz2
wwb-d2ab1b9a2dd138621d354674962d6bce15de3207.tar.lz
wwb-d2ab1b9a2dd138621d354674962d6bce15de3207.tar.xz
wwb-d2ab1b9a2dd138621d354674962d6bce15de3207.tar.zst
wwb-d2ab1b9a2dd138621d354674962d6bce15de3207.zip
Initial commit
Signed-off-by: Matt Strapp <matt@mattstrapp.net>
-rw-r--r--.gitignore21
-rw-r--r--Cargo.toml9
-rw-r--r--rustfmt.toml0
-rw-r--r--wwb/Cargo.toml11
-rw-r--r--wwb/report.json5
-rw-r--r--wwb/src/lib.rs20
-rw-r--r--wwb/src/main.rs147
7 files changed, 213 insertions, 0 deletions
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
--- /dev/null
+++ b/rustfmt.toml
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::<Result<Vec<&str>, _>>()
+ .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)
+ );
+ }
+}