use anyhow::Result; use bmp::Image; use std::{fs::File, io::{self, BufRead, Cursor}}; use rand::prelude::*; use rayon::prelude::*; #[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] struct Radians(f64); #[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] struct Degrees(f64); impl From for Radians { fn from(value: Degrees) -> Self { Radians(value.0 * std::f64::consts::PI / 180f64) } } impl Into for Radians { fn into(self) -> f64 { self.0 } } impl From for Degrees { fn from(value: Radians) -> Self { Degrees(value.0 * (180f64 / std::f64::consts::PI)) } } impl Into for Degrees { fn into(self) -> f64 { self.0 } } #[derive(PartialEq, Clone, Debug)] struct Location { lat: Radians, lon: Radians, } impl From<(Degrees, Degrees)> for Location { fn from(value: (Degrees, Degrees)) -> Self { Location { lat: value.0.into(), lon: value.1.into() } } } const IMG_NORTH_BORDER: Radians = Radians(0.9570757299); // Degrees(54.8364); const IMG_WEST_BORDER: Radians = Radians(0.2464911049); // Degrees(14.1229); const IMG_SOUTH_BORDER: Radians = Radians(0.8552532214); // Degrees(49.0024); const IMG_EAST_BORDER: Radians = Radians(0.4214184745); // Degrees(24.1455); const EPSILON: f64 = 0.00001f64; struct ImageMeta { image: Image, horizontal_scale: f64, vertical_scale: f64 } #[inline(always)] fn gcd(a: &Location, b: &Location) -> f64 { return f64::acos( f64::sin(a.lat.into())*f64::sin(b.lat.into()) + f64::cos(a.lat.into())*f64::cos(b.lat.into())*f64::cos( f64::abs(a.lon.0 - b.lon.0) ) ) } fn dist(location: &Location, shops: &Vec) -> f64 { shops.into_iter().map(|a| { gcd(a, &location) }).min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater)).unwrap() } fn in_poland( location: &Location, im: &ImageMeta, ) -> bool { if location.lat < IMG_SOUTH_BORDER || location.lat >= IMG_NORTH_BORDER || location.lon < IMG_WEST_BORDER || location.lon >= IMG_EAST_BORDER { return false } let offlat = location.lat.0 - IMG_SOUTH_BORDER.0; let offlon = location.lon.0 - IMG_WEST_BORDER.0; let ppos: (f64, f64) = (offlon * im.horizontal_scale, offlat * im.vertical_scale); let ppos = (ppos.0 as u32, im.image.get_height() - (ppos.1 as u32) - 1); return im.image.get_pixel(ppos.0, ppos.1) == bmp::consts::BLACK; } fn start_point( im: &ImageMeta ) -> Location { loop { let loc = Location { lat: Radians(rand::thread_rng().gen_range(IMG_SOUTH_BORDER.0 .. IMG_NORTH_BORDER.0)), lon: Radians(rand::thread_rng().gen_range(IMG_WEST_BORDER.0 .. IMG_EAST_BORDER.0) ) }; if in_poland(&loc, im) { return loc; } } } fn get_next_point(curr: Location, im: &ImageMeta, shops: &Vec) -> (f64, Location) { [ Location {lat: Radians(curr.lat.0 + EPSILON), lon: Radians(curr.lon.0)}, Location {lat: Radians(curr.lat.0 - EPSILON), lon: Radians(curr.lon.0)}, Location {lat: Radians(curr.lat.0), lon: Radians(curr.lon.0 + EPSILON)}, Location {lat: Radians(curr.lat.0), lon: Radians(curr.lon.0 - EPSILON)}, Location {lat: Radians(curr.lat.0 + EPSILON), lon: Radians(curr.lon.0 + EPSILON)}, Location {lat: Radians(curr.lat.0 + EPSILON), lon: Radians(curr.lon.0 - EPSILON)}, Location {lat: Radians(curr.lat.0 - EPSILON), lon: Radians(curr.lon.0 - EPSILON)}, Location {lat: Radians(curr.lat.0 - EPSILON), lon: Radians(curr.lon.0 + EPSILON)}, curr ].into_iter().filter(|loc| { in_poland(loc, im) }).map(|loc| { (dist(&loc, shops), loc) }).max_by(|x, y| { x.0.partial_cmp(&y.0).unwrap() }).unwrap() } fn find_local_best(start: Location, im: &ImageMeta, shops: &Vec) -> (f64, Location) { let mut curr = start; loop { let next = get_next_point(curr.clone(), im, shops); if curr == next.1 { return next; } curr = next.1; } } fn search(im: &ImageMeta, shops: &Vec) -> (Location, f64) { let mut best_dist = 0f64; let mut best_point = Location {lat: Radians(0f64), lon: Radians(0f64)}; for iter in 0..3000 { let start = start_point(im); let local = find_local_best(start, im, shops); if local.0 > best_dist { best_dist = local.0; best_point = local.1.clone(); println!("At iter {}, new best", iter); } } return (best_point, best_dist); } fn main() -> Result<()> { let file = File::open("./pos.txt")?; let mut frog_shops: Vec = Vec::with_capacity(10778); for line in io::BufReader::new(file).lines() { let line = line?; let numbers: Vec = line.split(" ").map(str::parse::).map(Result::unwrap).collect(); frog_shops.push(Location { lat: Degrees(numbers[0]).into(), lon: Degrees(numbers[1]).into() }); } let poland = include_bytes!("../poland.bmp"); let mut cursor = Cursor::new(poland); let img = bmp::from_reader(&mut cursor)?; // println!("{:?}", img); // println!("{:?}", dist(( // radians!(52f64), radians!(19f64) // ), &frog_shops)); // println!("{:?}", in_poland((radians!(52f64), radians!(19f64)), &img, p_horiz_scale, p_verti_scale)); // println!("{:?}", degrees_but_tuple!(start_point(&img, p_horiz_scale, p_verti_scale))); // println!("{:?}", degrees_but_tuple!(find_local_best( // (radians!(52f64), radians!(19f64)), &img, p_horiz_scale, p_verti_scale, &frog_shops // ))); let imagemeta = ImageMeta { horizontal_scale: (img.get_width() as f64) / (IMG_EAST_BORDER.0 - IMG_WEST_BORDER.0), vertical_scale: (img.get_height() as f64) / (IMG_NORTH_BORDER.0 - IMG_SOUTH_BORDER.0), image: img }; // let result = (0..100) // .into_iter() // .into_par_iter() // .map(|_| search(&imagemeta, &frog_shops)) // .max_by(|a, b| { // a.1.partial_cmp(&b.1).unwrap() // }).unwrap(); let result = search(&imagemeta, &frog_shops); let best_point: (Degrees, Degrees) = (result.0.lat.into(), result.0.lon.into()); println!("Found best point at ({}, {}); Distance is {}", best_point.0.0, best_point.1.0, result.1); Ok(()) }