@happychef/algorithm 1.1.1 → 1.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happychef/algorithm",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Restaurant and reservation algorithm utilities",
5
5
  "main": "index.js",
6
6
  "author": "happy chef",
@@ -135,14 +135,15 @@ function calculateDistance(tableA, tableB) {
135
135
  }
136
136
 
137
137
  /**
138
- * OPTIMIZED: Backtracking function to find a combination of tables (multi-table assignment).
139
- * Improvements:
140
- * - Pre-computed valid tables list to avoid redundant checks
141
- * - Better pruning with capacity tracking
142
- * - Early termination when exact match found
138
+ * HIGHLY OPTIMIZED: Backtracking with aggressive pruning for multi-table assignment.
139
+ * Key optimizations:
140
+ * - Tables are PRE-FILTERED, so no validity checks needed here
141
+ * - Aggressive branch pruning with capacity tracking
142
+ * - Early termination on optimal solutions
143
+ * - Minimal logging to reduce overhead
143
144
  */
144
- function findMultiTableCombination(tables, guestsNeeded, startIndex, currentSet, best, requiredSlots, tableOccupiedSlots, reservationDateStr, reservationTimeStr) {
145
- // Base case: All guests are seated
145
+ function findMultiTableCombination(tables, guestsNeeded, startIndex, currentSet, best) {
146
+ // Base case: All guests seated
146
147
  if (guestsNeeded <= 0) {
147
148
  // Calculate total distance for the current combination
148
149
  let distanceSum = 0;
@@ -151,82 +152,72 @@ function findMultiTableCombination(tables, guestsNeeded, startIndex, currentSet,
151
152
  distanceSum += calculateDistance(currentSet[i], currentSet[j]);
152
153
  }
153
154
  }
154
- // Update best solution if this one is better (fewer tables, then lower distance)
155
+ // Update if better (fewer tables, or same count with lower distance)
155
156
  if (currentSet.length < best.tableCount || (currentSet.length === best.tableCount && distanceSum < best.minDistance)) {
156
157
  best.minDistance = distanceSum;
157
- best.tables = [...currentSet]; // Store a copy
158
+ best.tables = [...currentSet];
158
159
  best.tableCount = currentSet.length;
159
- console.log(`Found new best table combination: ${best.tables.map(t => t.tableNumber).join(', ')}`);
160
160
  }
161
161
  return;
162
162
  }
163
163
 
164
- if (startIndex >= tables.length) {
164
+ // PRUNING: Can't improve on current best table count
165
+ if (currentSet.length >= best.tableCount && best.tableCount !== Infinity) {
165
166
  return;
166
167
  }
167
168
 
168
- // OPTIMIZATION: Prune if we already have enough tables in current set
169
- if (currentSet.length >= best.tableCount && best.tableCount !== Infinity) {
170
- return; // Can't improve on current best
169
+ // PRUNING: Not enough tables remaining
170
+ if (startIndex >= tables.length) {
171
+ return;
171
172
  }
172
173
 
173
- // OPTIMIZATION: Calculate max possible capacity from remaining tables
174
- let maxPossibleCapacity = 0;
174
+ // PRUNING: Calculate remaining capacity (tables are pre-filtered, so all are valid)
175
+ let maxRemainingCapacity = 0;
175
176
  for (let i = startIndex; i < tables.length; i++) {
176
- const tbl = tables[i];
177
- // Only consider valid & free tables for potential capacity
178
- if (isTemporaryTableValid(tbl, reservationDateStr, reservationTimeStr) &&
179
- isTableFreeForAllSlots(tbl.tableNumber, requiredSlots, tableOccupiedSlots))
180
- {
181
- maxPossibleCapacity += tbl.maxCapacity;
182
- }
177
+ maxRemainingCapacity += tables[i].maxCapacity;
178
+ }
179
+ if (maxRemainingCapacity < guestsNeeded) {
180
+ return; // Impossible to satisfy
183
181
  }
184
182
 
185
- if (maxPossibleCapacity < guestsNeeded) {
186
- return; // Impossible to seat remaining guests
183
+ // OPTIMIZATION: Limit search depth for large table sets
184
+ const remainingTables = tables.length - startIndex;
185
+ if (remainingTables > 15 && currentSet.length === 0) {
186
+ // For first iteration with many tables, only try largest tables
187
+ const sortedBySize = tables.slice(startIndex).sort((a, b) => b.maxCapacity - a.maxCapacity);
188
+ const topTables = sortedBySize.slice(0, Math.min(10, sortedBySize.length));
189
+
190
+ for (const tbl of topTables) {
191
+ const canSeat = Math.min(tbl.maxCapacity, guestsNeeded);
192
+ if (canSeat >= tbl.minCapacity || canSeat >= guestsNeeded) {
193
+ currentSet.push(tbl);
194
+ findMultiTableCombination(tables, guestsNeeded - canSeat, startIndex + 1, currentSet, best);
195
+ currentSet.pop();
196
+
197
+ if (best.tableCount === 1) return; // Found optimal
198
+ }
199
+ }
200
+ return;
187
201
  }
188
202
 
203
+ // Standard backtracking for remaining cases
189
204
  for (let i = startIndex; i < tables.length; i++) {
190
205
  const tbl = tables[i];
191
-
192
- // --- Core Checks ---
193
- // 1. Check if temporary table is valid for this date/time
194
- if (!isTemporaryTableValid(tbl, reservationDateStr, reservationTimeStr)) {
195
- continue; // Skip invalid temporary table
196
- }
197
-
198
- // 2. Check if table is free for all required slots
199
- if (!isTableFreeForAllSlots(tbl.tableNumber, requiredSlots, tableOccupiedSlots)) {
200
- continue; // Skip occupied table
201
- }
202
-
203
- // 3. Check if table contributes meaningfully
204
206
  const canSeat = Math.min(tbl.maxCapacity, guestsNeeded);
207
+
208
+ // Skip if table can't contribute meaningfully
205
209
  if (canSeat < tbl.minCapacity && canSeat < guestsNeeded) {
206
- continue; // Don't use a table for fewer than its min capacity unless it fulfills the remainder
210
+ continue;
207
211
  }
208
212
 
209
- if (canSeat <= 0) continue; // Table doesn't help
210
-
211
- // --- Recurse ---
212
- currentSet.push(tbl); // Add table to current combination
213
-
214
- findMultiTableCombination(
215
- tables,
216
- guestsNeeded - canSeat, // Reduce guests needed
217
- i + 1, // Explore next tables
218
- currentSet,
219
- best,
220
- requiredSlots,
221
- tableOccupiedSlots,
222
- reservationDateStr,
223
- reservationTimeStr
224
- );
213
+ if (canSeat <= 0) continue;
225
214
 
226
- currentSet.pop(); // Backtrack: remove table to explore other combinations
215
+ currentSet.push(tbl);
216
+ findMultiTableCombination(tables, guestsNeeded - canSeat, i + 1, currentSet, best);
217
+ currentSet.pop();
227
218
 
228
- // OPTIMIZATION: Early exit if we found a single-table solution (best possible)
229
- if (best.tableCount === 1) {
219
+ // EARLY EXIT: Found optimal single or two-table solution
220
+ if (best.tableCount <= 2) {
230
221
  return;
231
222
  }
232
223
  }
@@ -304,11 +295,7 @@ function assignTablesForGivenTime(restaurantData, date, time, guests, tableOccup
304
295
  guests,
305
296
  0, // Start index
306
297
  [], // Initial empty set
307
- best, // Best solution object
308
- requiredSlots,
309
- tableOccupiedSlots,
310
- date, // Pass context
311
- time // Pass context
298
+ best // Best solution object (no need for slots/occupancy - already filtered)
312
299
  );
313
300
 
314
301
  if (best.tables.length > 0) {
@@ -455,10 +442,9 @@ function isTimeAvailableSync(restaurantData, date, time, guests, reservations, s
455
442
  findMultiTableCombination(
456
443
  validTables, // Use pre-filtered tables
457
444
  guests,
458
- 0, [], best,
459
- requiredSlots,
460
- tableOccupiedSlots, // Use the final occupancy map
461
- date, time
445
+ 0, // Start index
446
+ [], // Empty current set
447
+ best // Best solution
462
448
  );
463
449
 
464
450
  const result = best.tables.length > 0;