|
|
@@ -12,11 +12,12 @@ from util import prompt
|
|
|
from transformation.cloner import clone_od
|
|
|
from api.od import ODAPI
|
|
|
|
|
|
+from examples.semantics.operational.simulator import Simulator, RandomDecisionMaker, InteractiveDecisionMaker, filter_valid_actions
|
|
|
+
|
|
|
state = DevState()
|
|
|
|
|
|
-print("Loading meta-meta-model...")
|
|
|
+# Load meta-meta-model
|
|
|
scd_mmm = bootstrap_scd(state)
|
|
|
-print("Done")
|
|
|
|
|
|
# Design meta-model
|
|
|
woods_mm_cs = """
|
|
|
@@ -51,9 +52,8 @@ woods_mm = parser.parse_od(
|
|
|
m_text=woods_mm_cs,
|
|
|
mm=scd_mmm)
|
|
|
|
|
|
-print("MM valid?")
|
|
|
conf = Conformance(state, woods_mm, scd_mmm)
|
|
|
-print(render_conformance_check_result(conf.check_nominal()))
|
|
|
+print("MM ...", render_conformance_check_result(conf.check_nominal()))
|
|
|
|
|
|
# Runtime meta-model
|
|
|
woods_rt_mm_cs = woods_mm_cs + """
|
|
|
@@ -142,9 +142,8 @@ woods_rt_mm = parser.parse_od(
|
|
|
m_text=woods_rt_mm_cs,
|
|
|
mm=scd_mmm)
|
|
|
|
|
|
-print("RT-MM valid?")
|
|
|
conf = Conformance(state, woods_rt_mm, scd_mmm)
|
|
|
-print(render_conformance_check_result(conf.check_nominal()))
|
|
|
+print("RT-MM ...", render_conformance_check_result(conf.check_nominal()))
|
|
|
|
|
|
# print("--------------")
|
|
|
# print(indent(
|
|
|
@@ -180,9 +179,8 @@ woods_m = parser.parse_od(
|
|
|
m_text=woods_m_cs,
|
|
|
mm=woods_mm)
|
|
|
|
|
|
-print("M valid?")
|
|
|
conf = Conformance(state, woods_m, woods_mm)
|
|
|
-print(render_conformance_check_result(conf.check_nominal()))
|
|
|
+print("M ...", render_conformance_check_result(conf.check_nominal()))
|
|
|
|
|
|
# Our runtime model - the part that changes with every execution step
|
|
|
woods_rt_initial_m_cs = woods_m_cs + """
|
|
|
@@ -218,9 +216,8 @@ woods_rt_m = parser.parse_od(
|
|
|
m_text=woods_rt_initial_m_cs,
|
|
|
mm=woods_rt_mm)
|
|
|
|
|
|
-print("RT-M valid?")
|
|
|
conf = Conformance(state, woods_rt_m, woods_rt_mm)
|
|
|
-print(render_conformance_check_result(conf.check_nominal()))
|
|
|
+print("RT-M ...", render_conformance_check_result(conf.check_nominal()))
|
|
|
|
|
|
|
|
|
# Helpers
|
|
|
@@ -251,7 +248,7 @@ def advance_time(od):
|
|
|
if od.get_slot_value(bear_state, "dead"):
|
|
|
continue # bear already dead
|
|
|
old_hunger = od.get_slot_value(bear_state, "hunger")
|
|
|
- new_hunger = min(old_hunger + 5, 100)
|
|
|
+ new_hunger = min(old_hunger + 10, 100)
|
|
|
od.set_slot_value(bear_state, "hunger", new_hunger)
|
|
|
bear = od.get_target(od.get_outgoing(bear_state, "of")[0])
|
|
|
bear_name = od.get_name(bear)
|
|
|
@@ -277,48 +274,35 @@ def attack(od, animal_name: str, man_name: str):
|
|
|
msgs.append(f"{animal_name} is now attacking {man_name}")
|
|
|
return msgs
|
|
|
|
|
|
-def get_actions(od):
|
|
|
- # can always advance time:
|
|
|
- actions = { "advance time": advance_time }
|
|
|
-
|
|
|
- # who can attack whom?
|
|
|
- for _, afraid_link in od.get_all_instances("afraidOf"):
|
|
|
- man = od.get_source(afraid_link)
|
|
|
- animal = od.get_target(afraid_link)
|
|
|
- animal_name = od.get_name(animal)
|
|
|
- man_name = od.get_name(man)
|
|
|
- man_state = state_of(od, man)
|
|
|
- animal_state = state_of(od, animal)
|
|
|
- descr = f"{animal_name} ({od.get_type_name(animal)}) attacks {man_name} ({od.get_type_name(man)})"
|
|
|
- actions[descr] = functools.partial(attack, animal_name=animal_name, man_name=man_name)
|
|
|
-
|
|
|
- return { action_descr: functools.partial(exec_pure, action, od) for action_descr, action in actions.items() }
|
|
|
-
|
|
|
-# Copy model before modifying it
|
|
|
-def exec_pure(action, od):
|
|
|
- cloned_rt_m = clone_od(state, od.m, od.mm)
|
|
|
- new_od = ODAPI(state, cloned_rt_m, od.mm)
|
|
|
- msgs = action(new_od)
|
|
|
- return (new_od, msgs)
|
|
|
-
|
|
|
-def filter_actions(actions):
|
|
|
- result = {}
|
|
|
- def make_tuple(new_od, msgs):
|
|
|
+
|
|
|
+def get_all_actions(od):
|
|
|
+ def _get_actions(od):
|
|
|
+ # can always advance time:
|
|
|
+ yield ("advance time", advance_time)
|
|
|
+
|
|
|
+ # who can attack whom?
|
|
|
+ for _, afraid_link in od.get_all_instances("afraidOf"):
|
|
|
+ man = od.get_source(afraid_link)
|
|
|
+ animal = od.get_target(afraid_link)
|
|
|
+ animal_name = od.get_name(animal)
|
|
|
+ man_name = od.get_name(man)
|
|
|
+ man_state = state_of(od, man)
|
|
|
+ animal_state = state_of(od, animal)
|
|
|
+ descr = f"{animal_name} ({od.get_type_name(animal)}) attacks {man_name} ({od.get_type_name(man)})"
|
|
|
+ yield (descr, functools.partial(attack, animal_name=animal_name, man_name=man_name))
|
|
|
+
|
|
|
+ # Copy model before modifying it
|
|
|
+ def exec_pure(action, od):
|
|
|
+ cloned_rt_m = clone_od(state, od.m, od.mm)
|
|
|
+ new_od = ODAPI(state, cloned_rt_m, od.mm)
|
|
|
+ msgs = action(new_od)
|
|
|
return (new_od, msgs)
|
|
|
- for name, callback in actions.items():
|
|
|
- print(f"attempt '{name}' ...", end='\r')
|
|
|
- (new_od, msgs) = callback()
|
|
|
- conf = Conformance(state, new_od.m, new_od.mm)
|
|
|
- errors = conf.check_nominal()
|
|
|
- # erase current line:
|
|
|
- print(" ", end='\r')
|
|
|
- if len(errors) == 0:
|
|
|
- # updated RT-M is conform, we have a valid action:
|
|
|
- yield (name, functools.partial(make_tuple, new_od, msgs))
|
|
|
-
|
|
|
-def unfilter_actions(actions, od):
|
|
|
- for name, callback in actions.items():
|
|
|
- yield (name, callback)
|
|
|
+
|
|
|
+ for descr, action in _get_actions(od):
|
|
|
+ yield (descr, functools.partial(exec_pure, action, od))
|
|
|
+
|
|
|
+def get_valid_actions(od):
|
|
|
+ return filter_valid_actions(get_all_actions(od))
|
|
|
|
|
|
def render_woods(od):
|
|
|
txt = ""
|
|
|
@@ -352,53 +336,33 @@ def render_woods(od):
|
|
|
txt += f" 👨 {od.get_name(man)} ({render_dead(man_state)}) {render_attacking(man_state)}{being_attacked}\n"
|
|
|
return txt
|
|
|
|
|
|
-od = ODAPI(state, woods_rt_m, woods_rt_mm)
|
|
|
-
|
|
|
-RANDOM_SEED = 0
|
|
|
-
|
|
|
-r = random.Random(RANDOM_SEED)
|
|
|
-
|
|
|
-def random_choice(options):
|
|
|
- arr = [action for descr, action in options]
|
|
|
- i = math.floor(r.random()*len(arr))
|
|
|
- return arr[i]
|
|
|
|
|
|
def termination_condition(od):
|
|
|
_, time = get_time(od)
|
|
|
- return time >= 10 # stop after 10 steps
|
|
|
+ if time >= 10:
|
|
|
+ return "Took too long"
|
|
|
+
|
|
|
+ # End simulation when 2 animals are dead
|
|
|
+ who_is_dead = []
|
|
|
+ for _, animal_state in od.get_all_instances("AnimalState"):
|
|
|
+ if od.get_slot_value(animal_state, "dead"):
|
|
|
+ animal_name = od.get_name(animal_of(od, animal_state))
|
|
|
+ who_is_dead.append(animal_name)
|
|
|
+ if len(who_is_dead) >= 2:
|
|
|
+ return f"{' and '.join(who_is_dead)} are dead"
|
|
|
+
|
|
|
+sim = Simulator(
|
|
|
+ action_generator=get_valid_actions,
|
|
|
+ # action_generator=get_actions,
|
|
|
+ decision_maker=RandomDecisionMaker(seed=0),
|
|
|
+ # decision_maker=InteractiveDecisionMaker(),
|
|
|
+ termination_condition=termination_condition,
|
|
|
+ check_conformance=True,
|
|
|
+ verbose=True,
|
|
|
+ renderer=render_woods,
|
|
|
+)
|
|
|
|
|
|
-print(f"Using random seed: {RANDOM_SEED} (only applicable to random simulation)")
|
|
|
-
|
|
|
-while True:
|
|
|
- print("--------------")
|
|
|
- print(indent(render_woods(od), 4))
|
|
|
- print("--------------")
|
|
|
-
|
|
|
- if termination_condition(od):
|
|
|
- print("Termination condition satisfied. Quit.")
|
|
|
- break
|
|
|
-
|
|
|
- # print(indent(
|
|
|
- # renderer.render_od(state,
|
|
|
- # m_id=od.m,
|
|
|
- # mm_id=od.mm),
|
|
|
- # 4))
|
|
|
-
|
|
|
- # 1. Only 'valid' actions or all actions?
|
|
|
- # actions = unfilter_actions(get_actions(od), od)
|
|
|
- actions = filter_actions(get_actions(od))
|
|
|
-
|
|
|
- # 2. Manual or random selection?
|
|
|
- # action = prompt.choose("Select action:", actions)
|
|
|
- action = random_choice(actions)
|
|
|
-
|
|
|
- if action == None:
|
|
|
- print("No enabled actions. Quit.")
|
|
|
- break
|
|
|
-
|
|
|
- (od, msgs) = action()
|
|
|
- print(indent('\n'.join(f"▸ {msg}" for msg in msgs), 2))
|
|
|
+od = ODAPI(state, woods_rt_m, woods_rt_mm)
|
|
|
|
|
|
- print()
|
|
|
- conf = Conformance(state, od.m, od.mm)
|
|
|
- print(render_conformance_check_result(conf.check_nominal()))
|
|
|
+print()
|
|
|
+sim.run(od)
|