Skip to content

Commit

Permalink
add additional unit test where to find cheapest plan; divide plan + p…
Browse files Browse the repository at this point in the history
…lanCheapest
  • Loading branch information
svencc committed Sep 13, 2023
1 parent 9d49744 commit 24fccce
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/main/java/lib/gecom/GeAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void Update() {
final HashMap<String, Integer> agentsBelieves = new HashMap<>();
if (currentActionStack.isEmpty()) {
for (GeSubgoal subgoal : prioritizedSubgoals) {
final Optional<Queue<GeAction>> plan = planner.plan(agentsBelieves, possibleActions, subgoal.getStatesToReach());
final Optional<Queue<GeAction>> plan = planner.planCheapest(agentsBelieves, possibleActions, subgoal.getStatesToReach());
if (plan.isPresent()) {
currentActionStack.addAll(plan.get());
currentGoal = subgoal;
Expand Down
71 changes: 55 additions & 16 deletions src/main/java/lib/gecom/GePlanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,19 @@
public class GePlanner {

@NonNull
public Optional<Queue<GeAction>> plan(
public Optional<Queue<GeAction>> planCheapest(
@NonNull final HashMap<String, Integer> agentsBelieves,
@NonNull final List<GeAction> possibleActions,
@NonNull final HashMap<String, Integer> goal
) {
final List<GeAction> usableActions = new ArrayList<>();
possibleActions.forEach(possibleAction -> {
if (possibleAction.isAchievable()) {
usableActions.add(possibleAction);
}
});
final List<GeNode> leaves = plan(agentsBelieves, possibleActions, goal);

final List<GeNode> 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) {
Expand All @@ -60,6 +49,56 @@ public Optional<Queue<GeAction>> plan(
return Optional.of(new LinkedList<GeAction>(result));
}

public List<GeNode> plan(
@NonNull final HashMap<String, Integer> agentsBelieves,
@NonNull final List<GeAction> possibleActions,
@NonNull final HashMap<String, Integer> goal
) {
final List<GeAction> usableActions = new ArrayList<>();
possibleActions.forEach(possibleAction -> {
if (possibleAction.isAchievable()) {
usableActions.add(possibleAction);
}
});

final List<GeNode> 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<GeAction> 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<GeAction>(result));
}

private boolean buildGraph(
@NonNull final GeNode parent,
@NonNull final List<GeNode> foundPlans,
Expand Down
71 changes: 63 additions & 8 deletions src/test/java/lib/gecom/GePlannerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class GePlannerTest {

@Test
void plan_testIfIsRunnable_givesEmptyPlan() {
void planCheapest_testIfIsRunnable_givesEmptyPlan() {
// Arrange
final HashMap<String, Integer> agentsBelieves = new HashMap<>();
final List<GeAction> possibleActions = new ArrayList<>();
Expand All @@ -20,14 +20,14 @@ void plan_testIfIsRunnable_givesEmptyPlan() {
final GePlanner planner = new GePlanner();

// Act
final Optional<Queue<GeAction>> planToTest = planner.plan(agentsBelieves, possibleActions, goal);
final Optional<Queue<GeAction>> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal);

// Assert
assertTrue(planToTest.isEmpty());
}

@Test
void plan_whenAllIsEmpty_givesEmptyPlan() {
void planCheapest_whenAllIsEmpty_givesEmptyPlan() {
// Arrange
final HashMap<String, Integer> goal = new HashMap<>();
final List<GeAction> possibleActions = new ArrayList<>();
Expand All @@ -36,14 +36,14 @@ void plan_whenAllIsEmpty_givesEmptyPlan() {
final GePlanner planner = new GePlanner();

// Act
final Optional<Queue<GeAction>> planToTest = planner.plan(agentsBelieves, possibleActions, goal);
final Optional<Queue<GeAction>> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal);

// Assert
assertTrue(planToTest.isEmpty());
}

@Test
void plan_withOneSimpleAchievableAction_givesOnePlan() {
void planCheapest_withOneSimpleAchievableAction_givesOnePlan() {
// Arrange
// Believes:
final HashMap<String, Integer> agentsBelieves = new HashMap<>();
Expand All @@ -68,7 +68,7 @@ void plan_withOneSimpleAchievableAction_givesOnePlan() {

// Act
final GePlanner planner = new GePlanner();
final Optional<Queue<GeAction>> planToTest = planner.plan(agentsBelieves, possibleActions, goal);
final Optional<Queue<GeAction>> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal);

// Assert
assertFalse(planToTest.isEmpty());
Expand All @@ -77,7 +77,7 @@ void plan_withOneSimpleAchievableAction_givesOnePlan() {
}

@Test
void plan_withTwoSimpleAchievableAction_givesOnePlan() {
void planCheapest_withTwoSimpleAchievableAction_givesOnePlan() {
// Arrange
// Believes:
final HashMap<String, Integer> agentsBelieves = new HashMap<>();
Expand Down Expand Up @@ -112,7 +112,7 @@ void plan_withTwoSimpleAchievableAction_givesOnePlan() {

// Act
final GePlanner planner = new GePlanner();
final Optional<Queue<GeAction>> planToTest = planner.plan(agentsBelieves, possibleActions, goal);
final Optional<Queue<GeAction>> planToTest = planner.planCheapest(agentsBelieves, possibleActions, goal);

// Assert
assertFalse(planToTest.isEmpty());
Expand All @@ -122,6 +122,61 @@ void plan_withTwoSimpleAchievableAction_givesOnePlan() {
assertEquals(action2, planToTest.get().poll());
}

@Test
void planCheapest_withTwoConcurrentAchievableBranches_givesCheapestPlan() {
// Arrange
// Believes:
final HashMap<String, Integer> agentsBelieves = new HashMap<>();
agentsBelieves.put("i-am-hungry", 1);
agentsBelieves.put("has-something-to-eat", 0);

// List of possible actions:
final List<GeAction> possibleActions = new ArrayList<>();

// Goal
final HashMap<String, Integer> 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<Queue<GeAction>> 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
Expand Down

0 comments on commit 24fccce

Please sign in to comment.