RedHelper.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import random
  2. """ RedHelper
  3. 将关卡red文件转化为json文件
  4. """
  5. class RedHelper:
  6. """ 初始化 """
  7. def __init__(self):
  8. print("init helper...")
  9. """
  10. __nScrewTypes
  11. 模拟C++算法分配钉子的时候使用
  12. 钉子的种类数
  13. 如果screwconfig里面总共有10种钉子
  14. 我们挑其中的__nScrewTypes种
  15. 往挑好、摆好的盘子上放
  16. """
  17. self.__nScrewTypes = 3
  18. print(f"钉子总种类{self.__nScrewTypes}")
  19. """ 从red文件的dict中获取使用的模板ID """
  20. def get_template_IDs(self, levelData: dict) -> list:
  21. # ['nodeGraph']['children'][0]['children'] -> [模板, 模板, 模板...]
  22. # [0] 对应的是 red工程文件中的"元素" 目前只有一个
  23. # 关卡red文件格式:
  24. # 关卡1.red
  25. # --CCLayer
  26. # ------元素
  27. # ----------模板号
  28. # ----------模板号
  29. # ----------模板号
  30. template_list = levelData['nodeGraph']['children'][0]['children']
  31. template_id_list = [template['displayName'] for template in template_list]
  32. return template_id_list
  33. """ 从模板中获取盘子配置 """
  34. def get_plate_configs(self, template: dict) -> list:
  35. # ['nodeGraph']['children'][0]['children'] -> [盘子, 盘子, 盘子]
  36. # [0]对应CCNode 目前只有一个
  37. # 关卡模板文件格式:
  38. # 模板1.red
  39. # --CCLayer
  40. # ------CCNode
  41. # ----------盘子号
  42. # ----------盘子号
  43. # ----------盘子号
  44. plates = template['nodeGraph']['children'][0]['children']
  45. plate_configs = []
  46. for plate in plates:
  47. properties = plate['properties']
  48. plate_data = {'plateId': int(plate['displayName'])}
  49. for plate_property in properties:
  50. property_name = plate_property['name']
  51. if property_name == 'position':
  52. plate_data['position'] = [round(float(plate_property['value'][i]), 1) for i in range(2)]
  53. elif property_name == 'scale':
  54. plate_data['scale'] = round(float(plate_property['value']), 1)
  55. elif property_name == 'rotation':
  56. plate_data['rotate'] = round(float(plate_property['value']), 1)
  57. elif property_name == 'zorder':
  58. plate_data['zorder'] = int(plate_property['value'], 1)
  59. if 'position' not in plate_data:
  60. plate_data['position'] = [0, 0]
  61. # 目前大部分文件中没有后面这几个参数 除非在redream工程中设置过
  62. if 'scale' not in plate_data:
  63. plate_data['scale'] = 1.0
  64. if 'rotate' not in plate_data:
  65. plate_data['rotate'] = 0.0
  66. if 'zorder' not in plate_data:
  67. plate_data['zorder'] = 1
  68. plate_configs.append(plate_data)
  69. return plate_configs
  70. """ 获得盘子能容纳的最大的钉子个数 """
  71. def get_max_screws_of_plate(self, plate_condition: dict) -> int:
  72. # ['nodeGraph']['children'][0]['children'].size()
  73. max_screws_of_plate = len(plate_condition['nodeGraph']['children'][0]['children'])
  74. return max_screws_of_plate
  75. """
  76. @brief 随机选取盘子情况 返回对应情况的id
  77. @param max_screw_of_plate: 盘子上钉子最大个数
  78. @return 盘子配置情况的下标(从0开始)
  79. """
  80. def generate_condition_id(self, max_screws_of_plate) -> int:
  81. condition_id = random.randint(1, max_screws_of_plate)
  82. # -1 是因为盘子的情况配置下标从0开始
  83. return condition_id - 1
  84. """
  85. @brief 调整选择的盘子情况 结果要满足三消的个数
  86. @param plate_conditions: 被选择的盘子的数据列表
  87. @param conditions_selected: 被选择的盘子情况的下标列表
  88. @return 调整后的被选择的盘子情况的下标列表
  89. """
  90. def adjust_conditions_selected(self, plate_conditions: list, condition_selected: list) -> list:
  91. # print(plate_conditions)
  92. print(condition_selected)
  93. n_holes_available_of_plates = []
  94. for condition in plate_conditions:
  95. # print(condition)
  96. n_hole_available_of_plate = []
  97. for c in condition['nodeGraph']['children'][0]['children']:
  98. # print(len(c['children']))
  99. n_hole_available_of_plate.append(len(c['children']))
  100. n_holes_available_of_plates.append(n_hole_available_of_plate)
  101. # print(n_holes_available_of_plates)
  102. # 记录每个盘子包含多少个钉子洞
  103. n_holes_used_in_each_condition_selected = []
  104. for i in range(len(condition_selected)):
  105. n_holes_used_in_each_condition_selected.append(n_holes_available_of_plates[i][condition_selected[i]])
  106. total_holes_used = sum(n_holes_used_in_each_condition_selected)
  107. # print(total_holes_used)
  108. # 总钉子个数不满足三消条件 需要调整
  109. n_redundant_holes = total_holes_used % 3
  110. if n_redundant_holes != 0:
  111. # 缺少 / 多余的hole的个数
  112. holes_need_to_adjust = [3 - n_redundant_holes, n_redundant_holes]
  113. holes_delta = []
  114. for i in range(len(n_holes_used_in_each_condition_selected)):
  115. holes_delta.append([
  116. hole_number - n_holes_used_in_each_condition_selected[i] for hole_number in
  117. n_holes_available_of_plates[i]
  118. ])
  119. # print(holes_delta)
  120. # 减少或增加到3的倍数
  121. # 先简单的每个组plate找一遍 大概率可以只调整一个plate的情况就能满足3的倍数
  122. # TODO: 以后有复杂情况再说
  123. adjust_complete = False
  124. for delta in holes_need_to_adjust:
  125. for i, condition in enumerate(holes_delta):
  126. for j, number in enumerate(condition):
  127. if number == delta:
  128. condition_selected[i] = j
  129. adjust_complete = True
  130. break
  131. if adjust_complete:
  132. break
  133. if adjust_complete:
  134. break
  135. return condition_selected
  136. # """
  137. # @brief 看看是否每种钉子的出现次数都是3的倍数 不满足要调整
  138. # @param 每个盘子上分配的钉子种类的列表
  139. # @return 调整后的每个盘子上分配的钉子种类的列表
  140. # """
  141. #
  142. # def adjust_screws_distribution(self, screw_types_list: list) -> list:
  143. # screw_type_to_n_dict = {}
  144. # for screws_on_plate in screw_types_list:
  145. # for screw_id in screws_on_plate:
  146. # if screw_id in screw_type_to_n_dict:
  147. # screw_type_to_n_dict[screw_id] += 1
  148. # else:
  149. # screw_type_to_n_dict[screw_id] = 1
  150. #
  151. # # 个数需要调整的钉子的id
  152. # screw_need_to_adjust = {}
  153. # for screw_id in screw_type_to_n_dict:
  154. # n_delta = screw_type_to_n_dict[screw_id] % 3
  155. # if n_delta != 0:
  156. # screw_need_to_adjust[screw_id] = n_delta
  157. #
  158. # # 让所有需要调整的钉子都先向下减少到3的倍数
  159. # # 多出来的空位数肯定是3的倍数
  160. # # 再往里随机分配就好
  161. # for screw_id in screw_need_to_adjust:
  162. """
  163. @brief 从盘子情况数据中获得对应情况所能容纳的钉子个数的列表
  164. @param plate_conditions: 盘子的情况的列表
  165. @param condition_selected: 选择的盘子情况的下标
  166. @return 对应的盘子情况所能容纳钉子的个数的列表
  167. """
  168. def get_n_screw_of_conditions(self, plate_conditions: list, condition_selected: list) -> list:
  169. n_screw_of_conditions = []
  170. for i, condition_id in enumerate(condition_selected):
  171. n_screw_of_conditions.append(
  172. len(plate_conditions[i]['nodeGraph']['children'][0]['children'][condition_id]['children'])
  173. )
  174. return n_screw_of_conditions
  175. """
  176. @brief 根据盘子情况和随机挑选的情况生成盘子上摆放的钉子类型
  177. @param condition_selected: 随机挑选出的盘子的情况的下标
  178. @param n_screws_of_conditions: 被选中的盘子情况能容纳的钉子个数的列表
  179. @return 为被选中的盘子生成的钉子种类的ID列表
  180. """
  181. def generate_screw_types_for_plates(self, condition_selected: list, n_screws_of_conditions: list) -> list:
  182. total_holes = sum(n_screws_of_conditions)
  183. # 总分组数
  184. total_groups = int(total_holes / self.__nScrewTypes)
  185. # 随机选择钉子的组数
  186. screw_id_to_group_n_dict = {}
  187. for i in range(1, self.__nScrewTypes + 1):
  188. screw_id_to_group_n_dict[i] = random.randint(1, int(total_groups/self.__nScrewTypes))
  189. total_groups -= screw_id_to_group_n_dict[i]
  190. i = 1
  191. while total_groups > 0:
  192. i = i % self.__nScrewTypes + 1
  193. screw_id_to_group_n_dict[i] += 1
  194. i += 1
  195. total_groups -= 1
  196. screw_id_to_group_n_dict[1] += total_groups
  197. screw_types = []
  198. for screw_id in screw_id_to_group_n_dict:
  199. screw_types.extend([screw_id] * screw_id_to_group_n_dict[screw_id] * 3)
  200. random.shuffle(screw_types)
  201. ret = []
  202. n_screw_added = 0
  203. for n_screw in n_screws_of_conditions:
  204. ret.append([screw_types[j] for j in range(n_screw_added, n_screw_added + n_screw)])
  205. n_screw_added += n_screw
  206. return ret
  207. """ 为每种盘子生成钉子数据 """
  208. def generate_screw_configs(self, plate_condition: dict, condition_selected: int, screw_types: list) -> list:
  209. # ['nodeGraph']['children'][0]['children'] -> [空位, 空位, 空位...]
  210. condition = plate_condition['nodeGraph']['children'][0]['children'][condition_selected]
  211. screws = [
  212. {
  213. "screwId": screw_types[i],
  214. "scale": 1,
  215. "rotate": 0,
  216. "position": []
  217. } for i in range(len(screw_types))
  218. ]
  219. i = 0
  220. for hole in condition['children']:
  221. for hole_property in hole['properties']:
  222. if hole_property['name'] == 'position':
  223. screws[i]['position'] = [round(float(hole_property['value'][j]), 1) for j in range(2)]
  224. i += 1
  225. break
  226. return screws
  227. """ 将钉子和盘子的配置合成为最终的关卡配置 """
  228. def generate_final_level_config(self, plate_configs_of_this_level: list, screw_configs: list) -> dict:
  229. for i in range(len(plate_configs_of_this_level)):
  230. plate_configs_of_this_level[i]['screws'] = screw_configs[i]
  231. plate_position = plate_configs_of_this_level[i]['position']
  232. for screw in plate_configs_of_this_level[i]['screws']:
  233. screw['position'] = [screw['position'][j] + plate_position[j] for j in range(2)]
  234. final_level_config = {
  235. 'plateData': plate_configs_of_this_level
  236. }
  237. return final_level_config
  238. """
  239. 将plate的zorder从都是1修正为正确的数字
  240. """
  241. def correct_plate_zorders(self, plate_configs_of_this_level: list) -> list:
  242. plate_cnt = 1
  243. for plate in plate_configs_of_this_level:
  244. plate['zorder'] = plate_cnt
  245. plate_cnt += 1
  246. return plate_configs_of_this_level