planning 1.0.1

A library allowing the planning of minimal sequences of actions to achieve a goal state.
Documentation
use planning::*;

#[test]
fn plan_complex() {
    type Pos = (i32, i32);

    fn manhattan_distance(a: Pos, b: Pos) -> i32 {
        (a.0 - b.0).abs() + (a.1 - b.1).abs()
    }

    #[derive(PartialEq, Eq, Hash, Clone)]
    struct MyState {
        has_wood: bool,
        has_axe: bool,
        house_built: bool,
        position: Pos,
        nearest_tree: Pos,
        nearest_axe: Pos,
    }

    #[derive(PartialEq, Eq, Hash, Clone, Debug)]
    enum MyAction {
        ChopTree,
        GrabAxe,
        BuildHouse,
        GoToTree,
        GoToAxe,
        GoHome,
    }

    impl Action<MyState> for MyAction {
        fn is_applicable(&self, state: &MyState) -> bool {
            match self {
                MyAction::ChopTree => state.has_axe && state.position == state.nearest_tree,
                MyAction::GrabAxe => !state.has_axe && state.position == state.nearest_axe,
                MyAction::BuildHouse => state.has_wood && state.position == (0, 0),
                MyAction::GoToTree => state.position != state.nearest_tree,
                MyAction::GoToAxe => state.position != state.nearest_axe,
                MyAction::GoHome => state.position != (0, 0),
            }
        }

        fn apply_mut(&self, state: &mut MyState) {
            match self {
                MyAction::ChopTree => state.has_wood = true,
                MyAction::GrabAxe => state.has_axe = true,
                MyAction::BuildHouse => state.house_built = true,
                MyAction::GoToTree => state.position = state.nearest_tree,
                MyAction::GoToAxe => state.position = state.nearest_axe,
                MyAction::GoHome => state.position = (0, 0),
            }
        }

        fn cost(&self, state: &MyState) -> i32 {
            match self {
                MyAction::GoToTree => manhattan_distance(state.position, state.nearest_tree),
                MyAction::GoToAxe => manhattan_distance(state.position, state.nearest_axe),
                MyAction::GoHome => manhattan_distance(state.position, (0, 0)),
                _ => 1,
            }
        }
    }

    #[derive(PartialEq, Eq, Hash, Clone)]
    struct MyGoal;

    impl Goal<MyState> for MyGoal {
        fn is_satisfied(&self, state: &MyState) -> bool {
            state.house_built
        }

        fn heuristic(&self, state: &MyState) -> i32 {
            let mut result = 0;
            if !state.has_axe {
                result += manhattan_distance(state.position, state.nearest_axe);
            }
            if !state.has_wood {
                result += manhattan_distance(state.nearest_axe, state.nearest_tree);
            }
            if !state.house_built {
                result += manhattan_distance(state.nearest_tree, (0, 0));
            }
            result
        }
    }

    let initial_state = MyState {
        has_wood: false,
        has_axe: false,
        house_built: false,
        position: (0, 0),
        nearest_tree: (1, 1),
        nearest_axe: (2, 2),
    };

    let actions = vec![
        MyAction::ChopTree,
        MyAction::GrabAxe,
        MyAction::BuildHouse,
        MyAction::GoToTree,
        MyAction::GoToAxe,
        MyAction::GoHome,
    ];

    let goal = MyGoal;

    let (path, cost) = plan(&initial_state, &actions, &goal).unwrap();
    assert_eq!(
        path,
        vec![
            MyAction::GoToAxe,
            MyAction::GrabAxe,
            MyAction::GoToTree,
            MyAction::ChopTree,
            MyAction::GoHome,
            MyAction::BuildHouse,
        ]
    );
    assert_eq!(cost, 11);
}