Řešení KSP úlohy 33-3-4 Obsazování území https://ksp.mff.cuni.cz/h/ulohy/33/zadani3.html#task-33-3-4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
9.0 KiB

use db::{MemoryLayoutDB, SqliteLayoutDB, LayoutDB, SavedLayout};
use city::{City, House, HouseLayout, get_price};
use itertools::Itertools;
use rand::{thread_rng, Rng, SeedableRng};
use rand::rngs::StdRng;
use rayon::prelude::*;
mod city;
mod db;
mod combine;
mod subcity;
mod optimization;
mod population;
fn main() {
let mut sqlite_db = SqliteLayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB");
eprintln!("Loaded the DB, {} stored layouts", sqlite_db.layouts().len());
let city = City::read_from_file("01.in", city::INPUT_CITY_WIDTH, city::INPUT_CITY_HEIGHT);
eprintln!("Loaded the city file, {} houses", city.get_house_count());
let best_layout: SavedLayout = sqlite_db.layouts().iter()
.sorted_by(|x, y| get_price(&city, x.houses()).cmp(&get_price(&city, y.houses())))
.map(|layout| (*layout).clone())
.next().expect("No best layout found");
eprintln!("Found best layout, ID {}, price {}", best_layout.id(), get_price(&city, best_layout.houses()));
let x_range = 5533..=12000;
let y_range = 4750..=12500;
//let x_range = 5533..=8000;
//let y_range = 4750..=8000;
eprintln!("X {}-{}, Y {}-{}", x_range.start(), x_range.end(), y_range.start(), y_range.end());
let static_houses: Vec<House> = best_layout.houses().iter()
.filter(|house| !(x_range.contains(&house.x) && y_range.contains(&house.y)))
.map(|&house| house)
.collect();
let removed_houses: Vec<House> = best_layout.houses().iter()
.filter(|house| x_range.contains(&house.x) && y_range.contains(&house.y))
.map(|&house| house)
.collect();
let removed_price: u32 = removed_houses.iter().map(|x| city.get_price(*x) as u32).sum();
eprintln!("Price of all removed houses: {}", removed_price);
let subcity = subcity::build_subcity(&city, &static_houses);
eprintln!("Built subcity, width {}, height {}, {} houses, offset ({},{})",
subcity.city().width(),
subcity.city().height(),
subcity.city().get_house_count(),
subcity.x_offset(),
subcity.y_offset()
);
//let mut subcity_db = MemoryLayoutDB::new();
let filename = format!("X{}_{}Y{}_{}ID{}.sqlite", x_range.start(), x_range.end(), y_range.start(), y_range.end(), best_layout.id());
let mut subcity_db = SqliteLayoutDB::from_file(&filename).unwrap();
//for layout in subcity_db.layouts().iter()
// .filter(|x| get_price(subcity.city(), x.houses()) < removed_price)
// .sorted_by(|x, y| get_price(subcity.city(), x.houses()).cmp(&get_price(subcity.city(), y.houses()))) {
// let price = get_price(subcity.city(), layout.houses());
// let mut full_houses = subcity.to_full_houses(layout.houses());
// assert!(city::is_valid(&city, &full_houses).is_some());
// let mut house_layout = HouseLayout::new(&city);
// for house in &full_houses {
// house_layout.add_house(*house);
// }
// let seed: u64 = thread_rng().gen();
// let mut rng = StdRng::seed_from_u64(seed);
// optimization::iterate_improvements(&mut house_layout, &mut rng, true);
// eprintln!("Improvements finished");
// assert!(house_layout.is_valid());
// let improved_price = city::get_price(&city, house_layout.houses());
// if improved_price < city::get_price(&city, &full_houses) {
// eprintln!("Found improvement, new price {}, updating houses", improved_price);
// full_houses = house_layout.houses().clone();
// }
// assert!(city::is_valid(&city, &full_houses).is_some());
// println!("Layout {}, price {}, full price {}", layout.id(), price, city::get_price(&city, &full_houses));
// // Be careful with duplicates here
// //sqlite_db.add_layout(&full_houses, true);
// //println!("Inserted into the global DB");
//}
//return;
// Architecture:
// Build `FULL_RANDOM_LAYOUTS` random layouts
// loop {
// Try combining `CUT_COMBINE_TOP_LAYOUTS` top layouts using vertical/horizontal cuts
// Generate `WEIGHTED_RANDOM_LAYOUTS` layouts weighted by scores from existing layouts
// }
const FULL_RANDOM_LAYOUTS: usize = 100;
const DB_CHOICE_PROBABILITY: f64 = 0.90;
const WEIGHTED_RANDOM_LAYOUTS: usize = 200;
const CUT_COMBINE_TOP_LAYOUTS: usize = 500;
const IGNORED_WEIGHT_RATIO: f64 = 0.5;
if subcity_db.layouts().len() == 0 {
let mut full_random_layouts = Vec::new();
full_random_layouts.par_extend((0..FULL_RANDOM_LAYOUTS).into_par_iter().map(|i| {
let seed: u64 = thread_rng().gen();
let mut rng = StdRng::seed_from_u64(seed);
let mut layout = HouseLayout::new(subcity.city());
//eprintln!("Starting random population {}", i);
population::populate_random(&mut layout, &mut rng);
//eprintln!("Finished random init {}, price: {}, houses: {}", i, layout.price(), layout.houses().len());
optimization::iterate_improvements(&mut layout, &mut rng, false);
//eprintln!("Finished iterated improvements {}, price: {}, houses: {}", i, layout.price(), layout.houses().len());
layout.houses().clone()
}));
for houses in &full_random_layouts {
subcity_db.add_layout(houses, true);
}
eprintln!("Finished initial full random population");
} else {
eprintln!("Skipping initial full random population because DB is non-empty [{} layouts]", subcity_db.layouts().len());
}
let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap();
let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap();
eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len());
let mut cache = combine::CompatibilityCache::new();
// TODO: Deduplication of the DB
loop {
// This is a bottleneck when it comes to multithreading, it only runs on a single thread
combine::iterate_combines(&mut subcity_db, CUT_COMBINE_TOP_LAYOUTS, subcity.city(), &mut cache, false);
let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap();
let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap();
eprintln!("Finished cut combines");
eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len());
let mut weighted_random_layouts = Vec::new();
// Only using the underlying memory-based DB is required here as the SqliteLayoutDB
// is not thread-safe. We are only reading in the parallel iterator, so that's fine.
let memory_db = subcity_db.memory_db();
weighted_random_layouts.par_extend((0..WEIGHTED_RANDOM_LAYOUTS).into_par_iter().map(|i| {
let seed: u64 = thread_rng().gen();
let mut rng = StdRng::seed_from_u64(seed);
let mut layout = HouseLayout::new(subcity.city());
let price_range = worst_price - best_price;
let max_price = best_price as f64 + (price_range as f64 * (1. - IGNORED_WEIGHT_RATIO));
//eprintln!("Starting random weighted population {}, using DB, score range {}-{}, DB use probability {}...", i, best_price, worst_price, DB_CHOICE_PROBABILITY);
population::populate_using_db(&mut layout, &mut rng, memory_db, best_price as f64, max_price as f64, DB_CHOICE_PROBABILITY);
//eprintln!("Finished random init {}, price: {}, houses: {}", i, layout.price(), layout.houses().len());
optimization::iterate_improvements(&mut layout, &mut rng, false);
//eprintln!("Finished iterated improvements {}, price: {}, houses: {}", i, layout.price(), layout.houses().len());
layout.houses().clone()
}));
for houses in &weighted_random_layouts {
subcity_db.add_layout(houses, true);
}
let w_best: u32 = weighted_random_layouts.iter().map(|houses| houses.iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap();
let w_worst: u32 = weighted_random_layouts.iter().map(|houses| houses.iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap();
let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap();
let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap();
eprintln!("Finished weighted random population, price range {}-{}", w_best, w_worst);
eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len());
}
}