|
|
@ -1,40 +1,75 @@ |
|
|
|
use crate::city; |
|
|
|
use crate::db::LayoutDB; |
|
|
|
use crate::db::{LayoutDB, SavedLayout}; |
|
|
|
use crate::city::{City, House, SIZE}; |
|
|
|
use itertools::Itertools; |
|
|
|
use itertools::iproduct; |
|
|
|
use std::collections::VecDeque; |
|
|
|
use std::collections::{VecDeque, HashMap}; |
|
|
|
use std::collections::vec_deque::Iter; |
|
|
|
|
|
|
|
pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &Vec<Vec<House>>, db: &mut LayoutDB, transposed: bool) -> bool { |
|
|
|
let mut improved = false; |
|
|
|
struct LeftState<'a> { |
|
|
|
layout: &'a SavedLayout, |
|
|
|
sorted_houses: Vec<House>, |
|
|
|
line: LeftLine |
|
|
|
} |
|
|
|
|
|
|
|
struct RightState<'a> { |
|
|
|
layout: &'a SavedLayout, |
|
|
|
line: RightLine |
|
|
|
} |
|
|
|
|
|
|
|
pub struct CompatibilityCache { |
|
|
|
map: HashMap<(usize, usize, usize, bool), bool> |
|
|
|
} |
|
|
|
|
|
|
|
impl CompatibilityCache { |
|
|
|
pub fn new() -> CompatibilityCache { |
|
|
|
CompatibilityCache { |
|
|
|
map: HashMap::new() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn is_compatible(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, index: usize, y_axis: bool) -> Option<&bool> { |
|
|
|
self.map.get(&(left_layout.id(), right_layout.id(), index, y_axis)) |
|
|
|
} |
|
|
|
|
|
|
|
// Sorted in reverse so we can remove from the end
|
|
|
|
let mut left_houses_sorted: Vec<Vec<House>> = 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<Vec<House>> = right_layouts.iter() |
|
|
|
.map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect()) |
|
|
|
.collect(); |
|
|
|
pub fn set_compatible(&mut self, left_layout: &SavedLayout, right_layout: &SavedLayout, index: usize, y_axis: bool, compatible: bool) { |
|
|
|
self.map.insert((left_layout.id(), right_layout.id(), index, y_axis), compatible); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts: &Vec<SavedLayout>, db: &mut LayoutDB, cache: &mut CompatibilityCache, transposed: bool) -> bool { |
|
|
|
let mut improved = false; |
|
|
|
|
|
|
|
let mut lefts = Vec::new(); |
|
|
|
let mut rights = Vec::new(); |
|
|
|
|
|
|
|
for _ in left_layouts { |
|
|
|
lefts.push(LeftLine::new()); |
|
|
|
for left_layout in left_layouts { |
|
|
|
// Sorted in reverse so we can remove from the end
|
|
|
|
let sorted_houses: Vec<_> = left_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); |
|
|
|
|
|
|
|
lefts.push(LeftState { |
|
|
|
layout: &left_layout, |
|
|
|
sorted_houses, |
|
|
|
line: LeftLine::new() |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
for mut right_layout in right_houses_sorted { |
|
|
|
let mut right = RightLine::new(); |
|
|
|
for right_layout in right_layouts { |
|
|
|
let mut sorted_houses: Vec<_> = right_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect(); |
|
|
|
|
|
|
|
let mut line = RightLine::new(); |
|
|
|
// Make sure that we include all houses initially
|
|
|
|
while let Some(house) = right_layout.pop() { |
|
|
|
right.add_house(house, &city); |
|
|
|
while let Some(house) = sorted_houses.pop() { |
|
|
|
line.add_house(house, &city); |
|
|
|
} |
|
|
|
rights.push(right); |
|
|
|
rights.push(RightState { |
|
|
|
layout: right_layout, |
|
|
|
line |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
let mut best_price = left_layouts.iter().chain(right_layouts.iter()) |
|
|
|
.map(|layout| city::get_price(&city, layout)) |
|
|
|
.map(|layout| city::get_price(&city, layout.houses())) |
|
|
|
.min(); |
|
|
|
|
|
|
|
let axis = if transposed { "y" } else { "x" }; |
|
|
@ -44,11 +79,11 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: & |
|
|
|
eprintln!("Starting {} {}", axis, 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() { |
|
|
|
for left in lefts.iter_mut() { |
|
|
|
while let Some(house) = left.sorted_houses.last() { |
|
|
|
if house.x == x { |
|
|
|
left_line.add_house(*house, &city); |
|
|
|
left_houses.pop(); |
|
|
|
left.line.add_house(*house, &city); |
|
|
|
left.sorted_houses.pop(); |
|
|
|
} else { |
|
|
|
break; |
|
|
|
} |
|
|
@ -56,7 +91,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: & |
|
|
|
} |
|
|
|
|
|
|
|
for right_line in rights.iter_mut() { |
|
|
|
right_line.remove_houses(x, &city); |
|
|
|
right_line.line.remove_houses(x, &city); |
|
|
|
} |
|
|
|
|
|
|
|
// Check compatibility of lines
|
|
|
@ -66,21 +101,37 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: & |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
let pairs: Vec<_> = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate()) |
|
|
|
let pairs = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate()) |
|
|
|
.filter(|((left_i, _), (right_i, _))| 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(); |
|
|
|
.map(|((left_i, left), (right_i, right))| (left, right, left.line.price + right.line.price, left_i, right_i)) |
|
|
|
.sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2)); |
|
|
|
|
|
|
|
let mut compatibles = 0; |
|
|
|
let mut incompatibles = 0; |
|
|
|
let mut cache_hits = 0; |
|
|
|
for (left, right, price, left_i, right_i) in pairs { |
|
|
|
if is_compatible(city, &left, &right) { |
|
|
|
if let Some(min_price) = best_price { |
|
|
|
if price >= min_price { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
let compatible = match cache.is_compatible(left.layout, right.layout, x, transposed) { |
|
|
|
None => { |
|
|
|
let compatible = is_compatible(city, &left.line, &right.line); |
|
|
|
cache.set_compatible(left.layout, right.layout, x, transposed, compatible); |
|
|
|
compatible |
|
|
|
} |
|
|
|
Some(compatible) => { |
|
|
|
cache_hits += 1; |
|
|
|
*compatible |
|
|
|
} |
|
|
|
}; |
|
|
|
if compatible { |
|
|
|
if best_price.is_none() || price < best_price.unwrap() { |
|
|
|
best_price = Some(price); |
|
|
|
eprintln!("{} - new best score, cut on {} {}, left {} - right {}, printing", price, axis, x, left_i, right_i); |
|
|
|
println!("{} - new best score, cut on {} {}, left {} - right {}", price, axis, x, left_i, right_i); |
|
|
|
let mut new_houses: Vec<_> = left.houses().copied().chain(right.houses().copied()).collect(); |
|
|
|
let mut new_houses: Vec<_> = left.line.houses().copied().chain(right.line.houses().copied()).collect(); |
|
|
|
if transposed { |
|
|
|
new_houses = transpose_layout(&new_houses); |
|
|
|
} |
|
|
@ -90,7 +141,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: & |
|
|
|
} |
|
|
|
|
|
|
|
// We only add best results to avoid overfilling the database with similar layouts
|
|
|
|
db.add_layout(&new_houses, true); |
|
|
|
db.add_layout(&new_houses, false); |
|
|
|
improved = true; |
|
|
|
} |
|
|
|
compatibles += 1; |
|
|
@ -102,7 +153,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: & |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
eprintln!("{} incompatibles checked before {} compatible", incompatibles, compatibles); |
|
|
|
eprintln!("{} incompatibles checked before {} compatible ({} cache hits)", incompatibles, compatibles, cache_hits); |
|
|
|
} |
|
|
|
|
|
|
|
improved |
|
|
|