use crate::error::WumpError;
use anyhow::Result;
use slotmap::{new_key_type, Key, SlotMap};
use std::any::Any;
use std::fmt::Debug;
new_key_type! {
pub struct VertexKey;
}
pub trait Payload: Debug + Default {
fn as_any(&self) -> &dyn Any;
}
#[derive(Debug, Default)]
pub struct Vertex<P: Payload> {
key: VertexKey,
neighbors: Vec<VertexKey>,
_payload: P,
}
impl<P: Payload> Vertex<P> {
pub fn _with_neighbors(key: VertexKey, payload: P, new_neighbors: &[VertexKey]) -> Self {
assert!(new_neighbors.len() <= 3);
let neighbors = new_neighbors.to_vec();
Self {
key,
neighbors,
_payload: payload,
}
}
pub fn _new(key: VertexKey, payload: P) -> Self {
Self::_with_neighbors(key, payload, &[])
}
pub fn join_neighbors(key_1: &VertexKey, key_2: &VertexKey, vertex_map: &mut VertexMap<P>) {
Self::try_join_neighbors(key_1, key_2, vertex_map).unwrap();
}
pub fn try_join_neighbors(
key_1: &VertexKey,
key_2: &VertexKey,
vertex_map: &mut VertexMap<P>,
) -> Result<()> {
vertex_map[*key_1].try_add_neighbor_key(*key_2)?;
vertex_map[*key_2].try_add_neighbor_key(*key_1)?;
Ok(())
}
fn try_add_neighbor_key(&mut self, key: VertexKey) -> Result<()> {
if self.neighbors.len() > 3 {
Err(WumpError::OutOfBoundsError(self.neighbors.len()))?
} else {
self.neighbors.push(key);
Ok(())
}
}
fn _add_neighbor_key(&mut self, key: VertexKey) {
self.try_add_neighbor_key(key).unwrap()
}
pub fn key(&self) -> VertexKey {
self.key
}
pub fn neighbors(&self) -> &[VertexKey] {
&self.neighbors
}
pub fn _payload(&self) -> &impl Payload {
&self._payload
}
pub fn has_neighbor(&self, key: &VertexKey) -> bool {
self.neighbors.contains(key)
}
}
pub type VertexMap<P> = SlotMap<VertexKey, Vertex<P>>;
#[derive(Debug)]
struct Polygon<const N: usize> {
vertices: [VertexKey; N],
}
impl<const N: usize> Polygon<N> {
fn _try_new(incoming_vertices: &[VertexKey]) -> Result<Self> {
let vertices: [VertexKey; N] = incoming_vertices.try_into()?;
Ok(Self { vertices })
}
fn _new(incoming_vertices: &[VertexKey]) -> Self {
Self::_try_new(incoming_vertices).unwrap()
}
fn create<P: Payload>(vertex_map: &mut VertexMap<P>) -> Self {
let mut vertices = [VertexKey::null(); N];
for index in 0..N {
let key = vertex_map.insert(Default::default());
vertex_map[key].key = key;
vertices[index] = key;
}
let polygon = Self { vertices };
polygon._connect_vertices(vertex_map);
polygon
}
fn _connect_vertices<P: Payload>(&self, vertex_map: &mut VertexMap<P>) {
let mut previous_vertex_id = self.vertices[N - 1];
for vertex_id in self.vertices {
Vertex::join_neighbors(&vertex_id, &previous_vertex_id, vertex_map);
previous_vertex_id = vertex_id;
}
}
fn _rotate(&mut self, increment: i32) {
let n = i32::try_from(N).unwrap();
let mut source: i32 = 0;
let mut source_key = self.vertices[0];
let mut dest: i32;
let mut saved_key: VertexKey;
for _ in 0..N {
dest = (source + increment + n) % n;
saved_key = self.vertices[dest as usize];
self.vertices[dest as usize] = source_key;
source = dest;
source_key = saved_key;
}
}
fn as_slice(&self) -> &[VertexKey] {
return &self.vertices;
}
}
pub trait Polyhedron {
fn as_slice(&self) -> &[VertexKey];
fn as_mut_slice(&mut self) -> &mut [VertexKey];
}
pub struct Dodecahedron {
vertices: [VertexKey; 20],
}
impl Dodecahedron {
fn try_new(incoming_vertices: &[VertexKey]) -> Result<Self> {
let vertices: [VertexKey; 20] = incoming_vertices.try_into()?;
Ok(Self { vertices })
}
fn new(vertices: &[VertexKey]) -> Self {
Self::try_new(vertices).unwrap()
}
pub(crate) fn create<P: Payload>(vertex_map: &mut VertexMap<P>) -> Self {
let top_pentagon: Polygon<5> = Polygon::create(vertex_map);
let middle_decagon: Polygon<10> = Polygon::create(vertex_map);
let bottom_pentagon: Polygon<5> = Polygon::create(vertex_map);
let middle_decagon_rotation: i32 = -2;
let bottom_pentagon_rotation: i32 = 3;
for index in 0..middle_decagon.vertices.len() {
let pentagon_index = index / 2;
let index_rotation = (index as i32 + 10 - middle_decagon_rotation) % 10;
assert!(index_rotation >= 0);
let vertex_key_rotation = &middle_decagon.vertices[index_rotation as usize];
if index % 2 == 0 {
Vertex::join_neighbors(
vertex_key_rotation,
&top_pentagon.vertices[pentagon_index],
vertex_map,
);
} else {
let pentagon_index_rotation =
(pentagon_index as i32 + 5 - bottom_pentagon_rotation) % 5;
Vertex::join_neighbors(
vertex_key_rotation,
&bottom_pentagon.vertices[pentagon_index_rotation as usize],
vertex_map,
);
}
}
let all_vertices: Vec<VertexKey> = top_pentagon
.as_slice()
.iter()
.chain(middle_decagon.as_slice())
.chain(bottom_pentagon.as_slice())
.cloned()
.collect();
Self::new(&all_vertices)
}
}
impl Polyhedron for Dodecahedron {
fn as_slice(&self) -> &[VertexKey] {
&self.vertices
}
fn as_mut_slice(&mut self) -> &mut [VertexKey] {
&mut self.vertices
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Copy, Clone, Debug, Default)]
struct Tag(usize);
impl Payload for Tag {
fn as_any(&self) -> &dyn Any {
self
}
}
#[test]
fn test_dodecahedron() {
fn get_neighbors_tags(vertex_key: &VertexKey, vertex_map: &VertexMap<Tag>) -> Vec<Tag> {
let neighbors = vertex_map[*vertex_key].neighbors.clone();
let tags: Vec<Tag> = neighbors
.iter()
.map(|&n| {
vertex_map[n]
._payload
.as_any()
.downcast_ref::<Tag>()
.unwrap()
.clone()
})
.collect();
tags
}
fn get_tag(vertex_key: &VertexKey, vertex_map: &VertexMap<Tag>) -> Tag {
vertex_map[*vertex_key]
._payload
.as_any()
.downcast_ref::<Tag>()
.unwrap()
.clone()
}
let mut vertex_map: VertexMap<Tag> = SlotMap::with_key();
let dodecahedron = Dodecahedron::create(&mut vertex_map);
for vertex_key in &dodecahedron.vertices {
assert!(!vertex_key.is_null());
let vertex = &vertex_map[*vertex_key];
assert_eq!(vertex.key, *vertex_key);
assert_eq!(vertex.neighbors.len(), 3);
for neighbor_key in vertex.neighbors() {
assert!(!neighbor_key.is_null());
let neighbor = &vertex_map[*neighbor_key];
assert!(neighbor.neighbors.contains(vertex_key));
}
}
for (index, vertex_key) in dodecahedron.as_slice().iter().enumerate() {
vertex_map[*vertex_key]._payload = Tag(index + 1);
}
let room_map = [
(1, (5, 2, 8)),
(2, (1, 3, 10)),
(3, (2, 4, 12)),
(4, (3, 5, 14)),
(5, (4, 1, 6)),
(6, (15, 7, 5)),
(7, (6, 8, 17)),
(8, (7, 9, 1)),
(9, (8, 10, 18)),
(10, (9, 11, 2)),
(11, (10, 12, 19)),
(12, (11, 13, 3)),
(13, (12, 14, 20)),
(14, (13, 15, 4)),
(15, (14, 6, 16)),
(16, (20, 17, 15)),
(17, (16, 18, 7)),
(18, (17, 19, 9)),
(19, (18, 20, 11)),
(20, (19, 16, 13)),
];
for (index, vertex_key) in dodecahedron.as_slice().iter().enumerate() {
let vertex_tag = get_tag(vertex_key, &vertex_map);
let vertex_neighbors: Vec<_> = get_neighbors_tags(vertex_key, &vertex_map)
.iter()
.map(|x| x.0)
.collect();
let (room, neighbor_rooms) = room_map[index];
assert_eq!(vertex_tag.0, room);
assert_eq!(vertex_neighbors.len(), 3);
assert!(vertex_neighbors.contains(&neighbor_rooms.0));
assert!(vertex_neighbors.contains(&neighbor_rooms.1));
assert!(vertex_neighbors.contains(&neighbor_rooms.2));
}
}
#[test]
fn test_polygon() {
fn verify_neighbors(vertex_keys: &Vec<VertexKey>, vertex_map: &VertexMap<()>) {
let n = vertex_keys.len();
let mut previous_key = vertex_keys[n - 1];
for (index, vertex_key) in vertex_keys.iter().enumerate() {
let neighbors = &vertex_map[*vertex_key].neighbors;
assert_eq!(neighbors.len(), 2);
assert!(neighbors.contains(&previous_key));
let next_key = vertex_keys[(index + 1) % n];
assert!(neighbors.contains(&next_key));
previous_key = *vertex_key;
}
}
let mut vertex_map: VertexMap<()> = SlotMap::with_key();
let mut pentagon: Polygon<5> = Polygon::create(&mut vertex_map);
let pentagon_vertices = pentagon.as_slice();
assert_eq!(pentagon_vertices.len(), 5);
assert!(pentagon_vertices.iter().all(|&v| !v.is_null()));
let vertices_0 = pentagon_vertices.to_vec();
verify_neighbors(&vertices_0, &vertex_map);
pentagon.rotate(-1);
let vertices_1 = pentagon.as_slice().to_vec();
verify_neighbors(&vertices_1, &vertex_map);
assert_eq!(vertices_0[0], vertices_1[4]);
assert_eq!(vertices_0[1], vertices_1[0]);
assert_eq!(vertices_0[2], vertices_1[1]);
assert_eq!(vertices_0[3], vertices_1[2]);
assert_eq!(vertices_0[4], vertices_1[3]);
pentagon.rotate(2);
let vertices_2 = pentagon.as_slice().to_vec();
verify_neighbors(&vertices_2, &vertex_map);
assert_eq!(vertices_1[0], vertices_2[2]);
assert_eq!(vertices_1[1], vertices_2[3]);
assert_eq!(vertices_1[2], vertices_2[4]);
assert_eq!(vertices_1[3], vertices_2[0]);
assert_eq!(vertices_1[4], vertices_2[1]);
}
}