fucking hell

This commit is contained in:
Rph :3 2024-08-21 00:10:50 +02:00
commit 5b949b892c
20 changed files with 11469 additions and 0 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.REPL.enableREPLSmartSend": false
}

209
Cargo.lock generated Normal file
View File

@ -0,0 +1,209 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "Froggy"
version = "0.1.0"
dependencies = [
"anyhow",
"bmp",
"rand",
"rayon",
]
[[package]]
name = "anyhow"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "bmp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69985ff4f58085ac696454692d0b646a66ad1f9cc9be294c91dc51bb5df511ae"
dependencies = [
"byteorder",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "syn"
version = "2.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "Froggy"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
bmp = "0.5.0"
rand = "0.8.5"
rayon = "1.10.0"
[profile.release]
debug = true

113
find.py Normal file
View File

@ -0,0 +1,113 @@
import json
import random
import functools
import datetime
import multiprocessing
from math import sqrt, acos, cos, sin, radians, degrees
from PIL import Image
img_north_border = radians(54.8364)
img_west_border = radians(14.1229)
img_south_border = radians(49.0024)
img_east_border = radians(24.1455)
img_path = "poland.bmp"
img = Image.open(img_path)
print(img.size)
p_horiz_scale = img.width / (img_east_border - img_west_border)
p_verti_scale = img.height / (img_north_border - img_south_border)
with open("pos.json") as df:
data = json.loads(df.read())
frog_shop_positions = [(radians(x['lat']), radians(x['lon'])) for x in data]
# @functools.cache
# def dist(lat, lon):
# return min([sqrt((lat-i[0])**2 + (lon-i[1])**2) for i in frog_shop_positions])
def gcd(a: tuple[float, float], b: tuple[float, float]) -> float:
return acos(sin(a[0])*sin(b[0]) + cos(a[0])*cos(b[0])*cos(abs(a[1]-b[1])))
def dist(lat, lon):
return min([gcd((lat, lon), i) for i in frog_shop_positions])
def in_poland(lat, lon, im):
if lat < img_south_border or lat >= img_north_border or lon < img_west_border or lon >= img_east_border:
return False
offlat = lat - img_south_border
offlon = lon - img_west_border
ppos = (offlon * p_horiz_scale, offlat * p_verti_scale)
ppos = (ppos[0], img.height - ppos[1] - 1)
# try:
return im.getpixel(ppos) == (0, 0, 0)
# except Exception as e:
# print("FUCK")
# print(e)
# print(lat, lon)
# exit(1)
def start_point(im):
lat, lon = 0, 0
while not in_poland(lat, lon, im):
lat = random.uniform(img_south_border, img_north_border)
lon = random.uniform(img_west_border, img_east_border)
return (lat, lon)
epsilon = 0.00005
def get_next_point(curr, im):
point_candidates = [
(curr[0] + epsilon, curr[1]),
(curr[0] - epsilon, curr[1]),
(curr[0], curr[1] + epsilon),
(curr[0], curr[1] - epsilon),
]
points = [x for x in point_candidates if in_poland(x[0], x[1], im)]
best_dist = dist(curr[0], curr[1])
best = curr
for point in points:
d = dist(point[0], point[1])
if d > best_dist:
best = point
return best
def find_local_best(start, im):
iters = 0
curr = None
next = start
start_time = datetime.datetime.now()
while curr != next:
curr = next
print(curr)
iters += 1
next = get_next_point(curr, im)
print(f"Local minimum at {next}, {dist(next[0], next[1])} ({datetime.datetime.now() - start_time})")
return next
def search(id):
best_point = None
best_dist = 0
img = Image.open(img_path)
for i in range(2000):
print(f"Runner {id} at attempt {i}")
start = start_point(img)
local = find_local_best(start, img)
if dist(local[0], local[1]) > best_dist:
best_point = local
best_dist = dist(local[0], local[1])
print(f"Better than previous, new best for {id} is {best_point} with {best_dist}")
return (best_point, best_dist)
# with multiprocessing.Pool(16) as p:
# found = p.map(search, range(40))
# best_overall = max(found, key=lambda x: x[1])
# print(f"Found best point at {degrees(best_overall[0][0]), degrees(best_overall[0][1])} with distance {degrees(best_overall[1])}")
#print(degrees(dist(radians(49.92), radians(14.19))))
fuck = find_local_best(
(radians(52), radians(19)), img
)
print(degrees(fuck[0]), degrees(fuck[1]))

96
flake.lock Normal file
View File

@ -0,0 +1,96 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1724015816,
"narHash": "sha256-hVESnM7Eiz93+4DeiE0a1TwMeaeph1ytRJ5QtqxYRWg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9aa35efbea27d320d0cdc5f922f0890812affb60",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1718428119,
"narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay",
"utils": "utils"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1724120436,
"narHash": "sha256-/MvfxTjco5UDBF6SEvwyeXrXwZG7nz7/mDVreQNKsWg=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "48e61fe824f5823e4f3f15dd9a75c19c63649269",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

27
flake.nix Normal file
View File

@ -0,0 +1,27 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils";
rust-overlay.url = "github:oxalica/rust-overlay";
};
outputs = { self, nixpkgs, utils, rust-overlay }:
utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { inherit system overlays; };
in {
devShell = with pkgs; mkShell {
buildInputs = with pkgs; [
python312 python312Packages.decorator python312Packages.pillow
perf-tools
linuxPackages_latest.perf
(rust-bin.stable.latest.default.override {
extensions = [ "rust-src" ];
targets = ["x86_64-unknown-linux-gnu"];
})
];
};
}
);
}

BIN
poland.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 MiB

6
poland.bpw Normal file
View File

@ -0,0 +1,6 @@
0.00084698025551684
0
0
-0.00084698025551684
14.12177084785133729
54.83555301974448071

BIN
poland2.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 MiB

6
polang.pgw Normal file
View File

@ -0,0 +1,6 @@
0.00577623762376237
0
0
-0.00577623762376237
14.12620198019802054
54.83062376237623425

BIN
polang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

1
pos.json Normal file

File diff suppressed because one or more lines are too long

10778
pos.txt Normal file

File diff suppressed because it is too large Load Diff

8
preprocess.py Normal file
View File

@ -0,0 +1,8 @@
import json
with open("raw.json", "r") as f:
string = f.read()
rawls = json.loads(string)
latlons = [{'lat': x['lat'], 'lon': x['lon']} for x in rawls if x['lon'] != None]
with open("pos.json", "w") as w:
w.write(json.dumps(latlons))

1
raw.json Normal file

File diff suppressed because one or more lines are too long

5
refrogger.js Normal file
View File

@ -0,0 +1,5 @@
const frogs = require('./pos.json');
for (let { lat, lon } of frogs) {
process.stdout.write(`${lat} ${lon}\n`);
}

1
sample.json Normal file
View File

@ -0,0 +1 @@
[{"lat": 52.444686, "lon": 16.881193}, {"lat": 52.411081, "lon": 16.926432}, {"lat": 52.410221, "lon": 17.078734}, {"lat": 52.385385, "lon": 16.914945}, {"lat": 50.326686, "lon": 19.186558}, {"lat": 53.234833, "lon": 20.175232}, {"lat": 52.094746, "lon": 17.020672}, {"lat": 51.878283, "lon": 17.016359}, {"lat": 50.334049, "lon": 18.886466}, {"lat": 52.408623, "lon": 16.941987}, {"lat": 52.392868, "lon": 16.889022}, {"lat": 50.364616, "lon": 19.036451}, {"lat": 50.3688, "lon": 18.849958}, {"lat": 52.894126, "lon": 16.171218}, {"lat": 51.42558, "lon": 17.932531}]

200
src/main.rs Normal file
View File

@ -0,0 +1,200 @@
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<Degrees> for Radians {
fn from(value: Degrees) -> Self {
Radians(value.0 * std::f64::consts::PI / 180f64)
}
}
impl Into<f64> for Radians {
fn into(self) -> f64 {
self.0
}
}
impl From<Radians> for Degrees {
fn from(value: Radians) -> Self {
Degrees(value.0 * (180f64 / std::f64::consts::PI))
}
}
impl Into<f64> 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<Location>) -> 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<Location>) -> (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<Location>) -> (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>) -> (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<Location> = Vec::with_capacity(10778);
for line in io::BufReader::new(file).lines() {
let line = line?;
let numbers: Vec<f64> = line.split(" ").map(str::parse::<f64>).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(())
}