use std::error::Error;
use std::fmt::Debug;
use std::hash::Hash;
use petgraph::graph::NodeIndex;
use petgraph::{Direction, Graph};
use super::Node;
use crate::prelude::{Game, GameError};
pub trait Expansion<const N: usize, G: Game<N>, T> {
type Error: Error;
fn expand(
&self,
game: &G,
tree: &mut Graph<Node<N, G::State, T>, G::Action>,
node_idx: NodeIndex,
) -> Result<(), Self::Error>;
}
#[derive(Debug, thiserror::Error)]
pub enum GreedyError<const N: usize, G: Game<N>> {
#[error(transparent)]
GameError(G::Error),
}
impl<const N: usize, G, E> From<E> for GreedyError<N, G>
where
G: Game<N, Error = E>,
E: GameError,
{
fn from(value: G::Error) -> Self {
Self::GameError(value)
}
}
#[derive(Debug, Default)]
pub struct Greedy;
impl<const N: usize, G, T> Expansion<N, G, T> for Greedy
where
G: Game<N>,
G::Action: Hash + Eq + Debug + Clone,
G::State: Clone,
T: Default + Copy + Debug,
{
type Error = GreedyError<N, G>;
fn expand(
&self,
game: &G,
tree: &mut Graph<Node<N, G::State, T>, G::Action>,
node_idx: NodeIndex,
) -> Result<(), Self::Error> {
let node = &tree[node_idx];
log::debug!("running the greedy expansion strategy from {:?}", node);
let expanded_actions = tree
.edges_directed(node_idx, Direction::Outgoing)
.map(|er| er.weight())
.collect::<Vec<_>>();
for action in game.available_actions(&node.state)? {
if !expanded_actions.contains(&&action) {
let newstate = game.do_action(&action, node.state.clone())?;
let newnode = Node::new(newstate);
let newnode_idx = tree.add_node(newnode);
tree.add_edge(node_idx, newnode_idx, action);
return Ok(());
}
}
log::debug!("nothing to expand");
Ok(())
}
}