diff --git a/src/main.rs b/src/main.rs index 6184a6c..e964c7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -66,58 +66,112 @@ impl House { } } +struct HouseLayout<'a> { + city: &'a City, + reachable: Vec, + houses: Vec, + reachable_houses: usize +} + +impl<'a> HouseLayout<'a> { + pub fn new(city: &'a City) -> Self { + HouseLayout { city, reachable: vec![0; SIZE * SIZE], houses: Vec::new(), reachable_houses: 0 } + } + + pub fn cover_count(&self, house: House) -> u16 { + self.reachable[house.y * SIZE + house.x] + } + + pub fn is_covered(&self, house: House) -> bool { + self.cover_count(house) > 0 + } + + pub fn add_house(&mut self, house: House) -> usize { + for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { + for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { + let index = y as usize * SIZE + x as usize; + + if self.reachable[index] == 0 && self.city.is_house_xy(x as usize, y as usize) { + self.reachable_houses += 1; + } + + self.reachable[index] += 1; + } + } + self.houses.push(house); + self.houses.len() - 1 + } + + pub fn remove_house(&mut self, index: usize) { + let house = self.houses.swap_remove(index); + + for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { + for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { + let index = y as usize * SIZE + x as usize; + + self.reachable[index] -= 1; + + if self.reachable[index] == 0 && self.city.is_house_xy(x as usize, y as usize) { + self.reachable_houses -= 1; + } + } + } + } + + pub fn is_valid(&self) -> bool { + self.reachable_houses == self.city.buyable_house_count + } + + pub fn price(&self) -> u32 { + get_price(self.city, &self.houses) + } + + pub fn houses(&self) -> &Vec { + &self.houses + } +} + fn main() { let city = City::read_from_file("01.in"); + const AROUND_RANGE: i32 = 200; + const MAX_CANDIDATES: usize = 200; + //const MAX_FAILED_ITERATIONS: usize = 50; + println!("Params: AROUND_RANGE {}, MAX_CANDIDATES {}", AROUND_RANGE, MAX_CANDIDATES); + eprintln!("Params: AROUND_RANGE {}, MAX_CANDIDATES {}", AROUND_RANGE, MAX_CANDIDATES); + let mut best_price: Option = None; loop { let seed: u64 = thread_rng().gen(); eprintln!("Starting seed {}", seed); let mut rng = StdRng::seed_from_u64(seed); - let mut reachable = vec![false; SIZE * SIZE]; - let mut houses: Vec = Vec::new(); - let mut claimed_houses = 0; + let mut layout = HouseLayout::new(&city); loop { loop { let x = rng.gen_range(0..SIZE); let y = rng.gen_range(0..SIZE); let house = House::new(x, y); - if city.is_house_xy(x, y) && !reachable[y * SIZE + x] { - for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { - for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { - let index = y as usize * SIZE + x as usize; - if !reachable[index] { - reachable[index] = true; - if city.is_house_xy(x as usize, y as usize) { - claimed_houses += 1; - } - } - } - } - houses.push(house); - //eprintln!("{} houses", houses.len()); + if city.is_house_xy(x, y) && !layout.is_covered(house) { + layout.add_house(house); break; } } - let finished = claimed_houses == city.get_house_count(); - - if finished { + if layout.is_valid() { break; } } - let mut price = get_price(&city, &houses); - eprintln!("Finished random init, price: {}", price); + eprintln!("Finished random init, price: {}", layout.price()); + + let mut untried_houses = layout.houses().clone(); + untried_houses.shuffle(&mut rng); - const AROUND_RANGE: i32 = 50; - const MAX_CANDIDATES: usize = 20; - const MAX_FAILED_ITERATIONS: usize = 50; + while untried_houses.len() > 0 { + let house = untried_houses.pop().unwrap(); + let mut house_index = layout.houses().iter().position(|x| *x == house).unwrap(); - let mut failed_iterations = 0; - while failed_iterations < MAX_FAILED_ITERATIONS { - let house = &houses.choose(&mut rng).unwrap(); let mut new_candidates = Vec::new(); for delta_y in -AROUND_RANGE..=AROUND_RANGE { for delta_x in -AROUND_RANGE..=AROUND_RANGE { @@ -131,45 +185,61 @@ fn main() { new_candidates.sort_by(|a, b| city.get_price(&a).cmp(&city.get_price(&b))); if new_candidates.len() == 0 { - eprintln!("Did not find candidate"); + //eprintln!("Did not find candidate"); } else { for (i, &candidate) in new_candidates.iter().enumerate() { if i > MAX_CANDIDATES { + //eprintln!("No valid candidate"); break; } - eprint!("Found candidate {}...", i); - let mut new_houses: Vec<_> = houses.to_vec().into_iter().filter(|h| &h != house).collect(); - new_houses.push(candidate); - // TODO: This is_valid check could be way more efficient - if let Some(new_price) = is_valid(&city, &new_houses) { - let price_diff = new_price as i64 - price as i64; - eprintln!(" candidate is valid, price diff: {}.", price_diff); + //eprint!("Found candidate {}...", i); + + let old_price = layout.price(); + layout.remove_house(house_index); + + if layout.is_valid() { + // The candidate is not needed, the house was unnecessary + let new_price = layout.price(); + let price_diff = new_price as i64 - old_price as i64; + //eprintln!(" candidate is valid, price diff: {}.", price_diff); + eprintln!("Removed a house, diff {}", price_diff); eprintln!("Improved price: {}", new_price); - price = new_price; - houses = new_houses; - failed_iterations = 0; + untried_houses = layout.houses().clone(); + untried_houses.shuffle(&mut rng); + break; + } + + let candidate_index = layout.add_house(candidate); + + if layout.is_valid() { + let new_price = layout.price(); + let price_diff = new_price as i64 - old_price as i64; + //eprintln!(" candidate is valid, price diff: {}.", price_diff); + eprintln!("Improved price: {}", new_price); + untried_houses = layout.houses().clone(); + untried_houses.shuffle(&mut rng); break; } else { - eprintln!(" candidate is invalid."); + //eprintln!(" candidate is invalid."); + layout.remove_house(candidate_index); + house_index = layout.add_house(house); } } } - - // Successful iterations always break - failed_iterations += 1; } + let price = layout.price(); if best_price.is_none() || price < best_price.unwrap() { best_price = Some(price); eprintln!("Finished randomization, price: {}, new best, printing", price); println!("Price {}, seed {}", price, seed); - print_houses(&houses); + print_houses(&layout.houses()); println!(); } else { eprintln!("Finished randomization, price: {}, printing", price); println!("Price {}, seed {}", price, seed); - print_houses(&houses); + print_houses(&layout.houses()); println!(); } }