Add Axial coordinates system

This commit is contained in:
Savanni D'Gerinel 2023-01-14 17:19:36 -05:00
parent be995d2f26
commit c5528d1ceb
7 changed files with 539 additions and 4 deletions

View File

@ -5,6 +5,12 @@ changeset-dev:
changeset-test: changeset-test:
cd changeset && make test cd changeset && make test
coordinates-dev:
cd coordinates && make dev
coordinates-test:
cd coordinates && make test
emseries-dev: emseries-dev:
cd emseries && make dev cd emseries && make dev

325
coordinates/Cargo.lock generated Normal file
View File

@ -0,0 +1,325 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "coordinates"
version = "0.1.0"
dependencies = [
"proptest",
"thiserror",
]
[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
dependencies = [
"instant",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[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.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proptest"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
dependencies = [
"bit-set",
"bitflags",
"byteorder",
"lazy_static",
"num-traits",
"quick-error 2.0.1",
"rand",
"rand_chacha",
"rand_xorshift",
"regex-syntax",
"rusty-fork",
"tempfile",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
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 = "rand_xorshift"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "rusty-fork"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
dependencies = [
"fnv",
"quick-error 1.2.3",
"tempfile",
"wait-timeout",
]
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

10
coordinates/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "coordinates"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
proptest = "1.0"
thiserror = "1.0"

9
coordinates/Makefile Normal file
View File

@ -0,0 +1,9 @@
dev:
cargo watch -x build
test:
cargo watch -x test
test-once:
cargo test

163
coordinates/src/hex.rs Normal file
View File

@ -0,0 +1,163 @@
/// Ĉi-tiu modulo enhavas la elementojn por kub-koordinato.
///
/// This code is based on https://www.redblobgames.com/grids/hexagons/
use crate::Error;
use std::collections::HashSet;
/// An address within the hex coordinate system
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct HexAddr {
q: i32,
r: i32,
}
impl HexAddr {
/// Create a new axial coordinate address.
pub fn new(q: i32, r: i32) -> Self {
Self { q, r }
}
pub fn origin() -> HexAddr {
HexAddr { q: 0, r: 0 }
}
pub fn q(&self) -> i32 {
self.q
}
pub fn r(&self) -> i32 {
self.r
}
pub fn s(&self) -> i32 {
-self.q - self.r
}
/// Get a list of coordinates adjacent to this one.
pub fn adjacencies(&self) -> impl Iterator<Item = HexAddr> {
vec![
HexAddr::new(self.q + 1, self.r),
HexAddr::new(self.q, self.r + 1),
HexAddr::new(self.q - 1, self.r + 1),
HexAddr::new(self.q - 1, self.r),
HexAddr::new(self.q, self.r - 1),
HexAddr::new(self.q + 1, self.r - 1),
]
.into_iter()
}
/// Test whether a coordinate is adjacent to this one
pub fn is_adjacent(&self, dest: &HexAddr) -> bool {
dest.adjacencies().collect::<Vec<HexAddr>>().contains(&self)
}
/// Measure the distance to a destination
pub fn distance(&self, dest: &HexAddr) -> usize {
(((self.q() - dest.q()).abs() + (self.r() - dest.r()).abs() + (self.s() - dest.s()).abs())
/ 2) as usize
}
/// Get an iteration to all of the coordinates within the specified distance of this one.
pub fn within(&self, distance: usize) -> impl Iterator<Item = HexAddr> {
let eko = self.clone();
let mut rezulto: HashSet<HexAddr> = HashSet::new();
let mut vico: Vec<HexAddr> = Vec::new();
vico.push(eko);
while vico.len() > 0 {
let elem = vico.remove(0);
for adj in elem.adjacencies() {
if self.distance(&adj) <= distance && !rezulto.contains(&adj) {
vico.push(adj.clone());
}
}
rezulto.insert(elem);
}
rezulto.remove(self);
rezulto.into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
use std::collections::HashSet;
#[test]
fn distance_0_is_empty() {
let addr = HexAddr::origin();
let lst: Vec<HexAddr> = addr.within(0).collect();
assert_eq!(lst.len(), 0);
}
#[test]
fn distance_1_has_six_addresses() {
let hexaddr = HexAddr::origin();
let lst: Vec<HexAddr> = hexaddr.within(1).collect();
assert_eq!(lst.len(), 6);
}
#[test]
fn distance_2_has_18_addresses() {
let hexaddr = HexAddr::origin();
let lst: Vec<HexAddr> = hexaddr.within(2).collect();
assert_eq!(lst.len(), 18);
}
fn address() -> (std::ops::Range<i32>, std::ops::Range<i32>) {
(-65536_i32..65535, -65536_i32..65535)
}
proptest! {
#[test]
fn produces_adjacent_coordinates((x, y) in address(), idx in 0_usize..6) {
let coord1 = HexAddr::new(x, y);
let lst1: HashSet<HexAddr> = coord1.adjacencies().collect();
assert_eq!(lst1.len(), 6);
let lst1 = lst1.into_iter().collect::<Vec<_>>();
let coord2 = &lst1[idx];
let lst2: Vec<HexAddr> = coord2.adjacencies().collect();
assert!(lst2.contains(&coord1));
}
#[test]
fn tests_adjacencies((q, r) in address(), idx in 0_usize..6) {
let coord1 = HexAddr::new(q, r);
let lst1: Vec<HexAddr> = coord1.adjacencies().collect();
assert_eq!(lst1.len(), 6);
let coord2 = &lst1[idx];
assert!(coord2.is_adjacent(&coord1));
assert!(coord1.is_adjacent(&coord2));
}
#[test]
fn measures_distance((q1, r1) in address(),
(q2, r2) in address()) {
let hexaddr_1 = HexAddr::new(q1, r1);
let hexaddr_2 = HexAddr::new(q2, r2);
let s1 = -q1 - r1;
let s2 = -q2 - r2;
let expected = (((q1 - q2).abs() + (r1 - r2).abs() + (s1 - s2).abs()) / 2) as usize;
assert_eq!(hexaddr_1.distance(&hexaddr_2), expected);
assert_eq!(hexaddr_2.distance(&hexaddr_1), expected);
}
#[test]
fn calculates_distance((q, r) in address(), distance in 0_usize..6) {
let hexaddr = HexAddr::new(q, r);
let en_distancaj_hexaddr: Vec<HexAddr> = hexaddr.within(distance).collect();
let expected_cnt = ((0..distance+1).map(|v| v * 6).fold(0, |acc, val| acc + val)) as usize;
assert_eq!(en_distancaj_hexaddr.len(), expected_cnt);
for c in en_distancaj_hexaddr {
assert!(c.distance(&hexaddr) <= distance as usize);
}
}
}
}

22
coordinates/src/lib.rs Normal file
View File

@ -0,0 +1,22 @@
/*
Copyright 2022, Savanni D'Gerinel <savanni@luminescent-dreams.com>
This file is part of Koordinatoj.
Lumeto is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Lumeto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Lumeto. If not, see <https://www.gnu.org/licenses/>.
*/
use thiserror;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("La enhavita koordinato ne estas valida en ĉi-tiu sistemo")]
InvalidCoordinate,
}
mod hex;
pub use hex::HexAddr;

View File

@ -17,11 +17,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1672335022, "lastModified": 1672580127,
"narHash": "sha256-417aTRDZpob/UNsA4A0ilBUBQ0H8u+Wx9+OJAAygufQ=", "narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "88eaf4a29b5b87a5f83fab413caba24cb13633f0", "rev": "0874168639713f547c05947c76124f78441ea46c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -52,7 +52,7 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"narHash": "sha256-ruR2xo30Vn7kY2hAgg2Z2xrCvNePxck6mgR5a8u+zow=", "narHash": "sha256-VCSmIYJy/ZzTvEGjdfITmTYfybXBgZpMjyjDndbou+8=",
"type": "tarball", "type": "tarball",
"url": "https://github.com/oxalica/rust-overlay/archive/master.tar.gz" "url": "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"
}, },