move.py 7.2KB

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