mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-26 04:41:52 -08:00
203 lines
6.4 KiB
Rust
203 lines
6.4 KiB
Rust
use rand::{Rng, thread_rng, prelude::SliceRandom};
|
|
use std::{io, collections::HashSet};
|
|
|
|
fn main() {
|
|
//DATA
|
|
enum Direction {
|
|
LEFT=0,
|
|
UP=1,
|
|
RIGHT=2,
|
|
DOWN=3,
|
|
}
|
|
impl Direction {
|
|
fn val(&self) -> usize {
|
|
match self {
|
|
Direction::LEFT=>0,
|
|
Direction::UP=>1,
|
|
Direction::RIGHT=>2,
|
|
Direction::DOWN=>3,
|
|
}
|
|
}
|
|
}
|
|
const EXIT_DOWN:usize = 1;
|
|
const EXIT_RIGHT:usize = 2;
|
|
let mut rng = thread_rng(); //rng
|
|
/*
|
|
vector of:
|
|
vectors of:
|
|
integers
|
|
Initially set to 0, unprocessed cells.
|
|
Filled in with consecutive non-zero numbers as cells are processed
|
|
*/
|
|
let mut used; //2d vector
|
|
/*
|
|
vector of:
|
|
vectors of:
|
|
integers
|
|
Remains 0 if there is no exit down or right
|
|
Set to 1 if there is an exit down
|
|
Set to 2 if there is an exit right
|
|
Set to 3 if there are exits down and right
|
|
*/
|
|
let mut walls; //2d vector
|
|
let width;
|
|
let height;
|
|
let entrance_column; //rng, column of entrance
|
|
let mut row;
|
|
let mut col;
|
|
let mut count;
|
|
|
|
|
|
|
|
//print welcome message
|
|
println!("
|
|
AMAZING PROGRAM
|
|
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n\n");
|
|
|
|
//prompt for input
|
|
width = get_user_input("What is your width?");
|
|
print!("\n"); //one blank line below
|
|
height = get_user_input("What is your height?");
|
|
print!("\n\n\n\n");//4 blank lines below
|
|
|
|
//generate maze
|
|
//initialize used and wall vectors
|
|
//2d vectors when you don't know the sizes at compile time are wierd, but here's how it's done :)
|
|
used = vec![0; (width * height) as usize];
|
|
let mut used: Vec<_> = used.as_mut_slice().chunks_mut(width as usize).collect();
|
|
let used = used.as_mut_slice(); //accessible as used[][]
|
|
//2d vectors when you don't know the sizes at compile time are wierd, but here's how it's done :)
|
|
walls = vec![0; (width * height) as usize];
|
|
let mut walls: Vec<_> = walls.as_mut_slice().chunks_mut(width as usize).collect();
|
|
let walls = walls.as_mut_slice(); //accessible as walls[][]
|
|
|
|
entrance_column=rng.gen_range(0..width-1);
|
|
row = 0;
|
|
col = entrance_column;
|
|
count = 1;
|
|
used[row][col] = count;
|
|
count += 1;
|
|
|
|
while count != width*height + 1 {
|
|
//remove possible directions that are blocked or
|
|
//hit cells already processed
|
|
let mut possible_directions: HashSet<usize> = vec![Direction::LEFT.val(),Direction::UP.val(),Direction::RIGHT.val(),Direction::DOWN.val()].into_iter().collect(); //create it as a vector bc that's easy, then convert it to a hashset
|
|
if col==0 || used[row][col-1]!=0 {
|
|
possible_directions.remove(&Direction::LEFT.val());
|
|
}
|
|
if row==0 || used[row-1][col]!=0 {
|
|
possible_directions.remove(&Direction::UP.val());
|
|
}
|
|
if col==width-1 || used[row][col+1]!=0 {
|
|
possible_directions.remove(&Direction::RIGHT.val());
|
|
}
|
|
if row==height-1 || used[row+1][col]!=0 {
|
|
possible_directions.remove(&Direction::DOWN.val());
|
|
}
|
|
|
|
//If we can move in a direction, move and make opening
|
|
if possible_directions.len() != 0 { //all values in possible_directions are not NONE
|
|
let pos_dir_vec: Vec<_> = possible_directions.into_iter().collect(); // convert the set to a vector to get access to the choose method
|
|
//select a random direction
|
|
match pos_dir_vec.choose(&mut rng).expect("error") {
|
|
0=> {
|
|
col -= 1;
|
|
walls[row][col] = EXIT_RIGHT;
|
|
},
|
|
1=> {
|
|
row -= 1;
|
|
walls[row][col] = EXIT_DOWN;
|
|
},
|
|
2=>{
|
|
walls[row][col] = walls[row][col] + EXIT_RIGHT;
|
|
col += 1;
|
|
},
|
|
3=>{
|
|
walls[row][col] = walls[row][col] + EXIT_DOWN;
|
|
row += 1;
|
|
},
|
|
_=>{},
|
|
}
|
|
used[row][col]=count;
|
|
count += 1;
|
|
}
|
|
//otherwise, move to the next used cell, and try again
|
|
else {
|
|
loop {
|
|
if col != width-1 {col += 1;}
|
|
else if row != height-1 {row+=1; col=0;}
|
|
else {row=0;col=0;}
|
|
|
|
if used[row][col] != 0 {break;}
|
|
}
|
|
}
|
|
}
|
|
// Add a random exit
|
|
col=rng.gen_range(0..width);
|
|
row=height-1;
|
|
walls[row][col]+=1;
|
|
|
|
//print maze
|
|
//first line
|
|
for c in 0..width {
|
|
if c == entrance_column {
|
|
print!(". ");
|
|
}
|
|
else {
|
|
print!(".--");
|
|
}
|
|
}
|
|
println!(".");
|
|
//rest of maze
|
|
for r in 0..height {
|
|
print!("I");
|
|
for c in 0..width {
|
|
if walls[r][c]<2 {print!(" I");}
|
|
else {print!(" ");}
|
|
}
|
|
println!();
|
|
for c in 0..width {
|
|
if walls[r][c] == 0 || walls[r][c]==2 {print!(":--");}
|
|
else {print!(": ");}
|
|
}
|
|
println!(".");
|
|
}
|
|
|
|
//stops the program from ending until you give input, useful when running a compiled .exe
|
|
println!("\n\npress ENTER to exit");
|
|
io::stdin().read_line(&mut String::new()).expect("closing");
|
|
}
|
|
|
|
fn get_user_input(prompt: &str) -> usize {
|
|
//DATA
|
|
let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
|
|
|
|
//input loop
|
|
return loop {
|
|
|
|
//print prompt
|
|
println!("{}", prompt);
|
|
|
|
//read user input from standard input, and store it to raw_input
|
|
raw_input.clear(); //clear input
|
|
io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
|
|
|
|
//from input, try to read a number
|
|
match raw_input.trim().parse::<usize>() {
|
|
Ok(i) => {
|
|
if i>1 { //min size 1
|
|
break i; // this escapes the loop, returning i
|
|
}
|
|
else {
|
|
println!("INPUT OUT OF RANGE. TRY AGAIN.");
|
|
continue;// run the loop again
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("MEANINGLESS DIMENSION. TRY AGAIN. {}", e.to_string().to_uppercase());
|
|
continue; // run the loop again
|
|
}
|
|
};
|
|
}
|
|
}
|