diff --git a/90_Tower/rust/src/disk.rs b/90_Tower/rust/src/disk.rs index c15399d9..9d5b4b6d 100644 --- a/90_Tower/rust/src/disk.rs +++ b/90_Tower/rust/src/disk.rs @@ -1,5 +1,5 @@ pub struct Disk { - size: u8, + pub size: u8, } impl Disk { diff --git a/90_Tower/rust/src/game.rs b/90_Tower/rust/src/game.rs index 14f3cf8e..50ef0fe1 100644 --- a/90_Tower/rust/src/game.rs +++ b/90_Tower/rust/src/game.rs @@ -1,33 +1,138 @@ -use crate::{needle::Needle, util}; +use crate::{ + disk::Disk, + needle::Needle, + util::{self, prompt, PromptResult}, +}; pub struct Game { pub needles: Vec, - _moves: usize, + disk_count: u8, + moves: usize, } impl Game { pub fn new() -> Self { let mut needles = Vec::new(); + let disk_count = util::get_disk_count(); for i in 1..=3 { let disks = match i { - 1 => util::generate_disks(4), - 2 => util::generate_disks(3), - _ => Vec::new(), + 1 => { + let mut disks = Vec::new(); + + let mut half_size = 7; + for _ in (1..=disk_count).rev() { + disks.push(Disk::new(half_size * 2 + 1)); + half_size -= 1; + } + + disks + } + 2 | 3 => Vec::new(), + _ => panic!("THERE MUST BE EXACTLY THREE NEEDLES!"), }; - needles.push(Needle { disks, number:i }); + needles.push(Needle { disks, number: i }); } - Game { needles, _moves: 0 } + Game { + needles, + disk_count, + moves: 0, + } + } + + pub fn update(&mut self) -> bool { + self.draw(); + + loop { + let (disk_index, from_needle_index) = self.get_disk_to_move(); + let to_needle_index = self.ask_which_needle(); + + if from_needle_index == to_needle_index { + println!("DISK IS ALREADY AT THAT NEEDLE!"); + break; + } + + let to_needle = &self.needles[to_needle_index]; + + if to_needle.disks.len() == 0 + || to_needle.disks[0].size > self.needles[from_needle_index].disks[disk_index].size + { + self.move_disk(disk_index, from_needle_index, to_needle_index); + break; + } else { + println!("CAN'T PLACE ON A SMALLER DISK!"); + } + } + + if self.needles[2].disks.len() == self.disk_count as usize { + self.draw(); + println!("CONGRATULATIONS!!"); + println!("YOU HAVE PERFORMED THE TASK IN {} MOVES.", self.moves); + return true; + } + + false } pub fn draw(&self) { + println!(""); for r in (1..=7).rev() { for n in &self.needles { n.draw(r) } println!(""); } + println!(""); + } + + fn get_disk_to_move(&self) -> (usize, usize) { + loop { + if let PromptResult::Number(n) = prompt(true, "WHICH DISK WOULD YOU LIKE TO MOVE?") { + let smallest_disk = 15 - ((self.disk_count - 1) * 2); + + if n < smallest_disk as i32 || n > 15 || (n % 2) == 0 { + println!("PLEASE ENTER A VALID DISK!") + } else { + for (n_i, needle) in self.needles.iter().enumerate() { + if let Some((i, _)) = needle + .disks + .iter() + .enumerate() + .find(|(_, disk)| disk.size == n as u8) + { + if i == (needle.disks.len() - 1) { + return (i, n_i); + } + + println!("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE."); + } + } + } + } + } + } + + fn ask_which_needle(&self) -> usize { + loop { + if let PromptResult::Number(n) = prompt(true, "PLACE DISK ON WHICH NEEDLE?") { + if n <= 0 || n > 3 { + println!("PLEASE ENTER A VALID NEEDLE."); + } else { + return (n - 1) as usize; + } + } + } + } + + fn move_disk(&mut self, disk: usize, from: usize, to: usize) { + let from = &mut self.needles[from]; + let size = from.disks[disk].size; + + from.disks.remove(disk); + self.needles[to].add(size); + + self.moves += 1; } } diff --git a/90_Tower/rust/src/main.rs b/90_Tower/rust/src/main.rs index ae54ccb7..5ec26079 100644 --- a/90_Tower/rust/src/main.rs +++ b/90_Tower/rust/src/main.rs @@ -1,4 +1,5 @@ use game::Game; +use util::PromptResult; mod disk; mod game; @@ -6,6 +7,44 @@ mod needle; mod util; fn main() { - let game = Game::new(); - game.draw(); + println!("\n\n\t\tTOWERS"); + println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + + println!("TOWERS OF HANOI PUZZLE\n"); + + println!("YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT"); + println!("TOWER, ON AT A TIME, NEVER PUTTING A LARGER DISK ON A"); + println!("SMALLER DISK.\n"); + + let mut quit = false; + + while !quit { + let mut game = Game::new(); + + println!(""); + println!( + r#"IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE. +3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE, +7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH +2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS +THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES +ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL +START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM +TO NEEDLE 3. + +GOOD LUCK!"# + ); + + loop { + if game.update() { + break; + } + } + + if let PromptResult::YesNo(r) = util::prompt(false, "TRY AGAIN (YES OR NO)?") { + quit = !r; + } + } + + println!("THANKS FOR THE GAME!"); } diff --git a/90_Tower/rust/src/needle.rs b/90_Tower/rust/src/needle.rs index b1e1d44c..b2286eaf 100644 --- a/90_Tower/rust/src/needle.rs +++ b/90_Tower/rust/src/needle.rs @@ -7,8 +7,6 @@ pub struct Needle { impl Needle { pub fn draw(&self, row: u8) { - //println!("printing row: {}", row); - let row = row as usize; if self.disks.len() >= row { @@ -21,4 +19,8 @@ impl Needle { print!("{offset} "); } } + + pub fn add(&mut self, size: u8) { + self.disks.push(Disk { size }); + } } diff --git a/90_Tower/rust/src/util.rs b/90_Tower/rust/src/util.rs index 76ff38b7..9feacf94 100644 --- a/90_Tower/rust/src/util.rs +++ b/90_Tower/rust/src/util.rs @@ -1,19 +1,51 @@ -use crate::disk::Disk; +use std::io; -pub fn generate_disks(amount: u8) -> Vec { - if amount > 7 { - println!("CANNOT HAVE MORE THAN 7 DISKS!"); - } - - // check for if amount == 0 - - let mut disks = Vec::new(); - - let mut half_size = 7; - for _ in (1..=amount).rev() { - disks.push(Disk::new(half_size * 2 + 1)); - half_size -= 1; - } - - disks +pub enum PromptResult { + Number(i32), + YesNo(bool), +} + +pub fn prompt(numeric: bool, msg: &str) -> PromptResult { + use PromptResult::*; + loop { + println!("{}", msg); + + let mut input = String::new(); + + io::stdin() + .read_line(&mut input) + .expect("Failed to read line."); + + let input = input.trim().to_string(); + + if numeric { + if let Ok(n) = input.parse::() { + return Number(n); + } + + println!("PLEASE ENTER A 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."), + } + } + } +} + +pub fn get_disk_count() -> u8 { + loop { + if let PromptResult::Number(n) = + prompt(true, "HOW MANY DISKS DO YOU WANT TO MOVE (7 IS MAX)?") + { + if n <= 2 { + println!("THERE MUST BE AT LEAST 3 DISKS!") + } else if n > 7 { + println!("THERE CAN'T BE MORE THAN 7 DISKS!") + } else { + return n as u8; + } + } + } }