|
| 1 | +use std::{ |
| 2 | + cmp, |
| 3 | + env::args, |
| 4 | + hash::{DefaultHasher, Hash, Hasher}, |
| 5 | + io::stdin, |
| 6 | + iter::Sum, |
| 7 | + num::ParseIntError, |
| 8 | + ops::{AddAssign, Mul}, |
| 9 | + process::exit, |
| 10 | +}; |
| 11 | + |
| 12 | +use rand::{Rng, SeedableRng}; |
| 13 | +use rand_chacha::ChaChaRng; |
| 14 | + |
| 15 | +const FARM_SIZE: u32 = 50; |
| 16 | +const GRID_SIZE: usize = 100; |
| 17 | + |
| 18 | +#[derive(Debug)] |
| 19 | +struct PrefixSum2D { |
| 20 | + prefix: Vec<Vec<u32>>, |
| 21 | +} |
| 22 | + |
| 23 | +impl PrefixSum2D { |
| 24 | + pub fn new(grid: &Vec<Vec<u32>>) -> Self { |
| 25 | + let mut prefix = vec![vec![0; grid[0].len()]; grid.len()]; |
| 26 | + |
| 27 | + prefix[0][0] = grid[0][0]; |
| 28 | + for x in 1..grid[0].len() { |
| 29 | + prefix[0][x] = prefix[0][x - 1] + grid[0][x]; |
| 30 | + } |
| 31 | + for y in 1..grid.len() { |
| 32 | + prefix[y][0] = prefix[y - 1][0] + grid[y][0]; |
| 33 | + } |
| 34 | + |
| 35 | + for y in 1..grid.len() { |
| 36 | + for x in 1..grid[0].len() { |
| 37 | + prefix[y][x] = |
| 38 | + prefix[y - 1][x] + prefix[y][x - 1] - prefix[y - 1][x - 1] + grid[y][x]; |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + Self { prefix } |
| 43 | + } |
| 44 | + |
| 45 | + // inclusive exclusive |
| 46 | + pub fn get_subrange_sum(&self, topleft: (usize, usize), bottomright: (usize, usize)) -> u32 { |
| 47 | + if bottomright.0 == 0 || bottomright.1 == 0 { |
| 48 | + return 0; |
| 49 | + } |
| 50 | + let sub_sum = self.prefix[bottomright.0 - 1][bottomright.1 - 1]; |
| 51 | + if topleft.0 == 0 && topleft.1 == 0 { |
| 52 | + return sub_sum; |
| 53 | + } else if topleft.0 == 0 { |
| 54 | + return sub_sum - self.prefix[bottomright.0 - 1][topleft.1 - 1]; |
| 55 | + } else if topleft.1 == 0 { |
| 56 | + return sub_sum - self.prefix[topleft.0 - 1][bottomright.1 - 1]; |
| 57 | + } |
| 58 | + sub_sum + self.prefix[topleft.0 - 1][topleft.1 - 1] |
| 59 | + - self.prefix[topleft.0 - 1][bottomright.1 - 1] |
| 60 | + - self.prefix[bottomright.0 - 1][topleft.1 - 1] |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | + |
| 65 | +fn min_area_with_chunks(prefix_sum_world: &PrefixSum2D) -> u32 { |
| 66 | + let mut min_chunk_area = u32::MAX; |
| 67 | + for top in 0..prefix_sum_world.prefix.len() { |
| 68 | + for bottom in (top + 1)..prefix_sum_world.prefix.len() { |
| 69 | + for left in 0..prefix_sum_world.prefix[0].len() { |
| 70 | + for right in (left + 1)..prefix_sum_world.prefix[0].len() { |
| 71 | + if prefix_sum_world.get_subrange_sum((top, left), (bottom, right)) >= FARM_SIZE |
| 72 | + { |
| 73 | + // dbg!((top, left)); |
| 74 | + // dbg!((bottom, right)); |
| 75 | + min_chunk_area = |
| 76 | + cmp::min(min_chunk_area, ((bottom - top) * (right - left)) as u32); |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + (16 * 16) * dbg!(min_chunk_area) |
| 84 | +} |
| 85 | + |
| 86 | +fn generate(world: Vec<Vec<u32>>) -> String { |
| 87 | + world |
| 88 | + .iter() |
| 89 | + .map(|row| row |
| 90 | + .iter() |
| 91 | + .map(|chunk| { |
| 92 | + if *chunk == 0 { |
| 93 | + '.' |
| 94 | + } else { |
| 95 | + 'S' |
| 96 | + } |
| 97 | + }) |
| 98 | + .collect::<String>()) |
| 99 | + .collect::<Vec<String>>() |
| 100 | + .join("\n") |
| 101 | +} |
| 102 | + |
| 103 | +fn main() { |
| 104 | + let args: Vec<String> = args().collect(); |
| 105 | + assert_eq!(args.len(), 3); |
| 106 | + |
| 107 | + let mut default_hasher = DefaultHasher::new(); |
| 108 | + args[2].hash(&mut default_hasher); |
| 109 | + let seed = default_hasher.finish(); |
| 110 | + |
| 111 | + let mut rng = ChaChaRng::seed_from_u64(seed); |
| 112 | + |
| 113 | + let mut world = vec![vec![0; GRID_SIZE]; GRID_SIZE]; |
| 114 | + // 10% chance of slime chunks |
| 115 | + // 0: normal |
| 116 | + // 1: slime chunk |
| 117 | + for _ in 0..((((GRID_SIZE * GRID_SIZE) as f32) * 0.1) as usize) { |
| 118 | + loop { |
| 119 | + let x = rng.random_range(0..GRID_SIZE); |
| 120 | + let y = rng.random_range(0..GRID_SIZE); |
| 121 | + if world[y][x] == 0 { |
| 122 | + world[y][x] = 1; |
| 123 | + break; |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + let prefix_sum = PrefixSum2D::new(&world); |
| 129 | + |
| 130 | + let solution = min_area_with_chunks(&prefix_sum); |
| 131 | + |
| 132 | + match args[1].as_str() { |
| 133 | + // If `fuzz`-ing, just output the prompt. |
| 134 | + "generate" => { |
| 135 | + println!("{}", generate(world)) |
| 136 | + } |
| 137 | + "validate" => { |
| 138 | + let mut buffer = String::new(); |
| 139 | + let _ = stdin().read_line(&mut buffer); |
| 140 | + let input = buffer.trim().parse::<u32>(); |
| 141 | + if let Ok(val) = input { |
| 142 | + if val == solution { |
| 143 | + exit(0); |
| 144 | + } else if val > solution { |
| 145 | + eprintln!("Incorrect, Your number is too high."); |
| 146 | + exit(1); |
| 147 | + } else { |
| 148 | + eprintln!("Incorrect, Your number is too low."); |
| 149 | + exit(1); |
| 150 | + } |
| 151 | + } else { |
| 152 | + eprintln!("Invalid input. Expected a positive number."); |
| 153 | + exit(1); |
| 154 | + } |
| 155 | + } |
| 156 | + // In this case, there is only one "correct" answer. |
| 157 | + // In debugging, this can be directly outputted. |
| 158 | + "solution" => { |
| 159 | + println!("{}", solution); |
| 160 | + } |
| 161 | + _ => panic!(), |
| 162 | + } |
| 163 | +} |
0 commit comments