simulation.py 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. # coding: utf-8
  2. import time
  3. import typing
  4. from synergine2.config import Config
  5. from synergine2.simulation import SimulationBehaviour
  6. from synergine2.simulation import Simulation
  7. from synergine2.simulation import Event
  8. from synergine2.simulation import SubjectMechanism
  9. from synergine2.simulation import SubjectBehaviour
  10. from synergine2_xyz.move.intention import MoveToIntention
  11. from synergine2_xyz.simulation import XYZSimulation
  12. class RequestMoveBehaviour(SimulationBehaviour):
  13. move_intention_class = MoveToIntention
  14. @classmethod
  15. def merge_data(cls, new_data, start_data=None):
  16. # TODO: behaviour/Thing dedicated to Gui -> Simulation ?
  17. pass # This behaviour is designed to be launch by terminal
  18. def __init__(
  19. self,
  20. config: Config,
  21. simulation: Simulation,
  22. ):
  23. super().__init__(config, simulation)
  24. self.simulation = typing.cast(XYZSimulation, self.simulation)
  25. def run(self, data):
  26. # TODO: behaviour/Thing dedicated to Gui -> Simulation ?
  27. pass # This behaviour is designed to be launch by terminal
  28. def action(self, data) -> typing.List[Event]:
  29. subject_id = data['subject_id']
  30. move_to = data['move_to']
  31. try:
  32. subject = self.simulation.subjects.index[subject_id]
  33. subject.intentions.set(self.move_intention_class(
  34. move_to,
  35. start_time=time.time(),
  36. gui_action=data['gui_action'],
  37. ))
  38. except KeyError:
  39. # TODO: log error here
  40. pass
  41. return []
  42. class MoveToMechanism(SubjectMechanism):
  43. def run(self):
  44. # TODO: Si move to: Si nouveau: a*, si bloque, a*, sinon rien
  45. # TODO: pourquoi un mechanism plutot que dans run du behaviour ? faire en sorte que lorsque on calcule,
  46. # si un subject est déjà passé par là et qu'il va au même endroit, ne pas recalculer.
  47. try:
  48. # TODO: MoveToIntention doit être configurable
  49. move = self.subject.intentions.get(MoveToIntention)
  50. move = typing.cast(MoveToIntention, move)
  51. new_path = None
  52. if not move.path:
  53. move.path = self.simulation.physics.found_path(
  54. start=self.subject.position,
  55. end=move.move_to,
  56. subject=self.subject,
  57. )
  58. # Note: We are in process, move change will be lost
  59. new_path = move.path
  60. # move.path = []
  61. # new_path = move.path
  62. # for i in range(20):
  63. # move.path.append((
  64. # self.subject.position[0],
  65. # self.subject.position[1] + i,
  66. # ))
  67. next_move = move.path[move.path_progression + 1]
  68. # TODO: fin de path
  69. if not self.simulation.is_possible_position(next_move):
  70. # TODO: refaire le path
  71. new_path = ['...']
  72. return {
  73. 'new_path': new_path,
  74. 'last_intention_time': move.last_intention_time,
  75. 'just_reach': move.just_reach,
  76. 'initial': move.initial,
  77. 'gui_action': move.gui_action,
  78. }
  79. except IndexError: # TODO: Specialize ? No movement left
  80. return {
  81. 'finished': True,
  82. }
  83. except KeyError: # TODO: Specialize ? No MoveIntention
  84. return None
  85. class FinishMoveEvent(Event):
  86. def __init__(
  87. self,
  88. subject_id: int,
  89. from_position: typing.Tuple[int, int],
  90. to_position: typing.Tuple[int, int],
  91. gui_action: typing.Any,
  92. move_duration: float=0.0,
  93. *args,
  94. **kwargs
  95. ):
  96. super().__init__(*args, **kwargs)
  97. self.subject_id = subject_id
  98. self.from_position = from_position
  99. self.to_position = to_position
  100. self.gui_action = gui_action
  101. self.move_duration = move_duration
  102. def repr_debug(self) -> str:
  103. return '{}: subject_id:{}, from_position:{} to_position: {}'.format(
  104. type(self).__name__,
  105. self.subject_id,
  106. self.from_position,
  107. self.to_position,
  108. )
  109. class StartMoveEvent(Event):
  110. def __init__(
  111. self,
  112. subject_id: int,
  113. from_position: typing.Tuple[int, int],
  114. to_position: typing.Tuple[int, int],
  115. gui_action: typing.Any,
  116. move_duration: float=0.0,
  117. *args,
  118. **kwargs
  119. ):
  120. super().__init__(*args, **kwargs)
  121. self.subject_id = subject_id
  122. self.from_position = from_position
  123. self.to_position = to_position
  124. self.gui_action = gui_action
  125. self.move_duration = move_duration
  126. def repr_debug(self) -> str:
  127. return '{}: subject_id:{}, from_position:{} to_position: {}'.format(
  128. type(self).__name__,
  129. self.subject_id,
  130. self.from_position,
  131. self.to_position,
  132. )
  133. class MoveToBehaviour(SubjectBehaviour):
  134. move_to_mechanism = MoveToMechanism
  135. use = [move_to_mechanism]
  136. def run(self, data):
  137. move_to_data = data[self.move_to_mechanism]
  138. if move_to_data:
  139. if move_to_data.get('finished'):
  140. return move_to_data
  141. if self._can_move_to_next_step(move_to_data):
  142. move_to_data['reach_next'] = True
  143. return move_to_data
  144. if self._is_fresh_new_step(move_to_data):
  145. move_to_data['reach_next'] = False
  146. return move_to_data
  147. return False
  148. def _can_move_to_next_step(self, move_to_data: dict) -> bool:
  149. raise NotImplementedError()
  150. def _is_fresh_new_step(self, move_to_data: dict) -> bool:
  151. return move_to_data['just_reach'] or move_to_data['initial']
  152. def finalize_event(self, move_to_data: dict, event: Event) -> None:
  153. pass
  154. def action(self, data) -> [Event]:
  155. # TODO: MoveToIntention doit être configurable
  156. try:
  157. if data.get('finished'):
  158. self.subject.intentions.remove(MoveToIntention)
  159. return []
  160. move = self.subject.intentions.get(MoveToIntention)
  161. except KeyError: # TODO: Specialize exception
  162. # Action don't exist anymore
  163. return []
  164. move = typing.cast(MoveToIntention, move)
  165. new_path = data['new_path']
  166. if new_path:
  167. move.path = new_path
  168. move.path_progression = -1
  169. previous_position = self.subject.position
  170. new_position = move.path[move.path_progression + 1]
  171. if data['reach_next']:
  172. # TODO: fin de path
  173. move.path_progression += 1
  174. self.subject.position = new_position
  175. move.last_intention_time = time.time()
  176. move.just_reach = True
  177. event = FinishMoveEvent(
  178. self.subject.id,
  179. previous_position,
  180. new_position,
  181. gui_action=move.gui_action,
  182. )
  183. else:
  184. move.just_reach = False
  185. event = StartMoveEvent(
  186. self.subject.id,
  187. previous_position,
  188. new_position,
  189. gui_action=move.gui_action,
  190. )
  191. move.initial = False
  192. # Note: Need to explicitly set to update shared data
  193. self.subject.intentions.set(move)
  194. self.finalize_event(data, event)
  195. return [event]