use std::{collections::HashSet, hash::Hash};
use crate::{
edge::{Edge, EdgeAx},
hex::{Hex, HexAx},
};
#[cfg(feature = "bevy")]
use bevy_ecs::prelude::Component;
#[cfg(feature = "bevy")]
use bevy_reflect::Reflect;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait Node: Into<NodeAx> + From<NodeAx> + Hash + Eq + Copy {
fn hexes<T>(&self) -> HashSet<T>
where
T: Hex,
{
let self_ax: NodeAx = (*self).into();
let mut result = HashSet::new();
match self_ax.q.rem_euclid(3) {
1 => {
assert_eq!(self_ax.r.rem_euclid(3), 1);
result.insert(HexAx::new((self_ax.q - 1) / 3, (self_ax.r + 2) / 3).into());
result.insert(HexAx::new((self_ax.q - 1) / 3, (self_ax.r - 1) / 3).into());
result.insert(HexAx::new((self_ax.q + 2) / 3, (self_ax.r - 1) / 3).into());
}
2 => {
assert_eq!(self_ax.r.rem_euclid(3), 2);
result.insert(HexAx::new((self_ax.q - 2) / 3, (self_ax.r + 1) / 3).into());
result.insert(HexAx::new((self_ax.q + 1) / 3, (self_ax.r + 1) / 3).into());
result.insert(HexAx::new((self_ax.q + 1) / 3, (self_ax.r - 2) / 3).into());
}
_ => panic!(
"Tried to evaluate hexes of an invalid node coordinate: {:?}",
self_ax
),
}
result
}
fn edges<T>(&self) -> HashSet<T>
where
T: Edge,
{
let self_ax: NodeAx = (*self).into();
let mut result = HashSet::new();
match self_ax.q.rem_euclid(3) {
1 => {
assert_eq!(self_ax.r.rem_euclid(3), 1);
result.insert(EdgeAx::new((self_ax.q * 2 - 2) / 3, (self_ax.r * 2 + 1) / 3).into());
result.insert(EdgeAx::new((self_ax.q * 2 + 1) / 3, (self_ax.r * 2 + 1) / 3).into());
result.insert(EdgeAx::new((self_ax.q * 2 + 1) / 3, (self_ax.r * 2 - 2) / 3).into());
}
2 => {
assert_eq!(self_ax.r.rem_euclid(3), 2);
result.insert(EdgeAx::new((self_ax.q * 2 - 1) / 3, (self_ax.r * 2 + 2) / 3).into());
result.insert(EdgeAx::new((self_ax.q * 2 - 1) / 3, (self_ax.r * 2 - 1) / 3).into());
result.insert(EdgeAx::new((self_ax.q * 2 + 2) / 3, (self_ax.r * 2 - 1) / 3).into());
}
_ => panic!(
"Tried to evaluate hexes of an invalid node coordinate: {:?}",
self_ax
),
}
result
}
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, Hash, Default)]
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NodeAx {
pub q: isize,
pub r: isize,
}
impl NodeAx {
pub fn new(q: isize, r: isize) -> Self {
NodeAx { q, r }
}
}
impl Node for NodeAx {}
#[derive(PartialEq, Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "bevy", derive(Reflect, Component))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FNodeAx {
pub q: f32,
pub r: f32,
}
impl FNodeAx {
pub fn new(q: f32, r: f32) -> Self {
FNodeAx { q, r }
}
}
impl From<&NodeAx> for FNodeAx {
fn from(value: &NodeAx) -> Self {
FNodeAx::new(value.q as f32, value.r as f32)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{edge::*, hex::*, hex_range::*, neighbours::*};
use std::collections::HashSet;
#[test]
fn neighbouring_edges_share_node_cases() {
for hex in HexRange::sphere(&HexAx::new(0, 0), 2).compute::<HexAx>() {
for e in hex.edges() {
verify_neighbouring_edge_share_node(&e);
}
}
}
fn verify_neighbouring_edge_share_node(e: &EdgeAx) {
let my_nodes = e.nodes().into_iter().collect::<HashSet<NodeAx>>();
for n in e.neighbours() {
assert_eq!(
my_nodes
.intersection(&n.nodes())
.collect::<HashSet<&NodeAx>>()
.len(),
1
);
}
}
#[test]
fn hex_has_6_nodes_which_each_have_3_hexes_including_the_aforementioned_hex() {
for hex in HexRange::sphere(&HexAx::new(0, 0), 3).compute::<HexAx>() {
let nodes = hex.nodes::<NodeAx>();
assert_eq!(nodes.len(), 6);
for node in nodes {
let hexes: HashSet<HexAx> = node.hexes();
assert_eq!(hexes.len(), 3);
assert!(hexes.contains(&hex));
}
}
}
#[test]
fn hex_nodes_each_touch_2_edges_that_touch_the_aforementioned_hex() {
for hex in HexRange::sphere(&HexAx::new(0, 0), 3).compute::<HexAx>() {
let hex_edges: HashSet<EdgeAx> = hex.edges();
let nodes = hex.nodes::<NodeAx>();
for node in nodes {
let edges: HashSet<EdgeAx> = node.edges();
assert_eq!(edges.len(), 3);
assert_eq!(
edges
.intersection(&hex_edges)
.collect::<HashSet<&EdgeAx>>()
.len(),
2
);
}
}
}
}