@jetta/blocks-allocator 0.2.1-1 → 0.2.1-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/Allocation_Optimization/item_consumption_heuristic/Item_Chooser.js +6 -138
- package/src/Allocation_Optimization/item_consumption_heuristic/axle_load/axle_load_exceeds_limit.js +27 -16
- package/src/Allocation_Optimization/item_consumption_heuristic/axle_load/candidate_item_axle_score.js +42 -0
- package/src/Allocation_Optimization/item_consumption_heuristic/axle_load/evaluate_layout_axle_load.js +28 -8
- package/src/Allocation_Optimization/item_consumption_heuristic/axle_load/layout_fits_axle_load.js +3 -2
- package/src/Allocation_Optimization/item_consumption_heuristic/select_axle_balanced_slot_item.js +143 -0
- package/src/Allocation_Optimization/item_consumption_heuristic/select_slot_item.js +149 -0
- package/src/Allocation_Optimization/item_consumption_heuristic/slot_candidate_acceptance.js +110 -0
- package/src/Allocation_Optimization/item_consumption_heuristic/slot_candidate_order.js +49 -0
- package/src/allocation_flow/leftover_catalog_retry.js +67 -0
- package/src/allocation_flow/run_allocation_iteration.js +4 -25
- package/src/models/transport/Container.js +11 -0
- package/src/space_and_block_chooser/compound_first_with_leftover_fill.js +2 -2
- package/src/space_and_block_chooser/leftover_sku_hashes.js +159 -0
- package/src/space_and_block_chooser/leftover_space_memory.js +21 -5
- package/src/space_and_block_chooser/leftover_unitary_admission.js +89 -0
- package/src/space_and_block_chooser/leftover_unitary_blocks.js +73 -98
- package/src/space_and_block_chooser/leftover_unitary_empty_spaces.js +2 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const Cargo_Profile = require('../../models/Cargo_Profile')
|
|
2
|
+
const Allocated_Item = require('../../models/Allocated_Item')
|
|
3
|
+
const candidate_item_fits_axle_load = require('./axle_load/candidate_item_fits_axle_load')
|
|
4
|
+
const candidate_item_axle_score = require('./axle_load/candidate_item_axle_score')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builds the allocated item at the slot position.
|
|
8
|
+
* @param {object} allocation_item
|
|
9
|
+
* @param {object} item_position
|
|
10
|
+
* @returns {object}
|
|
11
|
+
*/
|
|
12
|
+
function build_allocated_item(allocation_item, item_position)
|
|
13
|
+
{
|
|
14
|
+
return new Allocated_Item(allocation_item, item_position.x, item_position.y, item_position.z)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Accepts the first legal candidate and commits stacking.
|
|
19
|
+
* @param {object} data
|
|
20
|
+
* @param {object} container
|
|
21
|
+
* @param {object} allocation_item
|
|
22
|
+
* @param {object} item_position
|
|
23
|
+
* @returns {object|undefined}
|
|
24
|
+
*/
|
|
25
|
+
function try_accept_candidate(data, container, allocation_item, item_position)
|
|
26
|
+
{
|
|
27
|
+
const allocated_item = build_allocated_item(allocation_item, item_position)
|
|
28
|
+
if (!candidate_item_fits_axle_load(data, allocated_item))
|
|
29
|
+
{
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
if (should_defer_loose_on_block_stacking_validation(data.block))
|
|
33
|
+
{
|
|
34
|
+
return allocated_item
|
|
35
|
+
}
|
|
36
|
+
if (!container.is_a_valid_stacking(allocated_item))
|
|
37
|
+
{
|
|
38
|
+
return undefined
|
|
39
|
+
}
|
|
40
|
+
return allocated_item
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Scores a candidate without writing the stacking index.
|
|
45
|
+
* @param {object} data
|
|
46
|
+
* @param {object} context
|
|
47
|
+
* @param {object} allocation_item
|
|
48
|
+
* @returns {{ allocated_item: object, ratio: number, allocation_item: object }|undefined}
|
|
49
|
+
*/
|
|
50
|
+
function score_without_commit(data, context, allocation_item)
|
|
51
|
+
{
|
|
52
|
+
const allocated_item = build_allocated_item(allocation_item, context.item_position)
|
|
53
|
+
const score = candidate_item_axle_score(data, allocated_item)
|
|
54
|
+
if (!score.fits || !stacking_probe_allows(data, context.container, allocated_item))
|
|
55
|
+
{
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
return { allocated_item, ratio: score.ratio, allocation_item }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Commits the chosen candidate into the live stacking index.
|
|
63
|
+
* @param {object} data
|
|
64
|
+
* @param {object} container
|
|
65
|
+
* @param {{ allocated_item: object }} scored
|
|
66
|
+
* @returns {object|undefined}
|
|
67
|
+
*/
|
|
68
|
+
function commit_scored_candidate(data, container, scored)
|
|
69
|
+
{
|
|
70
|
+
if (should_defer_loose_on_block_stacking_validation(data.block))
|
|
71
|
+
{
|
|
72
|
+
return scored.allocated_item
|
|
73
|
+
}
|
|
74
|
+
if (!container.is_a_valid_stacking(scored.allocated_item))
|
|
75
|
+
{
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
return scored.allocated_item
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {object} data
|
|
83
|
+
* @param {object} container
|
|
84
|
+
* @param {object} allocated_item
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
function stacking_probe_allows(data, container, allocated_item)
|
|
88
|
+
{
|
|
89
|
+
if (should_defer_loose_on_block_stacking_validation(data.block))
|
|
90
|
+
{
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
if (typeof container.probe_stacking === 'function')
|
|
94
|
+
{
|
|
95
|
+
return container.probe_stacking(allocated_item)
|
|
96
|
+
}
|
|
97
|
+
return container.is_a_valid_stacking(allocated_item)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function should_defer_loose_on_block_stacking_validation(block)
|
|
101
|
+
{
|
|
102
|
+
return Cargo_Profile.stack_loose_on_tie_ratio_blocks()
|
|
103
|
+
&& block?.get_slot_list?.().length === 1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
try_accept_candidate,
|
|
108
|
+
score_without_commit,
|
|
109
|
+
commit_scored_candidate
|
|
110
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const No_Available_Items_Exception = require('./exceptions/No_Available_Items_Exception')
|
|
2
|
+
const No_Slots_For_Items_Exception = require('./exceptions/No_Slots_For_Items_Exception')
|
|
3
|
+
const { order_items_by_capacity_bias } = require('./Capacity_Bias_Item_Order')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Removes one candidate from the working list.
|
|
7
|
+
* @param {object[]} candidate_item_list
|
|
8
|
+
* @param {object} allocation_item
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
11
|
+
function remove_item_from_candidate_list(candidate_item_list, allocation_item)
|
|
12
|
+
{
|
|
13
|
+
const index = candidate_item_list.indexOf(allocation_item)
|
|
14
|
+
if (index > -1)
|
|
15
|
+
{
|
|
16
|
+
candidate_item_list.splice(index, 1)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Next candidate in the existing capacity-bias order.
|
|
22
|
+
* @param {object[]} item_list
|
|
23
|
+
* @param {number|undefined} qty_of_slots
|
|
24
|
+
* @param {object} container
|
|
25
|
+
* @returns {object}
|
|
26
|
+
*/
|
|
27
|
+
function get_item(item_list, qty_of_slots, container)
|
|
28
|
+
{
|
|
29
|
+
if (qty_of_slots === undefined)
|
|
30
|
+
{
|
|
31
|
+
throw new No_Slots_For_Items_Exception()
|
|
32
|
+
}
|
|
33
|
+
if (item_list.length === 0)
|
|
34
|
+
{
|
|
35
|
+
throw new No_Available_Items_Exception()
|
|
36
|
+
}
|
|
37
|
+
const ordered_item_list = order_items_by_capacity_bias(item_list, container)
|
|
38
|
+
const preferable_item_list = ordered_item_list.filter(item => item.get_amount() >= qty_of_slots)
|
|
39
|
+
if (preferable_item_list.length > 0)
|
|
40
|
+
{
|
|
41
|
+
return preferable_item_list[0]
|
|
42
|
+
}
|
|
43
|
+
return ordered_item_list[0]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
get_item,
|
|
48
|
+
remove_item_from_candidate_list
|
|
49
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const leftover_unitary_blocks = require('../space_and_block_chooser/leftover_unitary_blocks')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Records a stall, unless this is the one-shot retry that admits hidden unitaries.
|
|
5
|
+
* @param {object} context
|
|
6
|
+
* @param {object[]} effective_item_list
|
|
7
|
+
* @param {Record<string, number>} amounts_before
|
|
8
|
+
* @param {object} empty_space
|
|
9
|
+
* @param {string} fingerprint
|
|
10
|
+
* @param {object[]} block_list
|
|
11
|
+
* @returns {boolean} True when any effective item amount decreased.
|
|
12
|
+
*/
|
|
13
|
+
function update_skip_state_after_attempt(
|
|
14
|
+
context,
|
|
15
|
+
effective_item_list,
|
|
16
|
+
amounts_before,
|
|
17
|
+
empty_space,
|
|
18
|
+
fingerprint,
|
|
19
|
+
block_list
|
|
20
|
+
)
|
|
21
|
+
{
|
|
22
|
+
if (amount_decreased(effective_item_list, amounts_before))
|
|
23
|
+
{
|
|
24
|
+
context.space_skip_state.clear_stalls()
|
|
25
|
+
context.recovery_state.count = 0
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
if (defer_stall_for_hidden_unitaries(context, block_list))
|
|
29
|
+
{
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
context.space_skip_state.record_stall(empty_space, fingerprint)
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {object[]} effective_item_list
|
|
38
|
+
* @param {Record<string, number>} amounts_before
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
function amount_decreased(effective_item_list, amounts_before)
|
|
42
|
+
{
|
|
43
|
+
return effective_item_list.some(item => item.get_amount() < amounts_before[item.get_id()])
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Opens the leftover catalog once when a no-progress attempt used only remembered unitaries.
|
|
48
|
+
* @param {object} context
|
|
49
|
+
* @param {object[]} block_list
|
|
50
|
+
* @returns {boolean} True when the space must be retried before it is stalled.
|
|
51
|
+
*/
|
|
52
|
+
function defer_stall_for_hidden_unitaries(context, block_list)
|
|
53
|
+
{
|
|
54
|
+
const session = context.chooser_session?.obligate_compound_session
|
|
55
|
+
const remembered_only = leftover_unitary_blocks.blocks_are_remembered_unitaries_only(
|
|
56
|
+
block_list ?? [],
|
|
57
|
+
session
|
|
58
|
+
)
|
|
59
|
+
if (!remembered_only)
|
|
60
|
+
{
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
session.admit_non_remembered_unitaries = true
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { update_skip_state_after_attempt }
|
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
update_item_continuation_lock
|
|
10
10
|
} = require('./item_continuation_lock_gate')
|
|
11
11
|
const { empty_space_stacks_on_tie_ratio_supporter } = require('../Allocation_Optimization/tie_ratio/tie_ratio_orientation_below_empty_space')
|
|
12
|
+
const { update_skip_state_after_attempt } = require('./leftover_catalog_retry')
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Runs one allocation iteration: chooser, LP attempt, skip-state update.
|
|
@@ -69,7 +70,8 @@ async function run_allocation_iteration(context, container, locked_item)
|
|
|
69
70
|
effective_item_list,
|
|
70
71
|
amounts_before,
|
|
71
72
|
empty_space,
|
|
72
|
-
fingerprint
|
|
73
|
+
fingerprint,
|
|
74
|
+
block_list
|
|
73
75
|
)
|
|
74
76
|
update_item_continuation_lock(context, wall_output.single_slot_allocated_item_ids)
|
|
75
77
|
context.picking_continuity.update_from_wall(wall_output.last_allocated_item_id)
|
|
@@ -117,29 +119,6 @@ function update_compound_probe_session(context, block_list, effective_item_list,
|
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
/**
|
|
121
|
-
* @param {object} context
|
|
122
|
-
* @param {object[]} effective_item_list
|
|
123
|
-
* @param {Record<string, number>} amounts_before
|
|
124
|
-
* @param {object} empty_space
|
|
125
|
-
* @param {string} fingerprint
|
|
126
|
-
* @returns {boolean} True when any effective item amount decreased.
|
|
127
|
-
*/
|
|
128
|
-
function update_skip_state_after_attempt(context, effective_item_list, amounts_before, empty_space, fingerprint)
|
|
129
|
-
{
|
|
130
|
-
const progressed = effective_item_list.some(
|
|
131
|
-
item => item.get_amount() < amounts_before[item.get_id()]
|
|
132
|
-
)
|
|
133
|
-
if (progressed)
|
|
134
|
-
{
|
|
135
|
-
context.space_skip_state.clear_stalls()
|
|
136
|
-
context.recovery_state.count = 0
|
|
137
|
-
return true
|
|
138
|
-
}
|
|
139
|
-
context.space_skip_state.record_stall(empty_space, fingerprint)
|
|
140
|
-
return false
|
|
141
|
-
}
|
|
142
|
-
|
|
143
122
|
/**
|
|
144
123
|
* @param {object[]} block_list
|
|
145
124
|
* @returns {void}
|
|
@@ -152,4 +131,4 @@ function assert_blocks_available(block_list)
|
|
|
152
131
|
}
|
|
153
132
|
}
|
|
154
133
|
|
|
155
|
-
module.exports = { run_allocation_iteration }
|
|
134
|
+
module.exports = { run_allocation_iteration, update_skip_state_after_attempt }
|
|
@@ -87,6 +87,17 @@ class Container extends Space
|
|
|
87
87
|
return this.#stacking_feasibility_checker.validate_stackings(item_data)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Checks stacking on a clone so the live index stays unchanged.
|
|
92
|
+
* @param {object} item
|
|
93
|
+
* @returns {boolean}
|
|
94
|
+
*/
|
|
95
|
+
probe_stacking(item)
|
|
96
|
+
{
|
|
97
|
+
const probe = this.#stacking_feasibility_checker.clone()
|
|
98
|
+
return probe.validate_stackings(create_input(item))
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
/**
|
|
91
102
|
* Drops provisional stacking commits left by failed slot/wall attempts.
|
|
92
103
|
* Restores the pre-attempt checkpoint when stacking was touched; otherwise a no-op.
|
|
@@ -24,10 +24,10 @@ async function resolve_after_compound_probe(
|
|
|
24
24
|
)
|
|
25
25
|
{
|
|
26
26
|
const compound_block_list = compound_blocks_from_list(pre_selected_block_list)
|
|
27
|
-
const leftover_block_list = leftover_unitary_blocks(
|
|
27
|
+
const leftover_block_list = leftover_unitary_blocks.from_session(
|
|
28
28
|
pre_selected_block_list,
|
|
29
29
|
compound_block_list,
|
|
30
|
-
filter_data.obligate_compound_session
|
|
30
|
+
filter_data.obligate_compound_session
|
|
31
31
|
)
|
|
32
32
|
leftover_unitary_blocks.remember_leftover_sku_hashes(
|
|
33
33
|
filter_data.obligate_compound_session,
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const { cube_map_function } = require('../Blocks_Preparation/Cube_Map_Function')
|
|
2
|
+
const round = require('../tools/Rounder')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Stores leftover SKU shape hashes while compounds still remain.
|
|
6
|
+
* @param {{ leftover_sku_shape_hashes?: string[] }|undefined} session
|
|
7
|
+
* @param {object[]} leftover_block_list
|
|
8
|
+
* @param {object[]} compound_block_list
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
11
|
+
function remember_leftover_sku_hashes(session, leftover_block_list, compound_block_list)
|
|
12
|
+
{
|
|
13
|
+
if (!session || compound_block_list.length === 0 || leftover_block_list.length === 0)
|
|
14
|
+
{
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
if (session.leftover_sku_shape_hashes?.length > 0)
|
|
18
|
+
{
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
session.leftover_sku_shape_hashes = [...leftover_sku_shape_hashes(leftover_block_list)]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Width/height/length hashes for leftover 1-slot blocks, including swapped floor axes.
|
|
26
|
+
* @param {object[]} leftover_block_list
|
|
27
|
+
* @returns {Set<string>}
|
|
28
|
+
*/
|
|
29
|
+
function leftover_sku_shape_hashes(leftover_block_list)
|
|
30
|
+
{
|
|
31
|
+
const hashes = new Set()
|
|
32
|
+
for (const block of leftover_block_list)
|
|
33
|
+
{
|
|
34
|
+
add_slot_orientation_hashes(hashes, block)
|
|
35
|
+
}
|
|
36
|
+
return hashes
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {Set<string>} hashes
|
|
41
|
+
* @param {object} block
|
|
42
|
+
* @returns {void}
|
|
43
|
+
*/
|
|
44
|
+
function add_slot_orientation_hashes(hashes, block)
|
|
45
|
+
{
|
|
46
|
+
if (typeof block.get_slot_list !== 'function')
|
|
47
|
+
{
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
const slot_list = block.get_slot_list()
|
|
51
|
+
if (slot_list.length !== 1 || typeof slot_list[0].get_width !== 'function')
|
|
52
|
+
{
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
hashes.add(cube_map_function(slot_list[0]))
|
|
56
|
+
hashes.add(swapped_length_width_hash(slot_list[0]))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {object} slot
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
function swapped_length_width_hash(slot)
|
|
64
|
+
{
|
|
65
|
+
return round(slot.get_length()) + ';' + round(slot.get_height()) + ';' + round(slot.get_width())
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {Set<string>|string[]|undefined} remembered_sku_hashes
|
|
70
|
+
* @returns {Set<string>}
|
|
71
|
+
*/
|
|
72
|
+
function to_hash_set(remembered_sku_hashes)
|
|
73
|
+
{
|
|
74
|
+
if (!remembered_sku_hashes)
|
|
75
|
+
{
|
|
76
|
+
return new Set()
|
|
77
|
+
}
|
|
78
|
+
if (remembered_sku_hashes instanceof Set)
|
|
79
|
+
{
|
|
80
|
+
return remembered_sku_hashes
|
|
81
|
+
}
|
|
82
|
+
return new Set(remembered_sku_hashes)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {object[]} compound_block_list
|
|
87
|
+
* @returns {Set<string>}
|
|
88
|
+
*/
|
|
89
|
+
function compound_slot_shape_hashes(compound_block_list)
|
|
90
|
+
{
|
|
91
|
+
const hashes = new Set()
|
|
92
|
+
for (const block of compound_block_list)
|
|
93
|
+
{
|
|
94
|
+
add_compound_slot_hashes(hashes, block)
|
|
95
|
+
}
|
|
96
|
+
return hashes
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {Set<string>} hashes
|
|
101
|
+
* @param {object} block
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function add_compound_slot_hashes(hashes, block)
|
|
105
|
+
{
|
|
106
|
+
if (typeof block.get_slot_list !== 'function')
|
|
107
|
+
{
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
for (const slot of block.get_slot_list())
|
|
111
|
+
{
|
|
112
|
+
add_measured_slot_hash(hashes, slot)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {Set<string>} hashes
|
|
118
|
+
* @param {object} slot
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
function add_measured_slot_hash(hashes, slot)
|
|
122
|
+
{
|
|
123
|
+
if (typeof slot.get_width !== 'function')
|
|
124
|
+
{
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
hashes.add(cube_map_function(slot))
|
|
128
|
+
hashes.add(swapped_length_width_hash(slot))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {object} block
|
|
133
|
+
* @param {Set<string>} remembered
|
|
134
|
+
* @returns {boolean}
|
|
135
|
+
*/
|
|
136
|
+
function is_unitary_matching_hashes(block, remembered)
|
|
137
|
+
{
|
|
138
|
+
if (typeof block.get_slot_list !== 'function')
|
|
139
|
+
{
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
const slot_list = block.get_slot_list()
|
|
143
|
+
if (slot_list.length !== 1 || typeof slot_list[0].get_width !== 'function')
|
|
144
|
+
{
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
return remembered.has(cube_map_function(slot_list[0]))
|
|
148
|
+
|| remembered.has(swapped_length_width_hash(slot_list[0]))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
remember_leftover_sku_hashes,
|
|
153
|
+
leftover_sku_shape_hashes,
|
|
154
|
+
to_hash_set,
|
|
155
|
+
compound_slot_shape_hashes,
|
|
156
|
+
is_unitary_matching_hashes,
|
|
157
|
+
cube_map_function,
|
|
158
|
+
swapped_length_width_hash
|
|
159
|
+
}
|
|
@@ -56,12 +56,10 @@ function skip_probe_or_leftover_resolution(filter_data, empty_space_list, pre_se
|
|
|
56
56
|
{
|
|
57
57
|
const session = filter_data.obligate_compound_session
|
|
58
58
|
const leftover_spaces = spaces_matching_remembered_leftover(empty_space_list, session)
|
|
59
|
-
const leftover_block_list = leftover_unitary_blocks(
|
|
59
|
+
const leftover_block_list = leftover_unitary_blocks.from_session(
|
|
60
60
|
pre_selected_block_list,
|
|
61
|
-
pre_selected_block_list
|
|
62
|
-
|
|
63
|
-
),
|
|
64
|
-
session?.leftover_sku_shape_hashes
|
|
61
|
+
compound_blocks(pre_selected_block_list),
|
|
62
|
+
session
|
|
65
63
|
)
|
|
66
64
|
if (leftover_spaces.length > 0 && leftover_block_list.length > 0)
|
|
67
65
|
{
|
|
@@ -75,6 +73,24 @@ function skip_probe_or_leftover_resolution(filter_data, empty_space_list, pre_se
|
|
|
75
73
|
return skip_probe_resolution(empty_space_list, pre_selected_block_list)
|
|
76
74
|
}
|
|
77
75
|
|
|
76
|
+
/**
|
|
77
|
+
* @param {object[]} block_list
|
|
78
|
+
* @returns {object[]}
|
|
79
|
+
*/
|
|
80
|
+
function compound_blocks(block_list)
|
|
81
|
+
{
|
|
82
|
+
return block_list.filter(is_compound_block)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {object} block
|
|
87
|
+
* @returns {boolean}
|
|
88
|
+
*/
|
|
89
|
+
function is_compound_block(block)
|
|
90
|
+
{
|
|
91
|
+
return typeof block.get_slot_list === 'function' && block.get_slot_list().length > 1
|
|
92
|
+
}
|
|
93
|
+
|
|
78
94
|
function space_bbox(space)
|
|
79
95
|
{
|
|
80
96
|
if (typeof space.get_x !== 'function')
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const {
|
|
2
|
+
to_hash_set,
|
|
3
|
+
is_unitary_matching_hashes
|
|
4
|
+
} = require('./leftover_sku_hashes')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 1-slot blocks whose shape is outside the remembered leftover allow-list.
|
|
8
|
+
* @param {object[]} pre_selected_block_list
|
|
9
|
+
* @param {Set<string>|string[]} [remembered_sku_hashes]
|
|
10
|
+
* @returns {object[]}
|
|
11
|
+
*/
|
|
12
|
+
function unitaries_outside_remembered_hashes(pre_selected_block_list, remembered_sku_hashes)
|
|
13
|
+
{
|
|
14
|
+
const remembered = to_hash_set(remembered_sku_hashes)
|
|
15
|
+
return pre_selected_block_list.filter(block => is_hidden_unitary(block, remembered))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Remembered matches plus every other 1-slot block, in pre-selected order.
|
|
20
|
+
* @param {object[]} pre_selected_block_list
|
|
21
|
+
* @param {object[]} remembered_matches
|
|
22
|
+
* @param {Set<string>|string[]|undefined} remembered_sku_hashes
|
|
23
|
+
* @returns {object[]}
|
|
24
|
+
*/
|
|
25
|
+
function unite_with_hidden_unitaries(pre_selected_block_list, remembered_matches, remembered_sku_hashes)
|
|
26
|
+
{
|
|
27
|
+
const hidden = new Set(
|
|
28
|
+
unitaries_outside_remembered_hashes(pre_selected_block_list, remembered_sku_hashes)
|
|
29
|
+
)
|
|
30
|
+
const remembered = new Set(remembered_matches)
|
|
31
|
+
return pre_selected_block_list.filter(block => remembered.has(block) || hidden.has(block))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* True when every block is a remembered 1-slot and the one-shot release has not run.
|
|
36
|
+
* @param {object[]} block_list
|
|
37
|
+
* @param {{ leftover_sku_shape_hashes?: string[], admit_non_remembered_unitaries?: boolean }|undefined} session
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function blocks_are_remembered_unitaries_only(block_list, session)
|
|
41
|
+
{
|
|
42
|
+
if (!session || session.admit_non_remembered_unitaries === true || block_list.length === 0)
|
|
43
|
+
{
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
const remembered = to_hash_set(session.leftover_sku_shape_hashes)
|
|
47
|
+
if (remembered.size === 0)
|
|
48
|
+
{
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
return block_list.every(block => is_unitary_matching_hashes(block, remembered))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {object} block
|
|
56
|
+
* @param {Set<string>} remembered
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
function is_hidden_unitary(block, remembered)
|
|
60
|
+
{
|
|
61
|
+
if (!is_single_slot_block(block))
|
|
62
|
+
{
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
if (remembered.size === 0)
|
|
66
|
+
{
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
return !is_unitary_matching_hashes(block, remembered)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {object} block
|
|
74
|
+
* @returns {boolean}
|
|
75
|
+
*/
|
|
76
|
+
function is_single_slot_block(block)
|
|
77
|
+
{
|
|
78
|
+
if (typeof block.get_slot_list !== 'function')
|
|
79
|
+
{
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
return block.get_slot_list().length === 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
unitaries_outside_remembered_hashes,
|
|
87
|
+
unite_with_hidden_unitaries,
|
|
88
|
+
blocks_are_remembered_unitaries_only
|
|
89
|
+
}
|