generated from tipragot/rust
parent
dae48df64a
commit
d782bcccdb
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1318,6 +1318,7 @@ dependencies = [
|
||||||
"bevy_egui",
|
"bevy_egui",
|
||||||
"num",
|
"num",
|
||||||
"partial-min-max",
|
"partial-min-max",
|
||||||
|
"paste",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -15,3 +15,4 @@ bevy = "0.12.1"
|
||||||
bevy_egui = "0.24.0"
|
bevy_egui = "0.24.0"
|
||||||
num = "0.4.1"
|
num = "0.4.1"
|
||||||
partial-min-max = "0.4.0"
|
partial-min-max = "0.4.0"
|
||||||
|
paste = "1.0.14"
|
||||||
|
|
|
@ -1,155 +1,266 @@
|
||||||
//! All functions related to calculations in a hexagonal grid.
|
//! All functions related to calculations in a hexagonal grid.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
|
||||||
|
|
||||||
use num::cast::AsPrimitive;
|
use std::ops::{
|
||||||
use num::{FromPrimitive, Signed};
|
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
|
||||||
use partial_min_max::{max, min};
|
};
|
||||||
|
|
||||||
/// Represents a number that can be used in a hexagonal grid.
|
use num::{cast::AsPrimitive, FromPrimitive};
|
||||||
pub trait HexNumber: Signed + PartialEq + Copy + PartialOrd + FromPrimitive {}
|
use paste::paste;
|
||||||
|
|
||||||
impl<T: Signed + PartialEq + Copy + PartialOrd + FromPrimitive> HexNumber for T {}
|
/// Represents a number that can be used in calculations for hexagonal grids.
|
||||||
|
pub trait Number:
|
||||||
|
Copy
|
||||||
|
+ PartialEq
|
||||||
|
+ PartialOrd
|
||||||
|
+ Add<Output = Self>
|
||||||
|
+ Sub<Output = Self>
|
||||||
|
+ Mul<Output = Self>
|
||||||
|
+ Div<Output = Self>
|
||||||
|
+ Rem<Output = Self>
|
||||||
|
+ Neg<Output = Self>
|
||||||
|
+ AddAssign
|
||||||
|
+ SubAssign
|
||||||
|
+ MulAssign
|
||||||
|
+ DivAssign
|
||||||
|
+ RemAssign
|
||||||
|
+ std::fmt::Debug
|
||||||
|
{
|
||||||
|
/// The number -2.
|
||||||
|
const MINUS_TWO: Self;
|
||||||
|
|
||||||
|
/// The number -1.
|
||||||
|
const MINUS_ONE: Self;
|
||||||
|
|
||||||
|
/// The number 0.
|
||||||
|
const ZERO: Self;
|
||||||
|
|
||||||
|
/// The number 1.
|
||||||
|
const ONE: Self;
|
||||||
|
|
||||||
|
/// The number 2.
|
||||||
|
const TWO: Self;
|
||||||
|
|
||||||
|
/// Returns the maximum of `self` and `other`.
|
||||||
|
fn max(self, other: Self) -> Self {
|
||||||
|
if self > other { self } else { other }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum of `self` and `other`.
|
||||||
|
fn min(self, other: Self) -> Self {
|
||||||
|
if self < other { self } else { other }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the absolute value of `self`.
|
||||||
|
fn abs(self) -> Self {
|
||||||
|
if self < Self::ZERO { -self } else { self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! number_impl {
|
||||||
|
($($t:ty,)*) => {paste!{$(
|
||||||
|
impl Number for $t {
|
||||||
|
const MINUS_ONE: Self = - [< 1 $t >];
|
||||||
|
const MINUS_TWO: Self = - [< 2 $t >];
|
||||||
|
const ZERO: Self = [< 0 $t >];
|
||||||
|
const ONE: Self = [< 1 $t >];
|
||||||
|
const TWO: Self = [< 2 $t >];
|
||||||
|
}
|
||||||
|
)*}};
|
||||||
|
}
|
||||||
|
|
||||||
|
number_impl! {
|
||||||
|
i8, i16, i32, i64, i128, isize,
|
||||||
|
f32, f64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a position in a hexagonal grid.
|
/// Represents a position in a hexagonal grid.
|
||||||
/// We use the axial coordinate system explained in this
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
/// [documentation](https://www.redblobgames.com/grids/hexagons/#coordinates).
|
pub struct HexPosition<T: Number>(pub T, pub T);
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct HexPosition<T: HexNumber> {
|
|
||||||
/// Q coordinate.
|
|
||||||
pub q: T,
|
|
||||||
|
|
||||||
/// R coordinate.
|
/// All possible directions in a hexagonal grid.
|
||||||
pub r: T,
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum HexDirection {
|
||||||
|
/// The direction right.
|
||||||
|
Right,
|
||||||
|
|
||||||
|
/// The direction up-right.
|
||||||
|
UpRight,
|
||||||
|
|
||||||
|
/// The direction up-left.
|
||||||
|
UpLeft,
|
||||||
|
|
||||||
|
/// The direction left.
|
||||||
|
Left,
|
||||||
|
|
||||||
|
/// The direction down-left.
|
||||||
|
DownLeft,
|
||||||
|
|
||||||
|
/// The direction down-right.
|
||||||
|
DownRight,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HexNumber + AsPrimitive<f32>> HexPosition<T> {
|
impl HexDirection {
|
||||||
/// Returns the distance between two [HexPosition]s.
|
/// Returns the vector of the direction.
|
||||||
///
|
pub fn to_vector<T: Number>(self) -> HexPosition<T> {
|
||||||
/// # How it works
|
match self {
|
||||||
///
|
HexDirection::Right => HexPosition(T::ONE, T::ZERO),
|
||||||
/// In the hexagonal grid, using the
|
HexDirection::UpRight => HexPosition(T::ONE, T::MINUS_ONE),
|
||||||
/// [cube coordinate system](https://www.redblobgames.com/grids/hexagons/#coordinates),
|
HexDirection::UpLeft => HexPosition(T::ZERO, T::MINUS_ONE),
|
||||||
/// it's akin to a cube in 3D space.
|
HexDirection::Left => HexPosition(T::MINUS_ONE, T::ZERO),
|
||||||
/// The Manhattan distance between two positions is equal to half of
|
HexDirection::DownLeft => HexPosition(T::MINUS_ONE, T::ONE),
|
||||||
/// the sum of abs(dx) + abs(dy) + abs(dz).
|
HexDirection::DownRight => HexPosition(T::ZERO, T::ONE),
|
||||||
/// However, in hexagonal grids, z is defined as -q - r.
|
}
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use border_wars::map::hex::HexPosition;
|
|
||||||
///
|
|
||||||
/// let a = HexPosition { q: 0, r: 0 };
|
|
||||||
/// let b = HexPosition { q: 1, r: 1 };
|
|
||||||
///
|
|
||||||
/// assert_eq!(a.distance_to(&b), 2);
|
|
||||||
/// ```
|
|
||||||
pub fn distance_to(&self, other: &Self) -> T {
|
|
||||||
// Calculate the difference between the q and r coordinates.
|
|
||||||
let dq = (self.q - other.q).abs();
|
|
||||||
let dr = (self.r - other.r).abs();
|
|
||||||
let ds = dq + dr;
|
|
||||||
|
|
||||||
// Manhattan distance = (abs(dq) + abs(dr) + abs(ds)) / 2
|
|
||||||
(dq + dr + ds) / (T::one() + T::one())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the current [HexPosition] into a pixel coordinate.
|
|
||||||
/// Input: The size of the hexagon in pixels (witdh, height).
|
|
||||||
///
|
|
||||||
/// If you want to learn more about pixel coordinates conversion,
|
|
||||||
/// you can check the
|
|
||||||
/// [documentation](https://www.redblobgames.com/grids/hexagons/#hex-to-pixel).
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use border_wars::map::hex::HexPosition;
|
|
||||||
///
|
|
||||||
/// let position = HexPosition { q: 1, r: 0 };
|
|
||||||
/// assert_eq!(
|
|
||||||
/// position.to_pixel_coordinates((1.0, 1.0)),
|
|
||||||
/// (3f32.sqrt(), 0.0)
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn to_pixel_coordinates(&self, size: (f32, f32)) -> (f32, f32) {
|
|
||||||
(
|
|
||||||
size.0
|
|
||||||
* 3f32
|
|
||||||
.sqrt()
|
|
||||||
.mul_add(self.q.as_(), 3f32.sqrt() / 2.0 * (self.r.as_())),
|
|
||||||
size.1 * (3.0 / 2.0 * self.r.as_()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HexNumber + Eq + Hash + std::cmp::PartialOrd + num::ToPrimitive> HexPosition<T> {
|
/// A hexagonal ring iterator.
|
||||||
/// Returns all positions within a given `range` from the current
|
pub struct HexRing<T: Number> {
|
||||||
/// `HexPosition`.
|
current: HexPosition<T>,
|
||||||
///
|
direction: HexDirection,
|
||||||
/// This function iterates over the possible q and r values within the
|
radius: usize,
|
||||||
/// specified range.
|
index: usize,
|
||||||
/// Note that the original position is also returned.
|
}
|
||||||
///
|
|
||||||
/// For more details, refer to: https://www.redblobgames.com/grids/hexagons/#range
|
impl<T: Number> Iterator for HexRing<T> {
|
||||||
///
|
type Item = HexPosition<T>;
|
||||||
/// # Example
|
|
||||||
///
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
/// ```
|
if self.index >= self.radius {
|
||||||
/// use border_wars::map::hex::HexPosition;
|
self.direction = match self.direction {
|
||||||
///
|
HexDirection::Right => HexDirection::UpRight,
|
||||||
/// let position = HexPosition { q: 0, r: 0 };
|
HexDirection::UpRight => HexDirection::UpLeft,
|
||||||
///
|
HexDirection::UpLeft => HexDirection::Left,
|
||||||
/// let positions = position.range(1);
|
HexDirection::Left => HexDirection::DownLeft,
|
||||||
///
|
HexDirection::DownLeft => HexDirection::DownRight,
|
||||||
/// assert_eq!(positions.len(), 7);
|
HexDirection::DownRight => return None,
|
||||||
/// ```
|
};
|
||||||
pub fn range(&self, range: T) -> HashSet<Self> {
|
self.index = 0;
|
||||||
let mut result_positions = HashSet::new();
|
}
|
||||||
for q in num::range_inclusive(-range, range) {
|
let result = self.current;
|
||||||
for r in num::range_inclusive(max(-range, -q - range), min(range, -q + range)) {
|
self.current += self.direction.to_vector();
|
||||||
result_positions.insert(Self { q, r });
|
self.index += 1;
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let remaining = match self.direction {
|
||||||
|
HexDirection::Right => self.radius * 6,
|
||||||
|
HexDirection::UpRight => self.radius * 5,
|
||||||
|
HexDirection::UpLeft => self.radius * 4,
|
||||||
|
HexDirection::Left => self.radius * 3,
|
||||||
|
HexDirection::DownLeft => self.radius * 2,
|
||||||
|
HexDirection::DownRight => self.radius,
|
||||||
|
} - self.index;
|
||||||
|
(remaining, Some(remaining))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hexagonal spiral iterator.
|
||||||
|
pub struct HexSpiral<T: Number> {
|
||||||
|
origin: HexPosition<T>,
|
||||||
|
current: HexRing<T>,
|
||||||
|
radius: usize,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Number + AsPrimitive<usize>+ FromPrimitive> Iterator for HexSpiral<T> {
|
||||||
|
type Item = HexPosition<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.index == 0 {
|
||||||
|
self.index += 1;
|
||||||
|
return Some(self.origin);
|
||||||
|
}
|
||||||
|
if self.index > self.radius {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut result = self.current.next();
|
||||||
|
if result.is_none() && self.index < self.radius {
|
||||||
|
self.index += 1;
|
||||||
|
self.current = self.origin.ring(T::from_usize(self.index).unwrap());
|
||||||
|
result = self.current.next();
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T: Number + AsPrimitive<usize>+ FromPrimitive> HexPosition<T> {
|
||||||
|
/// Creates a new hexagonal position.
|
||||||
|
pub fn new(x: T, y: T) -> Self {
|
||||||
|
Self(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the distance between two hexagonal positions.
|
||||||
|
pub fn distance(self, other: Self) -> T {
|
||||||
|
let HexPosition(x, y) = self - other;
|
||||||
|
x.abs() + y.abs() + (x + y).abs() / T::TWO
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the hexagonal ring of the given radius.
|
||||||
|
pub fn ring(self, radius: T) -> HexRing<T> {
|
||||||
|
let usize_radius = radius.as_();
|
||||||
|
HexRing {
|
||||||
|
current: self + HexDirection::DownLeft.to_vector() * radius,
|
||||||
|
direction: HexDirection::Right,
|
||||||
|
radius: usize_radius,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the hexagonal spiral of the given radius.
|
||||||
|
pub fn spiral(self, radius: T) -> HexSpiral<T> {
|
||||||
|
let usize_radius = radius.as_();
|
||||||
|
HexSpiral {
|
||||||
|
origin: self,
|
||||||
|
current: self.ring(T::ONE),
|
||||||
|
radius: usize_radius,
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_ops {
|
||||||
|
($(($t:ty, $n:ident),)*) => {paste!{$(
|
||||||
|
impl<T: Number> $t for HexPosition<T> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn $n(self, rhs: Self) -> Self {
|
||||||
|
Self(self.0.$n(rhs.0), self.1.$n(rhs.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result_positions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: HexNumber> Add<Self> for HexPosition<T> {
|
impl<T: Number> $t<T> for HexPosition<T> {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn add(self, other: Self) -> Self::Output {
|
fn $n(self, rhs: T) -> Self {
|
||||||
Self {
|
Self(self.0.$n(rhs), self.1.$n(rhs))
|
||||||
q: self.q + other.q,
|
}
|
||||||
r: self.r + other.r,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: HexNumber + AddAssign> AddAssign<Self> for HexPosition<T> {
|
impl<T: Number> [< $t Assign >] for HexPosition<T> {
|
||||||
fn add_assign(&mut self, other: Self) {
|
fn [< $n _assign >](&mut self, rhs: Self) {
|
||||||
self.q += other.q;
|
self.0.[< $n _assign >](rhs.0) ;
|
||||||
self.r += other.r;
|
self.1.[< $n _assign >](rhs.1) ;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: HexNumber> Sub<Self> for HexPosition<T> {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn sub(self, other: Self) -> Self {
|
|
||||||
Self {
|
|
||||||
q: self.q - other.q,
|
|
||||||
r: self.r - other.r,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
impl<T: Number> [< $t Assign >]<T> for HexPosition<T> {
|
||||||
|
fn [< $n _assign >](&mut self, rhs: T) {
|
||||||
|
self.0.[< $n _assign >](rhs);
|
||||||
|
self.1.[< $n _assign >](rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*}};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: HexNumber + SubAssign> SubAssign<Self> for HexPosition<T> {
|
|
||||||
fn sub_assign(&mut self, other: Self) {
|
impl_ops! {
|
||||||
self.q -= other.q;
|
(Add, add),
|
||||||
self.r -= other.r;
|
(Sub, sub),
|
||||||
}
|
(Mul, mul),
|
||||||
|
(Div, div),
|
||||||
|
(Rem, rem),
|
||||||
}
|
}
|
||||||
|
|
155
crates/border-wars/src/map/hex_save.rs
Normal file
155
crates/border-wars/src/map/hex_save.rs
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
//! All functions related to calculations in a hexagonal grid.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
||||||
|
|
||||||
|
use num::cast::AsPrimitive;
|
||||||
|
use num::{FromPrimitive, Signed};
|
||||||
|
use partial_min_max::{max, min};
|
||||||
|
|
||||||
|
/// Represents a number that can be used in a hexagonal grid.
|
||||||
|
pub trait HexNumber: Signed + PartialEq + Copy + PartialOrd + FromPrimitive {}
|
||||||
|
|
||||||
|
impl<T: Signed + PartialEq + Copy + PartialOrd + FromPrimitive> HexNumber for T {}
|
||||||
|
|
||||||
|
/// Represents a position in a hexagonal grid.
|
||||||
|
/// We use the axial coordinate system explained in this
|
||||||
|
/// [documentation](https://www.redblobgames.com/grids/hexagons/#coordinates).
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct HexPosition<T: HexNumber> {
|
||||||
|
/// Q coordinate.
|
||||||
|
pub q: T,
|
||||||
|
|
||||||
|
/// R coordinate.
|
||||||
|
pub r: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HexNumber + AsPrimitive<f32>> HexPosition<T> {
|
||||||
|
/// Returns the distance between two [HexPosition]s.
|
||||||
|
///
|
||||||
|
/// # How it works
|
||||||
|
///
|
||||||
|
/// In the hexagonal grid, using the
|
||||||
|
/// [cube coordinate system](https://www.redblobgames.com/grids/hexagons/#coordinates),
|
||||||
|
/// it's akin to a cube in 3D space.
|
||||||
|
/// The Manhattan distance between two positions is equal to half of
|
||||||
|
/// the sum of abs(dx) + abs(dy) + abs(dz).
|
||||||
|
/// However, in hexagonal grids, z is defined as -q - r.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let a = HexPosition { q: 0, r: 0 };
|
||||||
|
/// let b = HexPosition { q: 1, r: 1 };
|
||||||
|
///
|
||||||
|
/// assert_eq!(a.distance_to(&b), 2);
|
||||||
|
/// ```
|
||||||
|
pub fn distance_to(&self, other: &Self) -> T {
|
||||||
|
// Calculate the difference between the q and r coordinates.
|
||||||
|
let dq = (self.q - other.q).abs();
|
||||||
|
let dr = (self.r - other.r).abs();
|
||||||
|
let ds = dq + dr;
|
||||||
|
|
||||||
|
// Manhattan distance = (abs(dq) + abs(dr) + abs(ds)) / 2
|
||||||
|
(dq + dr + ds) / (T::one() + T::one())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the current [HexPosition] into a pixel coordinate.
|
||||||
|
/// Input: The size of the hexagon in pixels (witdh, height).
|
||||||
|
///
|
||||||
|
/// If you want to learn more about pixel coordinates conversion,
|
||||||
|
/// you can check the
|
||||||
|
/// [documentation](https://www.redblobgames.com/grids/hexagons/#hex-to-pixel).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let position = HexPosition { q: 1, r: 0 };
|
||||||
|
/// assert_eq!(
|
||||||
|
/// position.to_pixel_coordinates((1.0, 1.0)),
|
||||||
|
/// (3f32.sqrt(), 0.0)
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn to_pixel_coordinates(&self, size: (f32, f32)) -> (f32, f32) {
|
||||||
|
(
|
||||||
|
size.0
|
||||||
|
* 3f32
|
||||||
|
.sqrt()
|
||||||
|
.mul_add(self.q.as_(), 3f32.sqrt() / 2.0 * (self.r.as_())),
|
||||||
|
size.1 * (3.0 / 2.0 * self.r.as_()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HexNumber + Eq + Hash + std::cmp::PartialOrd + num::ToPrimitive> HexPosition<T> {
|
||||||
|
/// Returns all positions within a given `range` from the current
|
||||||
|
/// `HexPosition`.
|
||||||
|
///
|
||||||
|
/// This function iterates over the possible q and r values within the
|
||||||
|
/// specified range.
|
||||||
|
/// Note that the original position is also returned.
|
||||||
|
///
|
||||||
|
/// For more details, refer to: https://www.redblobgames.com/grids/hexagons/#range
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use border_wars::map::hex::HexPosition;
|
||||||
|
///
|
||||||
|
/// let position = HexPosition { q: 0, r: 0 };
|
||||||
|
///
|
||||||
|
/// let positions = position.range(1);
|
||||||
|
///
|
||||||
|
/// assert_eq!(positions.len(), 7);
|
||||||
|
/// ```
|
||||||
|
pub fn range(&self, range: T) -> HashSet<Self> {
|
||||||
|
let mut result_positions = HashSet::new();
|
||||||
|
for q in num::range_inclusive(-range, range) {
|
||||||
|
for r in num::range_inclusive(max(-range, -q - range), min(range, -q + range)) {
|
||||||
|
result_positions.insert(Self { q, r });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_positions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HexNumber> Add<Self> for HexPosition<T> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
q: self.q + other.q,
|
||||||
|
r: self.r + other.r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HexNumber + AddAssign> AddAssign<Self> for HexPosition<T> {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
self.q += other.q;
|
||||||
|
self.r += other.r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HexNumber> Sub<Self> for HexPosition<T> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
q: self.q - other.q,
|
||||||
|
r: self.r - other.r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HexNumber + SubAssign> SubAssign<Self> for HexPosition<T> {
|
||||||
|
fn sub_assign(&mut self, other: Self) {
|
||||||
|
self.q -= other.q;
|
||||||
|
self.r -= other.r;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue