use rand::Rng; // Allows you to generate random numbers use console::Term; // Allows reading key bind input use std::io::{stdin,stdout,Write}; // 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: 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 i16-1 } if (*right_wall-*left_wall).abs() < 8 { *left_wall-=3; *right_wall+=3; } while new_level.len() <= screen_width as usize { new_level.push(1) } for i in *left_wall+1..*right_wall-1 { 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] = -1; } else if i == *left_wall-1 && left_wall_change < 0 { new_level[i as usize] = -2; } else { new_level[i as usize] = -3; } } for i in *right_wall..screen_width as i16 { if i == *right_wall && right_wall_change > 0 { new_level[i as usize] = -1; } else if i == *right_wall && right_wall_change < 0 { new_level[i as usize] = -2; } else { new_level[i as usize] = -3; } } 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 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; } else { return true; } } 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; } 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... 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 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: 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 0..screen_height { generate_level(i as u64, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut preference, &mut levels) }; 'game_loop: loop { 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, _ => (), } } current_level+=1; if can_die { alive = check_if_alive(&levels, current_level as usize, x_coord as usize); } if alive == false { break 'game_loop } } }