Spaces:
Sleeping
Sleeping
File size: 7,504 Bytes
987f097 f512021 c728f53 f512021 c728f53 987f097 f512021 987f097 f512021 987f097 f512021 987f097 f512021 c728f53 f512021 987f097 f512021 c728f53 f512021 c728f53 f512021 987f097 f512021 987f097 f512021 987f097 f512021 987f097 f512021 987f097 f512021 c728f53 f512021 c728f53 f512021 c728f53 f512021 987f097 f512021 c728f53 f512021 987f097 c728f53 987f097 f512021 987f097 c728f53 987f097 f512021 987f097 c728f53 f512021 c728f53 f512021 c728f53 f512021 c728f53 f512021 c728f53 f512021 c728f53 f512021 | 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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | # 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
) |