use crate::city; use crate::city::{City, House, SIZE, HOUSE_RANGE}; use itertools::Itertools; use itertools::iproduct; use std::collections::VecDeque; use std::collections::vec_deque::Iter; pub fn try_combine(city: &City, left_layouts: &Vec<&Vec>, right_layouts: &Vec<&Vec>) { // Sorted in reverse so we can remove from the end let mut left_houses_sorted: Vec> = left_layouts.iter() .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) .collect(); let right_houses_sorted: Vec> = right_layouts.iter() .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) .collect(); let mut lefts = Vec::new(); let mut rights = Vec::new(); for _ in left_layouts { lefts.push(LeftLine::new()); } 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 for x in 0..SIZE { eprintln!("Starting x {}", x); // Update the lines for (left_line, left_houses) in lefts.iter_mut().zip(left_houses_sorted.iter_mut()) { while let Some(house) = left_houses.last() { if house.x == x { left_line.add_house(*house, &city); left_houses.pop(); } else { break; } } } for right_line in rights.iter_mut() { right_line.remove_houses(x, &city); } // Check compatibility of lines if x == 0 { // Cannot check this due to limitations in the implementation of LeftLine, // it wouldn't be very interesting anyway. continue; } let pairs: Vec<_> = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate()) .filter(|((left_i, left), (right_i, right))| left_i != right_i) .map(|((left_i, left), (right_i, right))| (left, right, left.price + right.price, left_i, right_i)) .sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2)) .collect(); let mut compatibles = 0; let mut incompatibles = 0; for (left, right, price, left_i, right_i) in pairs { if is_compatible(city, &left, &right) { if best_price.is_none() || price < best_price.unwrap() { best_price = Some(price); eprintln!("{} - new best score, cut on x {}, left {} - right {}, printing", price, x, left_i, right_i); 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; // All other pairs would be more expensive break; } else { incompatibles += 1; } } eprintln!("{}/{} compatible", compatibles, compatibles + incompatibles); } } fn is_compatible(city: &City, left: &LeftLine, right: &RightLine) -> bool { for y in 0..SIZE { let max_left_covered_x = left.get_max_covered_x(y); let min_right_covered_x = right.get_min_covered_x(y); // This range will often be empty for x in (max_left_covered_x+1)..min_right_covered_x { if city.is_house_xy(x, y) { return false; } } } true } struct LeftLine { covers: Vec, houses: Vec, price: u32 } struct RightLine { covers: Vec, houses: VecDeque, price: u32 } impl LeftLine { pub fn new() -> Self { // XXX: Careful, default of 0 includes covering first vertical line let covers = vec![0; SIZE]; let houses = Vec::new(); LeftLine {covers, houses, price: 0 } } pub fn add_house(&mut self, house: House, city: &City) { let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { // Should always be the max variant 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 { 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 { pub fn new() -> Self { let covers = vec![usize::MAX; SIZE]; let houses = VecDeque::new(); RightLine {covers, houses, price: 0} } pub fn add_house(&mut self, house: House, city: &City) { // Added houses have to always be ordered by x let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { self.covers[y] = self.covers[y].min(range_rect.left); } self.houses.push_back(house); self.price += city.get_price(house) as u32 } pub fn remove_houses(&mut self, x: usize, city: &City) { // Has to be called with x, x+1, x+2... while let Some(house) = self.houses.front() { if house.x == x { let removed_house = self.houses.pop_front().unwrap(); let removed_rect = removed_house.range_rectangle(); // Remove the now-outdated distances around the removed house for y in removed_rect.top..=removed_rect.bottom { self.covers[y] = usize::MAX; } // Update distances around the removed house if the area of any houses // intersects the removed area for house in &self.houses { let house_rect = house.range_rectangle(); let y_intersection = if removed_house.y < house.y { house_rect.top..=removed_rect.bottom } else { removed_rect.top..=house_rect.bottom }; for y in y_intersection { self.covers[y] = self.covers[y].min(house_rect.left); } } self.price -= city.get_price(removed_house) as u32; } else { break; } } } pub fn get_min_covered_x(&self, y: usize) -> usize { self.covers[y].min(SIZE) } pub fn get_side_price(&self) -> u32 { self.price } pub fn houses(&self) -> Iter<'_, House> { self.houses.iter() } }