diff --git a/falling.rs b/falling.rs index 5f97062..8b7dad7 100644 --- a/falling.rs +++ b/falling.rs @@ -1,81 +1,136 @@ -use rand::Rng; -use console::Term; -//use termsize::get; +use rand::Rng; // Allows you to generate random numbers +use console::Term; // Allows reading key bind input +use std::io::{stdin,stdout,Write}; -fn output_levels(starting_level: u64, ending_level: u64, character_x_coord: u16, character_icon: char, levels: &Vec>) { - print!("{}[2J", 27 as char); - for i in starting_level..ending_level { - for j in 0..levels[i as usize].len() { - let object = levels[i as usize][j as usize]; - if i == starting_level && j == character_x_coord as usize { print!("{}",character_icon) } - else if object >= 4 { print!(" ") } - else if object == 3 { print!("¯") } - else if object == 2 { print!("\\") } - else if object == 1 { print!("/") } - else { print!("|") } - } - println!(" {}",i); +// This function is used to Generate the output out a range of levels to the console, it accepts starting and +// ending levls to print and then it uses a loop to print out each level in between these, each +// level is comprised of numbers, each having a specific meaning for instance 0 is an obstacle and +// -2 is a slanted wall "/" +fn generate_output(starting_level: u64, ending_level: u64, character_x_coord: u16, character_icon: char, levels: &Vec>, debug_mode: bool) -> String { + let mut output: String = "".to_string(); + if debug_mode { + let pattern = "0123456789"; + let repeat_count = (levels[0].len() + 9) / 10; + let mut repeated = pattern.repeat(repeat_count); + repeated += "\n"; + output += &repeated; } + for i in starting_level..ending_level { + let level = &levels[i as usize]; // Cache the level for quicker access + for j in 0..level.len() { + let object = level[j as usize]; + // Directly check the player's position for rendering the icon + if i == starting_level && j == character_x_coord as usize { + output = output + &character_icon.to_string(); + } else { + match object { + -3 => output+="|", // Specific case for -3 + 0 => output+="¯", // Specific case for 0 + -1 => output+="\\", // Specific case for -1 + -2 => output+="/", // Specific case for -2 + _ => output+=" " // Default, no print for other cases + } + } + } + output+=" "; // At the end of each level, print that level + output+=&i.to_string(); + output+="\n"; + } + return output; } -fn generate_level(level_to_generate: u64, difficulty: u8, left_wall: &mut i8, right_wall: &mut i8, screen_width: u16, levels: &mut Vec>) { - let mut new_level: Vec = Vec::new(); - let left_wall_change = rand::thread_rng().gen_range(-1..2); - let right_wall_change = rand::thread_rng().gen_range(-1..2); - //println!("lwc: {}, rwc: {}", left_wall_change, right_wall_change); +fn generate_level(level_to_generate: u64, difficulty: i8, left_wall: &mut i16, right_wall: &mut i16, screen_width: u16, preference: &mut f32, levels: &mut Vec>) { + let mut new_level: Vec = Vec::new(); + + let left_wall_change = rand::thread_rng().gen_range(-1..2+(*preference as i16)); + let right_wall_change = rand::thread_rng().gen_range(-1..2+(*preference as i16)); *left_wall = *left_wall + left_wall_change; *right_wall = *right_wall + right_wall_change; if *left_wall <= 0 { *left_wall = 1; } if *right_wall >= screen_width.try_into().unwrap() { - *right_wall = screen_width as i8-1 + *right_wall = screen_width as i16-1 } - //println!("lw: {}, rw: {}", left_wall, right_wall); if (*right_wall-*left_wall).abs() < 8 { - *left_wall-=2; - *right_wall+=2; + *left_wall-=3; + *right_wall+=3; } - //let mut left_wall_temp = left_wall as i8 + left_wall_change; - //if left_wall_temp >= 0 { left_wall = left_wall_temp as u8 } - //right_wall = (right_wall as i8 + right_wall_change) as u8; - for i in 0..screen_width { - new_level.push(4) + while new_level.len() <= screen_width as usize { + new_level.push(1) } for i in *left_wall+1..*right_wall-1 { - let rng = rand::thread_rng(); - let object: u8 = rand::thread_rng().gen_range(3..difficulty); + let object: i8 = rand::thread_rng().gen_range(0..difficulty); new_level[i as usize] = object; } for i in 0..*left_wall { if i == *left_wall-1 && left_wall_change > 0 { - new_level[i as usize] = 2; + new_level[i as usize] = -1; } else if i == *left_wall-1 && left_wall_change < 0 { - new_level[i as usize] = 1; + new_level[i as usize] = -2; } else { - new_level[i as usize] = 0; + new_level[i as usize] = -3; } } - for i in *right_wall..screen_width as i8 { + for i in *right_wall..screen_width as i16 { if i == *right_wall && right_wall_change > 0 { - new_level[i as usize] = 2; + new_level[i as usize] = -1; } else if i == *right_wall && right_wall_change < 0 { - new_level[i as usize] = 1; + new_level[i as usize] = -2; } else { - new_level[i as usize] = 0; + new_level[i as usize] = -3; } } - levels.push(new_level); + while levels.len() <= level_to_generate as usize { + levels.push(new_level.clone()); + } + levels[level_to_generate as usize] = new_level; + + // Modification of the directional preference logic + *preference = *preference + rand::thread_rng().gen_range(-0.5..0.5); + if *preference > 3.0 { + *preference = -1.0; + } + else if *preference < -3.0 { + *preference = 1.0; + } + if *right_wall >= screen_width as i16 - 5 { + *preference = -1.0; + } + else if *left_wall <= 5 { + *preference = 1.0; + } } -fn check_if_alive(levels: &Vec>, level: usize, x_coord: usize) -> bool { - if levels[level][x_coord] == 0 || levels[level][x_coord] == 1 || levels[level][x_coord] == 2 || levels[level][x_coord] == 3 { +fn debug_mode_modify_variables(current_level: &mut u64) -> u8 { + println!("\n----------------VARIABLE MODIFICATION---------------\n"); + println!("Variables that can be modified:\ncurrent_level"); + println!("\nWhat variable do you want to modify? ('N' to return to game without modification)"); + + let variable_to_modify = input(); + + if variable_to_modify == "N" { + return 0; + } + else { + println!("New value for {}",variable_to_modify); + let new_value = input(); + + if variable_to_modify == "current_level" { + *current_level = new_value.parse().unwrap(); + } + } + return 1; +} + +fn check_if_alive(levels: &Vec>, level: usize, x_coord: usize) -> bool { + if levels[level][x_coord] <= 0 { println!("GAME OVER"); return false; } @@ -84,40 +139,98 @@ fn check_if_alive(levels: &Vec>, level: usize, x_coord: usize) -> bool { } } -pub fn main() { - let mut levels: Vec> = Vec::new(); +fn input() -> String{ + let mut s=String::new(); + let _=stdout().flush(); + stdin().read_line(&mut s).expect("Did not enter a correct string"); + if let Some('\n')=s.chars().next_back() { + s.pop(); + } + if let Some('\r')=s.chars().next_back() { + s.pop(); + } + return s; +} - let (screen_width, screen_height) = termion::terminal_size().unwrap(); +fn main() { + let debug_mode = true; // Enable or disable debugging mode, it will print some useful stats and + // let you modify stats midgame with e + let can_die = false; // I can't spell invinsiblitlity but this lets you not die... - println!("width: {}, height: {}",screen_width, screen_height); - //let screen_height: u8 = 70; - //let screen_width: u8 = 100; - let difficulty: u8 = 15; + let mut levels: Vec> = Vec::new(); // Define variables for level + + let (mut screen_width, mut screen_height) = termion::terminal_size().unwrap(); + screen_width = screen_width - 20; + if debug_mode { // Reduce screen height more if debug mode is on as we need space to print + // additional information + screen_height = screen_height - 17; + } + else { + screen_height = screen_height - 1; + } + let stdout = Term::buffered_stdout(); let mut current_level: u64 = 0; - let character_icon: char = 'µ'; + let mut character_icon: char = 'µ'; let mut x_coord: u16 = (screen_width as f32/ 2.0) as u16; // Distance from left wall - let mut left_wall: i8 = (screen_width as f32/ 4.0) as i8; - let mut right_wall: i8 = ((screen_width as f32 * 3.0)/ 4.0) as i8; + let mut left_wall: i16 = rand::thread_rng().gen_range(0..((screen_width as f32/ 2.0)-5.0) as i16) as i16; + let mut right_wall: i16 = rand::thread_rng().gen_range(((screen_width as f32 /2.0)+5.0) as i16..screen_width as i16) as i16; + let mut preference: f32 = rand::thread_rng().gen_range(-2.0..3.0); let mut alive: bool = true; + let mut difficulty: i8 = 6; - for i in 1..screen_height { - generate_level(i as u64, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut levels) + + for i in 0..screen_height { + generate_level(i as u64, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut preference, &mut levels) }; 'game_loop: loop { - generate_level(current_level, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut levels); - output_levels(current_level, current_level + screen_height as u64, x_coord, character_icon, &levels); + let start = std::time::Instant::now(); + generate_level(current_level + screen_height as u64, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut preference, &mut levels); + let end_gen = std::time::Instant::now(); + + let output = generate_output(current_level, current_level + screen_height as u64, x_coord, character_icon, &levels, debug_mode); + let end_gen_output = std::time::Instant::now(); + if ! debug_mode { // We don't want to clear screen if debug mode is on as it lets us see + // old stats and see console warnings from rust and stuff + print!("{}[2J", 27 as char); + } + println!("{}",output); + let end_output = std::time::Instant::now(); + + if debug_mode { // Print out debug info + println!("Time to Generate Level: {:?}\nTime to Generate Output: {:?}\nTime to Output levels: {:?}\nCurrent Level: {}\nCharacter's X coordinate: {}\nLeft Wall Position: {}\nRight Wall Position: {}\nDirectional Preference: {}\nAlive: {}\nDifficulty: {}\nCharcter Icon: {}\nItem Currently Ontop of: {}\nScreen Width: {}\nScreen Height: {}", + end_gen-start, + end_gen_output-end_gen, + end_output-end_gen_output, + current_level, + x_coord, + left_wall, + right_wall, + preference, + alive, + difficulty, + character_icon, + levels[current_level as usize][x_coord as usize], + screen_width, + screen_height + ); + } + if let Ok(character) = stdout.read_char() { match character { 'd' => if x_coord < screen_width-1 { x_coord += 1 }, 'a' => if x_coord > 1 { x_coord -= 1 }, + 'e' => if debug_mode { debug_mode_modify_variables(&mut current_level); continue 'game_loop }, 'q' => break 'game_loop, - _ => continue 'game_loop, + _ => (), } } + current_level+=1; - alive = check_if_alive(&levels, current_level as usize, x_coord as usize); + if can_die { + alive = check_if_alive(&levels, current_level as usize, x_coord as usize); + } if alive == false { break 'game_loop }