Optimized with a maintained HouseLayout
This commit is contained in:
parent
a01fa30954
commit
d6fd306c61
1 changed files with 115 additions and 45 deletions
162
src/main.rs
162
src/main.rs
|
@ -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());
|
||||
|
||||
const AROUND_RANGE: i32 = 50;
|
||||
const MAX_CANDIDATES: usize = 20;
|
||||
const MAX_FAILED_ITERATIONS: usize = 50;
|
||||
let mut untried_houses = layout.houses().clone();
|
||||
untried_houses.shuffle(&mut rng);
|
||||
|
||||
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!();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue