|
|
@ -66,58 +66,112 @@ impl House { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
struct HouseLayout<'a> { |
|
|
|
city: &'a City, |
|
|
|
reachable: Vec<u16>, |
|
|
|
houses: Vec<House>, |
|
|
|
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<House> { |
|
|
|
&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<u32> = 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<House> = 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!(); |
|
|
|
} |
|
|
|
} |
|
|
|