diff --git a/src/main/java/app/model/Map.java b/src/main/java/app/model/Map.java index 669488e8..6f7aa205 100644 --- a/src/main/java/app/model/Map.java +++ b/src/main/java/app/model/Map.java @@ -220,7 +220,7 @@ public void checkForCapture(Agent currentAgent) if(otherAgent.getType() != currentAgent.getType()) { double dist = currentAgent.getPosition().dist(otherAgent.getPosition()); - if(dist <= (currentAgent.getRadius() + otherAgent.getRadius() + 3)) + if(dist <= (currentAgent.getRadius() + otherAgent.getRadius() + 7)) { deleteAgent(otherAgent); } @@ -267,6 +267,22 @@ public boolean goalReached() return false; } + /** + * Method determines if there is an intruder within a Guard's visual field. + * @return True if an intruder is within a Guard's visual field. Else False. + */ + public boolean intruderVisual() + { + for(Agent a: agents) + { + if(a.getType() == Type.GUARD && a.isTypeSeen(Type.INTRUDER)) + { + return true; + } + } + return false; + } + public int agentsRemaining(Type type) { diff --git a/src/main/java/app/model/agents/AgentImp.java b/src/main/java/app/model/agents/AgentImp.java index 5a2e7bf1..b3bb87b1 100644 --- a/src/main/java/app/model/agents/AgentImp.java +++ b/src/main/java/app/model/agents/AgentImp.java @@ -274,25 +274,6 @@ public boolean noWallDetected(Vector vector, double moveLength) @Override public Agent nextState() { - // State GUARD - if(this.type == Type.GUARD) - { - if(isTypeSeen(Type.INTRUDER)) - return new DijkstraCaptureAgent(this); - } - - // State INTRUDER - else if(this.type == Type.INTRUDER) - { - if(isTypeSeen(Type.GUARD)) - return new IntelligentEvasionAgent(this); - - if(isTypeSeen(Type.TARGET)) - return new TargetAgent(this); - - return this; - } - - return this; + return StateTable.lookupState(this); } } \ No newline at end of file diff --git a/src/main/java/app/model/agents/AgentType.java b/src/main/java/app/model/agents/AgentType.java index d7ebb00f..233276e2 100644 --- a/src/main/java/app/model/agents/AgentType.java +++ b/src/main/java/app/model/agents/AgentType.java @@ -18,16 +18,17 @@ public enum AgentType ACO_RANKING, ACO_COLONY, WALL_FOLLOW, + WALL_FOLLOW_MED_DIR_HEURISTIC, + WALL_FOLLOW_HIGH_DIR_HEURISTIC, EVASION_RANDOM, EVASION_DIRECTED, - EVASION_RANDOMDIRECTED, EVASION_INTELLIGENT, EVASION_DISTANCE_MAX, EVASION_HIDEY, + CAPTURE_BASELINE, EVASION_RUNAWAY, - WALL_FOLLOW_MED_DIR_HEURISTIC, - WALL_FOLLOW_HIGH_DIR_HEURISTIC, CAPTURE_DIJKSTRA, + CAPTURE, RANDOM; public static Agent agentOf(AgentType agentType, Vector position, Vector direction, double radius, Type type) @@ -55,6 +56,12 @@ public static Agent agentOf(AgentType agentType, Vector position, Vector directi case WALL_FOLLOW -> { return new WallFollowAgent(position, direction, radius, type, 20); } + case WALL_FOLLOW_MED_DIR_HEURISTIC -> { + return new WFMedDirHeuristic(position, direction, radius, type, 20); + } + case WALL_FOLLOW_HIGH_DIR_HEURISTIC -> { + return new WFHighDirHeuristic(position, direction, radius, type, 20); + } case EVASION_RANDOM -> { return new EvasionRandom(position, direction, radius, type); } @@ -73,14 +80,14 @@ public static Agent agentOf(AgentType agentType, Vector position, Vector directi case EVASION_RUNAWAY -> { return new RunAwayAgent(position, direction, radius, type); } - case WALL_FOLLOW_MED_DIR_HEURISTIC -> { - return new WFMedDirHeuristic(position, direction, radius, type, 20); + case CAPTURE_BASELINE -> { + return new BaselineCaptureAgent(position, direction, radius, type); } - case WALL_FOLLOW_HIGH_DIR_HEURISTIC -> { - return new WFHighDirHeuristic(position, direction, radius, type, 20); + case CAPTURE -> { + return new CaptureAgent(position, direction, radius, type); } case CAPTURE_DIJKSTRA -> { - return new DijkstraCaptureAgent(position, direction, radius, type, 20); + return new DijkstraCaptureAgent(position, direction, radius, type, 20); } case RANDOM -> { return new AgentImp(position, direction, radius, type); diff --git a/src/main/java/app/model/agents/Capture/CaptureAgent.java b/src/main/java/app/model/agents/Capture/CaptureAgent.java index 78ad434c..219eb31c 100644 --- a/src/main/java/app/model/agents/Capture/CaptureAgent.java +++ b/src/main/java/app/model/agents/Capture/CaptureAgent.java @@ -10,6 +10,8 @@ import app.model.agents.ACO.AcoAgent; import app.model.agents.Agent; import app.model.agents.AgentImp; +import app.model.agents.AgentType; +import app.model.agents.StateTable; import app.model.agents.WallFollow.WallFollowAgent; import lombok.Getter; import lombok.Setter; @@ -224,7 +226,7 @@ public Vector checkMomentum(ArrayList intruderHistory) public Agent nextState() { if(maxTicsReached()) - return new AcoAgent(this); + return StateTable.acoTableSearch(this); return this; } diff --git a/src/main/java/app/model/agents/StateTable.java b/src/main/java/app/model/agents/StateTable.java new file mode 100644 index 00000000..7d5a8978 --- /dev/null +++ b/src/main/java/app/model/agents/StateTable.java @@ -0,0 +1,109 @@ +package app.model.agents; + +import app.model.Type; +import app.model.agents.ACO.*; +import app.model.agents.Capture.BaselineCaptureAgent; +import app.model.agents.Capture.CaptureAgent; +import app.model.agents.Capture.DijkstraCaptureAgent; +import app.model.agents.Evasion.*; +import app.model.agents.WallFollow.WFHighDirHeuristic; +import app.model.agents.WallFollow.WFMedDirHeuristic; +import app.model.agents.WallFollow.WallFollowAgent; +import lombok.Getter; +import lombok.Setter; + +public class StateTable +{ + @Setter private static AgentType defaultCaptureAgent = AgentType.CAPTURE; + @Setter private static AgentType defaultEvasionAgent = AgentType.EVASION_RANDOM; + @Getter private static AgentType defaultAcoAgent = AgentType.ACO_MOMENTUM; + @Getter private static AgentType defaultWFAgent = AgentType.WALL_FOLLOW_MED_DIR_HEURISTIC; + + /** + * Encodes state changes for all types of agents, returning either the current class + * or creates a new class of the required type for the desired next state + * @param currentState The current agent state + * @return The next state of the agent + */ + public static Agent lookupState(Agent currentState) + { + //State GUARD + if(currentState.getType() == Type.GUARD) + { + if(currentState.isTypeSeen(Type.INTRUDER)) + return captureTableSearch(currentState); + } + + //State INTRUDER + else if(currentState.getType() == Type.INTRUDER) + { + if(currentState.isTypeSeen(Type.GUARD)) + return evasionTableSearch(currentState); + + if(currentState.isTypeSeen(Type.TARGET)) + return new TargetAgent(currentState); + + return currentState; + } + return currentState; + } + + public static Agent captureTableSearch(Agent currentState) + { + switch(defaultCaptureAgent) + { + case CAPTURE_BASELINE -> {return new BaselineCaptureAgent(currentState);} + case CAPTURE -> {return new CaptureAgent(currentState);} + case CAPTURE_DIJKSTRA -> {return new DijkstraCaptureAgent(currentState);} + default -> throw new RuntimeException("Capture agent not specified"); + } + } + + public static Agent evasionTableSearch(Agent currentState) + { + switch(defaultEvasionAgent) + { + case EVASION_RANDOM -> {return new EvasionRandom(currentState);} + case EVASION_DIRECTED -> {return new EvasionDirected(currentState);} + case EVASION_DISTANCE_MAX -> {return new EvasionDistanceMax(currentState);} + case EVASION_HIDEY -> {return new HideyAgent(currentState);} + case EVASION_RUNAWAY -> {return new RunAwayAgent(currentState);} + default -> throw new RuntimeException("Evasion agent not specified"); + } + } + + public static Agent acoTableSearch(Agent currentState) + { + switch(defaultAcoAgent) + { + case ACO -> {return new AcoAgent(currentState);} + case ACO_COLONY -> {return new AcoColony(currentState);} + case ACO_MOMENTUM -> {return new AcoMomentum(currentState);} + case ACO_MIDI -> {return new AcoMid(currentState);} + case ACO_RANKING -> {return new AcoRanking(currentState);} + case ACO_MOMENTUM_SPIRAL_AVOIDANCE -> {return new AcoMomentumSpiralAvoidance(currentState);} + default -> throw new RuntimeException("ACO agent not specified"); + } + } + + public static Agent wfTableSearch(Agent currentState) + { + switch(defaultWFAgent) + { + case WALL_FOLLOW -> {return new WallFollowAgent(currentState);} + case WALL_FOLLOW_HIGH_DIR_HEURISTIC -> {return new WFHighDirHeuristic(currentState);} + case WALL_FOLLOW_MED_DIR_HEURISTIC -> {return new WFMedDirHeuristic(currentState);} + default -> throw new RuntimeException("WF agent not specified"); + } + } + + public static String getDefaultCaptureAgent() + { + return defaultCaptureAgent.name() + " "; + } + + public static String getDefaultEvasionAgent() + { + return defaultEvasionAgent.name(); + } +} diff --git a/src/main/java/app/model/agents/TargetAgent.java b/src/main/java/app/model/agents/TargetAgent.java index d097378f..8dd725b8 100644 --- a/src/main/java/app/model/agents/TargetAgent.java +++ b/src/main/java/app/model/agents/TargetAgent.java @@ -4,8 +4,6 @@ import app.controller.linAlg.Vector; import app.model.Move; import app.model.Type; -import app.model.agents.Evasion.RunAwayAgent; -import app.model.agents.WallFollow.WallFollowAgent; public class TargetAgent extends AgentImp { @@ -74,9 +72,9 @@ private boolean updateTargetRay() public Agent nextState() { if(isTypeSeen(Type.GUARD)) - return new RunAwayAgent(this); + return StateTable.evasionTableSearch(this); if(targetLost || moveFailed) - return new WallFollowAgent(this); + return StateTable.wfTableSearch(this); return this; } diff --git a/src/main/java/app/model/agents/Universe.java b/src/main/java/app/model/agents/Universe.java index 8c6e1401..7cdcc0ff 100644 --- a/src/main/java/app/model/agents/Universe.java +++ b/src/main/java/app/model/agents/Universe.java @@ -44,6 +44,7 @@ public static void createPerfectUniverse(MemoryGraph perfectWorld) if(instance == null) instance = new Universe(); + instance.hashMap.clear(); instance.hashMap.put(Type.GUARD, perfectWorld); instance.hashMap.put(Type.INTRUDER, perfectWorld); isPerfectUniverse = true; diff --git a/src/main/java/app/model/agents/WallFollow/WFHighDirHeuristic.java b/src/main/java/app/model/agents/WallFollow/WFHighDirHeuristic.java index 68659d71..9a1a17f0 100644 --- a/src/main/java/app/model/agents/WallFollow/WFHighDirHeuristic.java +++ b/src/main/java/app/model/agents/WallFollow/WFHighDirHeuristic.java @@ -2,6 +2,7 @@ import app.controller.linAlg.Vector; import app.model.Type; +import app.model.agents.Agent; public class WFHighDirHeuristic extends WallFollowAgent { @@ -11,4 +12,10 @@ public WFHighDirHeuristic(Vector position, Vector direction, double radius, Type super(position, direction, radius, type, moveLen); directionHeuristicWeight = 4; } + + public WFHighDirHeuristic(Agent other) + { + super(other); + directionHeuristicWeight = 4; + } } diff --git a/src/main/java/app/model/agents/WallFollow/WFMedDirHeuristic.java b/src/main/java/app/model/agents/WallFollow/WFMedDirHeuristic.java index 1c34d1d0..952e80e3 100644 --- a/src/main/java/app/model/agents/WallFollow/WFMedDirHeuristic.java +++ b/src/main/java/app/model/agents/WallFollow/WFMedDirHeuristic.java @@ -2,6 +2,7 @@ import app.controller.linAlg.Vector; import app.model.Type; +import app.model.agents.Agent; public class WFMedDirHeuristic extends WallFollowAgent { @@ -12,4 +13,9 @@ public WFMedDirHeuristic(Vector position, Vector direction, double radius, Type directionHeuristicWeight = 2; } + public WFMedDirHeuristic(Agent other) + { + super(other); + directionHeuristicWeight = 2; + } } diff --git a/src/test/java/experiments/Experiments.java b/src/test/java/experiments/Experiments.java index 3bf9f59b..013effad 100644 --- a/src/test/java/experiments/Experiments.java +++ b/src/test/java/experiments/Experiments.java @@ -1,9 +1,11 @@ package experiments; import app.controller.io.FileManager; +import app.controller.settings.RandomSettingsGenerator; import app.controller.settings.Settings; import app.model.Map; import app.model.agents.AgentType; +import app.model.agents.StateTable; import app.model.agents.Universe; import jogging.Logger; @@ -18,7 +20,7 @@ public class Experiments */ public static void main(String[] args) { - runInfiltration("experiment_map_1"); + runCapture(); } private static void runCoverage(String map_name) @@ -64,9 +66,10 @@ private static void runCoverage(String map_name) } } + @Deprecated private static void runCapture(String map_name) { - final int iterations = 10; + final int iterations = 100; final int[] no_of_guards = {1, 2, 3, 4 ,5, 6}; System.out.println("Loading map: " + map_name); @@ -94,6 +97,66 @@ private static void runCapture(String map_name) } } + /** + * Experiment provides a 1 vs 1 | Capture vs Evasion situation within the thunder-dome. + * Specify the default Capture and Evasion agents within the method to alter the pairings. + * Experiment output is the number of ticks until the Evasion agent is captured + */ + private static void runCapture() + { + StateTable.setDefaultCaptureAgent(AgentType.CAPTURE); + StateTable.setDefaultEvasionAgent(AgentType.EVASION_RANDOM); + + final String testName = "Capture_Experiment_" + + StateTable.getDefaultCaptureAgent() + "_" + + StateTable.getDefaultEvasionAgent(); + final int iterations = 200; + + Logger logger = new Logger(testName); + logger.setOutputCsv(); + + for(int i = 0; i < iterations; i++) + { + String test_heading = "Iteration: " + i + "/" + iterations + " "; + + Map map = generateRandomMap(); + + TestingEngine gameEngine = new TestingEngine(map, test_heading); + int data = (int) gameEngine.runCaptureTest(); + logger.log(test_heading + ", " + data); + } + } + + /** + * Experiment provides a 1 vs 1 | Capture vs Evasion situation within the thunder-dome. + * Specify the default Capture and Evasion agents within the method to alter the pairings. + * Experiment output is the number of ticks until the Capture agent has lost visual sighting of the Evading agent. + */ + public static void runEvasion() + { + StateTable.setDefaultCaptureAgent(AgentType.CAPTURE); + StateTable.setDefaultEvasionAgent(AgentType.EVASION_DIRECTED); + + final String testName = "Evasion_Experiment_" + + StateTable.getDefaultCaptureAgent() + "_" + + StateTable.getDefaultEvasionAgent(); + final int iterations = 200; + + Logger logger = new Logger(testName); + logger.setOutputCsv(); + + for(int i =0; i < iterations; i++) + { + String test_heading = "Iteration: " + i + "/" + iterations + " "; + + Map map = generateRandomMap(); + + TestingEngine gameEngine = new TestingEngine(map, test_heading); + int data = (int) gameEngine.runEvasionTest(); + logger.log(test_heading + "," + data); + } + } + private static void runInfiltration(String map_name) { final int iterations = 10; @@ -123,6 +186,17 @@ private static void runInfiltration(String map_name) } } } + + private static Map generateRandomMap() + { + Settings settings = RandomSettingsGenerator.generateRandomSettings(); + settings.setNoOfGuards(1); + settings.setNoOfIntruders(1); + + Map map = new Map(settings); + generatePerfectKnowledge(map); + return map; + } public static void generatePerfectKnowledge(Map map) { diff --git a/src/test/java/experiments/TestingEngine.java b/src/test/java/experiments/TestingEngine.java index ca9d506f..1ce49ae6 100644 --- a/src/test/java/experiments/TestingEngine.java +++ b/src/test/java/experiments/TestingEngine.java @@ -71,7 +71,7 @@ protected void updatePercentageBar(double percent) */ public long runCaptureTest() { - long limit = 100; + long limit = 200; while(!(map.agentsRemaining(Type.INTRUDER) == 0) && tics < limit) { @@ -82,6 +82,37 @@ public long runCaptureTest() return tics; } + /** + * Determines the quantity of ticks required for a capture agent to lose visual sight of an evading agent + * for more that an escape limit (Ticks equal or above this indicate an escape) + * @return The number of ticks until the evading agent has lost the capture agent + */ + public long runEvasionTest() + { + long limit = 200; + long noVisualDuration = 0; + long escapeLimit = 20; + + while(map.agentsRemaining(Type.INTRUDER) >= 1 && + noVisualDuration < escapeLimit && + tics < limit) + { + if(!map.intruderVisual()) + { + noVisualDuration ++; + } + else + { + noVisualDuration = 0; + } + + tick(); + updateTicsRemaining(tics, limit); + } + System.out.println(" - complete"); + return tics; + } + /** * A test to find the number of tics taken for the intruders to reach the goal (or a limit of n tics)