get_node() now returns Result<Option<&T>> to indicate tiles that are out of bounds. Fixed inconsistent spelling of neighbor -> neighbour

master
hheik 2026-05-05 17:24:24 +03:00
parent 45bab170b5
commit 28f5d3ea1f
1 changed files with 44 additions and 24 deletions

View File

@ -1,4 +1,5 @@
/// A trait for graph-like structures, where you can get a node by a unique value and find its neighbors. /// A trait for graph-like structures, where you can get a node by a unique value and find its
/// neighbours.
/// ///
/// **Node** represents some settled data type. The graph should likely contain `Option<Node>` /// **Node** represents some settled data type. The graph should likely contain `Option<Node>`
/// elements prepopulated with `None` values. For example `[[None; WIDTH]; HEIGHT]` for a 2D grid. /// elements prepopulated with `None` values. For example `[[None; WIDTH]; HEIGHT]` for a 2D grid.
@ -15,23 +16,33 @@ pub trait GraphLike<Node, NodeId> {
/// Used in the default implementation of `full_generation` /// Used in the default implementation of `full_generation`
const MAX_RETRIES: usize = 5; const MAX_RETRIES: usize = 5;
fn get_node(&self, id: NodeId) -> Option<&Node>; fn get_node(&self, id: NodeId) -> Result<Option<&Node>, ()>;
fn set_node(&mut self, id: NodeId, node: Option<Node>); fn set_node(&mut self, id: NodeId, node: Option<Node>);
fn iter_ids(&self) -> impl Iterator<Item = NodeId>; fn iter_ids(&self) -> impl Iterator<Item = NodeId>;
fn iter_neighbor_ids(&self, id: NodeId) -> impl Iterator<Item = NodeId>; fn iter_neighbour_ids(&self, id: NodeId) -> impl Iterator<Item = NodeId>;
fn iter_nodes<'a>(&'a self) -> impl Iterator<Item = (NodeId, Option<&'a Node>)> fn iter_nodes<'a>(&'a self) -> impl Iterator<Item = (NodeId, Option<&'a Node>)>
where where
Node: 'a, Node: 'a,
NodeId: Copy, NodeId: Copy,
{ {
self.iter_ids().map(|id| (id, self.get_node(id))) self.iter_ids().map(|id| {
(
id,
self.get_node(id)
.expect("GraphLike::iter_ids() should only return valid ids"),
)
})
} }
fn iter_empty(&self) -> impl Iterator<Item = NodeId> fn iter_empty(&self) -> impl Iterator<Item = NodeId>
where where
NodeId: Copy, NodeId: Copy,
{ {
self.iter_ids().filter(|id| self.get_node(*id).is_none()) self.iter_ids().filter(|id| {
self.get_node(*id)
.expect("GraphLike::iter_ids() should only return valid ids")
.is_none()
})
} }
fn iter_neighbour_nodes<'a>( fn iter_neighbour_nodes<'a>(
&'a self, &'a self,
@ -41,7 +52,13 @@ pub trait GraphLike<Node, NodeId> {
NodeId: Copy, NodeId: Copy,
Node: 'a, Node: 'a,
{ {
self.iter_neighbor_ids(id).map(|id| (id, self.get_node(id))) self.iter_neighbour_ids(id).map(|id| {
(
id,
self.get_node(id)
.expect("GraphLike::iter_neighbour_ids() should only return valid ids"),
)
})
} }
fn single_step( fn single_step(
@ -144,8 +161,11 @@ mod tests {
} }
impl GraphLike<u8, (usize, usize)> for TestGraph { impl GraphLike<u8, (usize, usize)> for TestGraph {
fn get_node(&self, id: (usize, usize)) -> Option<&u8> { fn get_node(&self, id: (usize, usize)) -> Result<Option<&u8>, ()> {
self.tiles[id.0][id.1].as_ref() if id.0 >= 3 || id.1 >= 3 {
return Err(());
}
Ok(self.tiles[id.0][id.1].as_ref())
} }
fn set_node(&mut self, id: (usize, usize), node: Option<u8>) { fn set_node(&mut self, id: (usize, usize), node: Option<u8>) {
@ -156,25 +176,25 @@ mod tests {
(0..3).flat_map(|i| (0..3).map(move |j| (i, j))) (0..3).flat_map(|i| (0..3).map(move |j| (i, j)))
} }
fn iter_neighbor_ids(&self, id: (usize, usize)) -> impl Iterator<Item = (usize, usize)> { fn iter_neighbour_ids(&self, id: (usize, usize)) -> impl Iterator<Item = (usize, usize)> {
let mut neighbors = vec![]; let mut neighbours = vec![];
if id.0 > 0 { if id.0 > 0 {
neighbors.push((id.0 - 1, id.1)); neighbours.push((id.0 - 1, id.1));
} }
if id.0 < 2 { if id.0 < 2 {
neighbors.push((id.0 + 1, id.1)); neighbours.push((id.0 + 1, id.1));
} }
if id.1 > 0 { if id.1 > 0 {
neighbors.push((id.0, id.1 - 1)); neighbours.push((id.0, id.1 - 1));
} }
if id.1 < 2 { if id.1 < 2 {
neighbors.push((id.0, id.1 + 1)); neighbours.push((id.0, id.1 + 1));
} }
neighbors.into_iter() neighbours.into_iter()
} }
} }
/// Entropy is the number of empty neighbors, and choose sets the value to the same value. /// Entropy is the number of empty neighbours, and choose sets the value to the same value.
/// Just for testing. /// Just for testing.
struct TestRuleset; struct TestRuleset;
@ -184,11 +204,11 @@ mod tests {
graph: &impl GraphLike<u8, (usize, usize)>, graph: &impl GraphLike<u8, (usize, usize)>,
id: (usize, usize), id: (usize, usize),
) -> Option<f32> { ) -> Option<f32> {
let neighbors = graph let neighbours = graph
.iter_neighbour_nodes(id) .iter_neighbour_nodes(id)
.filter(|n| n.1.is_some()) .filter(|n| n.1.is_some())
.count(); .count();
Some(4_usize.strict_sub(neighbors) as f32) Some(4_usize.strict_sub(neighbours) as f32)
} }
fn choose( fn choose(
@ -197,11 +217,11 @@ mod tests {
id: (usize, usize), id: (usize, usize),
_retry_count: Option<usize>, _retry_count: Option<usize>,
) -> Option<u8> { ) -> Option<u8> {
let neighbors = graph let neighbours = graph
.iter_neighbour_nodes(id) .iter_neighbour_nodes(id)
.filter(|n| n.1.is_some()) .filter(|n| n.1.is_some())
.count(); .count();
Some(4_usize.strict_sub(neighbors) as u8) Some(4_usize.strict_sub(neighbours) as u8)
} }
} }
@ -215,8 +235,8 @@ mod tests {
[None, None, None], [None, None, None],
], ],
}; };
assert_eq!(graph.get_node((0, 0)), None); assert_eq!(graph.get_node((0, 0)), Ok(None));
assert_eq!(graph.get_node((1, 2)), Some(&99)); assert_eq!(graph.get_node((1, 2)), Ok(Some(&99)));
} }
#[test] #[test]
@ -290,14 +310,14 @@ mod tests {
} }
#[test] #[test]
fn iter_neighbor_ids_works() { fn iter_neighbour_ids_works() {
// This really tests that the implementation itself works... // This really tests that the implementation itself works...
let graph = TestGraph { let graph = TestGraph {
tiles: [[None; 3]; 3], tiles: [[None; 3]; 3],
}; };
assert_eq!( assert_eq!(
graph graph
.iter_neighbor_ids((1, 1)) .iter_neighbour_ids((1, 1))
.collect::<HashSet<(usize, usize)>>(), .collect::<HashSet<(usize, usize)>>(),
vec![(0, 1), (2, 1), (1, 0), (1, 2)] vec![(0, 1), (2, 1), (1, 0), (1, 2)]
.into_iter() .into_iter()