physics.py 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # coding: utf-8
  2. import typing
  3. from dijkstar import Graph
  4. from dijkstar import find_path
  5. from synergine2.config import Config
  6. from synergine2.share import shared
  7. from synergine2_xyz.map import TMXMap, XYZTile
  8. from synergine2_xyz.subjects import XYZSubject
  9. from synergine2_xyz.utils import get_line_xy_path
  10. from synergine2_xyz.xyz import get_neighbor_positions
  11. class MoveCostComputer(object):
  12. def __init__(
  13. self,
  14. config: Config,
  15. ) -> None:
  16. self.config = config
  17. def for_subject(self, subject: XYZSubject):
  18. # TODO: Verifier ce que sont les parametres pour les nommer correctement
  19. def move_cost_func(previous_node: str, next_node: str, tile: XYZTile, unknown):
  20. return self.compute_move_cost(subject, tile, previous_node, next_node, unknown)
  21. return move_cost_func
  22. def compute_move_cost(
  23. self,
  24. subject: XYZSubject,
  25. tile: XYZTile,
  26. previous_node: str,
  27. next_node: str,
  28. unknown,
  29. ) -> float:
  30. return 1.0
  31. class Matrixes(object):
  32. _matrixes = shared.create('matrixes', value=lambda: {}) # type: typing.Dict[str, typing.List[typing.List[tuple]]]
  33. def __init__(self):
  34. self._value_structures = {} # type: typing.List[str]
  35. def initialize_empty_matrix(
  36. self,
  37. name: str,
  38. matrix_width: int,
  39. matrix_height: int,
  40. value_structure: typing.List[str],
  41. ) -> None:
  42. self._matrixes[name] = []
  43. self._value_structures[name] = value_structure
  44. for y in range(matrix_height):
  45. x_list = []
  46. for x in range(matrix_width):
  47. x_list.append(tuple([0.0] * len(value_structure)))
  48. self._matrixes[name].append(x_list)
  49. def get_matrix(self, name: str) -> typing.List[typing.List[tuple]]:
  50. return self._matrixes[name]
  51. def update_matrix(self, name: str, x: int, y: int, value: tuple) -> None:
  52. matrix = self.get_matrix(name)
  53. matrix[y][x] = value
  54. # TODO: Test if working and needed ? This is not perf friendly ...
  55. # Force shared data update
  56. self._matrixes = dict(self._matrixes)
  57. def get_path_positions(
  58. self,
  59. from_: typing.Tuple[int, int],
  60. to: typing.Tuple[int, int],
  61. ) -> typing.List[typing.Tuple[int, int]]:
  62. return get_line_xy_path(from_, to)
  63. def get_values_for_path(self, name: str, path_positions: typing.List[typing.Tuple[int, int]]):
  64. values = []
  65. matrix = self.get_matrix(name)
  66. for path_position in path_positions:
  67. x, y = path_position
  68. values.append(matrix[y][x])
  69. return values
  70. def get_value(self, matrix_name: str, x: int, y: int, value_name: str) -> float:
  71. matrix = self.get_matrix(matrix_name)
  72. values = matrix[y][x]
  73. value_position = self._value_structures[matrix_name].index(value_name)
  74. return values[value_position]
  75. class Physics(object):
  76. visibility_matrix = Matrixes
  77. move_cost_computer_class = MoveCostComputer
  78. def __init__(
  79. self,
  80. config: Config,
  81. ) -> None:
  82. self.config = config
  83. self.graph = Graph() # Graph of possible movements for dijkstar algorithm lib
  84. self.visibility_matrix = self.visibility_matrix()
  85. self.move_cost_computer = self.move_cost_computer_class(config)
  86. def load(self) -> None:
  87. pass
  88. def position_to_key(self, position: typing.Tuple[int, int]) -> str:
  89. return '{}.{}'.format(*position)
  90. def found_path(
  91. self,
  92. start: typing.Tuple[int, int],
  93. end: typing.Tuple[int, int],
  94. subject: XYZSubject,
  95. ) -> typing.List[typing.Tuple[int, int]]:
  96. start_key = self.position_to_key(start)
  97. end_key = self.position_to_key(end)
  98. found_path = find_path(self.graph, start_key, end_key, cost_func=self.move_cost_computer.for_subject(subject))
  99. regular_path = []
  100. for position in found_path[0][1:]:
  101. x, y = map(int, position.split('.'))
  102. regular_path.append((x, y))
  103. return regular_path
  104. class TMXPhysics(Physics):
  105. tmx_map_class = TMXMap
  106. def __init__(
  107. self,
  108. config: Config,
  109. map_file_path: str,
  110. ) -> None:
  111. super().__init__(config)
  112. self.map_file_path = map_file_path
  113. self.tmx_map = self.tmx_map_class(map_file_path)
  114. def load(self) -> None:
  115. self.load_graph_from_map(self.map_file_path)
  116. def load_graph_from_map(self, map_file_path: str) -> None:
  117. # TODO: tmx_map contient tout en cache, faire le dessous en exploitant tmx_map.
  118. for y in range(self.tmx_map.height):
  119. for x in range(self.tmx_map.width):
  120. position = self.position_to_key((x, y))
  121. for neighbor_position in get_neighbor_positions((x, y)):
  122. neighbor = '{}.{}'.format(*neighbor_position)
  123. neighbor_x, neighbor_y = neighbor_position
  124. if neighbor_x > self.tmx_map.width-1 or neighbor_x < 0:
  125. continue
  126. if neighbor_y > self.tmx_map.height-1 or neighbor_y < 0:
  127. continue
  128. # Note: movement consider future tile properties
  129. to_tile = self.tmx_map.layer_tiles('terrain')[neighbor]
  130. # Note: Voir https://pypi.python.org/pypi/Dijkstar/2.2
  131. self.graph.add_edge(position, neighbor, to_tile)