Vertical combines
This commit is contained in:
		
							parent
							
								
									5b10766ab9
								
							
						
					
					
						commit
						751e0d81ff
					
				
					 2 changed files with 101 additions and 54 deletions
				
			
		|  | @ -15,14 +15,11 @@ fn main() { | ||||||
| 
 | 
 | ||||||
|     let layouts = db.layouts(); |     let layouts = db.layouts(); | ||||||
|     let sorted: Vec<_> = layouts.iter().sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses()))).collect(); |     let sorted: Vec<_> = layouts.iter().sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses()))).collect(); | ||||||
|     let first = sorted[1]; |  | ||||||
|     let second = sorted[0]; |  | ||||||
|     eprintln!("Combining layouts (ID {}, price {}) and (ID {}, price {})", |  | ||||||
|               first.id(), |  | ||||||
|               city::get_price(&city, first.houses()), |  | ||||||
|               second.id(), |  | ||||||
|               city::get_price(&city, second.houses()), |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     combine::try_combine(&city, first.houses(), second.houses()); |     const TOP_LAYOUT_COUNT: usize = 10; | ||||||
|  | 
 | ||||||
|  |     let chosen_layouts = sorted.iter().take(TOP_LAYOUT_COUNT).map(|l| l.houses()).collect(); | ||||||
|  |     eprintln!("Starting to combine {} top houses DB; vertical cuts", TOP_LAYOUT_COUNT); | ||||||
|  | 
 | ||||||
|  |     combine::try_combine(&city, &chosen_layouts, &chosen_layouts); | ||||||
| } | } | ||||||
							
								
								
									
										140
									
								
								src/combine.rs
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								src/combine.rs
									
									
									
									
									
								
							|  | @ -2,36 +2,52 @@ use crate::city; | ||||||
| use crate::city::{City, House, SIZE, HOUSE_RANGE}; | use crate::city::{City, House, SIZE, HOUSE_RANGE}; | ||||||
| use itertools::Itertools; | use itertools::Itertools; | ||||||
| use std::collections::VecDeque; | use std::collections::VecDeque; | ||||||
|  | use std::collections::vec_deque::Iter; | ||||||
| 
 | 
 | ||||||
| pub fn try_combine(city: &City, layout1: &Vec<House>, layout2: &Vec<House>) { | pub fn try_combine(city: &City, left_layouts: &Vec<&Vec<House>>, right_layouts: &Vec<&Vec<House>>) { | ||||||
|     // Sorted in reverse so we can remove from the end
 |     // Sorted in reverse so we can remove from the end
 | ||||||
|     let mut houses1_sorted: Vec<House> = layout1.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); |     let mut left_houses_sorted: Vec<Vec<House>> = left_layouts.iter() | ||||||
|     let mut houses2_sorted: Vec<House> = layout2.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); |         .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) | ||||||
|  |         .collect(); | ||||||
|  |     let mut right_houses_sorted: Vec<Vec<House>> = right_layouts.iter() | ||||||
|  |         .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) | ||||||
|  |         .collect(); | ||||||
| 
 | 
 | ||||||
|     // TODO: We may want to maintain K left sides and K right sides to compare K^2 layouts at once at each x
 |     let mut lefts = Vec::new(); | ||||||
|  |     let mut rights = Vec::new(); | ||||||
| 
 | 
 | ||||||
|     // houses1 is left, houses2 is right
 |     for _ in left_layouts { | ||||||
| 
 |         lefts.push(LeftLine::new()); | ||||||
|     let mut left = LeftLine::new(); |  | ||||||
|     let mut right = RightLine::new(); |  | ||||||
| 
 |  | ||||||
|     // Make sure that we include all houses initially
 |  | ||||||
|     while let Some(house) = houses2_sorted.pop() { |  | ||||||
|         right.add_house(house); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     for mut right_layout in right_houses_sorted { | ||||||
|  |         let mut right = RightLine::new(); | ||||||
|  |         // Make sure that we include all houses initially
 | ||||||
|  |         while let Some(house) = right_layout.pop() { | ||||||
|  |             right.add_house(house, &city); | ||||||
|  |         } | ||||||
|  |         rights.push(right); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let mut best_price = None; | ||||||
|     // x is the last left coordinate, x+1 is right
 |     // x is the last left coordinate, x+1 is right
 | ||||||
|     for x in 0..SIZE { |     for x in 0..SIZE { | ||||||
|  |         eprintln!("Starting x {}", x); | ||||||
|         // Update the lines
 |         // Update the lines
 | ||||||
|         while let Some(house) = houses1_sorted.last() { |         for (mut left_line, mut left_houses) in lefts.iter_mut().zip(left_houses_sorted.iter_mut()) { | ||||||
|             if house.x == x { |             while let Some(house) = left_houses.last() { | ||||||
|                 left.add_house(*house); |                 if house.x == x { | ||||||
|                 houses1_sorted.pop(); |                     left_line.add_house(*house, &city); | ||||||
|             } else { |                     left_houses.pop(); | ||||||
|                 break; |                 } else { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         right.remove_houses(x); | 
 | ||||||
|  |         for mut right_line in rights.iter_mut() { | ||||||
|  |             right_line.remove_houses(x, &city); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         // Check compatibility of lines
 |         // Check compatibility of lines
 | ||||||
|         if x == 0 { |         if x == 0 { | ||||||
|  | @ -40,22 +56,35 @@ pub fn try_combine(city: &City, layout1: &Vec<House>, layout2: &Vec<House>) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if is_compatible(city, &left, &right) { |         let mut compatibles = 0; | ||||||
|             eprintln!("Compatible on X {}", x); |         let mut incompatibles = 0; | ||||||
|             let houses: Vec<_> = layout1.iter().filter(|h| h.x <= x).chain(layout2.iter().filter(|h| h.x > x)).map(|h| *h).collect(); |         for (left_i, left) in lefts.iter().enumerate() { | ||||||
|             eprintln!("Price {}", city::get_price(&city, &houses)); |             for (right_i, right) in rights.iter().enumerate() { | ||||||
|             //if let Some(price) = city::is_valid(&city, &houses) {
 |                 if left_i == right_i { | ||||||
|             //    eprintln!("Merge valid with price {}", price)
 |                     // Do not compare the same layout
 | ||||||
|             //} else {
 |                     continue; | ||||||
|             //    eprintln!("Merge actually invalid, printing invalid merge");
 |                 } | ||||||
|             //    println!("{}", houses.len());
 | 
 | ||||||
|             //    for house in houses {
 |                 if is_compatible(city, &left, &right) { | ||||||
|             //        println!("{} {}", house.y, house.x);
 |                     let price = left.price + right.price; | ||||||
|             //    }
 |                     if best_price.is_none() || price < best_price.unwrap() { | ||||||
|             //}
 |                         best_price = Some(price); | ||||||
|         } else { |                         eprintln!("{} - new best score, cut on x {}, left {} - right {}, printing", price, x, left_i, right_i); | ||||||
|             eprintln!("Incompatible on X {}", x); |                         println!("{} - new best score, cut on x {}, left {} - right {}", price, x, left_i, right_i); | ||||||
|  |                         let new_houses: Vec<_> = left.houses().chain(right.houses()).collect(); | ||||||
|  |                         println!("{}", new_houses.len()); | ||||||
|  |                         for house in new_houses { | ||||||
|  |                             println!("{} {}", house.y, house.x); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     compatibles += 1; | ||||||
|  |                 } else { | ||||||
|  |                     incompatibles += 1; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         eprintln!("{}/{} compatible", compatibles, compatibles + incompatibles); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -67,8 +96,6 @@ fn is_compatible(city: &City, left: &LeftLine, right: &RightLine) -> bool { | ||||||
|         // This range will often be empty
 |         // This range will often be empty
 | ||||||
|         for x in (max_left_covered_x+1)..min_right_covered_x { |         for x in (max_left_covered_x+1)..min_right_covered_x { | ||||||
|             if city.is_house_xy(x, y) { |             if city.is_house_xy(x, y) { | ||||||
|                 // This is an uncovered house
 |  | ||||||
|                 eprintln!("House ({},{}) in uncovered range [{},{}]", x, y, max_left_covered_x+1, min_right_covered_x-1); |  | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -78,53 +105,67 @@ fn is_compatible(city: &City, left: &LeftLine, right: &RightLine) -> bool { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct LeftLine { | struct LeftLine { | ||||||
|     covers: Vec<usize> |     covers: Vec<usize>, | ||||||
|  |     houses: Vec<House>, | ||||||
|  |     price: u32 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct RightLine { | struct RightLine { | ||||||
|     covers: Vec<usize>, |     covers: Vec<usize>, | ||||||
|     houses: VecDeque<House> |     houses: VecDeque<House>, | ||||||
|  |     price: u32 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl LeftLine { | impl LeftLine { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         // XXX: Careful, default of 0 includes covering first vertical line
 |         // XXX: Careful, default of 0 includes covering first vertical line
 | ||||||
|         let covers = vec![0; SIZE]; |         let covers = vec![0; SIZE]; | ||||||
|         LeftLine {covers} |         let houses = Vec::new(); | ||||||
|  |         LeftLine {covers, houses, price: 0 } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn add_house(&mut self, house: House) { |     pub fn add_house(&mut self, house: House, city: &City) { | ||||||
|         let range_rect = house.range_rectangle(); |         let range_rect = house.range_rectangle(); | ||||||
|         for y in range_rect.top..=range_rect.bottom { |         for y in range_rect.top..=range_rect.bottom { | ||||||
|             // Should always be the max variant
 |             // Should always be the max variant
 | ||||||
|             self.covers[y] = self.covers[y].max(range_rect.right); |             self.covers[y] = self.covers[y].max(range_rect.right); | ||||||
|         } |         } | ||||||
|  |         self.price += city.get_price(house) as u32; | ||||||
|  |         self.houses.push(house); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn get_max_covered_x(&self, y: usize) -> usize { |     pub fn get_max_covered_x(&self, y: usize) -> usize { | ||||||
|         self.covers[y] |         self.covers[y] | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_side_price(&self) -> u32 { | ||||||
|  |         self.price | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn houses(&self) -> std::slice::Iter<'_, House> { | ||||||
|  |         self.houses.iter() | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl RightLine { | impl RightLine { | ||||||
|     pub fn new() -> Self { |     pub fn new() -> Self { | ||||||
|         let covers = vec![usize::MAX; SIZE]; |         let covers = vec![usize::MAX; SIZE]; | ||||||
|         let houses = VecDeque::new(); |         let houses = VecDeque::new(); | ||||||
|         RightLine {covers, houses} |         RightLine {covers, houses, price: 0} | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn add_house(&mut self, house: House) { |     pub fn add_house(&mut self, house: House, city: &City) { | ||||||
|         // Added houses have to always be ordered by x
 |         // Added houses have to always be ordered by x
 | ||||||
|         eprintln!("Added house ({},{}) to right line", house.x, house.y); |  | ||||||
|         let range_rect = house.range_rectangle(); |         let range_rect = house.range_rectangle(); | ||||||
|         for y in range_rect.top..=range_rect.bottom { |         for y in range_rect.top..=range_rect.bottom { | ||||||
|             self.covers[y] = self.covers[y].min(range_rect.left); |             self.covers[y] = self.covers[y].min(range_rect.left); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self.houses.push_back(house); |         self.houses.push_back(house); | ||||||
|  |         self.price += city.get_price(house) as u32 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn remove_houses(&mut self, x: usize) { |     pub fn remove_houses(&mut self, x: usize, city: &City) { | ||||||
|         // Has to be called with x, x+1, x+2...
 |         // Has to be called with x, x+1, x+2...
 | ||||||
|         while let Some(house) = self.houses.front() { |         while let Some(house) = self.houses.front() { | ||||||
|             if house.x == x { |             if house.x == x { | ||||||
|  | @ -140,7 +181,6 @@ impl RightLine { | ||||||
|                 // intersects the removed area
 |                 // intersects the removed area
 | ||||||
|                 for house in &self.houses { |                 for house in &self.houses { | ||||||
|                     let house_rect = house.range_rectangle(); |                     let house_rect = house.range_rectangle(); | ||||||
|                     // TODO: Verify this intersection is correct
 |  | ||||||
|                     let y_intersection = if removed_house.y < house.y { |                     let y_intersection = if removed_house.y < house.y { | ||||||
|                         house_rect.top..=removed_rect.bottom |                         house_rect.top..=removed_rect.bottom | ||||||
|                     } else { |                     } else { | ||||||
|  | @ -151,6 +191,8 @@ impl RightLine { | ||||||
|                         self.covers[y] = self.covers[y].min(house_rect.left); |                         self.covers[y] = self.covers[y].min(house_rect.left); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|  |                 self.price -= city.get_price(removed_house) as u32; | ||||||
|             } else { |             } else { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  | @ -160,4 +202,12 @@ impl RightLine { | ||||||
|     pub fn get_min_covered_x(&self, y: usize) -> usize { |     pub fn get_min_covered_x(&self, y: usize) -> usize { | ||||||
|         self.covers[y].min(SIZE) |         self.covers[y].min(SIZE) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_side_price(&self) -> u32 { | ||||||
|  |         self.price | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn houses(&self) -> Iter<'_, House> { | ||||||
|  |         self.houses.iter() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue