基于节点与边的带权重随机文本类游戏地图生成与打印咨询
文本类游戏地图生成实现方案
核心设计
解决同层节点唯一标识、边绑定、打印三个核心问题:
- 每个节点使用
层号_同层序号格式的唯一ID作为标识,不会出现重复 - 用字典存储全量节点(key为节点ID),同时用嵌套列表存储每层的节点ID,兼顾快速查询和按层遍历需求
- 每个节点存储关联的上层节点ID列表,满足最少1个、最多3个上层连接的规则
- 打印时从最高层往最低层输出,每层占2行:第一行打印节点,第二行打印连接边
完整实现代码
import random from typing import Dict, List, Tuple # 配置项 # 节点类型与生成权重,可按需调整 NODE_WEIGHT: Dict[str, int] = { "R": 25, "?": 20, "M": 30, "$": 10, "E": 15, "T": 10 } # 总层数,固定0为起点层,16为终点层 TOTAL_LAYERS = 17 # 每层节点数量区间 LAYER_NODE_RANGE: Dict[int, Tuple[int, int]] = { 0: (1, 1), 1: (3, 3), 2: (3, 4), 3: (4, 5), 4: (4, 5), 5: (4, 5), 6: (4, 5), 7: (4, 5), 8: (4, 5), 9: (4, 5), 10: (4, 5), 11: (4, 5), 12: (4, 5), 13: (4, 5), 14: (3, 4), 15: (3, 4), 16: (1, 1) } # 打印时的节点水平间距 COL_SPACING = 6 def generate_map() -> Tuple[Dict, List[List[str]]]: """生成地图节点与边关系""" all_nodes = {} layers = [] # 第一步:生成所有节点 for layer_idx in range(TOTAL_LAYERS): min_cnt, max_cnt = LAYER_NODE_RANGE[layer_idx] node_cnt = random.randint(min_cnt, max_cnt) current_layer = [] for node_idx in range(node_cnt): node_id = f"{layer_idx}_{node_idx}" # 特殊处理起点、终点 if layer_idx == 0: node_type = "S" elif layer_idx == TOTAL_LAYERS - 1: node_type = "B" else: # 加权随机生成节点类型 types, weights = zip(*NODE_WEIGHT.items()) node_type = random.choices(types, weights=weights, k=1)[0] all_nodes[node_id] = { "id": node_id, "type": node_type, "layer": layer_idx, "idx": node_idx, "upper_nodes": [] # 存储上层连接的节点ID } current_layer.append(node_id) layers.append(current_layer) # 第二步:绑定上下层边关系 for layer_idx in range(TOTAL_LAYERS - 1): current_layer = layers[layer_idx] upper_layer = layers[layer_idx + 1] for node_id in current_layer: node = all_nodes[node_id] current_pos = node["idx"] # 限制只能连接上层相邻位置的节点,保证边的形状合理 optional_upper_idx = [] for offset in (-1, 0, 1): upper_pos = current_pos + offset if 0 <= upper_pos < len(upper_layer): optional_upper_idx.append(upper_pos) # 随机选择1~3个上层节点连接 connect_cnt = random.randint(1, min(3, len(optional_upper_idx))) selected_idx = random.sample(optional_upper_idx, connect_cnt) node["upper_nodes"] = [upper_layer[i] for i in selected_idx] return all_nodes, layers def print_map(all_nodes: Dict, layers: List[List[str]]): """控制台打印地图""" max_node_cnt = max(len(l) for l in layers) # 从最高层往最低层打印,和示例展示逻辑一致 for layer_idx in reversed(range(TOTAL_LAYERS)): current_layer = layers[layer_idx] # 计算当前层每个节点的打印位置 node_pos = {} offset = (max_node_cnt - len(current_layer)) * COL_SPACING // 2 for node_id in current_layer: pos = offset + all_nodes[node_id]["idx"] * COL_SPACING node_pos[node_id] = pos # 打印节点行 node_line = f"#{layer_idx:<2d}" for node_id in current_layer: node_type = all_nodes[node_id]["type"] node_line += " " * (node_pos[node_id] - len(node_line) + 4) node_line += node_type print(node_line) # 最低层不需要打印下方的边 if layer_idx == 0: break # 打印边行 edge_line = "# " lower_layer = layers[layer_idx - 1] # 计算下层节点的打印位置 lower_offset = (max_node_cnt - len(lower_layer)) * COL_SPACING // 2 lower_node_pos = { node_id: lower_offset + all_nodes[node_id]["idx"] * COL_SPACING for node_id in lower_layer } # 遍历当前层节点,生成连接下层的边 for lower_node_id in lower_layer: lower_x = lower_node_pos[lower_node_id] for upper_node_id in all_nodes[lower_node_id]["upper_nodes"]: if upper_node_id not in node_pos: continue upper_x = node_pos[upper_node_id] min_x = min(lower_x, upper_x) max_x = max(lower_x, upper_x) # 补空格到边的起始位置 while len(edge_line) < min_x + 4: edge_line += " " if lower_x == upper_x: # 垂直边 edge_line = edge_line[:lower_x + 4] + "|" + edge_line[lower_x + 5:] elif lower_x > upper_x: # 左斜杠 edge_line = edge_line[:upper_x + 4] + "\\" + edge_line[upper_x + 5:] else: # 右斜杠 edge_line = edge_line[:max_x + 4] + "/" + edge_line[max_x + 5:] print(edge_line) if __name__ == "__main__": all_nodes, layers = generate_map() print_map(all_nodes, layers)
内容的提问来源于stack exchange,提问作者Difio
相关产品推荐
相关产品推荐

