From 56d2ee7a99be816a8d06be15edf23291525fe340 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 13:27:57 -0400 Subject: [PATCH 01/23] Added headlamp state and action. --- .../models/HeadlampActionDescription.java | 20 +++++++ .../mobilerobot/models/HeadlampState.java | 53 ++++++++++++++++ .../models/HeadlampStateFormula.java | 60 +++++++++++++++++++ .../mobilerobot/models/SetHeadlampAction.java | 19 ++++++ 4 files changed, 152 insertions(+) create mode 100644 XPlanning/src/examples/mobilerobot/models/HeadlampActionDescription.java create mode 100644 XPlanning/src/examples/mobilerobot/models/HeadlampState.java create mode 100644 XPlanning/src/examples/mobilerobot/models/HeadlampStateFormula.java create mode 100644 XPlanning/src/examples/mobilerobot/models/SetHeadlampAction.java diff --git a/XPlanning/src/examples/mobilerobot/models/HeadlampActionDescription.java b/XPlanning/src/examples/mobilerobot/models/HeadlampActionDescription.java new file mode 100644 index 00000000..7801ffae --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/models/HeadlampActionDescription.java @@ -0,0 +1,20 @@ +package examples.mobilerobot.models; + +import language.domain.models.ActionDefinition; +import language.domain.models.IProbabilisticTransitionFormula; +import language.domain.models.StateVarDefinition; +import language.mdp.DiscriminantClass; +import language.mdp.EffectClass; +import language.mdp.FormulaActionDescription; +import language.mdp.Precondition; + +public class HeadlampActionDescription extends FormulaActionDescription { + + public HeadlampActionDescription(ActionDefinition setHeadlampDef, + Precondition precondition, StateVarDefinition rHeadlampDef) { + super(setHeadlampDef, precondition, + new DiscriminantClass(rHeadlampDef), new EffectClass(rHeadlampDef), + new HeadlampStateFormula(rHeadlampDef)); + } + +} diff --git a/XPlanning/src/examples/mobilerobot/models/HeadlampState.java b/XPlanning/src/examples/mobilerobot/models/HeadlampState.java new file mode 100644 index 00000000..50a1e40b --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/models/HeadlampState.java @@ -0,0 +1,53 @@ +package examples.mobilerobot.models; + +import language.domain.models.IStateVarAttribute; +import language.domain.models.IStateVarBoolean; +import language.exceptions.AttributeNameNotFoundException; + +public class HeadlampState implements IStateVarBoolean { + + private boolean mOn; + + public HeadlampState(boolean on) { + mOn = on; + } + + @Override + public IStateVarAttribute getAttributeValue(String name) throws AttributeNameNotFoundException { + throw new AttributeNameNotFoundException(name); + } + + @Override + public boolean getValue() { + return mOn; + } + + + /* + * Cached hashCode -- Effective Java + */ + private volatile int hashCode; + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof HeadlampState)) { + return false; + } + HeadlampState hlstate = (HeadlampState) obj; + return hlstate.mOn == mOn; + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = Boolean.hashCode(mOn); + hashCode = result; + } + return hashCode; + } + +} diff --git a/XPlanning/src/examples/mobilerobot/models/HeadlampStateFormula.java b/XPlanning/src/examples/mobilerobot/models/HeadlampStateFormula.java new file mode 100644 index 00000000..0d1b40c0 --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/models/HeadlampStateFormula.java @@ -0,0 +1,60 @@ +package examples.mobilerobot.models; + +import language.domain.models.IProbabilisticTransitionFormula; +import language.domain.models.StateVar; +import language.domain.models.StateVarDefinition; +import language.exceptions.XMDPException; +import language.mdp.Discriminant; +import language.mdp.Effect; +import language.mdp.EffectClass; +import language.mdp.ProbabilisticEffect; + +public class HeadlampStateFormula implements IProbabilisticTransitionFormula { + + private StateVarDefinition mrHeadlampDef; + private EffectClass mEffectClass; + + public HeadlampStateFormula (StateVarDefinition rHeadlampDef) { + this.mrHeadlampDef = rHeadlampDef; + this.mEffectClass = new EffectClass(rHeadlampDef); + } + + @Override + public ProbabilisticEffect formula(Discriminant discriminant, SetHeadlampAction action) throws XMDPException { + ProbabilisticEffect pe = new ProbabilisticEffect(mEffectClass); + Effect newHeadlampEffect = new Effect(mEffectClass); + StateVar rHeadhlamp = mrHeadlampDef.getStateVar(action.getValue()); + newHeadlampEffect.add(rHeadhlamp); + pe.put(newHeadlampEffect, 1.0); + return pe; + } + + /* + * Cached hashCode -- Effective Java + */ + private volatile int hashCode; + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof HeadlampStateFormula)) { + return false; + } + HeadlampStateFormula formula = (HeadlampStateFormula) obj; + return formula.mrHeadlampDef.equals(mrHeadlampDef); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = 17; + result = 31 * result + mrHeadlampDef.hashCode(); + hashCode = result; + } + return hashCode; + } + +} diff --git a/XPlanning/src/examples/mobilerobot/models/SetHeadlampAction.java b/XPlanning/src/examples/mobilerobot/models/SetHeadlampAction.java new file mode 100644 index 00000000..2e4468d2 --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/models/SetHeadlampAction.java @@ -0,0 +1,19 @@ +package examples.mobilerobot.models; + +import language.domain.models.Action; +import language.domain.models.StateVar; + +public class SetHeadlampAction extends Action { + + private StateVar mrHeadlampState; + + public SetHeadlampAction(StateVar rHeadlampState) { + super("setHeadlamp", rHeadlampState.getValue()); + mrHeadlampState = rHeadlampState; + } + + public HeadlampState getValue() { + return mrHeadlampState.getValue(); + } + +} From db47b29dd150acb0f6e249fb98997e0550343dc7 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 13:47:35 -0400 Subject: [PATCH 02/23] Hooked Headlamp into the model --- .../dsm/MobileRobotXMDPBuilder.java | 35 +++++++++++++++++-- .../metrics/EnergyConsumptionDomain.java | 26 +++++++++----- .../metrics/EnergyConsumptionQFunction.java | 9 ++++- 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 974ba98c..1c2608f3 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -17,12 +17,15 @@ import examples.mobilerobot.metrics.TravelTimeQFunction; import examples.mobilerobot.models.Area; import examples.mobilerobot.models.Distance; +import examples.mobilerobot.models.HeadlampActionDescription; +import examples.mobilerobot.models.HeadlampState; import examples.mobilerobot.models.Location; import examples.mobilerobot.models.MoveToAction; import examples.mobilerobot.models.Occlusion; import examples.mobilerobot.models.RobotLocationActionDescription; import examples.mobilerobot.models.RobotSpeed; import examples.mobilerobot.models.RobotSpeedActionDescription; +import examples.mobilerobot.models.SetHeadlampAction; import examples.mobilerobot.models.SetSpeedAction; import language.domain.metrics.CountQFunction; import language.domain.metrics.EventBasedMetric; @@ -78,6 +81,20 @@ public class MobileRobotXMDPBuilder { // SetSpeed action definition private ActionDefinition setSpeedDef = new ActionDefinition<>("setSpeed", setSpeedHalf, setSpeedFull); + + // --- Headlamp setting --- // + private HeadlampState headlampOn = new HeadlampState(true); + private HeadlampState headlampOff = new HeadlampState(false); + + // Robot's headlamp state variable + private StateVarDefinition rHeadlampDef = new StateVarDefinition<>("rHeadlamp", headlampOn, headlampOff); + + // Headlamp-setting actions + private SetHeadlampAction setHeadlampOff = new SetHeadlampAction(rHeadlampDef.getStateVar(headlampOff)); + private SetHeadlampAction setHeadlampOn = new SetHeadlampAction(rHeadlampDef.getStateVar(headlampOn)); + + // SetHeadlamp action defintion + private ActionDefinition setHeadlampDef = new ActionDefinition<>("setHeadlamp", setHeadlampOff, setHeadlampOn); // ------ // // --- QA functions --- // @@ -217,14 +234,28 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE // Action description for rSpeed RobotSpeedActionDescription rSpeedActionDesc = new RobotSpeedActionDescription(setSpeedDef, preSetSpeed, rSpeedDef); - + // PSO FactoredPSO setSpeedPSO = new FactoredPSO<>(setSpeedDef, preSetSpeed); setSpeedPSO.addActionDescription(rSpeedActionDesc); + + // SetHeadlamp + // Precondition + Precondition preSetHeadlamp = new Precondition<>(setHeadlampDef); + preSetHeadlamp.add(setHeadlampOn, rHeadlampDef, headlampOff); + preSetHeadlamp.add(setHeadlampOff, rHeadlampDef, headlampOn); + + // Action description for rHeadlamp + HeadlampActionDescription rHeadlampActionDesc = new HeadlampActionDescription(setHeadlampDef, preSetHeadlamp, rHeadlampDef); + + // PSO + FactoredPSO setHeadlampPSO = new FactoredPSO<>(setHeadlampDef, preSetHeadlamp); + setHeadlampPSO.addActionDescription(rHeadlampActionDesc); TransitionFunction transFunction = new TransitionFunction(); transFunction.add(moveToPSO); transFunction.add(setSpeedPSO); + transFunction.add(setHeadlampPSO); return transFunction; } @@ -256,7 +287,7 @@ private QSpace buildQFunctions() { metric); // Energy Consumption - EnergyConsumptionDomain energyDomain = new EnergyConsumptionDomain(rLocDef, rSpeedDef, moveToDef); + EnergyConsumptionDomain energyDomain = new EnergyConsumptionDomain(rLocDef, rSpeedDef, rHeadlampDef, moveToDef); EnergyConsumptionQFunction energyQFunction = new EnergyConsumptionQFunction(energyDomain); QSpace qSpace = new QSpace(); diff --git a/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionDomain.java b/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionDomain.java index b2ae879a..4affc61a 100644 --- a/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionDomain.java +++ b/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionDomain.java @@ -1,44 +1,52 @@ package examples.mobilerobot.metrics; +import examples.mobilerobot.models.HeadlampState; import examples.mobilerobot.models.Location; import examples.mobilerobot.models.MoveToAction; import examples.mobilerobot.models.RobotSpeed; import language.domain.metrics.TransitionStructure; import language.domain.models.ActionDefinition; import language.domain.models.StateVarDefinition; + /** * Defines the domain for the energy quality domain * - * The energy consumption quality is calculated based on the robot's location and speed, - * gets information from the MoveToAction, and effects the robot's location (because MoveToAction does) + * The energy consumption quality is calculated based on the robot's location and speed, gets information from the + * MoveToAction, and effects the robot's location (because MoveToAction does) * * */ public class EnergyConsumptionDomain extends TransitionStructure { - private StateVarDefinition mrLocDef; private StateVarDefinition mrSpeedDef; + private StateVarDefinition mrHeadlampDef; public EnergyConsumptionDomain(StateVarDefinition rLocDef, StateVarDefinition rSpeedDef, - ActionDefinition moveToDef) { - + StateVarDefinition rHeadlampDef, ActionDefinition moveToDef) { + this.mrLocDef = rLocDef; this.mrSpeedDef = rSpeedDef; + this.mrHeadlampDef = rHeadlampDef; addSrcStateVarDef(rLocDef); addSrcStateVarDef(rSpeedDef); - + addSrcStateVarDef(rHeadlampDef); + setActionDef(moveToDef); - + addDestStateVarDef(rLocDef); } - + public StateVarDefinition getLocationStateVar() { return mrLocDef; } - + public StateVarDefinition getSpeedStateVar() { return mrSpeedDef; } + + public StateVarDefinition getHeadlampStateVar() { + return mrHeadlampDef; + } } diff --git a/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionQFunction.java b/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionQFunction.java index 60c98654..88b1f9fc 100644 --- a/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionQFunction.java +++ b/XPlanning/src/examples/mobilerobot/metrics/EnergyConsumptionQFunction.java @@ -1,6 +1,7 @@ package examples.mobilerobot.metrics; import examples.mobilerobot.models.Distance; +import examples.mobilerobot.models.HeadlampState; import examples.mobilerobot.models.Location; import examples.mobilerobot.models.MoveToAction; import examples.mobilerobot.models.RobotSpeed; @@ -24,6 +25,7 @@ public class EnergyConsumptionQFunction implements IStandardMetricQFunction transit RobotSpeed speed = transition.getSrcStateVarValue(RobotSpeed.class, mDomain.getSpeedStateVar()); + // If the headlamp is on, then need to discharge at a higher rate + HeadlampState hl = transition.getSrcStateVarValue(HeadlampState.class, mDomain.getHeadlampStateVar()); + double consumption = distance.getDistance() / speed.getSpeed() * - (BASE_DISCHARGE_RATE + speed.getSpeed() * SPEED_INCREASE_DISCHARGE_RATE * 10); + (BASE_DISCHARGE_RATE + + speed.getSpeed() * SPEED_INCREASE_DISCHARGE_RATE * 10 + + (hl.getValue() ? HEADLAMP_DISCHARGE_RATE : 0)); return consumption; } From 63e4fc369ca2e3ba517524fd27b0bf0623e57dcb Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 13:51:17 -0400 Subject: [PATCH 03/23] Added headlamp on precondition for move in dark corridors --- .../examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 1c2608f3..9fbe3fd3 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -199,6 +199,10 @@ private StateVarTuple buildGoal(LocationNode goalNode) { goal.addStateVar(rLocDef.getStateVar(loc)); return goal; } + + private boolean isDark(Connection conn) { + return false; + } private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPException, MapTopologyException { // MoveTo: @@ -214,6 +218,9 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE for (Connection conn : connections) { Location locSrc = mLocMap.get(conn.getOtherNode(node)); preMoveTo.add(moveTo, rLocDef, locSrc); + if (isDark(conn)) { + preMoveTo.add(moveTo, rHeadlampDef, headlampOn); + } } } From e00cc907d89616be4c91e4f6e05eace2175da54a Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 14:51:50 -0400 Subject: [PATCH 04/23] Added toString --- XPlanning/src/examples/mobilerobot/models/HeadlampState.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/XPlanning/src/examples/mobilerobot/models/HeadlampState.java b/XPlanning/src/examples/mobilerobot/models/HeadlampState.java index 50a1e40b..7a37db95 100644 --- a/XPlanning/src/examples/mobilerobot/models/HeadlampState.java +++ b/XPlanning/src/examples/mobilerobot/models/HeadlampState.java @@ -21,6 +21,11 @@ public IStateVarAttribute getAttributeValue(String name) throws AttributeNameNot public boolean getValue() { return mOn; } + + @Override + public String toString() { + return Boolean.toString(mOn); + } /* From d09ff20527104bd58780c5ec7494cae2118146a3 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 14:52:46 -0400 Subject: [PATCH 05/23] Added darkness to the environment and loading --- XPlanning/data/mobilerobot/maps/GHC7-map0.json | 9 ++++++++- .../examples/mobilerobot/demo/MobileRobotXMDPLoader.java | 5 +++++ .../examples/mobilerobot/viz/MapBasedPolicyRenderer.java | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/XPlanning/data/mobilerobot/maps/GHC7-map0.json b/XPlanning/data/mobilerobot/maps/GHC7-map0.json index aae527a0..13d9a142 100644 --- a/XPlanning/data/mobilerobot/maps/GHC7-map0.json +++ b/XPlanning/data/mobilerobot/maps/GHC7-map0.json @@ -548,5 +548,12 @@ "to-id": "L22", "from-id": "L18" } - ] + ], + "lighting": [ + { + "dark": true, + "to-id": "L22", + "from-id": "L18" + } + ] } \ No newline at end of file diff --git a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXMDPLoader.java b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXMDPLoader.java index 5e3c3668..c359fb47 100644 --- a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXMDPLoader.java +++ b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXMDPLoader.java @@ -18,12 +18,14 @@ import examples.mobilerobot.dsm.Mission; import examples.mobilerobot.dsm.MobileRobotXMDPBuilder; import examples.mobilerobot.dsm.parser.AreaParser; +import examples.mobilerobot.dsm.parser.DarknessParser; import examples.mobilerobot.dsm.parser.IEdgeAttributeParser; import examples.mobilerobot.dsm.parser.INodeAttributeParser; import examples.mobilerobot.dsm.parser.MapTopologyReader; import examples.mobilerobot.dsm.parser.MissionReader; import examples.mobilerobot.dsm.parser.OcclusionParser; import examples.mobilerobot.models.Area; +import examples.mobilerobot.models.Darkness; import examples.mobilerobot.models.Occlusion; import language.exceptions.XMDPException; import language.mdp.XMDP; @@ -44,15 +46,18 @@ public MobileRobotXMDPLoader(File mapsJsonDir) { mMapsJsonDir = mapsJsonDir; AreaParser areaParser = new AreaParser(); OcclusionParser occlusionParser = new OcclusionParser(); + DarknessParser darknessParser = new DarknessParser(); Set> nodeAttributeParsers = new HashSet<>(); nodeAttributeParsers.add(areaParser); Set> edgeAttributeParsers = new HashSet<>(); edgeAttributeParsers.add(occlusionParser); + edgeAttributeParsers.add(darknessParser); mMapReader = new MapTopologyReader(nodeAttributeParsers, edgeAttributeParsers); // Default node/edge attribute values mDefaultNodeAttributes.put(areaParser.getAttributeName(), DEFAULT_AREA); mDefaultEdgeAttributes.put(occlusionParser.getAttributeName(), DEFAULT_OCCLUSION); + mDefaultEdgeAttributes.put(darknessParser.getAttributeName(), Darkness.LIGHT); } @Override diff --git a/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java b/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java index 39647d29..8462df89 100644 --- a/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java +++ b/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java @@ -34,6 +34,7 @@ import examples.mobilerobot.dsm.parser.MissionReader; import examples.mobilerobot.dsm.parser.OcclusionParser; import examples.mobilerobot.models.Area; +import examples.mobilerobot.models.Darkness; import examples.mobilerobot.models.Occlusion; import explanation.rendering.IPolicyRenderer; import graphscii.Edge; @@ -86,6 +87,7 @@ public static int[] getTerminalSize() throws Throwable { "| Edges: |", "| () : Sparse occlusion |", "| [] : Dense occlusion |", + "| o : Dark corridor |", "| " + (char )0x21D2 + " : 0.7m/s traversal |", "| " + (char )0x2192 + " : 0.35m/s traversal |", "+--------------------------+" }; @@ -154,6 +156,10 @@ public String labelFor(Edge e) { label = String.format("(%s)", label); break; } + + Darkness dark = (Darkness) e.getAttribute("lighting"); + if (dark == Darkness.DARK) + label = "o" + label; return label; } From 30d168f51bd83cbd2ccfecf3d9c058943cf260a5 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 14:53:41 -0400 Subject: [PATCH 06/23] Set up initial state for headlamp. --- .../src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 9fbe3fd3..94ac6b80 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -147,6 +147,7 @@ private StateSpace buildStateSpace(MapTopology map) throws NodeAttributeNotFound StateSpace stateSpace = new StateSpace(); stateSpace.addStateVarDefinition(rLocDef); stateSpace.addStateVarDefinition(rSpeedDef); + stateSpace.addStateVarDefinition(rHeadlampDef); return stateSpace; } @@ -190,6 +191,7 @@ private StateVarTuple buildInitialState(LocationNode startNode) { StateVarTuple initialState = new StateVarTuple(); initialState.addStateVar(rLocDef.getStateVar(loc)); initialState.addStateVar(rSpeedDef.getStateVar(DEFAULT_SPEED)); + initialState.addStateVar(rHeadlampDef.getStateVar(headlampOff)); return initialState; } From c52dca3b2c13d1cea2a45ed3d24d653d51d6f7b8 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Fri, 27 Aug 2021 14:54:06 -0400 Subject: [PATCH 07/23] Parsing of darkness --- .../dsm/parser/DarknessParser.java | 25 +++++++++++++++++++ .../examples/mobilerobot/models/Darkness.java | 7 ++++++ 2 files changed, 32 insertions(+) create mode 100644 XPlanning/src/examples/mobilerobot/dsm/parser/DarknessParser.java create mode 100644 XPlanning/src/examples/mobilerobot/models/Darkness.java diff --git a/XPlanning/src/examples/mobilerobot/dsm/parser/DarknessParser.java b/XPlanning/src/examples/mobilerobot/dsm/parser/DarknessParser.java new file mode 100644 index 00000000..c8044f0c --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/dsm/parser/DarknessParser.java @@ -0,0 +1,25 @@ +package examples.mobilerobot.dsm.parser; + +import org.json.simple.JSONObject; + +import examples.mobilerobot.models.Darkness; + +public class DarknessParser implements IEdgeAttributeParser { + + @Override + public String getAttributeName() { + return "lighting"; + } + + @Override + public String getJSONObjectKey() { + return "lighting"; + } + + @Override + public Darkness parseAttribute(JSONObject edgeObject) { + Boolean dark = (Boolean )edgeObject.getOrDefault("dark", false); + return dark ? Darkness.DARK : Darkness.LIGHT; + } + +} diff --git a/XPlanning/src/examples/mobilerobot/models/Darkness.java b/XPlanning/src/examples/mobilerobot/models/Darkness.java new file mode 100644 index 00000000..f50c4cbe --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/models/Darkness.java @@ -0,0 +1,7 @@ +package examples.mobilerobot.models; + +import examples.mobilerobot.dsm.IEdgeAttribute; + +public enum Darkness implements IEdgeAttribute { + LIGHT, DARK +} From 918fc8b493d03820d4b8a1908707178492ffa5b3 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Tue, 31 Aug 2021 13:46:38 -0400 Subject: [PATCH 08/23] Added some dark corridors and a preference for number of headlamp turnons --- XPlanning/data/mobilerobot/maps/GHC7-map0.json | 10 ++++++++++ XPlanning/data/mobilerobot/missions/mission0.json | 10 ++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/XPlanning/data/mobilerobot/maps/GHC7-map0.json b/XPlanning/data/mobilerobot/maps/GHC7-map0.json index 13d9a142..532e2ac2 100644 --- a/XPlanning/data/mobilerobot/maps/GHC7-map0.json +++ b/XPlanning/data/mobilerobot/maps/GHC7-map0.json @@ -554,6 +554,16 @@ "dark": true, "to-id": "L22", "from-id": "L18" + }, + { + "dark": true, + "to-id": "L21", + "from-id": "L17" + }, + { + "dark": true, + "to-id": "L24", + "from-id": "L27" } ] } \ No newline at end of file diff --git a/XPlanning/data/mobilerobot/missions/mission0.json b/XPlanning/data/mobilerobot/missions/mission0.json index 9c396be6..d88e09b4 100644 --- a/XPlanning/data/mobilerobot/missions/mission0.json +++ b/XPlanning/data/mobilerobot/missions/mission0.json @@ -15,7 +15,7 @@ "objective": "collision" }, { - "scaling-const": 0.2, + "scaling-const": 0.19, "min-step-value": 0.0, "max-step-value": 3.0, "objective": "intrusiveness" @@ -24,7 +24,13 @@ "objective": "energyConsumption", "scaling-const": 0.3, "min-step-value": 0.0, - "max-step-value": 2525 + "max-step-value": 4185.0 + }, + { + "objective": "headlampChange", + "scaling-const": 0.01, + "min-step-value": 0.0, + "max-step-value": 1.0 } ], "start-id": "L1" From a9b5e4b39d339106e99344b48c800237608f11cf Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Tue, 31 Aug 2021 13:47:16 -0400 Subject: [PATCH 09/23] Added headlamp domain and event --- .../dsm/MobileRobotXMDPBuilder.java | 23 +++++++++-- .../mobilerobot/metrics/HeadlampDomain.java | 25 ++++++++++++ .../mobilerobot/metrics/HeadlampEvent.java | 40 +++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java create mode 100644 XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 94ac6b80..969c4c3d 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -11,11 +11,14 @@ import examples.mobilerobot.metrics.CollisionEvent; import examples.mobilerobot.metrics.EnergyConsumptionDomain; import examples.mobilerobot.metrics.EnergyConsumptionQFunction; +import examples.mobilerobot.metrics.HeadlampDomain; +import examples.mobilerobot.metrics.HeadlampEvent; import examples.mobilerobot.metrics.IntrusiveMoveEvent; import examples.mobilerobot.metrics.IntrusivenessDomain; import examples.mobilerobot.metrics.TravelTimeDomain; import examples.mobilerobot.metrics.TravelTimeQFunction; import examples.mobilerobot.models.Area; +import examples.mobilerobot.models.Darkness; import examples.mobilerobot.models.Distance; import examples.mobilerobot.models.HeadlampActionDescription; import examples.mobilerobot.models.HeadlampState; @@ -202,8 +205,8 @@ private StateVarTuple buildGoal(LocationNode goalNode) { return goal; } - private boolean isDark(Connection conn) { - return false; + private boolean isDark(Connection conn) throws MapTopologyException{ + return conn.getConnectionAttribute(Darkness.class, "lighting") == Darkness.DARK; } private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPException, MapTopologyException { @@ -223,6 +226,9 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE if (isDark(conn)) { preMoveTo.add(moveTo, rHeadlampDef, headlampOn); } + else { + preMoveTo.add(moveTo, rHeadlampDef, headlampOff); + } } } @@ -298,12 +304,23 @@ private QSpace buildQFunctions() { // Energy Consumption EnergyConsumptionDomain energyDomain = new EnergyConsumptionDomain(rLocDef, rSpeedDef, rHeadlampDef, moveToDef); EnergyConsumptionQFunction energyQFunction = new EnergyConsumptionQFunction(energyDomain); - + + // Headlamp + HeadlampDomain headlampDomain = new HeadlampDomain(setHeadlampDef, rHeadlampDef); + HeadlampEvent headlampOn = new HeadlampEvent("headlampOn", headlampDomain, true); + HeadlampEvent headlampOff = new HeadlampEvent("headlampOff", headlampDomain, false); + EventBasedMetric headlampMetric = new EventBasedMetric<>( + HeadlampEvent.NAME, headlampDomain); + + headlampMetric.putEventValue(headlampOn, 1); + headlampMetric.putEventValue(headlampOff, 0); + NonStandardMetricQFunction headlampQFunction = new NonStandardMetricQFunction<>(headlampMetric); QSpace qSpace = new QSpace(); qSpace.addQFunction(timeQFunction); qSpace.addQFunction(collisionQFunction); qSpace.addQFunction(intrusiveQFunction); qSpace.addQFunction(energyQFunction); + qSpace.addQFunction(headlampQFunction); return qSpace; } diff --git a/XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java b/XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java new file mode 100644 index 00000000..920110f1 --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java @@ -0,0 +1,25 @@ +package examples.mobilerobot.metrics; + +import examples.mobilerobot.models.HeadlampState; +import examples.mobilerobot.models.SetHeadlampAction; +import language.domain.metrics.Transition; +import language.domain.metrics.TransitionStructure; +import language.domain.models.ActionDefinition; +import language.domain.models.StateVarDefinition; +import language.exceptions.VarNotFoundException; + +public class HeadlampDomain extends TransitionStructure { + + private StateVarDefinition mrHeadlampDef; + + public HeadlampDomain(ActionDefinition setHeadlampDef, StateVarDefinition rHeadlampDef) { + this.mrHeadlampDef = rHeadlampDef; + setActionDef(setHeadlampDef); + addDestStateVarDef(rHeadlampDef); + } + + public boolean getHealampOn(Transition transition) throws VarNotFoundException { + HeadlampState on = transition.getDestStateVarValue(HeadlampState.class, mrHeadlampDef); + return on.getValue(); + } +} diff --git a/XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java b/XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java new file mode 100644 index 00000000..ac7870d0 --- /dev/null +++ b/XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java @@ -0,0 +1,40 @@ +package examples.mobilerobot.metrics; + +import examples.mobilerobot.models.Location; +import examples.mobilerobot.models.SetHeadlampAction; +import language.domain.metrics.IEvent; +import language.domain.metrics.Transition; +import language.exceptions.AttributeNameNotFoundException; +import language.exceptions.VarNotFoundException; + +public class HeadlampEvent implements IEvent{ + + public static final String NAME = "headlampChange"; + private String mName; + private HeadlampDomain mDomain; + private boolean mOn; + + public HeadlampEvent(String name, HeadlampDomain domain, boolean on) { + mName = name; + mDomain = domain; + this.mOn = on; + } + + @Override + public String getName() { + return mName; + } + + @Override + public HeadlampDomain getTransitionStructure() { + return mDomain; + } + + @Override + public double getEventProbability(Transition transition) + throws VarNotFoundException, AttributeNameNotFoundException { + boolean headlamp = mDomain.getHealampOn(transition); + return headlamp == mOn ? 1 : 0; + } + +} From faa951e88bbad8cdd426055ed75ea9ed188114aa Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 10:14:51 -0400 Subject: [PATCH 10/23] Added rendering of dark places on the map --- .../mobilerobot/viz/MapBasedPolicyRenderer.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java b/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java index 8462df89..3934b0f6 100644 --- a/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java +++ b/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java @@ -28,6 +28,7 @@ import examples.mobilerobot.dsm.MapTopology; import examples.mobilerobot.dsm.Mission; import examples.mobilerobot.dsm.parser.AreaParser; +import examples.mobilerobot.dsm.parser.DarknessParser; import examples.mobilerobot.dsm.parser.IEdgeAttributeParser; import examples.mobilerobot.dsm.parser.INodeAttributeParser; import examples.mobilerobot.dsm.parser.MapTopologyReader; @@ -147,6 +148,10 @@ public String labelFor(Node t) { public String labelFor(Edge e) { Character dir = getAngleChar(e); String label = dir == null ? "" : ("" + dir); + Boolean headlamp = (Boolean )e.getAttribute("headlamp"); + if (headlamp != null && headlamp) { + label += "*"; + } Occlusion occ = (Occlusion) e.getAttribute("occlusion"); switch (occ) { case OCCLUDED: @@ -193,6 +198,7 @@ else if (angle >= 292 && angle <= 338) } // System.out.println( // String.format("%s -- %s: %.0f", e.getSource().getLabel(), e.getTarget().getLabel(), angle, ret)); + return ret; } @@ -215,15 +221,18 @@ public MapBasedPolicyRenderer(File mapsJsonDir, File missionJsonFile, int consol } AreaParser areaParser = new AreaParser(); OcclusionParser occlusionParser = new OcclusionParser(); + DarknessParser darknessParser = new DarknessParser(); Set> nodeAttributeParsers = new HashSet<>(); nodeAttributeParsers.add(areaParser); Set> edgeAttributeParsers = new HashSet<>(); edgeAttributeParsers.add(occlusionParser); + edgeAttributeParsers.add(darknessParser); mMapReader = new MapTopologyReader(nodeAttributeParsers, edgeAttributeParsers); // Default node/edge attribute values mDefaultNodeAttributes.put(areaParser.getAttributeName(), DEFAULT_AREA); mDefaultEdgeAttributes.put(occlusionParser.getAttributeName(), DEFAULT_OCCLUSION); + mDefaultEdgeAttributes.put(darknessParser.getAttributeName(), Darkness.LIGHT); Mission mission; try { @@ -289,6 +298,10 @@ public MapBasedPolicyRenderer(File mapsJsonDir, File missionJsonFile, int consol if (occlusion != null) { edge.setAttribute("occlusion", occlusion); } + Darkness darkness = e.getConnectionAttribute(Darkness.class, "lighting"); + if (darkness != null) { + edge.setAttribute("lighting", darkness); + } } } @@ -326,6 +339,7 @@ public void renderPolicy(String policyFile, String prefix) throws IOException { if ("moveTo".equals(actionType)) { String sourceLabel = (String) state.get("rLoc"); double speed = (Double) state.get("rSpeed"); + boolean headlamp = (Boolean )state.get("rHeadlamp"); String targetLabel = (String) ((JSONArray) action.get("params")).get(0); Edge e = m_graph.getEdgeBySourceTarget(sourceLabel, targetLabel); Node source = null; @@ -346,6 +360,7 @@ public void renderPolicy(String policyFile, String prefix) throws IOException { } e.setAttribute("dirAngle", angle); e.setAttribute("speed", speed == 0.35 ? "normal" : "fast"); + e.setAttribute("headlamp", headlamp); } } From ef1e4eba2b43475523a3036da530c9cdfb185183 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 10:15:19 -0400 Subject: [PATCH 11/23] Added action for headlamp turning on --- .../examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 969c4c3d..95c56594 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -186,6 +186,7 @@ private ActionSpace buildActionSpace(MapTopology map) throws MapTopologyExceptio ActionSpace actionSpace = new ActionSpace(); actionSpace.addActionDefinition(moveToDef); actionSpace.addActionDefinition(setSpeedDef); + actionSpace.addActionDefinition(setHeadlampDef); return actionSpace; } @@ -226,9 +227,9 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE if (isDark(conn)) { preMoveTo.add(moveTo, rHeadlampDef, headlampOn); } - else { - preMoveTo.add(moveTo, rHeadlampDef, headlampOff); - } +// else { +// preMoveTo.add(moveTo, rHeadlampDef, headlampOff); +// } } } From f90372454fe138aafa96842861d19f509e599d14 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 10:15:56 -0400 Subject: [PATCH 12/23] Added output of dark places on the map --- .../mobilerobot/demo/MobileRobotXPlanner.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java index 1a092d55..7e1953bc 100644 --- a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java +++ b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java @@ -15,6 +15,7 @@ import examples.common.XPlannerOutDirectories; import examples.mobilerobot.metrics.CollisionEvent; import examples.mobilerobot.metrics.EnergyConsumptionQFunction; +import examples.mobilerobot.metrics.HeadlampEvent; import examples.mobilerobot.metrics.IntrusiveMoveEvent; import examples.mobilerobot.metrics.TravelTimeQFunction; import examples.mobilerobot.viz.MapBasedPolicyRenderer; @@ -112,6 +113,15 @@ public static Vocabulary getVocabulary() { vocab.putNoun(EnergyConsumptionQFunction.NAME, "energy"); vocab.putVerb(EnergyConsumptionQFunction.NAME, "consumes"); vocab.putUnit(EnergyConsumptionQFunction.NAME, "milliwatt", "milliwatts"); + + + vocab.putNoun(HeadlampEvent.NAME, "headlamp change"); + vocab.putVerb(HeadlampEvent.NAME, "change to"); + vocab.putPreposition(HeadlampEvent.NAME, ""); + vocab.setOmitUnitWhenNounPresent(HeadlampEvent.NAME); + vocab.putUnit(HeadlampEvent.NAME, "time", "times"); + vocab.putCategoricalValue(HeadlampEvent.NAME, "headlampOn", "on"); + vocab.putCategoricalValue(HeadlampEvent.NAME, "headlampOff", "off"); return vocab; } @@ -121,6 +131,7 @@ public static QADecimalFormatter getQADecimalFormatter() { decimalFormatter.putDecimalFormat(CollisionEvent.NAME, "#.#"); decimalFormatter.putDecimalFormat(IntrusiveMoveEvent.NAME, "#"); decimalFormatter.putDecimalFormat(EnergyConsumptionQFunction.NAME, "#"); + decimalFormatter.putDecimalFormat(HeadlampEvent.NAME, "#"); return decimalFormatter; } @@ -129,10 +140,14 @@ public static void setVerbalizerOrdering(VerbalizerSettings verbalizerSettings) verbalizerSettings.appendQFunctionName(CollisionEvent.NAME); verbalizerSettings.appendQFunctionName(IntrusiveMoveEvent.NAME); verbalizerSettings.appendQFunctionName(EnergyConsumptionQFunction.NAME); + verbalizerSettings.appendQFunctionName(HeadlampEvent.NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.NON_INTRUSIVE_EVENT_NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.SOMEWHAT_INTRUSIVE_EVENT_NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.VERY_INTRUSIVE_EVENT_NAME); + + verbalizerSettings.appendEventName(HeadlampEvent.NAME, "headlampOn"); + verbalizerSettings.appendEventName(HeadlampEvent.NAME, "headlampOff"); } } \ No newline at end of file From 412433cfff9937c02f36ddc4ac5b0a39cac9b4e7 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 11:47:10 -0400 Subject: [PATCH 13/23] Removed change headlamp from list of qualities. --- XPlanning/data/mobilerobot/missions/mission0.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/XPlanning/data/mobilerobot/missions/mission0.json b/XPlanning/data/mobilerobot/missions/mission0.json index d88e09b4..b3a92e68 100644 --- a/XPlanning/data/mobilerobot/missions/mission0.json +++ b/XPlanning/data/mobilerobot/missions/mission0.json @@ -25,12 +25,6 @@ "scaling-const": 0.3, "min-step-value": 0.0, "max-step-value": 4185.0 - }, - { - "objective": "headlampChange", - "scaling-const": 0.01, - "min-step-value": 0.0, - "max-step-value": 1.0 } ], "start-id": "L1" From cd5fc42b54702993ee18eeeb41a625dda9c3a9d3 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 11:47:47 -0400 Subject: [PATCH 14/23] Removed change headlamp from list of qualities. --- .../mobilerobot/metrics/HeadlampDomain.java | 25 ------------ .../mobilerobot/metrics/HeadlampEvent.java | 40 ------------------- 2 files changed, 65 deletions(-) delete mode 100644 XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java delete mode 100644 XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java diff --git a/XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java b/XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java deleted file mode 100644 index 920110f1..00000000 --- a/XPlanning/src/examples/mobilerobot/metrics/HeadlampDomain.java +++ /dev/null @@ -1,25 +0,0 @@ -package examples.mobilerobot.metrics; - -import examples.mobilerobot.models.HeadlampState; -import examples.mobilerobot.models.SetHeadlampAction; -import language.domain.metrics.Transition; -import language.domain.metrics.TransitionStructure; -import language.domain.models.ActionDefinition; -import language.domain.models.StateVarDefinition; -import language.exceptions.VarNotFoundException; - -public class HeadlampDomain extends TransitionStructure { - - private StateVarDefinition mrHeadlampDef; - - public HeadlampDomain(ActionDefinition setHeadlampDef, StateVarDefinition rHeadlampDef) { - this.mrHeadlampDef = rHeadlampDef; - setActionDef(setHeadlampDef); - addDestStateVarDef(rHeadlampDef); - } - - public boolean getHealampOn(Transition transition) throws VarNotFoundException { - HeadlampState on = transition.getDestStateVarValue(HeadlampState.class, mrHeadlampDef); - return on.getValue(); - } -} diff --git a/XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java b/XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java deleted file mode 100644 index ac7870d0..00000000 --- a/XPlanning/src/examples/mobilerobot/metrics/HeadlampEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -package examples.mobilerobot.metrics; - -import examples.mobilerobot.models.Location; -import examples.mobilerobot.models.SetHeadlampAction; -import language.domain.metrics.IEvent; -import language.domain.metrics.Transition; -import language.exceptions.AttributeNameNotFoundException; -import language.exceptions.VarNotFoundException; - -public class HeadlampEvent implements IEvent{ - - public static final String NAME = "headlampChange"; - private String mName; - private HeadlampDomain mDomain; - private boolean mOn; - - public HeadlampEvent(String name, HeadlampDomain domain, boolean on) { - mName = name; - mDomain = domain; - this.mOn = on; - } - - @Override - public String getName() { - return mName; - } - - @Override - public HeadlampDomain getTransitionStructure() { - return mDomain; - } - - @Override - public double getEventProbability(Transition transition) - throws VarNotFoundException, AttributeNameNotFoundException { - boolean headlamp = mDomain.getHealampOn(transition); - return headlamp == mOn ? 1 : 0; - } - -} From cf0b50c76bb30cabf9100d4c912ab1ba1cfcd493 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 11:49:07 -0400 Subject: [PATCH 15/23] Removed change headlamp from list of qualities. --- .../mobilerobot/demo/MobileRobotXPlanner.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java index 7e1953bc..2b04b943 100644 --- a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java +++ b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java @@ -15,7 +15,6 @@ import examples.common.XPlannerOutDirectories; import examples.mobilerobot.metrics.CollisionEvent; import examples.mobilerobot.metrics.EnergyConsumptionQFunction; -import examples.mobilerobot.metrics.HeadlampEvent; import examples.mobilerobot.metrics.IntrusiveMoveEvent; import examples.mobilerobot.metrics.TravelTimeQFunction; import examples.mobilerobot.viz.MapBasedPolicyRenderer; @@ -114,14 +113,6 @@ public static Vocabulary getVocabulary() { vocab.putVerb(EnergyConsumptionQFunction.NAME, "consumes"); vocab.putUnit(EnergyConsumptionQFunction.NAME, "milliwatt", "milliwatts"); - - vocab.putNoun(HeadlampEvent.NAME, "headlamp change"); - vocab.putVerb(HeadlampEvent.NAME, "change to"); - vocab.putPreposition(HeadlampEvent.NAME, ""); - vocab.setOmitUnitWhenNounPresent(HeadlampEvent.NAME); - vocab.putUnit(HeadlampEvent.NAME, "time", "times"); - vocab.putCategoricalValue(HeadlampEvent.NAME, "headlampOn", "on"); - vocab.putCategoricalValue(HeadlampEvent.NAME, "headlampOff", "off"); return vocab; } @@ -131,7 +122,6 @@ public static QADecimalFormatter getQADecimalFormatter() { decimalFormatter.putDecimalFormat(CollisionEvent.NAME, "#.#"); decimalFormatter.putDecimalFormat(IntrusiveMoveEvent.NAME, "#"); decimalFormatter.putDecimalFormat(EnergyConsumptionQFunction.NAME, "#"); - decimalFormatter.putDecimalFormat(HeadlampEvent.NAME, "#"); return decimalFormatter; } @@ -140,14 +130,12 @@ public static void setVerbalizerOrdering(VerbalizerSettings verbalizerSettings) verbalizerSettings.appendQFunctionName(CollisionEvent.NAME); verbalizerSettings.appendQFunctionName(IntrusiveMoveEvent.NAME); verbalizerSettings.appendQFunctionName(EnergyConsumptionQFunction.NAME); - verbalizerSettings.appendQFunctionName(HeadlampEvent.NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.NON_INTRUSIVE_EVENT_NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.SOMEWHAT_INTRUSIVE_EVENT_NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.VERY_INTRUSIVE_EVENT_NAME); - verbalizerSettings.appendEventName(HeadlampEvent.NAME, "headlampOn"); - verbalizerSettings.appendEventName(HeadlampEvent.NAME, "headlampOff"); + } } \ No newline at end of file From 200ed9af0392e5d3cd7d9354cb95fb416ba28b3e Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 11:49:47 -0400 Subject: [PATCH 16/23] Added all the places for considering headlamps and moves. --- .../dsm/MobileRobotXMDPBuilder.java | 56 +++++++++---------- .../examples/mobilerobot/models/Darkness.java | 3 +- .../mobilerobot/models/MoveToAction.java | 22 +++++++- .../RobotLocationActionDescription.java | 5 +- .../models/RobotLocationFormula.java | 24 +++++--- .../prismconnector/PrismTranslatorHelper.java | 2 +- 6 files changed, 68 insertions(+), 44 deletions(-) diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 95c56594..4d48ffc8 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -11,8 +11,6 @@ import examples.mobilerobot.metrics.CollisionEvent; import examples.mobilerobot.metrics.EnergyConsumptionDomain; import examples.mobilerobot.metrics.EnergyConsumptionQFunction; -import examples.mobilerobot.metrics.HeadlampDomain; -import examples.mobilerobot.metrics.HeadlampEvent; import examples.mobilerobot.metrics.IntrusiveMoveEvent; import examples.mobilerobot.metrics.IntrusivenessDomain; import examples.mobilerobot.metrics.TravelTimeDomain; @@ -160,24 +158,29 @@ private ActionSpace buildActionSpace(MapTopology map) throws MapTopologyExceptio // Assume that all locations are reachable for (Location locDest : rLocDef.getPossibleValues()) { - MoveToAction moveTo = new MoveToAction(rLocDef.getStateVar(locDest)); - - // Derived attributes for each move action are obtained from edges in the map - LocationNode node = map.lookUpLocationNode(locDest.getId()); - Set connections = map.getConnections(node); - for (Connection conn : connections) { - Location locSrc = mLocMap.get(conn.getOtherNode(node)); - - // Distance - Distance distance = new Distance(conn.getDistance()); - moveTo.putDistanceValue(distance, rLocDef.getStateVar(locSrc)); - - // Occlusion - Occlusion occlusion = conn.getConnectionAttribute(Occlusion.class, "occlusion"); - moveTo.putOcclusionValue(occlusion, rLocDef.getStateVar(locSrc)); + for (HeadlampState hlState : rHeadlampDef.getPossibleValues()) { + MoveToAction moveTo = new MoveToAction(rLocDef.getStateVar(locDest), rHeadlampDef.getStateVar(hlState)); + + // Derived attributes for each move action are obtained from edges in the map + LocationNode node = map.lookUpLocationNode(locDest.getId()); + Set connections = map.getConnections(node); + for (Connection conn : connections) { + Location locSrc = mLocMap.get(conn.getOtherNode(node)); + + // Distance + Distance distance = new Distance(conn.getDistance()); + moveTo.putDistanceValue(distance, rLocDef.getStateVar(locSrc)); + + // Occlusion + Occlusion occlusion = conn.getConnectionAttribute(Occlusion.class, "occlusion"); + moveTo.putOcclusionValue(occlusion, rLocDef.getStateVar(locSrc)); + + Darkness darkness = conn.getConnectionAttribute(Darkness.class, "lighting"); + moveTo.putDarknessValue(darkness, rLocDef.getStateVar(locSrc)); + } + + moveTos.add(moveTo); } - - moveTos.add(moveTo); } // MoveTo action definition @@ -217,6 +220,7 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE for (MoveToAction moveTo : moveToDef.getActions()) { Location locDest = moveTo.getDestination(); + HeadlampState hlState = moveTo.getHeadlampState(); // Source location for each move action from the map LocationNode node = map.lookUpLocationNode(locDest.getId()); @@ -224,7 +228,7 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE for (Connection conn : connections) { Location locSrc = mLocMap.get(conn.getOtherNode(node)); preMoveTo.add(moveTo, rLocDef, locSrc); - if (isDark(conn)) { + if (isDark(conn) && !hlState.getValue()) { preMoveTo.add(moveTo, rHeadlampDef, headlampOn); } // else { @@ -235,7 +239,7 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE // Action description for rLoc RobotLocationActionDescription rLocActionDesc = new RobotLocationActionDescription(moveToDef, preMoveTo, - rLocDef); + rLocDef, rHeadlampDef); // PSO FactoredPSO moveToPSO = new FactoredPSO<>(moveToDef, preMoveTo); @@ -306,22 +310,12 @@ private QSpace buildQFunctions() { EnergyConsumptionDomain energyDomain = new EnergyConsumptionDomain(rLocDef, rSpeedDef, rHeadlampDef, moveToDef); EnergyConsumptionQFunction energyQFunction = new EnergyConsumptionQFunction(energyDomain); - // Headlamp - HeadlampDomain headlampDomain = new HeadlampDomain(setHeadlampDef, rHeadlampDef); - HeadlampEvent headlampOn = new HeadlampEvent("headlampOn", headlampDomain, true); - HeadlampEvent headlampOff = new HeadlampEvent("headlampOff", headlampDomain, false); - EventBasedMetric headlampMetric = new EventBasedMetric<>( - HeadlampEvent.NAME, headlampDomain); - headlampMetric.putEventValue(headlampOn, 1); - headlampMetric.putEventValue(headlampOff, 0); - NonStandardMetricQFunction headlampQFunction = new NonStandardMetricQFunction<>(headlampMetric); QSpace qSpace = new QSpace(); qSpace.addQFunction(timeQFunction); qSpace.addQFunction(collisionQFunction); qSpace.addQFunction(intrusiveQFunction); qSpace.addQFunction(energyQFunction); - qSpace.addQFunction(headlampQFunction); return qSpace; } diff --git a/XPlanning/src/examples/mobilerobot/models/Darkness.java b/XPlanning/src/examples/mobilerobot/models/Darkness.java index f50c4cbe..48faf980 100644 --- a/XPlanning/src/examples/mobilerobot/models/Darkness.java +++ b/XPlanning/src/examples/mobilerobot/models/Darkness.java @@ -1,7 +1,8 @@ package examples.mobilerobot.models; import examples.mobilerobot.dsm.IEdgeAttribute; +import language.domain.models.IActionAttribute; -public enum Darkness implements IEdgeAttribute { +public enum Darkness implements IEdgeAttribute, IActionAttribute { LIGHT, DARK } diff --git a/XPlanning/src/examples/mobilerobot/models/MoveToAction.java b/XPlanning/src/examples/mobilerobot/models/MoveToAction.java index d5d94a8b..2c56013d 100644 --- a/XPlanning/src/examples/mobilerobot/models/MoveToAction.java +++ b/XPlanning/src/examples/mobilerobot/models/MoveToAction.java @@ -28,9 +28,12 @@ public class MoveToAction implements IAction { private Action mAction; private StateVar mrLocDest; - public MoveToAction(StateVar rLocDest) { - mAction = new Action("moveTo", rLocDest.getValue()); + private StateVar mrHeadlamp; + + public MoveToAction(StateVar rLocDest, StateVar rHeadlamp) { + mAction = new Action("moveTo", rLocDest.getValue(), rHeadlamp.getValue()); mrLocDest = rLocDest; + mrHeadlamp = rHeadlamp; } public void putDistanceValue(Distance distance, StateVar rLocSrc) { @@ -44,11 +47,20 @@ public void putOcclusionValue(Occlusion occlusion, StateVar rLocSrc) { varSet.add(rLocSrc); mAction.putDerivedAttributeValue("occlusion", occlusion, varSet); } + + public void putDarknessValue(Darkness darkness, StateVar rLocSrc) { + Set> varSet = new HashSet<>(); + varSet.add(rLocSrc); + mAction.putDerivedAttributeValue("darkness", darkness, varSet); + } public Location getDestination() { return mrLocDest.getValue(); } + public HeadlampState getHeadlampState() { + return mrHeadlamp.getValue(); + } public Distance getDistance(StateVar rLocSrc) throws AttributeNameNotFoundException { Set> varSet = new HashSet<>(); varSet.add(rLocSrc); @@ -60,6 +72,12 @@ public Occlusion getOcclusion(StateVar rLocSrc) throws AttributeNameNo varSet.add(rLocSrc); return (Occlusion) getDerivedAttributeValue("occlusion", varSet); } + + public Darkness getDarkness(StateVar rLocSrc) throws AttributeNameNotFoundException { + Set> varSet = new HashSet<>(); + varSet.add(rLocSrc); + return (Darkness) getDerivedAttributeValue("darkness", varSet); + } @Override public String getName() { diff --git a/XPlanning/src/examples/mobilerobot/models/RobotLocationActionDescription.java b/XPlanning/src/examples/mobilerobot/models/RobotLocationActionDescription.java index b1fb6241..2117f891 100644 --- a/XPlanning/src/examples/mobilerobot/models/RobotLocationActionDescription.java +++ b/XPlanning/src/examples/mobilerobot/models/RobotLocationActionDescription.java @@ -34,12 +34,13 @@ public class RobotLocationActionDescription implements IActionDescription mrLocActionDesc; public RobotLocationActionDescription(ActionDefinition moveToDef, - Precondition precondition, StateVarDefinition rLocDef) { + Precondition precondition, StateVarDefinition rLocDef, StateVarDefinition rHeadlampDef) { DiscriminantClass discrClass = new DiscriminantClass(); discrClass.add(rLocDef); + discrClass.add(rHeadlampDef); EffectClass effectClass = new EffectClass(); effectClass.add(rLocDef); - RobotLocationFormula rLocFormula = new RobotLocationFormula(rLocDef); + RobotLocationFormula rLocFormula = new RobotLocationFormula(rLocDef, rHeadlampDef); mrLocActionDesc = new FormulaActionDescription<>(moveToDef, precondition, discrClass, effectClass, rLocFormula); } diff --git a/XPlanning/src/examples/mobilerobot/models/RobotLocationFormula.java b/XPlanning/src/examples/mobilerobot/models/RobotLocationFormula.java index b9b7086d..fe29d128 100644 --- a/XPlanning/src/examples/mobilerobot/models/RobotLocationFormula.java +++ b/XPlanning/src/examples/mobilerobot/models/RobotLocationFormula.java @@ -28,9 +28,11 @@ public class RobotLocationFormula implements IProbabilisticTransitionFormula mrLocDef; private EffectClass mEffectClass; // of rLoc + private StateVarDefinition mrHeadlampDef; - public RobotLocationFormula(StateVarDefinition rLocDef) { + public RobotLocationFormula(StateVarDefinition rLocDef, StateVarDefinition rHeadlampDef) { mrLocDef = rLocDef; + mrHeadlampDef = rHeadlampDef; mEffectClass = new EffectClass(); mEffectClass.add(rLocDef); @@ -39,8 +41,10 @@ public RobotLocationFormula(StateVarDefinition rLocDef) { @Override public ProbabilisticEffect formula(Discriminant discriminant, MoveToAction moveTo) throws XMDPException { Location srcLoc = discriminant.getStateVarValue(Location.class, mrLocDef); + HeadlampState srcHeadlamp = discriminant.getStateVarValue(HeadlampState.class, mrHeadlampDef); StateVar rLocSrc = mrLocDef.getStateVar(srcLoc); Occlusion occlusion = moveTo.getOcclusion(rLocSrc); + Darkness darkness = moveTo.getDarkness(rLocSrc); ProbabilisticEffect rLocProbEffect = new ProbabilisticEffect(mEffectClass); // Possible effects on rLoc Effect newLoc = new Effect(mEffectClass); @@ -48,12 +52,18 @@ public ProbabilisticEffect formula(Discriminant discriminant, MoveToAction moveT newLoc.add(mrLocDef.getStateVar(moveTo.getDestination())); oldLoc.add(rLocSrc); - if (occlusion == Occlusion.OCCLUDED) { - rLocProbEffect.put(newLoc, MOVE_PROB_OCCLUDED); - rLocProbEffect.put(oldLoc, 1 - MOVE_PROB_OCCLUDED); - } else { - rLocProbEffect.put(newLoc, MOVE_PROB_NON_OCCLUDED); - rLocProbEffect.put(oldLoc, 1 - MOVE_PROB_NON_OCCLUDED); + if (darkness == Darkness.DARK && !srcHeadlamp.getValue()) { + rLocProbEffect.put(newLoc, 0); + rLocProbEffect.put(oldLoc, 1); + } + else { + if (occlusion == Occlusion.OCCLUDED) { + rLocProbEffect.put(newLoc, MOVE_PROB_OCCLUDED); + rLocProbEffect.put(oldLoc, 1 - MOVE_PROB_OCCLUDED); + } else { + rLocProbEffect.put(newLoc, MOVE_PROB_NON_OCCLUDED); + rLocProbEffect.put(oldLoc, 1 - MOVE_PROB_NON_OCCLUDED); + } } return rLocProbEffect; diff --git a/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java b/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java index 95af01b6..90585de7 100644 --- a/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java +++ b/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java @@ -161,7 +161,7 @@ String buildHelperModule(ActionSpace actionDefs, ActionFilter helperActionFilter builder.append("\n\n"); // module helper [...] endmodule - builder.append("module helper"); + builder.append("module helper"); builder.append("\n"); if (hasGoal) { From 39fd939765afcee2509accb50a8461c20e06f2f3 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 12:03:13 -0400 Subject: [PATCH 17/23] Updated mission file to change weightings --- XPlanning/data/mobilerobot/missions/mission0.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/XPlanning/data/mobilerobot/missions/mission0.json b/XPlanning/data/mobilerobot/missions/mission0.json index d88e09b4..f8c86d7f 100644 --- a/XPlanning/data/mobilerobot/missions/mission0.json +++ b/XPlanning/data/mobilerobot/missions/mission0.json @@ -15,7 +15,7 @@ "objective": "collision" }, { - "scaling-const": 0.19, + "scaling-const": 0.2, "min-step-value": 0.0, "max-step-value": 3.0, "objective": "intrusiveness" @@ -25,12 +25,6 @@ "scaling-const": 0.3, "min-step-value": 0.0, "max-step-value": 4185.0 - }, - { - "objective": "headlampChange", - "scaling-const": 0.01, - "min-step-value": 0.0, - "max-step-value": 1.0 } ], "start-id": "L1" From 2f354d162e7a6ec9898b16be99f3dbb506ae5960 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 12:05:23 -0400 Subject: [PATCH 18/23] Updated mission file to change weightings --- XPlanning/data/mobilerobot/missions/mission0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XPlanning/data/mobilerobot/missions/mission0.json b/XPlanning/data/mobilerobot/missions/mission0.json index f8c86d7f..b3a92e68 100644 --- a/XPlanning/data/mobilerobot/missions/mission0.json +++ b/XPlanning/data/mobilerobot/missions/mission0.json @@ -15,7 +15,7 @@ "objective": "collision" }, { - "scaling-const": 0.2, + "scaling-const": 0.19, "min-step-value": 0.0, "max-step-value": 3.0, "objective": "intrusiveness" From c77e9a3626435d2806d567142bec285d0d5b2d54 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 13:30:08 -0400 Subject: [PATCH 19/23] Updated mission file to change weightings --- XPlanning/data/mobilerobot/missions/mission0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XPlanning/data/mobilerobot/missions/mission0.json b/XPlanning/data/mobilerobot/missions/mission0.json index b3a92e68..f8c86d7f 100644 --- a/XPlanning/data/mobilerobot/missions/mission0.json +++ b/XPlanning/data/mobilerobot/missions/mission0.json @@ -15,7 +15,7 @@ "objective": "collision" }, { - "scaling-const": 0.19, + "scaling-const": 0.2, "min-step-value": 0.0, "max-step-value": 3.0, "objective": "intrusiveness" From 013646b47bd41f9a0f4dcafd5ca99767be8aba56 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 13:31:42 -0400 Subject: [PATCH 20/23] Changes to line spacing. --- .../src/examples/mobilerobot/demo/MobileRobotXPlanner.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java index 2b04b943..1a092d55 100644 --- a/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java +++ b/XPlanning/src/examples/mobilerobot/demo/MobileRobotXPlanner.java @@ -112,7 +112,6 @@ public static Vocabulary getVocabulary() { vocab.putNoun(EnergyConsumptionQFunction.NAME, "energy"); vocab.putVerb(EnergyConsumptionQFunction.NAME, "consumes"); vocab.putUnit(EnergyConsumptionQFunction.NAME, "milliwatt", "milliwatts"); - return vocab; } @@ -134,8 +133,6 @@ public static void setVerbalizerOrdering(VerbalizerSettings verbalizerSettings) verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.NON_INTRUSIVE_EVENT_NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.SOMEWHAT_INTRUSIVE_EVENT_NAME); verbalizerSettings.appendEventName(IntrusiveMoveEvent.NAME, IntrusiveMoveEvent.VERY_INTRUSIVE_EVENT_NAME); - - } } \ No newline at end of file From c8c46c60d04facc2b41e6726b2427980c7f3ca10 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 13:32:45 -0400 Subject: [PATCH 21/23] Removed dead code --- .../src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java index 4d48ffc8..133abb0f 100644 --- a/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java +++ b/XPlanning/src/examples/mobilerobot/dsm/MobileRobotXMDPBuilder.java @@ -231,9 +231,6 @@ private TransitionFunction buildTransitionFunction(MapTopology map) throws XMDPE if (isDark(conn) && !hlState.getValue()) { preMoveTo.add(moveTo, rHeadlampDef, headlampOn); } -// else { -// preMoveTo.add(moveTo, rHeadlampDef, headlampOff); -// } } } From 491f0e6be07ff065364d26e3f153f6e3ee9dd8fb Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 13:35:05 -0400 Subject: [PATCH 22/23] Added headlamp to legend. --- .../src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java b/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java index 3934b0f6..3c720531 100644 --- a/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java +++ b/XPlanning/src/examples/mobilerobot/viz/MapBasedPolicyRenderer.java @@ -89,6 +89,7 @@ public static int[] getTerminalSize() throws Throwable { "| () : Sparse occlusion |", "| [] : Dense occlusion |", "| o : Dark corridor |", + "| * : Headlamp on |", "| " + (char )0x21D2 + " : 0.7m/s traversal |", "| " + (char )0x2192 + " : 0.35m/s traversal |", "+--------------------------+" }; From 082997917ee1cc7b9f74bab423fede5d91851039 Mon Sep 17 00:00:00 2001 From: Bradley Schmerl Date: Wed, 8 Sep 2021 13:36:05 -0400 Subject: [PATCH 23/23] Reverted to previous --- XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java b/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java index 90585de7..95af01b6 100644 --- a/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java +++ b/XPlanning/src/solver/prismconnector/PrismTranslatorHelper.java @@ -161,7 +161,7 @@ String buildHelperModule(ActionSpace actionDefs, ActionFilter helperActionFilter builder.append("\n\n"); // module helper [...] endmodule - builder.append("module helper"); + builder.append("module helper"); builder.append("\n"); if (hasGoal) {