diff --git a/src/combine-layouts.rs b/src/combine-layouts.rs index add1bb2..7e9cc46 100644 --- a/src/combine-layouts.rs +++ b/src/combine-layouts.rs @@ -1,4 +1,4 @@ -use db::{LayoutDB, SavedLayout}; +use db::{SqliteLayoutDB, SavedLayout}; use city::{City, House}; use itertools::Itertools; use crate::combine::transpose_layout; @@ -13,7 +13,7 @@ enum LastStep { } fn main() { - let mut db = LayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); + let mut db = SqliteLayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); eprintln!("Loaded the DB, {} stored layouts", db.layouts().len()); let city = City::read_from_file("01.in", city::INPUT_CITY_WIDTH, city::INPUT_CITY_HEIGHT); diff --git a/src/combine.rs b/src/combine.rs index 3f8fc46..16a5ec1 100644 --- a/src/combine.rs +++ b/src/combine.rs @@ -1,5 +1,5 @@ use crate::city; -use crate::db::{LayoutDB, SavedLayout, MergeLowerBound}; +use crate::db::{SqliteLayoutDB, SavedLayout, MergeLowerBound, LayoutDB}; use crate::city::{City, House}; use itertools::Itertools; use itertools::iproduct; @@ -37,7 +37,7 @@ impl CompatibilityCache { } } -pub fn create_new_best_combination(city: &City, left_layouts: &Vec, right_layouts: &Vec, db: &mut LayoutDB, cache: &mut CompatibilityCache, transposed: bool) -> bool { +pub fn create_new_best_combination(city: &City, left_layouts: &Vec, right_layouts: &Vec, db: &mut TDB, cache: &mut CompatibilityCache, transposed: bool) -> bool { let mut best_price = left_layouts.iter().chain(right_layouts.iter()) .map(|layout| city::get_price(&city, layout.houses())) .min(); diff --git a/src/db.rs b/src/db.rs index 9722b7d..68a6c11 100644 --- a/src/db.rs +++ b/src/db.rs @@ -2,8 +2,12 @@ use crate::city::House; use rusqlite::{Connection, NO_PARAMS, params, Result}; use std::collections::HashMap; -pub struct LayoutDB { +pub struct SqliteLayoutDB { connection: Connection, + memory_db: MemoryLayoutDB, +} + +pub struct MemoryLayoutDB { layouts: Vec, merge_lower_bounds: HashMap<(usize, usize, bool), MergeLowerBound>, } @@ -57,7 +61,76 @@ impl SavedLayout { } } -impl LayoutDB { +impl MemoryLayoutDB { + pub fn new(layouts: Vec, merge_lower_bounds: HashMap<(usize, usize, bool), MergeLowerBound>) -> Self { + MemoryLayoutDB { layouts, merge_lower_bounds } + } + + // This exists for use by the SqliteLayoutDB + fn add_layout_with_id(&mut self, houses: &Vec, layout_id: usize, fully_optimized: bool) { + self.layouts.push(SavedLayout { id: layout_id, houses: houses.clone() }); + } +} + +pub trait LayoutDB { + fn layouts(&self) -> &Vec; + fn add_layout(&mut self, houses: &Vec, fully_optimized: bool); + fn get_merge_lower_bound(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, y_axis: bool) -> Option; + fn add_merge_lower_bound(&mut self, lower_bound: MergeLowerBound); + fn add_merge_lower_bounds(&mut self, lower_bounds: Vec); +} + +impl LayoutDB for MemoryLayoutDB { + fn layouts(&self) -> &Vec { + &self.layouts + } + + fn add_layout(&mut self, houses: &Vec, fully_optimized: bool) { + let layout_id = self.layouts.iter().map(|x| x.id).max().unwrap_or_default() + 1; + self.layouts.push(SavedLayout { id: layout_id, houses: houses.clone() }); + } + + fn get_merge_lower_bound(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, y_axis: bool) -> Option { + if let Some(bound) = self.merge_lower_bounds.get(&(left_layout.id, right_layout.id, y_axis)) { + return Some(bound.price); + } + None + } + + fn add_merge_lower_bound(&mut self, lower_bound: MergeLowerBound) { + self.merge_lower_bounds.insert((lower_bound.left_layout_id, lower_bound.right_layout_id, lower_bound.y_axis), lower_bound); + } + + fn add_merge_lower_bounds(&mut self, lower_bounds: Vec) { + for lower_bound in lower_bounds { + self.merge_lower_bounds.insert((lower_bound.left_layout_id, lower_bound.right_layout_id, lower_bound.y_axis), lower_bound); + } + } +} + +impl LayoutDB for SqliteLayoutDB { + fn layouts(&self) -> &Vec { + self.layouts() + } + + fn add_layout(&mut self, houses: &Vec, fully_optimized: bool) { + self.add_layout(houses, fully_optimized).unwrap(); + } + + fn get_merge_lower_bound(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, y_axis: bool) -> Option { + self.get_merge_lower_bound(left_layout, right_layout, y_axis) + } + + fn add_merge_lower_bound(&mut self, lower_bound: MergeLowerBound) { + self.add_merge_lower_bound(lower_bound).unwrap() + } + + fn add_merge_lower_bounds(&mut self, lower_bounds: Vec) { + self.add_merge_lower_bounds(lower_bounds).unwrap() + } +} + +impl SqliteLayoutDB { pub fn from_file(filename: &str) -> Result { let connection = Connection::open(filename)?; let mut layouts: HashMap> = HashMap::new(); @@ -102,11 +175,12 @@ impl LayoutDB { } ).collect(); - Ok(LayoutDB { connection, layouts, merge_lower_bounds: merges }) + let memory_db = MemoryLayoutDB::new(layouts, merges); + Ok(SqliteLayoutDB { connection, memory_db}) } pub fn layouts(&self) -> &Vec { - &self.layouts + self.memory_db.layouts() } pub fn add_layout(&mut self, houses: &Vec, fully_optimized: bool) -> Result<()> { @@ -119,15 +193,12 @@ impl LayoutDB { params![layout_id, house.x as u32, house.y as u32])?; } transaction.commit()?; - self.layouts.push(SavedLayout { id: layout_id as usize, houses: houses.clone() }); + self.memory_db.add_layout_with_id(houses, layout_id as usize, fully_optimized); Ok(()) } pub fn get_merge_lower_bound(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, y_axis: bool) -> Option { - if let Some(bound) = self.merge_lower_bounds.get(&(left_layout.id, right_layout.id, y_axis)) { - return Some(bound.price); - } - None + self.memory_db.get_merge_lower_bound(left_layout, right_layout, y_axis) } pub fn add_merge_lower_bound(&mut self, lower_bound: MergeLowerBound) -> Result<()> { @@ -140,13 +211,13 @@ impl LayoutDB { ], )?; transaction.commit()?; - self.merge_lower_bounds.insert((lower_bound.left_layout_id, lower_bound.right_layout_id, lower_bound.y_axis), lower_bound); + self.memory_db.add_merge_lower_bound(lower_bound); Ok(()) } pub fn add_merge_lower_bounds(&mut self, lower_bounds: Vec) -> Result<()> { let transaction = self.connection.transaction()?; - for lower_bound in lower_bounds { + for lower_bound in &lower_bounds { transaction.execute("INSERT INTO merge_lower_bounds (left_layout_id, right_layout_id, axis, price) VALUES (?1, ?2, ?3, ?4) ON CONFLICT(left_layout_id, right_layout_id) DO UPDATE SET price = ?4", params![lower_bound.left_layout_id as u32, lower_bound.right_layout_id as u32, @@ -154,9 +225,9 @@ impl LayoutDB { lower_bound.price ], )?; - self.merge_lower_bounds.insert((lower_bound.left_layout_id, lower_bound.right_layout_id, lower_bound.y_axis), lower_bound); } transaction.commit()?; + self.memory_db.add_merge_lower_bounds(lower_bounds); Ok(()) } } diff --git a/src/import-logs.rs b/src/import-logs.rs index 11b2448..6e1b430 100644 --- a/src/import-logs.rs +++ b/src/import-logs.rs @@ -2,7 +2,7 @@ use std::io; use std::io::BufRead; use regex::Regex; use crate::city::House; -use crate::db::LayoutDB; +use crate::db::SqliteLayoutDB; mod city; mod db; @@ -14,7 +14,7 @@ enum State { } fn main() { - let mut db = LayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); + let mut db = SqliteLayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); let stdin = io::stdin(); let price_regex = Regex::new("^Price ([0-9]*), seed ([0-9]*)$").unwrap(); diff --git a/src/main.rs b/src/main.rs index 2ddd4dc..0640ebe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::fmt; use std::fmt::Formatter; use std::collections::{HashMap, HashSet}; use city::{HouseLayout, City, House}; -use crate::db::LayoutDB; +use crate::db::SqliteLayoutDB; use crate::population::{build_house_probabilities, populate_from_saved_layout}; use itertools::Itertools; @@ -23,7 +23,7 @@ enum RunType { } fn main() { - let mut db = LayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); + let mut db = SqliteLayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); eprintln!("Loaded the DB, {} stored layouts", db.layouts().len()); let city = City::read_from_file("01.in", city::INPUT_CITY_WIDTH, city::INPUT_CITY_HEIGHT); diff --git a/src/population.rs b/src/population.rs index 3a88a41..fa9516e 100644 --- a/src/population.rs +++ b/src/population.rs @@ -1,7 +1,7 @@ use rand::Rng; use crate::city::{House, HouseLayout, City}; use rand::prelude::*; -use crate::db::{LayoutDB, SavedLayout}; +use crate::db::{SqliteLayoutDB, SavedLayout, LayoutDB}; use crate::city; use std::collections::HashMap; use std::ops::Add; @@ -26,7 +26,7 @@ pub fn populate_random(layout: &mut HouseLayout, rng: &mut StdRng) { } } -pub fn build_house_probabilities(city: &City, db: &LayoutDB, min_score: f64, max_score: f64) -> (WeightedIndex, Vec){ +pub fn build_house_probabilities(city: &City, db: &TDB, min_score: f64, max_score: f64) -> (WeightedIndex, Vec){ let mut counts: HashMap = HashMap::new(); for layout in db.layouts() { let price = city::get_price(&city, layout.houses()) as f64; @@ -44,8 +44,8 @@ pub fn build_house_probabilities(city: &City, db: &LayoutDB, min_score: f64, max (index, houses.iter().map(|(house, _)| *house).collect()) } -pub fn populate_using_db(layout: &mut HouseLayout, mut rng: &mut StdRng, db: &LayoutDB, min_score: f64, max_score: f64, db_probability: f64) { - let (mut index, houses) = build_house_probabilities(&layout.city, &db, min_score, max_score); +pub fn populate_using_db(layout: &mut HouseLayout, mut rng: &mut StdRng, db: &TDB, min_score: f64, max_score: f64, db_probability: f64) { + let (mut index, houses) = build_house_probabilities(&layout.city, db, min_score, max_score); loop { if rng.gen::() < db_probability {