|
|
@ -1,5 +1,7 @@ |
|
|
|
use rand::prelude::{StdRng, SliceRandom}; |
|
|
|
use rand::{SeedableRng, Rng, thread_rng}; |
|
|
|
use std::fmt; |
|
|
|
use std::fmt::Formatter; |
|
|
|
|
|
|
|
pub const SIZE: usize = 16384; |
|
|
|
pub const HOUSE_RANGE: usize = 500; |
|
|
@ -87,7 +89,13 @@ pub struct Rectangle { |
|
|
|
bottom: usize, |
|
|
|
} |
|
|
|
|
|
|
|
struct HouseLayout<'a> { |
|
|
|
impl fmt::Display for Rectangle { |
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
|
|
|
write!(f, "L{}-{}R T{}-{}B", self.left, self.right, self.top, self.bottom) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub struct HouseLayout<'a> { |
|
|
|
city: &'a City, |
|
|
|
reachable: Vec<u16>, |
|
|
|
houses: Vec<House>, |
|
|
@ -103,6 +111,10 @@ impl<'a> HouseLayout<'a> { |
|
|
|
self.reachable[house.y * SIZE + house.x] |
|
|
|
} |
|
|
|
|
|
|
|
pub fn cover_count_xy(&self, x: usize, y: usize) -> u16 { |
|
|
|
self.reachable[y * SIZE + x] |
|
|
|
} |
|
|
|
|
|
|
|
pub fn is_covered(&self, house: House) -> bool { |
|
|
|
self.cover_count(house) > 0 |
|
|
|
} |
|
|
@ -157,12 +169,6 @@ impl<'a> HouseLayout<'a> { |
|
|
|
fn main() { |
|
|
|
let city = City::read_from_file("01.in"); |
|
|
|
|
|
|
|
const AROUND_RANGE: i32 = 50; |
|
|
|
const MAX_CANDIDATES: usize = 50; |
|
|
|
//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<u32> = None; |
|
|
|
loop { |
|
|
|
let seed: u64 = thread_rng().gen(); |
|
|
@ -195,11 +201,25 @@ fn main() { |
|
|
|
let house = untried_houses.pop().unwrap(); |
|
|
|
let mut house_index = layout.houses().iter().position(|x| *x == house).unwrap(); |
|
|
|
|
|
|
|
let move_rectangle = match get_valid_move_rectangle(&layout, house) { |
|
|
|
Ok(move_rectangle) => move_rectangle, |
|
|
|
Err(RectangleSearchError::UselessHouse) => { |
|
|
|
let old_price = layout.price(); |
|
|
|
layout.remove_house(house_index); |
|
|
|
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 (useless), diff {}", price_diff); |
|
|
|
eprintln!("Improved price: {}", new_price); |
|
|
|
untried_houses = layout.houses().clone(); |
|
|
|
untried_houses.shuffle(&mut rng); |
|
|
|
continue; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let mut new_candidates = Vec::new(); |
|
|
|
for delta_y in -AROUND_RANGE..=AROUND_RANGE { |
|
|
|
for delta_x in -AROUND_RANGE..=AROUND_RANGE { |
|
|
|
let new_x = (house.x as i32 + delta_x).max(0).min(SIZE as i32 - 1) as usize; |
|
|
|
let new_y = (house.y as i32 + delta_y).max(0).min(SIZE as i32 - 1) as usize; |
|
|
|
for new_y in move_rectangle.top..=move_rectangle.bottom { |
|
|
|
for new_x in move_rectangle.left..=move_rectangle.right { |
|
|
|
if city.is_house_xy(new_x, new_y) && city.get_price_xy(new_x, new_y) < city.get_price(&house) { |
|
|
|
new_candidates.push(House::new(new_x, new_y)); |
|
|
|
} |
|
|
@ -208,15 +228,11 @@ 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);
|
|
|
|
eprint!("Found candidate {}...", i); |
|
|
|
|
|
|
|
let old_price = layout.price(); |
|
|
|
layout.remove_house(house_index); |
|
|
@ -225,8 +241,16 @@ fn main() { |
|
|
|
// 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!("Move rectangle: {}", move_rectangle); |
|
|
|
let house_rect = house.range_rectangle(); |
|
|
|
eprintln!("Printing map in rect around house: {}", house_rect); |
|
|
|
for y in house_rect.top..=house_rect.bottom { |
|
|
|
for x in house_rect.left..=house_rect.right { |
|
|
|
eprint!("{}", layout.cover_count_xy(x, y)); |
|
|
|
} |
|
|
|
eprintln!(); |
|
|
|
} |
|
|
|
eprintln!("Improved price: {}", new_price); |
|
|
|
untried_houses = layout.houses().clone(); |
|
|
|
untried_houses.shuffle(&mut rng); |
|
|
@ -238,13 +262,13 @@ fn main() { |
|
|
|
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!(" 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); |
|
|
|
} |
|
|
@ -268,6 +292,55 @@ fn main() { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub enum RectangleSearchError { |
|
|
|
UselessHouse |
|
|
|
} |
|
|
|
|
|
|
|
pub fn get_valid_move_rectangle(layout: &HouseLayout, house: House) -> Result<Rectangle, RectangleSearchError> { |
|
|
|
// We first establish a bounding box for an that has to be covered if the house is removed.
|
|
|
|
let mut covered_rect: Option<Rectangle> = None; |
|
|
|
|
|
|
|
let range_rect = house.range_rectangle(); |
|
|
|
for y in range_rect.top..=range_rect.bottom { |
|
|
|
for x in range_rect.left..=range_rect.right { |
|
|
|
if layout.cover_count_xy(x, y) == 1 && layout.city.is_house_xy(x, y) { |
|
|
|
// This house is only covered by the house, it has to be covered from the new position as well.
|
|
|
|
if let Some(cover) = &mut covered_rect { |
|
|
|
cover.left = cover.left.min(x); |
|
|
|
cover.right = cover.right.max(x); |
|
|
|
cover.top = cover.top.min(y); |
|
|
|
cover.bottom = cover.bottom.max(y); |
|
|
|
} else { |
|
|
|
covered_rect = Some(Rectangle { left: x, right: x, top: y, bottom: y }); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if covered_rect.is_none() { |
|
|
|
return Err(RectangleSearchError::UselessHouse) |
|
|
|
} |
|
|
|
|
|
|
|
let covered_rect = covered_rect.unwrap(); |
|
|
|
|
|
|
|
// The distance of the rectangle from the original box tells us how much the house can move.
|
|
|
|
let dist_left = covered_rect.left - range_rect.left; |
|
|
|
let dist_right = range_rect.right - covered_rect.right; |
|
|
|
let dist_top = covered_rect.top - range_rect.top; |
|
|
|
let dist_bottom = range_rect.bottom - covered_rect.bottom; |
|
|
|
|
|
|
|
let left = if house.x <= dist_right { 0 } else { house.x - dist_right }; |
|
|
|
let right = if house.x >= SIZE - 1 - dist_left { SIZE - 1 } else { house.x + dist_left }; |
|
|
|
let top = if house.y <= dist_bottom { 0 } else { house.y - dist_bottom }; |
|
|
|
let bottom = if house.y >= SIZE - 1 - dist_top { SIZE - 1 } else { house.y + dist_top }; |
|
|
|
|
|
|
|
let valid_move_rectangle = Rectangle { |
|
|
|
left, right, top, bottom |
|
|
|
}; |
|
|
|
|
|
|
|
Ok(valid_move_rectangle) |
|
|
|
} |
|
|
|
|
|
|
|
pub fn get_neighbors(city: &City, house: &House) -> Vec<House> { |
|
|
|
let mut neighbors = Vec::new(); |
|
|
|
let range_rect = house.range_rectangle(); |
|
|
|