sliding-cubes-api / algorithm_3d_1.py
LLC536's picture
Update algorithm_3d_1.py
b3f457f verified
Raw
History Blame Contribute Delete
19.2 kB
# algorithm_3d_1.py
import heapq
from collections import deque
from utils import (
to_tuple_set,
to_list,
is_connected,
is_move_connected,
potential,
apply_move,
make_step_info,
build_response,
is_finished_3d
)
# ============================================================
# 1. HÌNH HỌC MOVE 3D: SLIDE VÀ CONVEX TRANSITION
# ============================================================
def is_slide_3d(c, e, blocks_set):
"""
Slide trong 3D: Khối di chuyển theo đường thẳng c-e (khoảng cách Manhattan bằng 1).
Ràng buộc 4-cycle: Phải tồn tại một chu trình 4 ô trong lưới Z^3, trong đó c, e
và ô kề bên của c đều thuộc blocks_set, tạo điểm tựa song song với hướng trượt.
"""
dx = e[0] - c[0]
dy = e[1] - c[1]
dz = e[2] - c[2]
if abs(dx) + abs(dy) + abs(dz) != 1:
return False
# Xác định các hướng vuông góc với vector dịch chuyển c->e
directions = []
if dx != 0:
directions = [(0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]
elif dy != 0:
directions = [(1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)]
else:
directions = [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0)]
for v in directions:
side_c = (c[0] + v[0], c[1] + v[1], c[2] + v[2])
side_e = (e[0] + v[0], e[1] + v[1], e[2] + v[2])
if side_c in blocks_set and side_e in blocks_set:
return True
return False
def is_convex_3d(c, e, blocks_set):
"""
Convex transition trong 3D: c và e nằm chéo nhau trên một mặt phẳng lưới 2x2.
Khoảng cách Manhattan bằng 2, thay đổi đúng 2 tọa độ.
Đúng một trong hai ô trung gian nối giữa c và e phải chứa khối làm điểm tựa xoay góc.
"""
dx = e[0] - c[0]
dy = e[1] - c[1]
dz = e[2] - c[2]
# Kiểm tra xem có phải dịch chuyển chéo trên 1 mặt phẳng lưới không (Manhattan = 2, 1 tọa độ ko đổi)
if (abs(dx) + abs(dy) + abs(dz) != 2) or (dx != 0 and dy != 0 and dz != 0):
return False
# Xác định 2 ô trung gian tạo thành hình vuông phẳng 2x2 chứa c và e
if dx != 0 and dy != 0:
n1 = (c[0] + dx, c[1], c[2])
n2 = (c[0], c[1] + dy, c[2])
elif dx != 0 and dz != 0:
n1 = (c[0] + dx, c[1], c[2])
n2 = (c[0], c[1], c[2] + dz)
else:
n1 = (c[0], c[1] + dy, c[2])
n2 = (c[0], c[1], c[2] + dz)
return (n1 in blocks_set) ^ (n2 in blocks_set)
def get_move_type_3d(c, e, blocks_set):
if is_slide_3d(c, e, blocks_set):
return "slide"
if is_convex_3d(c, e, blocks_set):
return "convex"
return None
def bounding_search_area_3d(blocks_set, margin=2):
min_x = min(b[0] for b in blocks_set) - margin
max_x = max(b[0] for b in blocks_set) + margin
min_y = min(b[1] for b in blocks_set) - margin
max_y = max(b[1] for b in blocks_set) + margin
min_z = min(b[2] for b in blocks_set) - margin
max_z = max(b[2] for b in blocks_set) + margin
return min_x, max_x, min_y, max_y, min_z, max_z
def generate_valid_moves_3d(blocks_set):
moves = []
if not blocks_set:
return moves
min_x, max_x, min_y, max_y, min_z, max_z = bounding_search_area_3d(blocks_set, margin=1)
empty_cells = [
(x, y, z)
for x in range(max(0, min_x), max_x + 1)
for y in range(max(0, min_y), max_y + 1)
for z in range(max(0, min_z), max_z + 1)
if (x, y, z) not in blocks_set
]
for c in blocks_set:
remaining = set(blocks_set)
remaining.remove(c)
if not is_connected(remaining, 3):
continue
for e in empty_cells:
move_type = get_move_type_3d(c, e, blocks_set)
if move_type is None:
continue
if is_move_connected(blocks_set, c, e, 3):
moves.append((c, e, move_type))
return moves
# ============================================================
# 2. ĐÁNH GIÁ CẤU HÌNH HÌNH HỌC 3D (Heuristic Scoring)
# ============================================================
def finished_holes_3d(blocks_set):
"""Tính các vị trí ô trống còn thiếu trong Cuboid để cấu hình đạt trạng thái Finished."""
holes = set()
for x, y, z in blocks_set:
for i in range(x + 1):
for j in range(y + 1):
for k in range(z + 1):
if (i, j, k) not in blocks_set:
holes.add((i, j, k))
return holes
def compact_target_3d(n):
"""Xây dựng tập bia đích dồn n khối về phía gốc tọa độ tối ưu theo thế năng."""
if n <= 0:
return set()
target = {(0, 0, 0)}
frontier = {(1, 0, 0), (0, 1, 0), (0, 0, 1)}
while len(target) < n:
candidates = []
for cell in frontier:
x, y, z = cell
if x < 0 or y < 0 or z < 0:
continue
ok = True
if x > 0 and (x - 1, y, z) not in target: ok = False
if y > 0 and (x, y - 1, z) not in target: ok = False
if z > 0 and (x, y, z - 1) not in target: ok = False
if ok:
pot_val = potential([cell], 3)
heapq.heappush(candidates, (pot_val, x + y + z, x, y, z, cell))
if not candidates:
break
_, _, _, _, _, chosen = heapq.heappop(candidates)
target.add(chosen)
frontier.discard(chosen)
x, y, z = chosen
frontier.add((x + 1, y, z))
frontier.add((x, y + 1, z))
frontier.add((x, y, z + 1))
return target
def manhattan_3d(a, b):
return abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2])
def target_distance_3d(blocks_set, target):
if not blocks_set or not target:
return 0
blocks = list(blocks_set)
target_cells = list(target)
d1 = sum(min(manhattan_3d(b, t) for t in target_cells) for b in blocks)
d2 = sum(min(manhattan_3d(t, b) for b in blocks) for t in target_cells)
return d1 + d2
def compaction_score_3d(blocks_set):
"""
Score đánh giá trạng thái 3D tổng hợp.
Phạt nặng các lỗ hổng bên trong (holes) và khoảng cách hình học tới cấu hình lý tưởng.
"""
pot = potential(blocks_set, 3)
holes = finished_holes_3d(blocks_set)
target = compact_target_3d(len(blocks_set))
dist = target_distance_3d(blocks_set, target)
return pot + 30 * len(holes) + 4 * dist
def is_better_state_3d(before, after):
if is_finished_3d(after):
return True
if compaction_score_3d(after) < compaction_score_3d(before):
return True
if potential(after, 3) < potential(before, 3):
return True
return False
# ============================================================
# 3. PIPELINE OPERATIONS PHÂN LỚP THEO BÀI BÁO 3D
# ============================================================
def one_step_candidates_3d(blocks_set, operation_name, filter_func=None):
result = []
for c, e, move_type in generate_valid_moves_3d(blocks_set):
if filter_func is not None and not filter_func(c, e, move_type):
continue
next_blocks = apply_move(blocks_set, c, e)
if is_better_state_3d(blocks_set, next_blocks):
result.append((c, e, move_type, operation_name, next_blocks))
return result
def choose_best_candidate_3d(candidates):
if not candidates:
return None
return min(
candidates,
key=lambda item: (
0 if is_finished_3d(item[4]) else 1,
compaction_score_3d(item[4]),
potential(item[4], 3),
item[1][2], # Ưu tiên tọa độ z đích thấp hơn
item[1][1], # Tiếp đến y đích thấp hơn
item[1][0] # Cuối cùng là x đích thấp hơn
)
)
def local_z_reduction(blocks_set):
"""Operation [a-d]: Kéo hạ độ cao khối xuống tầng dưới."""
def filt(c, e, move_type):
return e[2] < c[2] # Giảm cao độ z mang tính quyết định
candidates = one_step_candidates_3d(blocks_set, "local_z_reduction", filt)
res = choose_best_candidate_3d(candidates)
return (res[0], res[1], res[2], res[3]) if res else None
def get_pillar_heights(blocks_set, x, y):
return sorted([b[2] for b in blocks_set if b[0] == x and b[1] == y])
def pillar_shove_heuristic(blocks_set):
"""Operation [e]: Tịnh tiến cấu trúc cột hoặc xử lý kéo nghiêng."""
def filt(c, e, move_type):
# Ưu tiên các khối thuộc một trụ thẳng đứng có chiều cao >= 2
heights = get_pillar_heights(blocks_set, c[0], c[1])
if len(heights) < 2:
return False
# Di chuyển đẩy sang bên cạnh hoặc đi xuống dưới
return e[2] <= c[2] or e[0] < c[0] or e[1] < c[1]
candidates = one_step_candidates_3d(blocks_set, "pillar_shove", filt)
res = choose_best_candidate_3d(candidates)
return (res[0], res[1], res[2], res[3]) if res else None
def local_potential_reduction_3d(blocks_set):
"""Operation [f]: Tìm mọi bước đi đơn lẻ tối ưu thế năng cục bộ."""
candidates = one_step_candidates_3d(blocks_set, "local_potential_reduction")
res = choose_best_candidate_3d(candidates)
return (res[0], res[1], res[2], res[3]) if res else None
# ============================================================
# 4. PHÂN CHIA THÀNH PHẦN LOW / HIGH TRONG KHÔNG GIAN 3D
# ============================================================
def connected_components_3d(cells):
cells = set(cells)
if not cells:
return []
components = []
unvisited = set(cells)
while unvisited:
start = next(iter(unvisited))
comp = {start}
queue = deque([start])
unvisited.remove(start)
while queue:
x, y, z = queue.popleft()
for dx, dy, dz in [(1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)]:
nb = (x + dx, y + dy, z + dz)
if nb in unvisited:
unvisited.remove(nb)
comp.add(nb)
queue.append(nb)
components.append(comp)
return components
def get_low_high_components_3d(blocks_set):
low_cells = {b for b in blocks_set if b[2] == 0} # Mặt phẳng đáy z = 0
high_cells = {b for b in blocks_set if b[2] > 0} # Thành phần trên cao z > 0
return connected_components_3d(low_cells), connected_components_3d(high_cells)
def handling_low_components_3d(blocks_set):
"""Operation [g-j]: Xử lý dồn ép các cấu trúc khối nằm ở lớp đáy z=0."""
low_comps, _ = get_low_high_components_3d(blocks_set)
if not low_comps:
return None
# Tìm thành phần root chứa gốc (0,0,0)
root_comp = None
for comp in low_comps:
if (0, 0, 0) in comp:
root_comp = comp
break
if root_comp is None:
root_comp = low_comps[0]
non_root_cells = set()
for comp in low_comps:
if comp != root_comp:
non_root_cells.update(comp)
if not non_root_cells:
return None
# Lọc dịch chuyển thuộc thành phần không phải root hướng về phía gốc tọa độ
def filt(c, e, move_type):
if c not in non_root_cells:
return False
return e[0] < c[0] or e[1] < c[1] or e[2] == 0
candidates = one_step_candidates_3d(blocks_set, "handling_low_components", filt)
res = choose_best_candidate_3d(candidates)
return (res[0], res[1], res[2], res[3]) if res else None
# ============================================================
# 5. MACRO ESCAPE (BEAM SEARCH) PHÒNG TRÁNH BỊ KẸT 3D
# ============================================================
def move_sort_key_3d(blocks_set, move):
c, e, move_type = move
next_blocks = apply_move(blocks_set, c, e)
return (
0 if is_finished_3d(next_blocks) else 1,
compaction_score_3d(next_blocks),
potential(next_blocks, 3),
e[2], e[1], e[0]
)
def beam_escape_sequence_3d(blocks_set, max_depth=4, beam_width=40, branch_limit=20):
"""
Chuỗi dịch chuyển vĩ mô nhiều bước. Giải phóng các trường hợp bị nghẽn (Unlock trụ).
Chấp nhận thế năng tăng tạm thời ở các bước trung gian.
"""
start_score = compaction_score_3d(blocks_set)
start_pot = potential(blocks_set, 3)
start_state = tuple(sorted(blocks_set))
beam = [{
"blocks": set(blocks_set),
"sequence": [],
"score": start_score,
"potential": start_pot
}]
visited = {start_state}
best_sequence = None
best_key = None
for depth in range(1, max_depth + 1):
next_beam = []
for node in beam:
current_blocks = node["blocks"]
moves = generate_valid_moves_3d(current_blocks)
moves = sorted(moves, key=lambda m: move_sort_key_3d(current_blocks, m))[:branch_limit]
for c, e, move_type in moves:
after = apply_move(current_blocks, c, e)
state = tuple(sorted(after))
if state in visited:
continue
visited.add(state)
seq = node["sequence"] + [(c, e, move_type, "macro_escape")]
after_score = compaction_score_3d(after)
after_pot = potential(after, 3)
key = (0 if is_finished_3d(after) else 1, after_score, after_pot, len(seq))
if is_finished_3d(after) or after_score < start_score or (depth >= 2 and after_pot < start_pot):
if best_sequence is None or key < best_key:
best_sequence = seq
best_key = key
next_beam.append({
"blocks": after,
"sequence": seq,
"score": after_score,
"potential": after_pot
})
if best_sequence is not None:
return best_sequence
next_beam.sort(key=lambda x: (x["score"], x["potential"], len(x["sequence"])))
beam = next_beam[:beam_width]
if not beam:
break
return None
# ============================================================
# 6. ĐIỀU PHỐI ĐA PIPELINE CHỌN BƯỚC ĐI TIẾP THEO
# ============================================================
def choose_next_sequence_3d(blocks_set):
# 1. Thử các hoạt động Greedy 1 bước phân tầng
for op in [local_z_reduction, pillar_shove_heuristic, local_potential_reduction_3d, handling_low_components_3d]:
move = op(blocks_set)
if move is not None:
return [move]
# 2. Nếu kẹt, sử dụng Beam Search tìm chuỗi thoát hiểm phục vụ "Unlock"
macro_seq = beam_escape_sequence_3d(blocks_set, max_depth=4, beam_width=40, branch_limit=20)
if macro_seq:
return macro_seq
return None
# ============================================================
# 7. VÒNG LẶP CHÍNH THỰC THI THUẬT TOÁN (Main Pipeline)
# ============================================================
def compact_3d_exact(blocks, max_steps):
"""
Thuật toán nén cấu hình 3D hoàn chỉnh.
Tên hàm đã được đổi thành compact_3d_exact để tương thích 100% với file compaction.py của Web Server.
"""
blocks_set = to_tuple_set(blocks)
curr_blocks = blocks_set
curr_pot = potential(curr_blocks, 3)
initial_info = {
"blocks": to_list(curr_blocks),
"potential": curr_pot,
"is_connected": True,
"is_finished": is_finished_3d(curr_blocks)
}
steps = []
status = "completed" if is_finished_3d(curr_blocks) else "running"
if status == "completed":
return build_response(
True, "Compaction completed successfully.", 3,
initial_info, steps, initial_info, status, "paper_inspired_3d"
)
step_idx = 1
while step_idx <= max_steps:
if is_finished_3d(curr_blocks):
status = "completed"
break
sequence = choose_next_sequence_3d(curr_blocks)
if sequence is None:
status = "no_valid_move_found"
break
broken_sequence = False
for c, e, move_type, operation in sequence:
if step_idx > max_steps:
status = "max_steps_reached"
break
# Kiểm tra tính hợp lệ động tại thời điểm thực thi bước nhỏ trong chuỗi
if c not in curr_blocks or e in curr_blocks:
broken_sequence = True
break
current_move_type = get_move_type_3d(c, e, curr_blocks)
if current_move_type is None or not is_move_connected(curr_blocks, c, e, 3):
broken_sequence = True
break
blocks_before = set(curr_blocks)
pot_before = curr_pot
# Cập nhật trạng thái mô hình
curr_blocks = apply_move(curr_blocks, c, e)
curr_pot = potential(curr_blocks, 3)
finished_after = is_finished_3d(curr_blocks)
connected_after = is_connected(curr_blocks, 3)
step_info = make_step_info(
step_idx=step_idx,
from_c=c,
to_c=e,
move_type=current_move_type,
blocks_before=blocks_before,
blocks_after=curr_blocks,
pot_before=pot_before,
pot_after=curr_pot,
connected_after=connected_after,
finished_after=finished_after,
operation=operation
)
steps.append(step_info)
step_idx += 1
if finished_after:
status = "completed"
break
if status in ["completed", "max_steps_reached"] or broken_sequence:
if status == "running" and broken_sequence:
continue # Tính toán chọn lại chuỗi hành động mới nếu chuỗi cũ gãy động
break
if status == "running":
status = "max_steps_reached" if step_idx > max_steps else "no_valid_move_found"
final_finished = is_finished_3d(curr_blocks)
if final_finished:
status = "completed"
final_info = {
"blocks": to_list(curr_blocks),
"potential": curr_pot,
"is_connected": is_connected(curr_blocks, 3),
"is_finished": final_finished
}
message = {
"completed": "Compaction completed successfully.",
"max_steps_reached": "Maximum number of steps reached before finishing compaction.",
"no_valid_move_found": "No valid move found."
}.get(status, "No valid move found.")
return build_response(
True, message, 3, initial_info, steps, final_info, status, "paper_inspired_3d"
)