import heapq
# ========================
# 工具函数 1: 带时间感知的 A*(无 defaultdict,无集合运算)
# ========================
def astar_time_aware(start, goal, grid_size, static_obstacles, occupied):
"""
使用 A* 在时空网格中找路径
:param start: (i, j)
:param goal: (i, j)
:param grid_size: (m, n)
:param static_obstacles: list 或 set 的障碍物坐标
:param occupied: dict {t: {(i,j), ...}} 表示每个时间步被占的位置
:return: 路径 [(i,j), ...] 或 None
"""
m, n = grid_size
directions = [(0, 1), (1, 0), (0, -1), (-1, 0), (0, 0)] # 包含等待动作
def manhattan(i, j):
return abs(i - goal[0]) + abs(j - goal[1])
heap = []
h0 = manhattan(start[0], start[1])
heapq.heappush(heap, (h0, 0, start[0], start[1], 0, [start]))
visited = {} # 记录状态 (i, j, t) 是否已访问
max_lookahead = 50 # 防止无限搜索
while heap:
f, g, i, j, t, path = heapq.heappop(heap)
if (i, j) == goal:
return path
if t > max_lookahead:
continue
state = (i, j, t)
if state in visited:
continue
visited[state] = True
for di, dj in directions:
ni, nj = i + di, j + dj
if not (0 <= ni < m and 0 <= nj < n):
continue
if (ni, nj) in static_obstacles:
continue
# 查询 occupied[t+1] 中是否有 (ni,nj),安全获取
future_set = occupied.get(t + 1, set())
if (ni, nj) in future_set:
continue
new_g = g + 1
h = manhattan(ni, nj)
f_new = new_g + h
new_path = path + [(ni, nj)]
heapq.heappush(heap, (f_new, new_g, ni, nj, t + 1, new_path))
return None
# ========================
# 工具函数 2: 找矩阵中每个值的首次位置
# ========================
def find_positions(matrix):
"""
返回每个唯一值在矩阵中的第一次出现位置
:param matrix: 二维列表
:return: {value: (i, j)}
"""
pos = {}
used = set()
for i, row in enumerate(matrix):
for j, val in enumerate(row):
if val not in used:
pos[val] = (i, j)
used.add(val)
return pos
# ========================
# 工具函数 3: Tarjan 算法检测循环(SCC)
# ========================
def detect_all_cycles(current_pos, target_pos):
"""
检测当前到目标映射中的循环(长度>1)
:param current_pos: {val: (i,j)}
:param target_pos: {val: (i,j)}
:return: [[cycle_vals], ...]
"""
indices = {}
lowlink = {}
on_stack = set()
stack = []
index_counter = 0
cycles = []
def get_value_at(pos_map, i, j):
for v, (x, y) in pos_map.items():
if x == i and y == j:
return v
return None
def strongconnect(node):
nonlocal index_counter
indices[node] = index_counter
lowlink[node] = index_counter
index_counter += 1
stack.append(node)
on_stack.add(node)
i, j = current_pos[node]
next_val = get_value_at(target_pos, i, j)
if next_val and next_val in current_pos and next_val != node:
if next_val not in indices:
strongconnect(next_val)
lowlink[node] = min(lowlink[node], lowlink[next_val])
elif next_val in on_stack:
lowlink[node] = min(lowlink[node], indices[next_val])
if lowlink[node] == indices[node]:
component = []
while True:
w = stack.pop()
on_stack.remove(w)
component.append(w)
if w == node:
break
if len(component) > 1:
cycles.append(component)
for val in current_pos:
if val not in indices:
strongconnect(val)
return cycles
# ========================
# 主函数:plan_drone_moves(完全整合版)
# ========================
def plan_drone_moves(initial_matrix, target_matrix, static_obstacles=None):
m, n = len(initial_matrix), len(initial_matrix[0])
static_obstacles = static_obstacles or []
current_pos = find_positions(initial_matrix)
target_pos = find_positions(target_matrix)
# Step 1: 检测循环
cycles = detect_all_cycles(current_pos, target_pos)
if cycles:
print(f"🔁 Detected {len(cycles)} cycle(s): {cycles}")
# 不需要空地!我们选择循环中的一个 drone 作为“最后移动者”
for cycle in cycles:
last_mover = cycle[-1] # 最后一个移动
print(f"🎯 Will move others first, let {last_mover} move last")
# 修改 last_mover 的目标为“等待后再去”
# 实际上我们只是延迟它的调度
delay_list = [last_mover]
else:
delay_list = []
# Step 2: 排序移动项:不在 delay_list 中的优先
movable_values = [v for v in current_pos if current_pos[v] != target_pos[v]]
movable_values.sort(key=lambda x: 1 if x in delay_list else 0) # 延迟的排后面
# Step 3: 构建全局占用表
global_occupancy = {}
tasks = []
for idx, val in enumerate(movable_values):
drone_id = idx % 16
start = current_pos[val]
goal = target_pos[val]
path = astar_time_aware(
start=start,
goal=goal,
grid_size=(m, n),
static_obstacles=static_obstacles,
occupied=global_occupancy
)
if path is None:
print(f"❌ Failed to plan path for {val}")
return None
# 注册路径占用
for t, (i, j) in enumerate(path):
if t not in global_occupancy:
global_occupancy[t] = set()
global_occupancy[t].add((i, j))
moves = [
{'time_step': t, 'from': path[t], 'to': path[t+1], 'value': val}
for t in range(len(path)-1)
]
tasks.append({
'drone_id': drone_id,
'value': val,
'path': path,
'moves': moves
})
print("✅ Cycle broken by scheduling order.")
return tasks
# ========================
# 辅助函数:模拟执行并输出结果
# ========================
def simulate_execution(tasks, initial_matrix, target_matrix):
"""
执行任务并打印过程与结果
"""
if not tasks:
print("⚠️ No tasks to execute.")
return
result = [row[:] for row in initial_matrix] # 深拷贝
timeline = {}
# 构建时间线
for task in tasks:
for move in task['moves']:
t = move['time_step']
if t not in timeline:
timeline[t] = []
timeline[t].append(move)
print("\n⏳ Execution Timeline:")
for t in sorted(timeline.keys()):
print(f"t={t}:")
for move in timeline[t]:
frm, to = move['from'], move['to']
val = move['value']
did = move.get('drone_id', '?')
print(f" Drone#{did} moves {val} from {frm} → {to}")
result[to[0]][to[1]] = val
print("\n🎯 Target Matrix:")
for row in target_matrix:
print(["%3d" % x for x in row])
print("\n🏁 Final Matrix:")
for row in result:
print(["%3d" % x for x in row])
accuracy = sum(
result[i][j] == target_matrix[i][j]
for i in range(len(result)) for j in range(len(result[0]))
)
total = len(result) * len(result[0])
print(f"\n📊 Accuracy: {accuracy}/{total}")
# ========================
# 示例运行
# ========================
if __name__ == "__main__":
initial = [[46, 11, 31, 40, 30, 93, 74, 55, 92, 26], [84, 88, 25, 61, 25, 41, 24, 6, 39, 27], [82, 15, 74, 68, 41, 77, 87, 64, 51, 89], [52, 97, 31, 84, 47, 76, 7, 49, 22, 62], [53, 95, 85, 55, 35, 45, 7, 75, 72, 80], [83, 84, 62, 100, 86, 39, 67, 49, 14, 88], [18, 24, 78, 15, 93, 81, 26, 25, 67, 97], [52, 9, 36, 18, 44, 80, 6, 30, 38, 91], [93, 67, 75, 6, 79, 13, 63, 78, 97, 41], [10, 91, 53, 64, 79, 39, 39, 51, 44, 54]]
target = [[6, 6, 7, 10, 15, 24, 26, 36, 41, 51], [6, 7, 11, 15, 24, 27, 38, 41, 51, 62], [9, 13, 18, 25, 30, 39, 44, 52, 62, 72], [14, 18, 25, 30, 39, 44, 52, 63, 74, 78], [22, 25, 31, 39, 45, 53, 64, 74, 79, 83], [26, 31, 39, 46, 53, 64, 75, 79, 84, 87], [35, 40, 47, 54, 67, 75, 80, 84, 88, 91], [41, 49, 55, 67, 76, 80, 84, 88, 92, 93], [49, 55, 67, 77, 81, 85, 89, 93, 95, 97], [61, 68, 78, 82, 86, 91, 93, 97, 97, 100]]
obstacles = [] # 可添加如 {(0,1)}
tasks = plan_drone_moves(initial, target, obstacles)
simulate_execution(tasks, initial, target)
🔁 Detected 1 cycle(s): [[36, 55]]
🎯 Will move others first, let 55 move last
❌ Failed to plan path for 55
⚠️ No tasks to execute.