diff --git a/72_Queen/rust/Cargo.toml b/72_Queen/rust/Cargo.toml new file mode 100644 index 000000000..3b1d02f52 --- /dev/null +++ b/72_Queen/rust/Cargo.toml @@ -0,0 +1,9 @@ +[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] +rand = "0.8.5" diff --git a/72_Queen/rust/src/ai.rs b/72_Queen/rust/src/ai.rs new file mode 100644 index 000000000..cf4d03103 --- /dev/null +++ b/72_Queen/rust/src/ai.rs @@ -0,0 +1,30 @@ +use crate::util; + +const PREFERRED_MOVES: [u8; 5] = [158, 72, 75, 126, 127]; +const SAFE_MOVES: [u8; 2] = [44, 41]; + +pub fn get_computer_move(loc: u8) -> u8 { + if SAFE_MOVES.contains(&loc) { + return random_move(loc); + } + + for m in PREFERRED_MOVES { + if util::is_move_legal(loc, m) && m != loc { + return m; + } + } + + random_move(loc) +} + +fn random_move(l: u8) -> u8 { + let r: f32 = rand::random(); + + if r > 0.6 { + l + 11 + } else if r > 0.3 { + l + 21 + } else { + l + 10 + } +} diff --git a/72_Queen/rust/src/game.rs b/72_Queen/rust/src/game.rs new file mode 100644 index 000000000..2445980b6 --- /dev/null +++ b/72_Queen/rust/src/game.rs @@ -0,0 +1,180 @@ +use crate::{ + ai, + util::{self, prompt, PromptResult::*}, +}; + +pub struct Game { + blocks: [u8; 64], + location: Option, + player_move: bool, + show_board: bool, + forfeit: bool, +} + +impl Game { + pub fn new() -> Self { + let mut blocks = [0 as u8; 64]; + + let mut block = 91; + let mut i = 0; + + for _ in 0..8 { + for _ in 0..8 { + block -= 10; + blocks[i] = block; + i += 1; + } + block += 91; + } + + Game { + blocks, + location: None, + player_move: true, + show_board: false, + forfeit: false, + } + } + + pub fn update(&mut self) -> bool { + let mut still_going = true; + + if let Some(l) = self.location { + if self.show_board { + self.draw(l); + } + + match self.player_move { + true => self.player_move(l), + false => { + std::thread::sleep(std::time::Duration::from_secs(1)); + let loc = ai::get_computer_move(l); + println!("COMPUTER MOVES TO SQUARE {}", loc); + self.location = Some(loc); + } + } + + still_going = self.check_location(); + } else { + self.set_start_location(); + } + self.player_move = !self.player_move; + still_going + } + + fn set_start_location(&mut self) { + self.draw(0); + + if let YesNo(yes) = prompt(Some(false), "UPDATE BOARD?") { + self.show_board = yes; + } + + loop { + if let Numeric(n) = prompt(Some(true), "WHERE WOULD YOU LIKE TO START?") { + let n = n as u8; + + if util::is_legal_start(n) { + self.location = Some(n); + break; + } else { + println!("PLEASE READ THE DIRECTIONS AGAIN.\nYOU HAVE BEGUN ILLEGALLY.\n") + } + } + } + } + + fn player_move(&mut self, loc: u8) { + loop { + if let Numeric(n) = prompt(Some(true), "WHAT IS YOUR MOVE?") { + if n == 0 { + self.forfeit = true; + break; + } + + let n = n as u8; + + if util::is_move_legal(loc, n) { + self.location = Some(n); + break; + } else { + println!("Y O U C H E A T . . . TRY AGAIN? "); + } + } + } + } + + fn check_location(&self) -> bool { + if self.location == Some(158) { + util::print_gameover(self.player_move, self.forfeit); + return false; + } + + true + } + + fn draw(&self, loc: u8) { + let draw_h_border = |top: Option| { + let corners; + + if let Some(top) = top { + if top { + corners = ("┌", "┐", "┬"); + } else { + corners = ("└", "┘", "┴"); + } + } else { + corners = ("├", "┤", "┼"); + } + + print!("{}", corners.0); + + for i in 0..8 { + let corner = if i == 7 { corners.1 } else { corners.2 }; + + print!("───{}", corner); + } + println!(); + }; + + draw_h_border(Some(true)); + + let mut column = 0; + let mut row = 0; + + for block in self.blocks.iter() { + let block = *block as u8; + + let n = if block == loc { + format!("│{}", "*Q*".to_string()) + } else { + let b = block.to_string(); + + if block > 99 { + format!("│{}", b) + } else { + format!("│{} ", b) + } + }; + + print!("{}", n); + + column += 1; + + if column != 1 && (column % 8) == 0 { + column = 0; + row += 1; + + print!("│"); + println!(); + + if row == 8 { + draw_h_border(Some(false)); + } else { + draw_h_border(None); + } + } + } + + println!(); + } +} diff --git a/72_Queen/rust/src/main.rs b/72_Queen/rust/src/main.rs new file mode 100644 index 000000000..57d65e0f4 --- /dev/null +++ b/72_Queen/rust/src/main.rs @@ -0,0 +1,29 @@ +use crate::{ + game::Game, + util::{prompt, PromptResult::*}, +}; + +mod game; +mod util; +mod ai; + +fn main() { + util::intro(); + + loop { + let mut game = Game::new(); + + loop { + if !game.update() { + break; + } + } + + if let YesNo(y) = prompt(Some(false), "\nANYONE ELSE CARE TO TRY?") { + if !y { + println!("OK --- THANKS AGAIN."); + break; + } + } + } +} diff --git a/72_Queen/rust/src/util.rs b/72_Queen/rust/src/util.rs new file mode 100644 index 000000000..3439aabb0 --- /dev/null +++ b/72_Queen/rust/src/util.rs @@ -0,0 +1,114 @@ +use std::io; + +pub enum PromptResult { + Normal(String), + YesNo(bool), + Numeric(i32), +} + +pub fn prompt(is_numeric: Option, msg: &str) -> PromptResult { + use PromptResult::*; + + println!("{msg}"); + + loop { + let mut input = String::new(); + + io::stdin() + .read_line(&mut input) + .expect("**Failed to read input**"); + + if let Some(is_numeric) = is_numeric { + let input = input.trim(); + + if is_numeric { + if let Ok(n) = input.parse::() { + return Numeric(n); + } + println!("PLEASE ENTER A VALID NUMBER!"); + } else { + match input.to_uppercase().as_str() { + "YES" | "Y" => return YesNo(true), + "NO" | "N" => return YesNo(false), + _ => println!("PLEASE ENTER (Y)ES OR (N)O."), + } + } + } else { + return Normal(input); + } + } +} + +pub fn is_move_legal(loc: u8, mov: u8) -> bool { + let dt: i32 = mov as i32 - loc as i32; + + if dt.is_negative() { + return false; + } + + if (dt % 21) == 0 || (dt % 10) == 0 || (dt % 11) == 0 { + return true; + } + + false +} + +pub fn is_legal_start(loc: u8) -> bool { + let mut legal_spots = Vec::new(); + let start: u8 = 11; + + legal_spots.push(start); + + for i in 1..=7 { + legal_spots.push(start + (10 * i)); + } + + for i in 1..=7 { + legal_spots.push(start + (11 * i)); + } + + if legal_spots.contains(&loc) { + true + } else { + false + } +} + +pub fn print_gameover(win: bool, forfeit: bool) { + if win { + println!("C O N G R A T U L A T I O N S . . .\nYOU HAVE WON--VERY WELL PLAYED."); + println!( + "IT LOOKS LIKE I HAVE MET MY MATCH.\nTHANKS FOR PLAYING---I CAN'T WIN ALL THE TIME.\n", + ); + } else { + if forfeit { + println!("IT LOOKS LIKE I HAVE WON BY FORFEIT.\n"); + } else { + println!("NICE TRY, BUT IT LOOKS LIKE I HAVE WON.\nTHANKS FOR PLAYING."); + } + } +} + +pub fn intro() { + println!("\n\n\t\tQUEEN"); + println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + + if let PromptResult::YesNo(yes) = prompt(Some(false), "DO YOU WANT INSTRUCTIONS?") { + if yes { + println!( + r#"WE ARE GOING TO PLAY A GAME BASED ON ONE OF THE CHESS +MOVES. OUR QUEEN WILL BE ABLE TO MOVE ONLY TO THE LEFT, +DOWN, OR DIAGONALLY DOWN AND TO THE LEFT. +THE OBJECT OF THE GAME IS TO PLACE THE QUEEN IN THE LOWER +LEFT HAND SQUARE BY ALTERNATING MOVES BETWEEN YOU AND THE +COMPUTER. THE FIRST ONE TO PLACE THE QUEEN THERE WINS. +YOU GO FIRST AND PLACE THE QUEEN IN ANY ONE OF THE SQUARES +ON THE TOP ROW OR RIGHT HAND COLUMN. +THAT WILL BE YOUR FIRST MOVE. +WE ALTERNATE MOVES. +YOU MAY FORFEIT BY TYPING '0' AS YOUR MOVE. +BE SURE TO PRESS THE RETURN KEY AFTER EACH RESPONSE."# + ) + } + } +}