import random """ RedHelper 将关卡red文件转化为json文件 """ class RedHelper: """ 初始化 """ def __init__(self): print("init helper...") """ __nScrewTypes 模拟C++算法分配钉子的时候使用 钉子的种类数 如果screwconfig里面总共有10种钉子 我们挑其中的__nScrewTypes种 往挑好、摆好的盘子上放 """ self.__nScrewTypes = 3 print(f"钉子总种类{self.__nScrewTypes}") """ 从red文件的dict中获取使用的模板ID """ def get_template_IDs(self, levelData: dict) -> list: # ['nodeGraph']['children'][0]['children'] -> [模板, 模板, 模板...] # [0] 对应的是 red工程文件中的"元素" 目前只有一个 # 关卡red文件格式: # 关卡1.red # --CCLayer # ------元素 # ----------模板号 # ----------模板号 # ----------模板号 template_list = levelData['nodeGraph']['children'][0]['children'] template_id_list = [template['displayName'] for template in template_list] return template_id_list """ 从模板中获取盘子配置 """ def get_plate_configs(self, template: dict) -> list: # ['nodeGraph']['children'][0]['children'] -> [盘子, 盘子, 盘子] # [0]对应CCNode 目前只有一个 # 关卡模板文件格式: # 模板1.red # --CCLayer # ------CCNode # ----------盘子号 # ----------盘子号 # ----------盘子号 plates = template['nodeGraph']['children'][0]['children'] plate_configs = [] for plate in plates: properties = plate['properties'] plate_data = {'plateId': int(plate['displayName'])} for plate_property in properties: property_name = plate_property['name'] if property_name == 'position': plate_data['position'] = [round(float(plate_property['value'][i]), 1) for i in range(2)] elif property_name == 'scale': plate_data['scale'] = round(float(plate_property['value']), 1) elif property_name == 'rotation': plate_data['rotate'] = round(float(plate_property['value']), 1) elif property_name == 'zorder': plate_data['zorder'] = int(plate_property['value'], 1) if 'position' not in plate_data: plate_data['position'] = [0, 0] # 目前大部分文件中没有后面这几个参数 除非在redream工程中设置过 if 'scale' not in plate_data: plate_data['scale'] = 1.0 if 'rotate' not in plate_data: plate_data['rotate'] = 0.0 if 'zorder' not in plate_data: plate_data['zorder'] = 1 plate_configs.append(plate_data) return plate_configs """ 获得盘子能容纳的最大的钉子个数 """ def get_max_screws_of_plate(self, plate_condition: dict) -> int: # ['nodeGraph']['children'][0]['children'].size() max_screws_of_plate = len(plate_condition['nodeGraph']['children'][0]['children']) return max_screws_of_plate """ @brief 随机选取盘子情况 返回对应情况的id @param max_screw_of_plate: 盘子上钉子最大个数 @return 盘子配置情况的下标(从0开始) """ def generate_condition_id(self, max_screws_of_plate) -> int: condition_id = random.randint(1, max_screws_of_plate) # -1 是因为盘子的情况配置下标从0开始 return condition_id - 1 """ @brief 调整选择的盘子情况 结果要满足三消的个数 @param plate_conditions: 被选择的盘子的数据列表 @param conditions_selected: 被选择的盘子情况的下标列表 @return 调整后的被选择的盘子情况的下标列表 """ def adjust_conditions_selected(self, plate_conditions: list, condition_selected: list) -> list: # print(plate_conditions) print(condition_selected) n_holes_available_of_plates = [] for condition in plate_conditions: # print(condition) n_hole_available_of_plate = [] for c in condition['nodeGraph']['children'][0]['children']: # print(len(c['children'])) n_hole_available_of_plate.append(len(c['children'])) n_holes_available_of_plates.append(n_hole_available_of_plate) # print(n_holes_available_of_plates) # 记录每个盘子包含多少个钉子洞 n_holes_used_in_each_condition_selected = [] for i in range(len(condition_selected)): n_holes_used_in_each_condition_selected.append(n_holes_available_of_plates[i][condition_selected[i]]) total_holes_used = sum(n_holes_used_in_each_condition_selected) # print(total_holes_used) # 总钉子个数不满足三消条件 需要调整 n_redundant_holes = total_holes_used % 3 if n_redundant_holes != 0: # 缺少 / 多余的hole的个数 holes_need_to_adjust = [3 - n_redundant_holes, n_redundant_holes] holes_delta = [] for i in range(len(n_holes_used_in_each_condition_selected)): holes_delta.append([ hole_number - n_holes_used_in_each_condition_selected[i] for hole_number in n_holes_available_of_plates[i] ]) # print(holes_delta) # 减少或增加到3的倍数 # 先简单的每个组plate找一遍 大概率可以只调整一个plate的情况就能满足3的倍数 # TODO: 以后有复杂情况再说 adjust_complete = False for delta in holes_need_to_adjust: for i, condition in enumerate(holes_delta): for j, number in enumerate(condition): if number == delta: condition_selected[i] = j adjust_complete = True break if adjust_complete: break if adjust_complete: break return condition_selected # """ # @brief 看看是否每种钉子的出现次数都是3的倍数 不满足要调整 # @param 每个盘子上分配的钉子种类的列表 # @return 调整后的每个盘子上分配的钉子种类的列表 # """ # # def adjust_screws_distribution(self, screw_types_list: list) -> list: # screw_type_to_n_dict = {} # for screws_on_plate in screw_types_list: # for screw_id in screws_on_plate: # if screw_id in screw_type_to_n_dict: # screw_type_to_n_dict[screw_id] += 1 # else: # screw_type_to_n_dict[screw_id] = 1 # # # 个数需要调整的钉子的id # screw_need_to_adjust = {} # for screw_id in screw_type_to_n_dict: # n_delta = screw_type_to_n_dict[screw_id] % 3 # if n_delta != 0: # screw_need_to_adjust[screw_id] = n_delta # # # 让所有需要调整的钉子都先向下减少到3的倍数 # # 多出来的空位数肯定是3的倍数 # # 再往里随机分配就好 # for screw_id in screw_need_to_adjust: """ @brief 从盘子情况数据中获得对应情况所能容纳的钉子个数的列表 @param plate_conditions: 盘子的情况的列表 @param condition_selected: 选择的盘子情况的下标 @return 对应的盘子情况所能容纳钉子的个数的列表 """ def get_n_screw_of_conditions(self, plate_conditions: list, condition_selected: list) -> list: n_screw_of_conditions = [] for i, condition_id in enumerate(condition_selected): n_screw_of_conditions.append( len(plate_conditions[i]['nodeGraph']['children'][0]['children'][condition_id]['children']) ) return n_screw_of_conditions """ @brief 根据盘子情况和随机挑选的情况生成盘子上摆放的钉子类型 @param condition_selected: 随机挑选出的盘子的情况的下标 @param n_screws_of_conditions: 被选中的盘子情况能容纳的钉子个数的列表 @return 为被选中的盘子生成的钉子种类的ID列表 """ def generate_screw_types_for_plates(self, condition_selected: list, n_screws_of_conditions: list) -> list: total_holes = sum(n_screws_of_conditions) # 总分组数 total_groups = int(total_holes / self.__nScrewTypes) # 随机选择钉子的组数 screw_id_to_group_n_dict = {} for i in range(1, self.__nScrewTypes + 1): screw_id_to_group_n_dict[i] = random.randint(1, int(total_groups/self.__nScrewTypes)) total_groups -= screw_id_to_group_n_dict[i] i = 1 while total_groups > 0: i = i % self.__nScrewTypes + 1 screw_id_to_group_n_dict[i] += 1 i += 1 total_groups -= 1 screw_id_to_group_n_dict[1] += total_groups screw_types = [] for screw_id in screw_id_to_group_n_dict: screw_types.extend([screw_id] * screw_id_to_group_n_dict[screw_id] * 3) random.shuffle(screw_types) ret = [] n_screw_added = 0 for n_screw in n_screws_of_conditions: ret.append([screw_types[j] for j in range(n_screw_added, n_screw_added + n_screw)]) n_screw_added += n_screw return ret """ 为每种盘子生成钉子数据 """ def generate_screw_configs(self, plate_condition: dict, condition_selected: int, screw_types: list) -> list: # ['nodeGraph']['children'][0]['children'] -> [空位, 空位, 空位...] condition = plate_condition['nodeGraph']['children'][0]['children'][condition_selected] screws = [ { "screwId": screw_types[i], "scale": 1, "rotate": 0, "position": [] } for i in range(len(screw_types)) ] i = 0 for hole in condition['children']: for hole_property in hole['properties']: if hole_property['name'] == 'position': screws[i]['position'] = [round(float(hole_property['value'][j]), 1) for j in range(2)] i += 1 break return screws """ 将钉子和盘子的配置合成为最终的关卡配置 """ def generate_final_level_config(self, plate_configs_of_this_level: list, screw_configs: list) -> dict: for i in range(len(plate_configs_of_this_level)): plate_configs_of_this_level[i]['screws'] = screw_configs[i] plate_position = plate_configs_of_this_level[i]['position'] for screw in plate_configs_of_this_level[i]['screws']: screw['position'] = [screw['position'][j] + plate_position[j] for j in range(2)] final_level_config = { 'plateData': plate_configs_of_this_level } return final_level_config """ 将plate的zorder从都是1修正为正确的数字 """ def correct_plate_zorders(self, plate_configs_of_this_level: list) -> list: plate_cnt = 1 for plate in plate_configs_of_this_level: plate['zorder'] = plate_cnt plate_cnt += 1 return plate_configs_of_this_level