mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
8
90_Tower/rust/Cargo.toml
Normal file
8
90_Tower/rust/Cargo.toml
Normal file
@@ -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]
|
||||||
28
90_Tower/rust/src/disk.rs
Normal file
28
90_Tower/rust/src/disk.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
pub struct Disk {
|
||||||
|
pub size: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk {
|
||||||
|
pub fn new(size: u8) -> Self {
|
||||||
|
Disk { size }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self) {
|
||||||
|
let draw_space = || {
|
||||||
|
let space_amount = (15 - self.size) / 2;
|
||||||
|
|
||||||
|
if space_amount > 0 {
|
||||||
|
for _ in 0..space_amount {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_space();
|
||||||
|
for _ in 0..self.size {
|
||||||
|
print!("*");
|
||||||
|
}
|
||||||
|
draw_space();
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
138
90_Tower/rust/src/game.rs
Normal file
138
90_Tower/rust/src/game.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
use crate::{
|
||||||
|
disk::Disk,
|
||||||
|
needle::Needle,
|
||||||
|
util::{self, prompt, PromptResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Game {
|
||||||
|
pub needles: Vec<Needle>,
|
||||||
|
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 => {
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
90_Tower/rust/src/main.rs
Normal file
50
90_Tower/rust/src/main.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use game::Game;
|
||||||
|
use util::PromptResult;
|
||||||
|
|
||||||
|
mod disk;
|
||||||
|
mod game;
|
||||||
|
mod needle;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
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!");
|
||||||
|
}
|
||||||
26
90_Tower/rust/src/needle.rs
Normal file
26
90_Tower/rust/src/needle.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use crate::disk::Disk;
|
||||||
|
|
||||||
|
pub struct Needle {
|
||||||
|
pub disks: Vec<Disk>,
|
||||||
|
pub number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Needle {
|
||||||
|
pub fn draw(&self, row: u8) {
|
||||||
|
let row = row as usize;
|
||||||
|
|
||||||
|
if self.disks.len() >= row {
|
||||||
|
self.disks[row - 1].draw();
|
||||||
|
} else {
|
||||||
|
let offset = " ";
|
||||||
|
|
||||||
|
print!("{offset}");
|
||||||
|
print!("*");
|
||||||
|
print!("{offset} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, size: u8) {
|
||||||
|
self.disks.push(Disk { size });
|
||||||
|
}
|
||||||
|
}
|
||||||
51
90_Tower/rust/src/util.rs
Normal file
51
90_Tower/rust/src/util.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use std::io;
|
||||||
|
|
||||||
|
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::<i32>() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user