use std::collections::{HashMap, HashSet};
#[cfg(feature = "noise")]
use noise::NoiseFn;
use priority_queue::PriorityQueue;
use utils::neighbors;
use crate::*;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HexLayout<D: Default, T: Number>(HashMap<HexPosition<T>, D>);
impl<T: Default> HexLayout<T, isize> {
pub fn new_from_range(range: usize, center: HexPosition<isize>) -> Self {
let mut grid = HashMap::new();
let range = range as isize - 1;
for q in -range..=range {
for r in -range..=range {
let s = -q - r;
if s >= -range && s <= range {
let hex_pos = HexPosition::new(q + center.0, r + center.1);
grid.insert(hex_pos, T::default());
}
}
}
Self(grid)
}
}
impl<T: Number> HexLayout<f64, T> {
#[cfg(feature = "noise")]
pub fn init_noise<N: NoiseFn<f64, 2>>(&mut self, noise: N) {
let keys: Vec<_> = self.positions().cloned().collect();
for pos in keys {
let position = pos.to_pixel_coordinates();
let noise_value = noise.get([position.0 as f64, position.1 as f64]);
self.set(pos, noise_value);
}
}
}
impl<T: Default, S: Number> HexLayout<T, S> {
pub fn get(&self, pos: HexPosition<S>) -> Option<&T> {
self.0.get(&pos)
}
pub fn get_mut(&mut self, pos: HexPosition<S>) -> Option<&mut T> {
self.0.get_mut(&pos)
}
pub fn set(&mut self, pos: HexPosition<S>, data: T) -> Option<T> {
self.0.insert(pos, data)
}
pub fn delete(&mut self, pos: HexPosition<S>) -> Option<T> {
self.0.remove(&pos)
}
pub fn positions(&self) -> impl Iterator<Item = &HexPosition<S>> {
self.0.keys()
}
pub fn data(&self) -> impl Iterator<Item = &T> {
self.0.values()
}
pub fn data_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.0.values_mut()
}
pub fn iter(&self) -> impl Iterator<Item = (&HexPosition<S>, &T)> {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn clear(&mut self) {
self.0.clear();
}
pub fn and(&self, other: &Self) -> HashSet<HexPosition<S>> {
self.0
.keys()
.filter_map(|pos| {
if other.0.contains_key(pos) {
Some(*pos)
} else {
None
}
})
.collect()
}
pub fn or(&self, other: &Self) -> HashSet<HexPosition<S>> {
let mut result = HashSet::with_capacity(self.0.len() + other.0.len());
result.extend(self.0.keys().copied());
result.extend(other.0.keys().copied());
result
}
pub fn xor(&self, other: &Self) -> HashSet<HexPosition<S>> {
let mut result = HashSet::with_capacity(self.0.len() + other.0.len());
result.extend(self.0.keys().filter(|k| !other.0.contains_key(k)).copied());
result.extend(other.0.keys().filter(|k| !self.0.contains_key(k)).copied());
result
}
}
impl<S: Number> HexLayout<bool, S> {
pub fn pathfinding(&self, from: HexPosition<S>, to: HexPosition<S>) -> Vec<HexPosition<S>> {
if from == to {
return vec![from];
}
if !self.0.contains_key(&from) || !self.0.contains_key(&to) {
panic!("Position not in layout");
}
let mut frontier = PriorityQueue::new();
frontier.push(from, 0);
let mut came_from: HashMap<HexPosition<S>, Option<HexPosition<S>>> = HashMap::new();
came_from.insert(from, None);
let mut cost_so_far: HashMap<HexPosition<S>, i32> = HashMap::new();
cost_so_far.insert(from, 0);
while let Some((current, _)) = frontier.pop() {
if current == to {
break;
}
for next in self.neighbors_unblocked(current) {
if came_from.contains_key(&next) {
continue;
}
let Some(new_cost) = cost_so_far.get(¤t).map(|c| c + 1) else {
continue;
};
if !cost_so_far.contains_key(&next)
|| Some(new_cost) < cost_so_far.get(&next).copied()
{
cost_so_far.insert(next, new_cost);
frontier.push(next, -new_cost - next.distance(to).to_isize() as i32);
came_from.insert(next, Some(current));
}
}
}
let mut path = vec![to];
let mut current = to;
while let Some(Some(prev)) = came_from.get(¤t) {
path.push(*prev);
current = *prev;
}
path.reverse();
path
}
pub fn field_of_view(
&self,
center: HexPosition<S>,
range: Option<usize>,
) -> HashSet<HexPosition<S>> {
let mut visibles = HashSet::new();
for position in self.positions() {
let mut is_visible = true;
if let Some(radius) = range {
if position.distance(center).to_isize() > radius as isize {
is_visible = false;
}
}
for position_between in center.line_to(*position) {
if !self.0.contains_key(&position_between)
|| self.get(position_between) == Some(&true)
{
is_visible = false;
break;
}
}
if is_visible {
visibles.insert(*position);
}
}
visibles
}
pub fn field_of_move(&self, pos: HexPosition<S>, range: usize) -> HashSet<HexPosition<S>> {
let mut visited = HashSet::new();
visited.insert(pos);
let mut fringes = Vec::new();
fringes.push(vec![pos]);
for k in 1..=range {
fringes.push(Vec::new());
let mut to_add = Vec::with_capacity(fringes[k - 1].len() * 6);
for pos in fringes[k - 1].iter() {
for neighbor in self.neighbors_unblocked(*pos) {
if visited.contains(&neighbor) {
continue;
}
visited.insert(neighbor);
to_add.push(neighbor);
}
}
fringes[k] = to_add;
}
visited
}
pub fn neighbors_unblocked(&self, pos: HexPosition<S>) -> Vec<HexPosition<S>> {
let mut result_neighbors = Vec::with_capacity(6);
for neighbor in neighbors(pos) {
if !self.0.get(&neighbor).unwrap_or(&true) {
result_neighbors.push(neighbor);
}
}
result_neighbors
}
}