diff --git a/06_Banner/rust/src/main.rs b/06_Banner/rust/src/main.rs deleted file mode 100644 index dcfe9a4e0..000000000 --- a/06_Banner/rust/src/main.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::collections::HashMap; -use std::io::{self, stdin}; - -const LETTERS: [(char, [usize; 7]); 42] = [ - (' ', [0, 0, 0, 0, 0, 0, 0]), - ('A', [505, 37, 35, 34, 35, 37, 505]), - ('G', [125, 131, 258, 258, 290, 163, 101]), - ('E', [512, 274, 274, 274, 274, 258, 258]), - ('T', [2, 2, 2, 512, 2, 2, 2]), - ('W', [256, 257, 129, 65, 129, 257, 256]), - ('L', [512, 257, 257, 257, 257, 257, 257]), - ('S', [69, 139, 274, 274, 274, 163, 69]), - ('O', [125, 131, 258, 258, 258, 131, 125]), - ('N', [512, 7, 9, 17, 33, 193, 512]), - ('F', [512, 18, 18, 18, 18, 2, 2]), - ('K', [512, 17, 17, 41, 69, 131, 258]), - ('B', [512, 274, 274, 274, 274, 274, 239]), - ('D', [512, 258, 258, 258, 258, 131, 125]), - ('H', [512, 17, 17, 17, 17, 17, 512]), - ('M', [512, 7, 13, 25, 13, 7, 512]), - ('?', [5, 3, 2, 354, 18, 11, 5]), - ('U', [128, 129, 257, 257, 257, 129, 128]), - ('R', [512, 18, 18, 50, 82, 146, 271]), - ('P', [512, 18, 18, 18, 18, 18, 15]), - ('Q', [125, 131, 258, 258, 322, 131, 381]), - ('Y', [8, 9, 17, 481, 17, 9, 8]), - ('V', [64, 65, 129, 257, 129, 65, 64]), - ('X', [388, 69, 41, 17, 41, 69, 388]), - ('Z', [386, 322, 290, 274, 266, 262, 260]), - ('I', [258, 258, 258, 512, 258, 258, 258]), - ('C', [125, 131, 258, 258, 258, 131, 69]), - ('J', [65, 129, 257, 257, 257, 129, 128]), - ('1', [0, 0, 261, 259, 512, 257, 257]), - ('2', [261, 387, 322, 290, 274, 267, 261]), - ('*', [69, 41, 17, 512, 17, 41, 69]), - ('3', [66, 130, 258, 274, 266, 150, 100]), - ('4', [33, 49, 41, 37, 35, 512, 33]), - ('5', [160, 274, 274, 274, 274, 274, 226]), - ('6', [194, 291, 293, 297, 305, 289, 193]), - ('7', [258, 130, 66, 34, 18, 10, 8]), - ('8', [69, 171, 274, 274, 274, 171, 69]), - ('9', [263, 138, 74, 42, 26, 10, 7]), - ('=', [41, 41, 41, 41, 41, 41, 41]), - ('!', [1, 1, 1, 384, 1, 1, 1]), - ('0', [57, 69, 131, 258, 131, 69, 57]), - ('.', [1, 1, 129, 449, 129, 1, 1]), -]; - -fn main() { - print_banner().ok(); -} - -fn read_input() -> io::Result { - let mut input = String::new(); - stdin().read_line(&mut input)?; - Ok(input.trim().to_uppercase()) -} - -fn read_input_number() -> io::Result { - loop { - match read_input()?.parse::() { - Ok(num) => { - if num > 0 { - break Ok(num); - } else { - println!("Must be greater than zero"); - } - } - Err(_) => println!("Please enter a number greater than zero"), - } - } -} - -fn user_input() -> io::Result<(usize, usize, bool, String, String)> { - println!("Horizontal"); - let horizontal = read_input_number()?; - println!(); - - println!("Vertical "); - let vertical = read_input_number()?; - println!(); - - println!("Centered "); - let is_entered = read_input()?.starts_with('Y'); - println!(); - - println!("Character (type 'ALL' if you want character being printed) "); - let character = read_input()?; - println!(); - - println!("Statement "); - let statement = read_input()?; - println!(); - - // This means to prepare printer, just press Enter - println!("Set page "); - read_input()?; - println!(); - - Ok((horizontal, vertical, is_entered, character, statement)) -} - -fn print_banner() -> io::Result<()> { - let letters = HashMap::from(LETTERS); - - let (horizontal, vertical, is_entered, character, statement) = user_input()?; - - for statement_char in statement.chars() { - let x_str = if character == "ALL" { - statement_char.to_string() - } else { - character.clone() - }; - - if x_str == " " { - for _ in 0..(7 * horizontal) { - println!(); - } - continue; - } - - let mut s = [0; 7]; - if let Some(ss) = letters.get(&statement_char) { - s.copy_from_slice(ss); - } else { - println!("\nCannot print {statement_char}\n"); - } - let mut f = [0; 7]; - let mut j = [false; 9]; - - for u in 0..s.len() { - for k in (0..=8).rev() { - let mask = 1usize << k; - j[8 - k] = if mask >= s[u] { - false - } else { - s[u] -= mask; - true - }; - if s[u] == 1 { - f[u] = 8 - k; - break; - } - } - - let offset_str = if is_entered { - let n = (63 * 2 - vertical * 9) / 2 / x_str.len() + 1; - " ".repeat(n) - } else { - "".to_string() - }; - - let mut content_str = String::new(); - for b in &j[0..=f[u]] { - if *b { - content_str += &x_str.repeat(vertical); - } else { - content_str += &" ".repeat(x_str.len()).repeat(vertical); - } - } - - for _ in 0..horizontal { - println!("{offset_str}{content_str}"); - } - } - - for _ in 0..(2 * horizontal - 1) { - println!(); - } - } - - Ok(()) -} 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/06_Banner/rust/Cargo.toml b/08_Batnum/rust/Cargo.toml similarity index 100% rename from 06_Banner/rust/Cargo.toml rename to 08_Batnum/rust/Cargo.toml 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 |