From 7fa5200072a437818783e2b550fbcca9cbf24e79 Mon Sep 17 00:00:00 2001 From: apeng2012 Date: Sat, 2 Dec 2023 03:39:54 +0800 Subject: [PATCH] Port 08_Batnum to Rust --- 08_Batnum/rust/Cargo.lock | 7 ++ 08_Batnum/rust/Cargo.toml | 8 ++ 08_Batnum/rust/src/main.rs | 239 +++++++++++++++++++++++++++++++++++++ README.md | 4 +- 4 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 08_Batnum/rust/Cargo.lock create mode 100644 08_Batnum/rust/Cargo.toml create mode 100644 08_Batnum/rust/src/main.rs diff --git a/08_Batnum/rust/Cargo.lock b/08_Batnum/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/08_Batnum/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/08_Batnum/rust/Cargo.toml b/08_Batnum/rust/Cargo.toml new file mode 100644 index 000000000..1ec696335 --- /dev/null +++ b/08_Batnum/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/08_Batnum/rust/src/main.rs b/08_Batnum/rust/src/main.rs new file mode 100644 index 000000000..761e1939b --- /dev/null +++ b/08_Batnum/rust/src/main.rs @@ -0,0 +1,239 @@ +use std::io::{self, Write}; + +/// Print out the introduction and rules for the game. +fn print_intro() { + println!(); + println!(); + println!("{:>33}", "BATNUM"); + println!("{:>15}", "CREATIVE COMPUTING MORRISSTOWN, NEW JERSEY"); + println!(); + println!(); + println!(); + println!("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE"); + println!("COMPUTER IS YOUR OPPONENT."); + println!(); + println!("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU"); + println!("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE."); + println!("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR"); + println!("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS."); + println!("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME."); + println!("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING."); + println!(); +} + +/// This requests the necessary parameters to play the game. +/// five game parameters: +/// * pile_size - the starting size of the object pile +/// * min_select - minimum selection that can be made on each turn +/// * max_select - maximum selection that can be made on each turn +/// * start_option - computer first or player first +/// * win_option - goal is to take the last object +/// or the goal is to not take the last object +struct Params { + pub pile_size: usize, + pub min_select: usize, + pub max_select: usize, + pub start_option: StartOption, + pub win_option: WinOption, +} + +#[derive(PartialEq, Eq)] +enum StartOption { + ComputerFirst, + PlayerFirst, +} + +#[derive(PartialEq, Eq)] +enum WinOption { + TakeLast, + AvoidLast, +} + +impl Params { + pub fn get_params() -> Self { + let pile_size = Self::get_pile_size(); + let (min_select, max_select) = Self::get_min_max(); + let start_option = Self::get_start_option(); + let win_option = Self::get_win_option(); + + Self { + pile_size, + min_select, + max_select, + start_option, + win_option, + } + } + + fn get_pile_size() -> usize { + print!("ENTER PILE SIZE "); + let _ = io::stdout().flush(); + read_input_integer() + } + + fn get_win_option() -> WinOption { + print!("ENTER WIN OPTION: 1 TO TAKE LAST, 2 TO AVOID LAST: "); + let _ = io::stdout().flush(); + + loop { + match read_input_integer() { + 1 => { + return WinOption::TakeLast; + } + 2 => { + return WinOption::AvoidLast; + } + _ => { + print!("Please enter 1 or 2 "); + let _ = io::stdout().flush(); + continue; + } + } + } + } + + fn get_start_option() -> StartOption { + print!("ENTER START OPTION: 1 COMPUTER FIRST, 2 YOU FIRST "); + let _ = io::stdout().flush(); + + loop { + match read_input_integer() { + 1 => { + return StartOption::ComputerFirst; + } + 2 => { + return StartOption::PlayerFirst; + } + _ => { + print!("Please enter 1 or 2 "); + let _ = io::stdout().flush(); + continue; + } + } + } + } + + fn get_min_max() -> (usize, usize) { + print!("ENTER MIN "); + let _ = io::stdout().flush(); + let min = read_input_integer(); + + print!("ENTER MAX "); + let _ = io::stdout().flush(); + let max = read_input_integer(); + + (min, max) + } +} + +fn read_input_integer() -> usize { + loop { + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + match input.trim().parse::() { + Ok(num) => { + if num == 0 { + print!("Must be greater than zero "); + let _ = io::stdout().flush(); + continue; + } + return num; + } + Err(_err) => { + print!("Please enter a number greater than zero "); + let _ = io::stdout().flush(); + continue; + } + } + } +} + +fn player_move(pile_size: &mut usize, params: &Params) -> bool { + loop { + print!("YOUR MOVE "); + let _ = io::stdout().flush(); + + let player_move = read_input_integer(); + if player_move == 0 { + println!("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT."); + return true; + } + if player_move > params.max_select || player_move < params.min_select { + println!("ILLEGAL MOVE, REENTER IT"); + continue; + } + *pile_size -= player_move; + if *pile_size == 0 { + if params.win_option == WinOption::AvoidLast { + println!("TOUGH LUCK, YOU LOSE."); + } else { + println!("CONGRATULATIONS, YOU WIN.") + } + return true; + } + return false; + } +} + +fn computer_pick(pile_size: usize, params: &Params) -> usize { + let q = if params.win_option == WinOption::AvoidLast { + pile_size - 1 + } else { + pile_size + }; + let c = params.min_select + params.max_select; + let computer_pick = q - (c * (q / c)); + let computer_pick = if computer_pick < params.min_select { + params.min_select + } else { + computer_pick + }; + if computer_pick > params.max_select { + params.max_select + } else { + computer_pick + } +} + +fn computer_move(pile_size: &mut usize, params: &Params) -> bool { + if params.win_option == WinOption::TakeLast && *pile_size <= params.max_select { + println!("COMPUTER TAKES {pile_size} AND WINS."); + return true; + } + if params.win_option == WinOption::AvoidLast && *pile_size >= params.min_select { + println!("COMPUTER TAKES {} AND LOSES.", params.min_select); + return true; + } + + let curr_sel = computer_pick(*pile_size, params); + *pile_size -= curr_sel; + println!("COMPUTER TAKES {curr_sel} AND LEAVES {pile_size}"); + false +} + +fn play_game(params: &Params) { + let mut pile_size = params.pile_size; + + if params.start_option == StartOption::ComputerFirst && computer_move(&mut pile_size, params) { + return; + } + + loop { + if player_move(&mut pile_size, params) { + return; + } + if computer_move(&mut pile_size, params) { + return; + } + } +} + +fn main() -> ! { + loop { + print_intro(); + let params = Params::get_params(); + play_game(¶ms); + } +} diff --git a/README.md b/README.md index 0ddd2748f..31a2ada8a 100644 --- a/README.md +++ b/README.md @@ -83,9 +83,9 @@ NOTE: per [the official blog post announcement](https://blog.codinghorror.com/up | 03_Animal | x | x | x | x | x | x | x | x | x | x | | 04_Awari | x | x | x | | | x | x | x | x | x | | 05_Bagels | x | x | x | x | x | x | x | x | x | x | -| 06_Banner | x | x | x | | | x | x | x | | x | +| 06_Banner | x | x | x | | | x | x | x | x | x | | 07_Basketball | x | x | x | | | x | x | x | | x | -| 08_Batnum | x | x | x | | | x | x | x | | x | +| 08_Batnum | x | x | x | | | x | x | x | x | x | | 09_Battle | x | x | x | | | | x | | | x | | 10_Blackjack | x | x | x | | | | x | x | x | x | | 11_Bombardment | x | x | x | | | x | x | x | x | x |