use std::io::{stdin,stdout,Write}; use rand::Rng; use colored::*; use console::Term; use std::time::Instant; fn output_board(board: &Vec>, state: &Vec>, x_hovered: i64, y_hovered: i64) { let mut output: String = "".to_string(); for y in 0..board.len() { for x in 0..board[y].len() { let background_color: u8; if state[y][x] != 'u' { background_color = 128; } else { background_color = 196; } if x == x_hovered as usize && y == y_hovered as usize { output.push_str(&"X".truecolor(0,0,0).on_truecolor(background_color,background_color,background_color).to_string()); } else if state[y][x] == 'u' { if board[y][x] == -1 { output.push_str(&"▲".truecolor(128,64,0).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 0 { output.push_str(&" ".on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 1 { output.push_str(&"1".truecolor(1,0,254).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 2 { output.push_str(&"2".truecolor(1,127,1).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 3 { output.push_str(&"3".truecolor(254,0,0).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 4 { output.push_str(&"4".truecolor(1,0,128).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 5 { output.push_str(&"5".truecolor(129,1,2).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 6 { output.push_str(&"6".truecolor(0,128,129).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 7 { output.push_str(&"7".truecolor(0,0,0).on_truecolor(background_color,background_color,background_color).to_string()); } else if board[y][x] == 8 { output.push_str(&"8".truecolor(128,128,128).on_truecolor(background_color,background_color,background_color).to_string()); } } else if state[y][x] == 'm' { output.push_str(&"⚑".truecolor(228,16,32).on_truecolor(background_color,background_color,background_color).to_string()); } else if state[y][x] == '?' { output.push_str(&"?".truecolor(0,0,0).on_truecolor(background_color,background_color,background_color).to_string()); } else { output.push_str(&" ".on_truecolor(background_color,background_color,background_color).to_string()); } } output.push_str("\n"); } print!("{}[2J", 27 as char); println!("{}",output); } 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 place_nails(board: &mut Vec>, number_of_nails: i64, width: i64, height: i64) { for _nail in 0..number_of_nails { // Generate coordinates for nails, if there is already a nail there, then generate new // coordinates, should add case for if there are more mines than locations, but why would // someone do that? let mut x_coord = rand::thread_rng().gen_range(0..width); let mut y_coord = rand::thread_rng().gen_range(0..height); while board[y_coord as usize][x_coord as usize] == -1 { x_coord = rand::thread_rng().gen_range(0..width); y_coord = rand::thread_rng().gen_range(0..height); } board[y_coord as usize][x_coord as usize] = -1; } } fn determine_nails_in_range(board: &mut Vec>) { for x in 0..board.len() { for y in 0..board[x].len() { if board[x][y] != -1 { let mut num_of_nails = 0; for i in -1..2 { for j in -1..2 { // Check if index is in range (>= 0 and < length of vector) if (x as i64 + i >= 0 && (x as i64 + i) < board.len() as i64) && (y as i64 + j >= 0 && (y as i64 + j) < board[x].len() as i64) { if board[(x as i64 + i) as usize][(y as i64 + j) as usize] == -1 { num_of_nails += 1; // Increment the nail count } } } } board[x][y] = num_of_nails; } } } } fn find_connected_coordinates(grid: &Vec>, start_row: usize, start_col: usize) -> Vec<(usize, usize)> { let rows = grid.len(); let cols = grid[0].len(); let target_value = grid[start_row][start_col]; // Directions for 8 possible moves (vertical, horizontal, and diagonal) let directions: [(i64, i64); 8] = [ (-1, 0), // Up (1, 0), // Down (0, -1), // Left (0, 1), // Right (-1, -1),// Top-left diagonal (-1, 1), // Top-right diagonal (1, -1), // Bottom-left diagonal (1, 1), // Bottom-right diagonal ]; // Visited cells let mut visited = vec![vec![false; cols]; rows]; // Vector to store the reachable coordinates let mut result = Vec::new(); // DFS function fn dfs( grid: &Vec>, row: usize, col: usize, target_value: i64, visited: &mut Vec>, result: &mut Vec<(usize, usize)>, directions: &[(i64, i64)], ) { // Check if the position is out of bounds or already visited or doesn't match the target value if row >= grid.len() || col >= grid[0].len() || visited[row][col] || grid[row][col] != target_value { return; } // Mark the cell as visited and add the coordinate to the result visited[row][col] = true; result.push((row, col)); // Flag to check if we're at the boundary of the connected region let mut is_boundary = false; for &direction in directions.iter() { let (dx, dy) = direction; let new_row = row as i64 + dx; let new_col = col as i64 + dy; if new_row >= 0 && new_row < grid.len() as i64 && new_col >= 0 && new_col < grid[0].len() as i64 { let new_row = new_row as usize; let new_col = new_col as usize; if grid[new_row][new_col] != target_value { is_boundary = true; } dfs(grid, new_row, new_col, target_value, visited, result, directions); } } if is_boundary { for &direction in directions.iter() { let (dx, dy) = direction; let new_row = row as i64 + dx; let new_col = col as i64 + dy; if new_row >= 0 && new_row < grid.len() as i64 && new_col >= 0 && new_col < grid[0].len() as i64 { let new_row = new_row as usize; let new_col = new_col as usize; // Add cells with a different value to the result if grid[new_row][new_col] != target_value && !visited[new_row][new_col] { result.push((new_row, new_col)); } } } } } dfs(grid, start_row, start_col, target_value, &mut visited, &mut result, &directions); result } fn uncover(board: &Vec>, state: &mut Vec>, x_hovered: usize, y_hovered: usize, alive: &mut bool) { state[y_hovered][x_hovered] = 'u'; if board[y_hovered][x_hovered] == -1 { *alive = false; } if board[y_hovered][x_hovered] == 0 { let connected = find_connected_coordinates(&board, y_hovered, x_hovered); for (y,x) in connected.iter() { state[*y][*x] = 'u'; } } } fn detect_win(state: &Vec>, number_of_nails: i64, start: Instant) { let mut number_of_covered_cells: i64 = 0; for y in 0..state.len() { for x in 0..state[y].len() { if state[y][x] != 'u' { number_of_covered_cells = number_of_covered_cells + 1; } } } if number_of_covered_cells == number_of_nails { println!("Congratulations, you won!!!"); let end = std::time::Instant::now(); println!("In {:?}", end-start); std::process::exit(0); } } fn main() { let stdout = Term::buffered_stdout(); let mut board: Vec> = Vec::new(); let mut state: Vec> = Vec::new(); // u = uncovered, c = covered, m = marked let height: i64 = 8; let width: i64 = 8; let number_of_nails: i64 = 10; //let percentage_of_cells_are_nails: i64 = 15; //let number_of_nails: i64 = ((percentage_of_cells_are_nails / 100) as i64) * ((width * height) as i64) as i64; let mut x_hovered: i64 = 0; let mut y_hovered: i64 = 0; let mut alive: bool = true; for y in 0..height { board.push(Vec::new()); state.push(Vec::new()); for x in 0..width { board[y as usize].push(0); state[y as usize].push('c'); //board[x as usize][y as usize] = -1; } } let start = std::time::Instant::now(); place_nails(&mut board, number_of_nails, width, height); determine_nails_in_range(&mut board); output_board(&board, &state, x_hovered, y_hovered); 'gameloop: loop { if let Ok(character) = stdout.read_char() { match character { 'w' | 'k' => if y_hovered > 0 { y_hovered -= 1 }, 'a' | 'h' => if x_hovered > 0 { x_hovered -= 1}, 's' | 'j' => if y_hovered < height-1 { y_hovered += 1 }, 'd' | 'l' => if x_hovered < width-1 {x_hovered += 1}, 'u' => uncover(&board, &mut state, x_hovered as usize, y_hovered as usize, &mut alive), 'm' => { if state[y_hovered as usize][x_hovered as usize] == 'm' { state[y_hovered as usize][x_hovered as usize] = 'c' } else { state[y_hovered as usize][x_hovered as usize] = 'm' } }, '?' => { if state[y_hovered as usize][x_hovered as usize] == '?' { state[y_hovered as usize][x_hovered as usize] = 'c' } else { state[y_hovered as usize][x_hovered as usize] = '?' } }, 'q' => break 'gameloop, _ => (), } } if alive { output_board(&board, &state, x_hovered, y_hovered); detect_win(&state, number_of_nails, start); } else if alive == false { println!("GAME OVER!\nYou got Tetanus!"); println!("⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀"); println!(" ⢀⣾⣿O⣷⠀⠀⠀⠀⠀⠀"); println!(" ⠀⠸⣿⣿⣿C⠀⠀⠀- HOLY SHIT⠀⠀⠀"); println!(" ⣀⣤⡈⠛⢉⠀⠀⠀⠀⠀⠀⠀"); println!(" ⢀⣴⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀"); println!(" ⣴⣿⣿⢿⣿⣿⣿⣿⣿⠀⡄⠀⠀⠀⠀⠀"); println!(" ⠀⠀⠀⠀⢰⣿⡏⠀⢸⣿⣿⣿⣿⡇⢸⣷⣤⣀⠀⠀⠀"); println!(" ⠀⠀⠀⠀⣼⣿⠁⠀⢸⣿⣿⣿⣿⠁⠀⠙⠻⢿⣿⣶⠀"); println!(" ⠀⠀⠀⠀⠛⠋⠀⠀⠸⣿⣿⣿⡏⠀⠀⠀⠀⠀⠈⠉⠀"); println!(" ⠀⠀⠀⠀⠀⠀⠀⠀⣄⠙⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀"); println!(" ⠀⠀⠀⠀⠀⠀⠀⢸⣿⣦⠈⢿⣿⣿⣦⠀⠀⠀⠀⠀⠀"); println!(" ⠀⠀⠀⠀⠀⠀⠀⣼⣿⡟⠀⠀⠻⣿⣿⣧⠀⠀⠀⠀⠀"); println!(" ⠀⠀⠀⠀⠀⣠⣾⣿⠟⠁⠀⠀⠀⠘⢿⣿⣧/⠀⠀⠀"); println!(" ⠀⠀⠀⠀⢾⣿⠟⠁⠀⠀⠀⠀⠀⠀⠈⢻⣿⠇⠀⠀⠀"); break 'gameloop; } } }