use std::collections::{HashMap, HashSet};
use waremax_core::{EdgeId, NodeId, RobotId};
#[derive(Clone, Debug)]
pub enum WaitingFor {
Edge {
edge_id: EdgeId,
blocked_by: Vec<RobotId>,
},
Node {
node_id: NodeId,
blocked_by: Vec<RobotId>,
},
}
impl WaitingFor {
pub fn blockers(&self) -> &[RobotId] {
match self {
WaitingFor::Edge { blocked_by, .. } => blocked_by,
WaitingFor::Node { blocked_by, .. } => blocked_by,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct WaitForGraph {
waiting_for: HashMap<RobotId, WaitingFor>,
}
impl WaitForGraph {
pub fn new() -> Self {
Self {
waiting_for: HashMap::new(),
}
}
pub fn add_wait(&mut self, robot: RobotId, waiting_for: WaitingFor) {
self.waiting_for.insert(robot, waiting_for);
}
pub fn remove_wait(&mut self, robot: RobotId) {
self.waiting_for.remove(&robot);
}
pub fn is_waiting(&self, robot: RobotId) -> bool {
self.waiting_for.contains_key(&robot)
}
pub fn get_wait(&self, robot: RobotId) -> Option<&WaitingFor> {
self.waiting_for.get(&robot)
}
pub fn waiting_count(&self) -> usize {
self.waiting_for.len()
}
pub fn detect_cycle(&self) -> Option<Vec<RobotId>> {
for &start_robot in self.waiting_for.keys() {
if let Some(cycle) = self.find_cycle_from(start_robot) {
return Some(cycle);
}
}
None
}
fn find_cycle_from(&self, start: RobotId) -> Option<Vec<RobotId>> {
let mut visited = HashSet::new();
let mut path = Vec::new();
let mut path_set = HashSet::new();
self.dfs_find_cycle(start, &mut visited, &mut path, &mut path_set, start)
}
fn dfs_find_cycle(
&self,
current: RobotId,
visited: &mut HashSet<RobotId>,
path: &mut Vec<RobotId>,
path_set: &mut HashSet<RobotId>,
start: RobotId,
) -> Option<Vec<RobotId>> {
if visited.contains(¤t) {
return None;
}
if path_set.contains(¤t) {
let cycle_start_idx = path.iter().position(|&r| r == current)?;
return Some(path[cycle_start_idx..].to_vec());
}
path.push(current);
path_set.insert(current);
if let Some(waiting) = self.waiting_for.get(¤t) {
for &blocker in waiting.blockers() {
if blocker == start && path.len() > 1 {
path.push(start);
return Some(path.clone());
}
if self.waiting_for.contains_key(&blocker) {
if let Some(cycle) =
self.dfs_find_cycle(blocker, visited, path, path_set, start)
{
return Some(cycle);
}
}
}
}
path.pop();
path_set.remove(¤t);
visited.insert(current);
None
}
pub fn detect_all_cycles(&self) -> Vec<Vec<RobotId>> {
let mut cycles = Vec::new();
let mut found_robots = HashSet::new();
for &start_robot in self.waiting_for.keys() {
if found_robots.contains(&start_robot) {
continue;
}
if let Some(cycle) = self.find_cycle_from(start_robot) {
for &robot in &cycle {
found_robots.insert(robot);
}
cycles.push(cycle);
}
}
cycles
}
pub fn clear(&mut self) {
self.waiting_for.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_graph_no_cycle() {
let graph = WaitForGraph::new();
assert!(graph.detect_cycle().is_none());
}
#[test]
fn test_single_wait_no_cycle() {
let mut graph = WaitForGraph::new();
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
assert!(graph.detect_cycle().is_none());
}
#[test]
fn test_two_robot_cycle() {
let mut graph = WaitForGraph::new();
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
graph.add_wait(
RobotId(2),
WaitingFor::Node {
node_id: NodeId(50),
blocked_by: vec![RobotId(1)],
},
);
let cycle = graph.detect_cycle();
assert!(cycle.is_some());
let cycle = cycle.unwrap();
assert!(cycle.contains(&RobotId(1)));
assert!(cycle.contains(&RobotId(2)));
}
#[test]
fn test_three_robot_cycle() {
let mut graph = WaitForGraph::new();
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
graph.add_wait(
RobotId(2),
WaitingFor::Edge {
edge_id: EdgeId(101),
blocked_by: vec![RobotId(3)],
},
);
graph.add_wait(
RobotId(3),
WaitingFor::Edge {
edge_id: EdgeId(102),
blocked_by: vec![RobotId(1)],
},
);
let cycle = graph.detect_cycle();
assert!(cycle.is_some());
let cycle = cycle.unwrap();
assert_eq!(cycle.len(), 4); }
#[test]
fn test_chain_no_cycle() {
let mut graph = WaitForGraph::new();
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
graph.add_wait(
RobotId(2),
WaitingFor::Edge {
edge_id: EdgeId(101),
blocked_by: vec![RobotId(3)],
},
);
assert!(graph.detect_cycle().is_none());
}
#[test]
fn test_remove_wait_breaks_cycle() {
let mut graph = WaitForGraph::new();
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
graph.add_wait(
RobotId(2),
WaitingFor::Node {
node_id: NodeId(50),
blocked_by: vec![RobotId(1)],
},
);
assert!(graph.detect_cycle().is_some());
graph.remove_wait(RobotId(1));
assert!(graph.detect_cycle().is_none());
}
#[test]
fn test_multiple_blockers() {
let mut graph = WaitForGraph::new();
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2), RobotId(3)],
},
);
graph.add_wait(
RobotId(2),
WaitingFor::Node {
node_id: NodeId(50),
blocked_by: vec![RobotId(1)],
},
);
let cycle = graph.detect_cycle();
assert!(cycle.is_some());
}
#[test]
fn test_is_waiting() {
let mut graph = WaitForGraph::new();
assert!(!graph.is_waiting(RobotId(1)));
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
assert!(graph.is_waiting(RobotId(1)));
assert!(!graph.is_waiting(RobotId(2)));
}
#[test]
fn test_waiting_count() {
let mut graph = WaitForGraph::new();
assert_eq!(graph.waiting_count(), 0);
graph.add_wait(
RobotId(1),
WaitingFor::Edge {
edge_id: EdgeId(100),
blocked_by: vec![RobotId(2)],
},
);
assert_eq!(graph.waiting_count(), 1);
graph.add_wait(
RobotId(2),
WaitingFor::Node {
node_id: NodeId(50),
blocked_by: vec![RobotId(1)],
},
);
assert_eq!(graph.waiting_count(), 2);
graph.remove_wait(RobotId(1));
assert_eq!(graph.waiting_count(), 1);
}
}