opsem_python.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. ### Operational Semantics - coded in Python ###
  2. import functools
  3. from examples.semantics.operational.simulator import make_actions_pure, filter_valid_actions
  4. from examples.woods.common import *
  5. # Action: Time advances, whoever is being attacked dies, bears become hungrier
  6. def action_advance_time(od):
  7. msgs = []
  8. clock, old_time = get_time(od)
  9. new_time = old_time + 1
  10. od.set_slot_value(clock, "time", new_time)
  11. for _, attacking_link in od.get_all_instances("attacking"):
  12. man_state = od.get_target(attacking_link)
  13. animal_state = od.get_source(attacking_link)
  14. if od.get_type_name(animal_state) == "BearState":
  15. od.set_slot_value(animal_state, "hunger", max(od.get_slot_value(animal_state, "hunger") - 50, 0))
  16. od.set_slot_value(man_state, "dead", True)
  17. od.delete(attacking_link)
  18. msgs.append(f"{od.get_name(animal_of(od, animal_state))} kills {od.get_name(animal_of(od, man_state))}.")
  19. for _, bear_state in od.get_all_instances("BearState"):
  20. if od.get_slot_value(bear_state, "dead"):
  21. continue # bear already dead
  22. old_hunger = od.get_slot_value(bear_state, "hunger")
  23. new_hunger = min(old_hunger + 10, 100)
  24. od.set_slot_value(bear_state, "hunger", new_hunger)
  25. bear = od.get_target(od.get_outgoing(bear_state, "of")[0])
  26. bear_name = od.get_name(bear)
  27. if new_hunger == 100:
  28. od.set_slot_value(bear_state, "dead", True)
  29. msgs.append(f"Bear {bear_name} dies of hunger.")
  30. else:
  31. msgs.append(f"Bear {bear_name}'s hunger level is now {new_hunger}.")
  32. return msgs
  33. # Action: Animal attacks Man
  34. # Note: We must use the names of the objects as parameters, because when cloning, the IDs of objects change!
  35. def action_attack(od, animal_name: str, man_name: str):
  36. msgs = []
  37. animal = od.get(animal_name)
  38. man = od.get(man_name)
  39. animal_state = state_of(od, animal)
  40. man_state = state_of(od, man)
  41. attack_link = od.create_link(None, # auto-generate link name
  42. "attacking", animal_state, man_state)
  43. _, clock = od.get_all_instances("Clock")[0]
  44. current_time = od.get_slot_value(clock, "time")
  45. od.set_slot_value(attack_link, "starttime", current_time)
  46. msgs.append(f"{animal_name} is now attacking {man_name}")
  47. return msgs
  48. # Get all actions that can be performed (including those that bring us to a non-conforming state)
  49. def get_all_actions(od):
  50. def _generate_actions(od):
  51. # can always advance time:
  52. yield ("advance time", action_advance_time)
  53. # if A is afraid of B, then B can attack A:
  54. for _, afraid_link in od.get_all_instances("afraidOf"):
  55. man = od.get_source(afraid_link)
  56. animal = od.get_target(afraid_link)
  57. animal_name = od.get_name(animal)
  58. man_name = od.get_name(man)
  59. man_state = state_of(od, man)
  60. animal_state = state_of(od, animal)
  61. descr = f"{animal_name} ({od.get_type_name(animal)}) attacks {man_name} ({od.get_type_name(man)})"
  62. yield (descr, functools.partial(action_attack, animal_name=animal_name, man_name=man_name))
  63. return make_actions_pure(_generate_actions(od), od)
  64. # Only get those actions that bring us to a conforming state
  65. def get_valid_actions(od):
  66. return filter_valid_actions(get_all_actions(od))