Spaces:
Sleeping
Sleeping
| # algorithm_3d.py | |
| from utils import ( | |
| to_tuple_set, | |
| to_list, | |
| is_move_connected, | |
| is_connected, | |
| potential, | |
| apply_move, | |
| make_step_info, | |
| build_response | |
| ) | |
| def is_finished_3d(blocks_set): | |
| """ | |
| Kiểm tra cấu hình 3D đã nén xong chưa. | |
| Theo định nghĩa: với mỗi cube (x,y,z), toàn bộ cuboid | |
| từ (0,0,0) đến (x,y,z) phải có mặt trong cấu hình. | |
| """ | |
| 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: | |
| return False | |
| return True | |
| def add_vec(a, b): | |
| return tuple(a[i] + b[i] for i in range(3)) | |
| def sub_vec(a, b): | |
| return tuple(a[i] - b[i] for i in range(3)) | |
| def is_unit_axis_vector(v): | |
| """ | |
| Kiểm tra vector có phải dạng: | |
| (±1,0,0), (0,±1,0), (0,0,±1) | |
| """ | |
| return sum(abs(x) for x in v) == 1 | |
| def perpendicular_axis_vectors(direction): | |
| """ | |
| Với hướng slide direction, trả về 4 vector vuông góc theo 2 trục còn lại. | |
| Ví dụ: | |
| direction = (1,0,0) | |
| thì các hướng vuông góc là: | |
| (0,1,0), (0,-1,0), (0,0,1), (0,0,-1) | |
| """ | |
| result = [] | |
| for axis in range(3): | |
| if direction[axis] == 0: | |
| v_pos = [0, 0, 0] | |
| v_neg = [0, 0, 0] | |
| v_pos[axis] = 1 | |
| v_neg[axis] = -1 | |
| result.append(tuple(v_pos)) | |
| result.append(tuple(v_neg)) | |
| return result | |
| def is_slide_3d(c, e, blocks_set): | |
| """ | |
| Slide trong 3D: | |
| - c và e phải kề mặt nhau. | |
| - Tồn tại một mặt phẳng 2D chứa c và e sao cho trong 4-cycle: | |
| c, e, c + p, e + p | |
| thì e là ô trống, còn c, c+p, e+p đều có cube. | |
| """ | |
| direction = sub_vec(e, c) | |
| if not is_unit_axis_vector(direction): | |
| return False | |
| for p in perpendicular_axis_vectors(direction): | |
| c_side = add_vec(c, p) | |
| e_side = add_vec(e, p) | |
| if c_side in blocks_set and e_side 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 trong một mặt phẳng tọa độ. | |
| - Nghĩa là chúng khác nhau đúng 2 trục, mỗi trục lệch 1 đơn vị. | |
| - Trong 4-cycle đó, đúng một trong hai ô trung gian có cube. | |
| """ | |
| diff = sub_vec(e, c) | |
| changed_axes = [i for i in range(3) if diff[i] != 0] | |
| # Convex phải lệch đúng 2 trục | |
| if len(changed_axes) != 2: | |
| return False | |
| # Mỗi trục lệch đúng 1 | |
| if any(abs(diff[i]) != 1 for i in changed_axes): | |
| return False | |
| # Trục còn lại phải giữ nguyên | |
| if sum(abs(x) for x in diff) != 2: | |
| return False | |
| a, b = changed_axes | |
| n1 = list(c) | |
| n1[a] = e[a] | |
| n1 = tuple(n1) | |
| n2 = list(c) | |
| n2[b] = e[b] | |
| n2 = tuple(n2) | |
| in_n1 = n1 in blocks_set | |
| in_n2 = n2 in blocks_set | |
| return in_n1 ^ in_n2 | |
| def is_valid_move_3d(c, e, blocks_set): | |
| """ | |
| Trả về loại move nếu hợp lệ về mặt hình học: | |
| - "slide" | |
| - "convex" | |
| - None nếu không hợp lệ | |
| """ | |
| if is_slide_3d(c, e, blocks_set): | |
| return "slide" | |
| if is_convex_3d(c, e, blocks_set): | |
| return "convex" | |
| return None | |
| def generate_valid_moves_3d(blocks_set): | |
| moves = [] | |
| if not blocks_set: | |
| return moves | |
| min_x = min(b[0] for b in blocks_set) - 1 | |
| max_x = max(b[0] for b in blocks_set) + 1 | |
| min_y = min(b[1] for b in blocks_set) - 1 | |
| max_y = max(b[1] for b in blocks_set) + 1 | |
| min_z = min(b[2] for b in blocks_set) - 1 | |
| max_z = max(b[2] for b in blocks_set) + 1 | |
| empty_cells = [ | |
| (x, y, z) | |
| for x in range(min_x, max_x + 1) | |
| for y in range(min_y, max_y + 1) | |
| for z in range(min_z, max_z + 1) | |
| if (x, y, z) not in blocks_set | |
| ] | |
| for c in blocks_set: | |
| # Điều kiện quan trọng của bài báo: | |
| # Trong lúc move, C \ {c} phải liên thông. | |
| remaining = set(blocks_set) | |
| remaining.remove(c) | |
| if not is_connected(remaining, 3): | |
| continue | |
| for e in empty_cells: | |
| # Bản simulator hiện tại chưa hỗ trợ tọa độ âm | |
| if e[0] < 0 or e[1] < 0 or e[2] < 0: | |
| continue | |
| move_type = is_valid_move_3d(c, e, blocks_set) | |
| # Kiểm tra thêm trạng thái sau khi đặt cube xuống vẫn liên thông | |
| if move_type and is_move_connected(blocks_set, c, e, 3): | |
| moves.append((c, e, move_type)) | |
| return moves | |
| def compact_3d(blocks, max_steps): | |
| blocks_set = to_tuple_set(blocks) | |
| pot_initial = potential(blocks_set, 3) | |
| initial_info = { | |
| "blocks": blocks, | |
| "potential": pot_initial, | |
| "is_connected": True, | |
| "is_finished": is_finished_3d(blocks_set) | |
| } | |
| steps = [] | |
| curr_blocks = blocks_set | |
| curr_pot = pot_initial | |
| status = "completed" if is_finished_3d(curr_blocks) else "running" | |
| if status == "completed": | |
| final_info = { | |
| "blocks": to_list(curr_blocks), | |
| "potential": curr_pot, | |
| "is_connected": True, | |
| "is_finished": True | |
| } | |
| return build_response( | |
| True, | |
| "Compaction completed successfully.", | |
| 3, | |
| initial_info, | |
| steps, | |
| final_info, | |
| status | |
| ) | |
| for step_idx in range(1, max_steps + 1): | |
| valid_moves = generate_valid_moves_3d(curr_blocks) | |
| best_move = None | |
| best_pot = curr_pot | |
| for c, e, move_type in valid_moves: | |
| next_blocks = apply_move(curr_blocks, c, e) | |
| next_pot = potential(next_blocks, 3) | |
| if next_pot < best_pot: | |
| best_pot = next_pot | |
| best_move = (c, e, move_type, next_blocks) | |
| if not best_move: | |
| status = "completed" if is_finished_3d(curr_blocks) else "no_valid_move_found" | |
| break | |
| c, e, move_type, next_blocks = best_move | |
| finished_after = is_finished_3d(next_blocks) | |
| step_info = make_step_info( | |
| step_idx, | |
| c, | |
| e, | |
| move_type, | |
| curr_blocks, | |
| next_blocks, | |
| curr_pot, | |
| best_pot, | |
| True, | |
| finished_after | |
| ) | |
| steps.append(step_info) | |
| curr_blocks = next_blocks | |
| curr_pot = best_pot | |
| if finished_after: | |
| status = "completed" | |
| break | |
| if status == "running": | |
| if len(steps) == max_steps: | |
| status = "max_steps_reached" | |
| else: | |
| status = "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": True, | |
| "is_finished": final_finished | |
| } | |
| message = ( | |
| "Compaction completed successfully." | |
| if status == "completed" | |
| else "No valid move found." | |
| if status == "no_valid_move_found" | |
| else "Maximum number of steps reached before finishing compaction." | |
| ) | |
| return build_response( | |
| True, | |
| message, | |
| 3, | |
| initial_info, | |
| steps, | |
| final_info, | |
| status | |
| ) |