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() { | fn main() { | ||||||
|     let city = City::read_from_file("01.in"); |     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; |     let mut best_price: Option<u32> = None; | ||||||
|     loop { |     loop { | ||||||
|         let seed: u64 = thread_rng().gen(); |         let seed: u64 = thread_rng().gen(); | ||||||
|         eprintln!("Starting seed {}", seed); |         eprintln!("Starting seed {}", seed); | ||||||
| 
 | 
 | ||||||
|         let mut rng = StdRng::seed_from_u64(seed); |         let mut rng = StdRng::seed_from_u64(seed); | ||||||
|         let mut reachable = vec![false; SIZE * SIZE]; |         let mut layout = HouseLayout::new(&city); | ||||||
|         let mut houses: Vec<House> = Vec::new(); |  | ||||||
|         let mut claimed_houses = 0; |  | ||||||
|         loop { |         loop { | ||||||
|             loop { |             loop { | ||||||
|                 let x = rng.gen_range(0..SIZE); |                 let x = rng.gen_range(0..SIZE); | ||||||
|                 let y = rng.gen_range(0..SIZE); |                 let y = rng.gen_range(0..SIZE); | ||||||
|                 let house = House::new(x, y); |                 let house = House::new(x, y); | ||||||
|                 if city.is_house_xy(x, y) && !reachable[y * SIZE + x] { |                 if city.is_house_xy(x, y) && !layout.is_covered(house) { | ||||||
|                     for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { |                     layout.add_house(house); | ||||||
|                         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());
 |  | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             let finished = claimed_houses == city.get_house_count(); |             if layout.is_valid() { | ||||||
| 
 |  | ||||||
|             if finished { |  | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let mut price = get_price(&city, &houses); |         eprintln!("Finished random init, price: {}", layout.price()); | ||||||
|         eprintln!("Finished random init, price: {}", price); |  | ||||||
| 
 | 
 | ||||||
|         const AROUND_RANGE: i32 = 50; |         let mut untried_houses = layout.houses().clone(); | ||||||
|         const MAX_CANDIDATES: usize = 20; |         untried_houses.shuffle(&mut rng); | ||||||
|         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(); |             let mut new_candidates = Vec::new(); | ||||||
|             for delta_y in -AROUND_RANGE..=AROUND_RANGE { |             for delta_y in -AROUND_RANGE..=AROUND_RANGE { | ||||||
|                 for delta_x 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))); |             new_candidates.sort_by(|a, b| city.get_price(&a).cmp(&city.get_price(&b))); | ||||||
|             if new_candidates.len() == 0 { |             if new_candidates.len() == 0 { | ||||||
|                 eprintln!("Did not find candidate"); |                 //eprintln!("Did not find candidate");
 | ||||||
|             } else { |             } else { | ||||||
|                 for (i, &candidate) in new_candidates.iter().enumerate() { |                 for (i, &candidate) in new_candidates.iter().enumerate() { | ||||||
|                     if i > MAX_CANDIDATES { |                     if i > MAX_CANDIDATES { | ||||||
|  |                         //eprintln!("No valid candidate");
 | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     eprint!("Found candidate {}...", i); |                     //eprint!("Found candidate {}...", i);
 | ||||||
|                     let mut new_houses: Vec<_> = houses.to_vec().into_iter().filter(|h| &h != house).collect(); | 
 | ||||||
|                     new_houses.push(candidate); |                     let old_price = layout.price(); | ||||||
|                     // TODO: This is_valid check could be way more efficient
 |                     layout.remove_house(house_index); | ||||||
|                     if let Some(new_price) = is_valid(&city, &new_houses) { | 
 | ||||||
|                         let price_diff = new_price as i64 - price as i64; |                     if layout.is_valid() { | ||||||
|                         eprintln!(" candidate is valid, price diff: {}.", price_diff); |                         // 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); |                         eprintln!("Improved price: {}", new_price); | ||||||
|                         price = new_price; |                         untried_houses = layout.houses().clone(); | ||||||
|                         houses = new_houses; |                         untried_houses.shuffle(&mut rng); | ||||||
|                         failed_iterations = 0; |                         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; |                         break; | ||||||
|                     } else { |                     } else { | ||||||
|                         eprintln!(" candidate is invalid."); |                         //eprintln!(" candidate is invalid.");
 | ||||||
|  |                         layout.remove_house(candidate_index); | ||||||
|  |                         house_index = layout.add_house(house); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|             // Successful iterations always break
 |         let price = layout.price(); | ||||||
|             failed_iterations += 1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if best_price.is_none() || price < best_price.unwrap() { |         if best_price.is_none() || price < best_price.unwrap() { | ||||||
|             best_price = Some(price); |             best_price = Some(price); | ||||||
|             eprintln!("Finished randomization, price: {}, new best, printing", price); |             eprintln!("Finished randomization, price: {}, new best, printing", price); | ||||||
|             println!("Price {}, seed {}", price, seed); |             println!("Price {}, seed {}", price, seed); | ||||||
|             print_houses(&houses); |             print_houses(&layout.houses()); | ||||||
|             println!(); |             println!(); | ||||||
|         } else { |         } else { | ||||||
|             eprintln!("Finished randomization, price: {}, printing", price); |             eprintln!("Finished randomization, price: {}, printing", price); | ||||||
|             println!("Price {}, seed {}", price, seed); |             println!("Price {}, seed {}", price, seed); | ||||||
|             print_houses(&houses); |             print_houses(&layout.houses()); | ||||||
|             println!(); |             println!(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue