-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBFS.cpp
More file actions
130 lines (102 loc) · 4.03 KB
/
BFS.cpp
File metadata and controls
130 lines (102 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "BFS.h"
#include "../common/VizMacros.h"
#include <algorithm> // for reverse, abs
#include <limits> // for numeric_limits
BFS::BFS(HexMap& m) : map(m) {
int size = map.width * map.height;
nodeData.resize(size);
// 预分配最大容量,确保在 findPath 中 push_back 永不触发扩容
queueData.reserve(size);
}
void BFS::reset() {
int targetSize = map.width * map.height;
// 只有地图尺寸变化时才重新分配内存
// 正常关卡切换或重置不需要跑这个分支
if (nodeData.size() != targetSize) {
nodeData.clear();
nodeData.resize(targetSize);
queueData.clear();
queueData.reserve(targetSize);
currentGen = 0;
}
// 保持 currentGen 不变,利用 visitedGen 机制避免 O(N) 清零
}
std::vector<Hex> BFS::findPath(Hex start, Hex end) {
// 1. 基础合法性检查
// isWalkable 内部有边界检查
if (!map.isWalkable(start) || !map.isWalkable(end)) return {};
if (start == end) return {start};
// 2. 代数管理 (Generation System)
// 防止 currentGen 溢出 (约 20 亿次寻路后才会触发一次)
if (currentGen >= std::numeric_limits<int>::max() - 1) {
currentGen = 0;
// 只有溢出时才执行昂贵的清零操作
std::fill(nodeData.begin(), nodeData.end(), Node{0, -1});
}
currentGen++;
// 获取起点终点索引
int startIdx = getIdx(start);
int endIdx = getIdx(end);
// 二次确认索引有效性
if (startIdx == -1 || endIdx == -1) return {};
// 3. 初始化手动队列 (Zero Allocation)
queueData.clear(); // size 置 0,但 capacity 保持不变,无内存释放
// 使用 size_t 避免与 vector.size() 比较时的符号警告
size_t qHead = 0;
// 起点入队
nodeData[startIdx].visitedGen = currentGen;
nodeData[startIdx].parentIdx = -1;
queueData.push_back(startIdx);
bool found = false;
// 4. 核心搜索循环
// qHead < queueData.size() 模拟 !queue.empty()
while(qHead < queueData.size()) {
int currIdx = queueData[qHead++];
// 调试宏:请确保在 Release 模式下该宏为空,否则会严重拖慢循环
// 如果 VIZ_LOG 需要 Hex 对象,这里才转换,否则可省略
VIZ_LOG(getHex(currIdx));
// 获取当前 Hex 用于计算邻居 (这一步无法避免,需要几何关系)
Hex currHex = getHex(currIdx);
for (const auto& dir : HEX_DIRS) {
Hex next = currHex + dir;
int nextIdx = getIdx(next);
// A. 边界检查
if (nextIdx == -1) continue;
// B. 访问状态检查 (最快:读内存)
if (nodeData[nextIdx].visitedGen == currentGen) continue;
// C. 地形检查 (次快:读地图数据)
// 必须使用 isWalkableIdx 以避免重复的 Hex->Index 计算
if (!map.isWalkableIdx(nextIdx)) continue;
// 记录路径信息
nodeData[nextIdx].parentIdx = currIdx;
nodeData[nextIdx].visitedGen = currentGen;
// 【优化】提前退出 (Early Exit)
// 在入队前检查终点,省去一次入队出队开销
if (nextIdx == endIdx) {
found = true;
goto EndSearch;
}
queueData.push_back(nextIdx);
}
}
EndSearch:
// 5. 路径回溯
if (found) {
std::vector<Hex> path;
// 启发式预估内存:Hex 距离 + 少量缓冲
// dist = (|dq| + |dr| + |ds|) / 2
int dq = start.q - end.q;
int dr = start.r - end.r;
int ds = -dq - dr;
int dist = (std::abs(dq) + std::abs(dr) + std::abs(ds)) / 2;
path.reserve(dist + 8);
int curr = endIdx;
while (curr != -1) {
path.push_back(getHex(curr));
curr = nodeData[curr].parentIdx;
}
std::reverse(path.begin(), path.end());
return path;
}
return {};
}