ProceduralGenerationGodot/wavefunctioncollapse/grid_map_wave_func.gd
2025-04-03 00:35:39 +02:00

139 lines
4.5 KiB
GDScript

@tool
extends GridMap
# Godot ne supporte pas les types nested, c'est triste mais on fait un tool button pour nous aider
#@export var adjacency : Array[Array]
@export var adjacency : Dictionary[Globals.Blocks, AdjacencyRessource]
@export_tool_button("Step") var step_action = step
var dict_grid_map : Dictionary[Vector3i, Array] = {}
func get_cell_neighbor(cell_coords : Vector3i) -> Dictionary[Vector3i, Globals.Blocks]:
var neighbor : Dictionary[Vector3i, Globals.Blocks]
var delta : Vector3i
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0, 1]:
delta = Vector3i(dx, dy, dz)
if delta == Vector3i(0, 0, 0): continue
var cell_item = get_cell_item(delta)
neighbor[delta] = cell_item
return neighbor
func get_proba():
var used_cells := get_used_cells()
print("used_cells")
print(used_cells)
dict_grid_map = {}
#var adjacent_cells : Dictionary[Vector3i, bool] = {} # Cells sur la bordure
var used_cells_hah_set : Dictionary[Vector3i, bool] = {} # For fast look up
for cell in used_cells:
used_cells_hah_set.set(cell, true)
for cell_coords in used_cells:
var neighbors := get_cell_neighbor(cell_coords)
var current_cell := get_cell_item(cell_coords)
var adj_at_cell_coords := adjacency[current_cell].get_adjacency_dict()
#print("adj_at_cell_coords")
#print(adj_at_cell_coords)
for diff_n in neighbors:
var neighbor_pos = cell_coords + diff_n
if used_cells_hah_set.get(neighbor_pos): continue
if not neighbor_pos in dict_grid_map:
dict_grid_map.set(neighbor_pos, [])
dict_grid_map[neighbor_pos].resize(Globals.Blocks.size())
dict_grid_map[neighbor_pos].fill(1.0)
var proba_neighbor = dict_grid_map[neighbor_pos]
#print(adj_at_cell_coords[diff_n])
for block in range(dict_grid_map[neighbor_pos].size()):
if block in adj_at_cell_coords[diff_n]:
dict_grid_map[neighbor_pos][block] *= adj_at_cell_coords[diff_n][block]
else:
dict_grid_map[neighbor_pos][block] *= 0.0
#for block in range(proba_neighbor.size()):
#var proba_adj = adj_at_cell_coords[diff_n][current_cell] if\
#current_cell in adj_at_cell_coords[diff_n] else 0.0
#dict_grid_map[neighbor_pos][block] *= proba_adj
# Normalisation des probas
for cell_cooords in dict_grid_map:
var probas_array = dict_grid_map[cell_cooords]
var sum = probas_array.reduce(func(a, b): return a+b)
for block in range(probas_array.size()):
if sum < 0.001:
dict_grid_map[cell_cooords][block] = 1.0 / Globals.Blocks.size()
else:
dict_grid_map[cell_cooords][block] /= sum
print(dict_grid_map)
print("----\n---\n---\n---")
# Oui on utilise Shanon comme des sigma, les autres betas sur internet disent "entropy = nb de possibilites", mais on est sigma ici
func calculate_entropy(cell_coords : Vector3i):
var proba_array := dict_grid_map[cell_coords]
print(proba_array)
var entropy = 0.0
for prob in proba_array:
entropy += -(prob+0.001) * log(prob+0.001) # I'm afraid of log(0) guys
return entropy
func weighted_random_choice_index(weights: Array[float]) -> int:
if weights.is_empty():
printerr("weighted_random_choice_index: Input weights array cannot be empty.")
return -1
var total_weight: float = 0.0
for w in weights:
if w < 0.0:
printerr("weighted_random_choice_index: Weights cannot be negative.")
return -1
total_weight += w
if total_weight <= 0.0:
return randi() % weights.size()
var rnd: float = randf() * total_weight
for i in range(weights.size()):
var w = weights[i]
rnd -= w
if rnd <= 0.0:
return i
# Fallback (due to potential float inaccuracies)
return weights.size() - 1
func step():
get_proba()
print(dict_grid_map)
var lowest_entropy = 100.0
var best_cell_coords : Vector3i
for cell_coords in dict_grid_map.keys():
var entropy = calculate_entropy(cell_coords)
print("cell_coords {} entropy {}".format([cell_coords, entropy], "{}"))
if entropy < lowest_entropy:
lowest_entropy = entropy
best_cell_coords = cell_coords
var arr_float : Array[float] = Array(dict_grid_map[best_cell_coords], TYPE_FLOAT, "", null)
var best_cell = weighted_random_choice_index(arr_float)
if not best_cell in Globals.ShouldRotate:
set_cell_item(best_cell_coords, best_cell)
else:
# Rotate 90 degrees around the Y-axis
var rotation_axis = Vector3.UP
var rotation_angle_rad = deg_to_rad(90)
var desired_basis = Basis.IDENTITY.rotated(rotation_axis, rotation_angle_rad)
var orientation_index = get_orthogonal_index_from_basis(desired_basis)
set_cell_item(best_cell_coords, best_cell, orientation_index)