Record original-alignment implementation and traceability checkpoint
Capture current source, content data, tests, asset licenses, and original-specification traceability. Original implementation remains incomplete. The accepted matching UI generation passed 154 suites and 12,253 checks, with eight rendered staff-contract screens reviewed. Latest native Windows and full-original acceptance remain outstanding.
This commit is contained in:
+126
-33
@@ -14,7 +14,7 @@ const ORTHOGONAL_MOVE_COST := 100
|
||||
const DEFAULT_CONNECTOR_COST := 150
|
||||
const DEFAULT_CELL_CAPACITY := 4
|
||||
const CONGESTION_COST_PER_EXCESS := 75
|
||||
const MINIMUM_SPEED_MULTIPLIER := 0.25
|
||||
const MINIMUM_SPEED_MULTIPLIER := 0.5
|
||||
const STUCK_THRESHOLD_MINUTES := 30
|
||||
const ELEVATOR_STARVATION_MINUTES := 60
|
||||
const SNAPSHOT_VERSION := 5
|
||||
@@ -569,13 +569,10 @@ func get_speed_multiplier(cell: Vector3i) -> float:
|
||||
var traffic: Dictionary = _cell_traffic.get(cell, {})
|
||||
if traffic.is_empty():
|
||||
return 1.0
|
||||
var capacity := int(traffic[&"base_capacity"])
|
||||
var density := int(traffic[&"current_density"])
|
||||
return clampf(
|
||||
float(capacity) / float(maxi(capacity, density)),
|
||||
MINIMUM_SPEED_MULTIPLIER,
|
||||
1.0
|
||||
)
|
||||
# Original 4.3: overlapping units move at half speed. A wide corridor's
|
||||
# pathfinding capacity must not silently redefine this movement debuff.
|
||||
return MINIMUM_SPEED_MULTIPLIER if int(traffic[&"current_density"]) > 1 else 1.0
|
||||
|
||||
|
||||
|
||||
func project_actor_position(
|
||||
@@ -839,7 +836,8 @@ func dequeue_elevator_boarding(
|
||||
connector_id: StringName,
|
||||
current_floor: int,
|
||||
absolute_minute: int,
|
||||
available_capacity: int = -1
|
||||
available_capacity: int = -1,
|
||||
companions: Dictionary = {}
|
||||
) -> Array[Dictionary]:
|
||||
var boarded: Array[Dictionary] = []
|
||||
if not _connectors.has(connector_id):
|
||||
@@ -860,12 +858,27 @@ func dequeue_elevator_boarding(
|
||||
return boarded
|
||||
|
||||
var queue := get_elevator_queue(connector_id, absolute_minute)
|
||||
var selected: Dictionary = {}
|
||||
var by_actor: Dictionary = {}
|
||||
for request: Dictionary in queue: by_actor[request[&"unit_id"]] = request
|
||||
for request: Dictionary in queue:
|
||||
var actor: StringName = request[&"unit_id"]
|
||||
if selected.has(actor) or int(request[&"floor_from"]) != current_floor: continue
|
||||
var partner: StringName = companions.get(actor, &"")
|
||||
var group: Array[Dictionary] = [request]
|
||||
if not partner.is_empty():
|
||||
if companions.get(partner, &"") != actor or not by_actor.has(partner): continue
|
||||
var other: Dictionary = by_actor[partner]
|
||||
if other[&"floor_from"] != current_floor or other[&"floor_to"] != request[&"floor_to"]: continue
|
||||
group.append(other)
|
||||
if boarded.size() + group.size() > boarding_capacity:
|
||||
break # Keep the oldest pair together and ahead of later requests.
|
||||
for member: Dictionary in group:
|
||||
selected[member[&"unit_id"]] = true
|
||||
boarded.append(member.duplicate(true))
|
||||
var remaining: Array = []
|
||||
for request: Dictionary in queue:
|
||||
if boarded.size() < boarding_capacity and int(request[&"floor_from"]) == current_floor:
|
||||
boarded.append(request.duplicate(true))
|
||||
else:
|
||||
remaining.append(request.duplicate(true))
|
||||
if not selected.has(request[&"unit_id"]): remaining.append(request.duplicate(true))
|
||||
connector[&"request_queue"] = remaining
|
||||
connector[&"current_floor"] = current_floor
|
||||
connector[&"cabin_state"] = CABIN_DOOR_OPEN if not boarded.is_empty() else CABIN_IDLE
|
||||
@@ -1100,6 +1113,19 @@ func cached_path_count() -> int:
|
||||
return _path_cache.size()
|
||||
|
||||
|
||||
## Compare graph membership without serializing traffic, actors and connectors.
|
||||
## Duplicate expected cells are invalid, just as in snapshot crosslink validation.
|
||||
func matches_passable_cells(expected_cells: Array[Vector3i]) -> bool:
|
||||
if expected_cells.size() != _passable_cells.size():
|
||||
return false
|
||||
var seen: Dictionary = {}
|
||||
for cell: Vector3i in expected_cells:
|
||||
if seen.has(cell) or not _passable_cells.has(cell):
|
||||
return false
|
||||
seen[cell] = true
|
||||
return true
|
||||
|
||||
|
||||
func has_cell(cell: Vector3i) -> bool:
|
||||
return _passable_cells.has(cell)
|
||||
|
||||
@@ -1295,7 +1321,7 @@ func create_snapshot() -> Dictionary:
|
||||
var saved_cell_traffic: Array = []
|
||||
for cell_value: Variant in cells:
|
||||
var cell: Vector3i = cell_value
|
||||
var traffic: Dictionary = _cell_traffic.get(cell, _default_cell_traffic())
|
||||
var traffic: Dictionary = _cell_traffic[cell] if _cell_traffic.has(cell) else _default_cell_traffic()
|
||||
saved_cell_traffic.append({
|
||||
&"cell": cell,
|
||||
&"base_capacity": int(traffic[&"base_capacity"]),
|
||||
@@ -1579,17 +1605,20 @@ func _run_a_star(
|
||||
if start == goal:
|
||||
return [start]
|
||||
|
||||
var open_set: Dictionary = {start: true}
|
||||
var frontier: Array[Array] = []
|
||||
_heap_push(frontier, [start, 0, _heuristic(start, goal)])
|
||||
var closed_set: Dictionary = {}
|
||||
var came_from: Dictionary = {}
|
||||
var g_score: Dictionary = {start: 0}
|
||||
|
||||
while not open_set.is_empty():
|
||||
var current := _select_best_open_node(open_set, g_score, goal)
|
||||
while not frontier.is_empty():
|
||||
var entry := _heap_pop(frontier)
|
||||
var current: Vector3i = entry[0]
|
||||
if closed_set.has(current) or int(entry[1]) != int(g_score[current]):
|
||||
continue
|
||||
if current == goal:
|
||||
return _reconstruct_path(came_from, current)
|
||||
|
||||
open_set.erase(current)
|
||||
closed_set[current] = true
|
||||
var edges: Array = _adjacency.get(current, [])
|
||||
for edge_value: Variant in edges:
|
||||
@@ -1612,25 +1641,45 @@ func _run_a_star(
|
||||
g_score[neighbor] = tentative_cost
|
||||
if closed_set.has(neighbor):
|
||||
closed_set.erase(neighbor)
|
||||
open_set[neighbor] = true
|
||||
_heap_push(frontier, [neighbor, tentative_cost, tentative_cost + _heuristic(neighbor, goal)])
|
||||
|
||||
return []
|
||||
|
||||
|
||||
func _select_best_open_node(open_set: Dictionary, g_score: Dictionary, goal: Vector3i) -> Vector3i:
|
||||
var best := Vector3i.ZERO
|
||||
var has_best := false
|
||||
var best_score := _INFINITE_COST
|
||||
for candidate_value: Variant in open_set:
|
||||
var candidate: Vector3i = candidate_value
|
||||
var score: int = int(g_score[candidate]) + _heuristic(candidate, goal)
|
||||
if not has_best or score < best_score:
|
||||
best = candidate
|
||||
best_score = score
|
||||
has_best = true
|
||||
elif score == best_score and _coordinate_less(candidate, best):
|
||||
best = candidate
|
||||
return best
|
||||
# Query-local heap. Stale improved-cost entries are skipped on removal.
|
||||
# Keep the original f-score / x / y / layer order, including reopened nodes.
|
||||
func _heap_less(left: Array, right: Array) -> bool:
|
||||
return int(left[2]) < int(right[2]) or (int(left[2]) == int(right[2]) and _coordinate_less(left[0], right[0]))
|
||||
|
||||
|
||||
func _heap_push(heap: Array[Array], entry: Array) -> void:
|
||||
heap.append(entry)
|
||||
var index := heap.size() - 1
|
||||
while index > 0:
|
||||
var parent := (index - 1) >> 1
|
||||
if not _heap_less(entry, heap[parent]):
|
||||
break
|
||||
heap[index] = heap[parent]
|
||||
index = parent
|
||||
heap[index] = entry
|
||||
|
||||
|
||||
func _heap_pop(heap: Array[Array]) -> Array:
|
||||
var first := heap[0]
|
||||
var last: Array = heap.pop_back()
|
||||
if heap.is_empty():
|
||||
return first
|
||||
var index := 0
|
||||
while index * 2 + 1 < heap.size():
|
||||
var child := index * 2 + 1
|
||||
if child + 1 < heap.size() and _heap_less(heap[child + 1], heap[child]):
|
||||
child += 1
|
||||
if not _heap_less(heap[child], last):
|
||||
break
|
||||
heap[index] = heap[child]
|
||||
index = child
|
||||
heap[index] = last
|
||||
return first
|
||||
|
||||
|
||||
func _heuristic(from_cell: Vector3i, to_cell: Vector3i) -> int:
|
||||
@@ -2607,3 +2656,47 @@ func _dictionary_value(source: Dictionary, key: StringName, default_value: Varia
|
||||
return source[key]
|
||||
var string_key := str(key)
|
||||
return source.get(string_key, default_value)
|
||||
|
||||
|
||||
## Rescue uses BACK circulation, with FRONT access legs at either endpoint.
|
||||
## Optional managed elevators must have room for both patient and carrier.
|
||||
func find_backyard_path(start: Vector3i, goal: Vector3i, allow_elevators := false) -> Array[Vector3i]:
|
||||
if not has_cell(start) or not has_cell(goal) or _cell_is_temporarily_blocked(start) or _cell_is_temporarily_blocked(goal):
|
||||
return []
|
||||
if start == goal:
|
||||
return [start]
|
||||
var first := Vector4i(start.x, start.y, start.z, 2 if start.z == 1 else 0)
|
||||
var frontier: Array[Vector4i] = [first]
|
||||
var visited := {first: true}
|
||||
var parents: Dictionary = {}
|
||||
var cursor := 0
|
||||
while cursor < frontier.size():
|
||||
var state := frontier[cursor]
|
||||
cursor += 1
|
||||
var cell := Vector3i(state.x,state.y,state.z)
|
||||
for edge: Dictionary in _adjacency.get(cell, []):
|
||||
var next: Vector3i = edge[&"to"]
|
||||
if _cell_is_temporarily_blocked(next) or not _edge_meets_vertical_policy(cell, edge) or not _edge_is_accessible(edge, [&"staff"]):
|
||||
continue
|
||||
var connector: Dictionary = _connectors.get(edge[&"connector_id"], {})
|
||||
if connector.get(&"movement_type", &"") == MOVEMENT_ELEVATOR:
|
||||
if not allow_elevators or cell.z != 1 or next.z != 1 or not String(edge[&"connector_id"]).begins_with("transport.") or int(connector.get(&"cabin_capacity", 0)) < 2:
|
||||
continue
|
||||
if state.w == 3 and next.z == 1:
|
||||
continue
|
||||
var phase := state.w
|
||||
if next.z == 1: phase = 1 if state.w == 0 else 2
|
||||
elif state.w == 1: continue
|
||||
elif state.w == 2: phase = 3
|
||||
var key := Vector4i(next.x,next.y,next.z,phase)
|
||||
if visited.has(key): continue
|
||||
visited[key] = true
|
||||
parents[key] = state
|
||||
if next == goal and phase > 0:
|
||||
var result: Array[Vector3i] = [next]
|
||||
while parents.has(key):
|
||||
key = parents[key]
|
||||
result.push_front(Vector3i(key.x,key.y,key.z))
|
||||
return result
|
||||
frontier.append(key)
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user