Merge pull request #909 from apeng2012/08-batnum-rust

08 batnum rust
This commit is contained in:
Jeff Atwood
2023-12-01 13:10:19 -08:00
committed by GitHub
5 changed files with 248 additions and 175 deletions

View File

@@ -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<String> {
let mut input = String::new();
stdin().read_line(&mut input)?;
Ok(input.trim().to_uppercase())
}
fn read_input_number() -> io::Result<usize> {
loop {
match read_input()?.parse::<usize>() {
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(())
}

7
08_Batnum/rust/Cargo.lock generated Normal file
View File

@@ -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"

239
08_Batnum/rust/src/main.rs Normal file
View File

@@ -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::<usize>() {
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(&params);
}
}

View File

@@ -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 |