diff --git a/Cargo.lock b/Cargo.lock index 6bc4ce7..dd5d90c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,28 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "console" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50aab2529019abfabfa93f1e6c41ef392f91fbf179b347a7e96abb524884a08" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "unicode-width", + "winapi", + "winapi-util", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "getrandom" version = "0.2.0" @@ -23,12 +45,36 @@ dependencies = [ "wasi", ] +[[package]] +name = "indicatif" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" +dependencies = [ + "console", + "lazy_static", + "number_prefix", + "regex", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +[[package]] +name = "number_prefix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -40,6 +86,7 @@ name = "prague" version = "0.1.0" dependencies = [ "byteorder", + "indicatif", "rand", ] @@ -83,8 +130,70 @@ dependencies = [ "rand_core", ] +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "terminal_size" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd2d183bd3fac5f5fe38ddbeb4dc9aec4a39a9d7d59e7491d900302da01cbe1" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index ff5f70a..1aabe6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,12 @@ edition = "2018" [dependencies] byteorder = "1.3.4" rand = "0.8.0" +indicatif = "0.15.0" + +[[bin]] +name = "prague" +path = "src/main.rs" + +[[bin]] +name = "find-useless" +path = "src/find_useless.rs" diff --git a/src/find_useless.rs b/src/find_useless.rs new file mode 100644 index 0000000..ef0dda4 --- /dev/null +++ b/src/find_useless.rs @@ -0,0 +1,53 @@ +use indicatif::{ProgressBar, ProgressStyle}; +use std::collections::HashSet; +use main::{get_neighbors, House, City}; + +mod main; + +fn main() { + let city = City::read_from_file("01.in"); + let bar = ProgressBar::new(city.get_house_count() as u64); + bar.set_style(ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({msg}) ({eta})") + .progress_chars("#>-")); + + let mut useless_count = 0; + let mut checked_count = 0; + for y in 0..main::SIZE { + for x in 0..main::SIZE { + if city.is_house_xy(x, y) { + let house = House::new(x, y); + + let house_neighbors = get_neighbors(&city, &house); + let mut useless = true; + for neighbor in &house_neighbors { + if city.get_price(&house) < city.get_price(&neighbor) { + useless = false; + break; + } + let neighbor_neighbors: HashSet<_> = get_neighbors(&city, &neighbor).into_iter().collect(); + // Check if house_neighbors is a subset of neighbor_neighbors + let all_in = &house_neighbors.iter().all(|item| neighbor_neighbors.contains(item)); + if !all_in { + useless = false; + break; + } + } + + if useless { + println!("{} {}", y, x); + useless_count += 1; + } else { + //println!("Y{} X{} may be sometimes worth buying", y, x); + } + + checked_count += 1; + bar.set_message(&*format!("{}, {:.2}%", useless_count, 100.0 * useless_count as f64/checked_count as f64)); + bar.inc(1); + } + } + } + + bar.finish(); +} + diff --git a/src/main.rs b/src/main.rs index 6e24a7c..556a6ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,28 @@ -use std::fs::File; -use byteorder::{LittleEndian, ReadBytesExt}; -use rand::prelude::StdRng; +use rand::prelude::{StdRng, SliceRandom}; use rand::{SeedableRng, Rng, thread_rng}; -const SIZE: usize = 16384; +pub const SIZE: usize = 16384; -struct City { +pub struct City { prices: Vec, buyable_house_count: usize } impl City { + pub fn read_from_file(filename: &str) -> Self { + let values = std::fs::read(filename).unwrap(); + let mut prices: Vec = Vec::new(); + + for y in 0..SIZE { + for x in 0..SIZE { + let price = (values[(y * SIZE + x) * 2] as u16) | ((values[(y * SIZE + x) * 2] as u16) << 8); + prices.push(price); + } + } + + City::new(prices) + } + pub fn new(prices: Vec) -> Self { let mut buyable_house_count = 0; for &price in &prices { @@ -42,7 +54,8 @@ impl City { } } -struct House { +#[derive(Eq, PartialEq, Hash, Copy, Clone)] +pub struct House { x: usize, y: usize, } @@ -54,18 +67,7 @@ impl House { } fn main() { - let values = std::fs::read("01.in").unwrap(); - let mut prices: Vec = Vec::new(); - - for y in 0..SIZE { - for x in 0..SIZE { - let price = (values[(y * SIZE + x) * 2] as u16) | ((values[(y * SIZE + x) * 2] as u16) << 8); - prices.push(price); - } - } - - let city = City::new(prices); - println!("Created city"); + let city = City::read_from_file("01.in"); loop { let seed: u64 = thread_rng().gen(); @@ -101,16 +103,47 @@ fn main() { let finished = claimed_houses == city.get_house_count(); if finished { - let price = get_price(&city, &houses); - eprintln!("Price: {}", price); - //print_houses(&houses); break; } } + let mut price = get_price(&city, &houses); + eprintln!("Finished randomized init, price: {}", price); + loop { + let house = &houses.choose(&mut rng).unwrap(); + let mut new_candidate: Option = None; + for _ in 0..5000 { + let delta_x = rng.gen_range(-50..=50); + let delta_y = rng.gen_range(-50..=50); + let new_x = (house.x as i32 + delta_x).max(0).min(SIZE as i32 - 1) as usize; + let new_y = (house.y as i32 + delta_y).max(0).min(SIZE as i32 - 1) as usize; + if city.is_house_xy(new_x, new_y) && city.get_price_xy(new_x, new_y) < city.get_price(&house) { + new_candidate = Some(House::new(new_x, new_y)); + break; + } + } + + if let Some(candidate) = new_candidate { + eprint!("Found candidate..."); + let mut new_houses: Vec<_> = houses.to_vec().into_iter().filter(|h| &h != house).collect(); + new_houses.push(candidate); + if let Some(new_price) = is_valid(&city, &new_houses) { + let price_diff = new_price as i64 - price as i64; + eprintln!(" candidate is valid, price diff: {}.", price_diff); + eprintln!("New price: {}", new_price); + price = new_price; + houses = new_houses; + print_houses(&houses); + } else { + eprintln!(" candidate is invalid."); + } + } else { + eprintln!("Did not find candidate"); + } + } } } -fn get_neighbors(city: &City, house: &House) -> Vec { +pub fn get_neighbors(city: &City, house: &House) -> Vec { let mut neighbors = Vec::new(); for y in (house.y as i32 - 500).max(0)..=(house.y as i32 + 500).min((SIZE - 1) as i32) { for x in (house.x as i32 - 500).max(0)..=(house.x as i32 + 500).min((SIZE - 1) as i32) { @@ -125,9 +158,9 @@ fn get_neighbors(city: &City, house: &House) -> Vec { } fn print_houses(houses: &Vec) { - eprintln!("{}", houses.len()); + println!("{}", houses.len()); for house in houses { - eprintln!("{} {}", house.y, house.x); + println!("{} {}", house.y, house.x); } }