diff --git a/src/main/java/lib/gecom/GeAgent.java b/src/main/java/lib/gecom/GeAgent.java index 3558a194..99c5db59 100644 --- a/src/main/java/lib/gecom/GeAgent.java +++ b/src/main/java/lib/gecom/GeAgent.java @@ -76,7 +76,7 @@ public void Update() { final HashMap agentsBelieves = new HashMap<>(); if (currentActionStack.isEmpty()) { for (GeSubgoal subgoal : prioritizedSubgoals) { - final Optional> plan = planner.plan(agentsBelieves, possibleActions, subgoal.getStatesToReach()); + final Optional> plan = planner.planCheapest(agentsBelieves, possibleActions, subgoal.getStatesToReach()); if (plan.isPresent()) { currentActionStack.addAll(plan.get()); currentGoal = subgoal; diff --git a/src/main/java/lib/gecom/GePlanner.java b/src/main/java/lib/gecom/GePlanner.java index 3f30928f..a486346d 100644 --- a/src/main/java/lib/gecom/GePlanner.java +++ b/src/main/java/lib/gecom/GePlanner.java @@ -11,30 +11,19 @@ public class GePlanner { @NonNull - public Optional> plan( + public Optional> planCheapest( @NonNull final HashMap agentsBelieves, @NonNull final List possibleActions, @NonNull final HashMap goal ) { - final List usableActions = new ArrayList<>(); - possibleActions.forEach(possibleAction -> { - if (possibleAction.isAchievable()) { - usableActions.add(possibleAction); - } - }); + final List leaves = plan(agentsBelieves, possibleActions, goal); - final List leaves = new ArrayList<>(); - final GeNode start = new GeNode(null, agentsBelieves, null, 0f); - - boolean success = buildGraph(start, leaves, usableActions, goal); - - if (!success) { - System.out.println("No solution found!"); - // @TODO throw exception? and log! + if (leaves.isEmpty()) { return Optional.empty(); } - // TODO look to dijkstra's algorithm.... for the following: + // look for cheapest; @TODO look to dijkstra's algorithm + // iam sure there is a better way to do this than here! GeNode cheapest = null; for (GeNode leaf : leaves) { @@ -60,6 +49,56 @@ public Optional> plan( return Optional.of(new LinkedList(result)); } + public List plan( + @NonNull final HashMap agentsBelieves, + @NonNull final List possibleActions, + @NonNull final HashMap goal + ) { + final List usableActions = new ArrayList<>(); + possibleActions.forEach(possibleAction -> { + if (possibleAction.isAchievable()) { + usableActions.add(possibleAction); + } + }); + + final List leaves = new ArrayList<>(); + final GeNode start = new GeNode(null, agentsBelieves, null, 0f); + + boolean success = buildGraph(start, leaves, usableActions, goal); + + if (!success) { + System.out.println("No solution found!"); + // @TODO throw exception? and log! + return List.of(); + } + + return leaves; + +// // TODO look to dijkstra's algorithm.... for the following: +// GeNode cheapest = null; +// for (GeNode leaf : leaves) { +// if (cheapest == null) { +// cheapest = leaf; +// } else if (leaf.cost < cheapest.cost) { +// cheapest = leaf; +// } +// } +// +// // @TODO return a list of actions (linked list) +// final List result = new ArrayList<>(); +// GeNode node = cheapest; +// while (node != null) { +// if (node.action != null) { +// result.add(node.action); +// } +// node = node.parent; +// } +// Collections.reverse(result); // TEST THAT +// +// // could return a PriorityQueue with nodes; it is then sorted +// return Optional.of(new LinkedList(result)); + } + private boolean buildGraph( @NonNull final GeNode parent, @NonNull final List foundPlans, diff --git a/src/test/java/lib/gecom/GePlannerTest.java b/src/test/java/lib/gecom/GePlannerTest.java index 2707e9b8..b648c37e 100644 --- a/src/test/java/lib/gecom/GePlannerTest.java +++ b/src/test/java/lib/gecom/GePlannerTest.java @@ -11,7 +11,7 @@ class GePlannerTest { @Test - void plan_testIfIsRunnable_givesEmptyPlan() { + void planCheapest_testIfIsRunnable_givesEmptyPlan() { // Arrange final HashMap agentsBelieves = new HashMap<>(); final List possibleActions = new ArrayList<>(); @@ -20,14 +20,14 @@ void plan_testIfIsRunnable_givesEmptyPlan() { final GePlanner planner = new GePlanner(); // Act - final Optional> planToTest = planner.plan(agentsBelieves, possibleActions, goal); + final Optional> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal); // Assert assertTrue(planToTest.isEmpty()); } @Test - void plan_whenAllIsEmpty_givesEmptyPlan() { + void planCheapest_whenAllIsEmpty_givesEmptyPlan() { // Arrange final HashMap goal = new HashMap<>(); final List possibleActions = new ArrayList<>(); @@ -36,14 +36,14 @@ void plan_whenAllIsEmpty_givesEmptyPlan() { final GePlanner planner = new GePlanner(); // Act - final Optional> planToTest = planner.plan(agentsBelieves, possibleActions, goal); + final Optional> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal); // Assert assertTrue(planToTest.isEmpty()); } @Test - void plan_withOneSimpleAchievableAction_givesOnePlan() { + void planCheapest_withOneSimpleAchievableAction_givesOnePlan() { // Arrange // Believes: final HashMap agentsBelieves = new HashMap<>(); @@ -68,7 +68,7 @@ void plan_withOneSimpleAchievableAction_givesOnePlan() { // Act final GePlanner planner = new GePlanner(); - final Optional> planToTest = planner.plan(agentsBelieves, possibleActions, goal); + final Optional> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal); // Assert assertFalse(planToTest.isEmpty()); @@ -77,7 +77,7 @@ void plan_withOneSimpleAchievableAction_givesOnePlan() { } @Test - void plan_withTwoSimpleAchievableAction_givesOnePlan() { + void planCheapest_withTwoSimpleAchievableAction_givesOnePlan() { // Arrange // Believes: final HashMap agentsBelieves = new HashMap<>(); @@ -112,7 +112,7 @@ void plan_withTwoSimpleAchievableAction_givesOnePlan() { // Act final GePlanner planner = new GePlanner(); - final Optional> planToTest = planner.plan(agentsBelieves, possibleActions, goal); + final Optional> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal); // Assert assertFalse(planToTest.isEmpty()); @@ -122,6 +122,61 @@ void plan_withTwoSimpleAchievableAction_givesOnePlan() { assertEquals(action2, planToTest.get().poll()); } + @Test + void planCheapest_withTwoConcurrentAchievableBranches_givesCheapestPlan() { + // Arrange + // Believes: + final HashMap agentsBelieves = new HashMap<>(); + agentsBelieves.put("i-am-hungry", 1); + agentsBelieves.put("has-something-to-eat", 0); + + // List of possible actions: + final List possibleActions = new ArrayList<>(); + + // Goal + final HashMap goal = new HashMap<>(); + goal.put("i-am-hungry", 0); + + // Create an action that fulfills the goal + final GeAgent agent = new GeAgent(possibleActions); + final GeAction action1 = TestAction.builder() + .name("find-something-to-eat") + .agent(agent) // @TODO dependency of action to agent is not good. Decouple it. I dont know if the action should be bound to an agent Makes sense if action has an internal state! + .build(); + action1.getPreconditions().put("has-something-to-eat", 0); + action1.getAfterEffects().put("has-something-to-eat", 1); + possibleActions.add(action1); + + final GeAction action2 = TestAction.builder() + .name("eat") + .agent(agent) // @TODO dependency of action to agent is not good. Decouple it. I dont know if the action should be bound to an agent Makes sense if action has an internal state! + .build(); + action2.getPreconditions().put("has-something-to-eat", 1); + action2.getAfterEffects().put("i-am-hungry", 0); + action2.getAfterEffects().put("has-something-to-eat", 0); + possibleActions.add(action2); + + final GeAction action3 = TestAction.builder() + .name("hunt") + .agent(agent) // @TODO dependency of action to agent is not good. Decouple it. I dont know if the action should be bound to an agent Makes sense if action has an internal state! + .cost(10.0f) + .build(); + action3.getPreconditions().put("has-something-to-eat", 0); + action3.getAfterEffects().put("has-something-to-eat", 1); + possibleActions.add(action3); + + // Act + final GePlanner planner = new GePlanner(); + final Optional> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal); + + // Assert + assertFalse(planToTest.isEmpty()); + assertEquals(2, planToTest.get().size()); + assertTrue(planToTest.get().containsAll(List.of(action1, action2))); + assertEquals(action1, planToTest.get().poll()); + assertEquals(action2, planToTest.get().poll()); + } + @SuperBuilder public static class TestAction extends GeAction { @Override