diff --git a/js/matching-worker.js b/js/matching-worker.js index bc38709..99b58ce 100644 --- a/js/matching-worker.js +++ b/js/matching-worker.js @@ -176,7 +176,13 @@ class GeneticAlgorithm { const group = []; for (let j = 0; j < this.parallel; j++) { const idx = indices[i * this.parallel + j]; - group.push(this.cells[idx]); + // Safety check: ensure index is valid + if (idx >= 0 && idx < this.cells.length) { + group.push(this.cells[idx]); + } else { + // Fallback: use a random valid cell + group.push(this.cells[Math.floor(Math.random() * this.cells.length)]); + } } configuration.push(group); } @@ -184,31 +190,51 @@ class GeneticAlgorithm { } crossover(parent1, parent2) { + // Simple two-point crossover with repair const length = parent1.length; - const start = Math.floor(Math.random() * length); - const end = start + Math.floor(Math.random() * (length - start)); - const child = new Array(length).fill(-1); - const usedIndices = new Set(); - - for (let i = start; i <= end; i++) { - child[i] = parent1[i]; - usedIndices.add(parent1[i]); + // 50% chance to just return a copy of one parent (with shuffle) + if (Math.random() < 0.5) { + const child = [...parent1]; + // Swap a few random positions + for (let i = 0; i < 2; i++) { + const a = Math.floor(Math.random() * length); + const b = Math.floor(Math.random() * length); + [child[a], child[b]] = [child[b], child[a]]; + } + return child; } - let childIdx = (end + 1) % length; - for (let i = 0; i < length; i++) { - const parent2Idx = (end + 1 + i) % length; - if (!usedIndices.has(parent2[parent2Idx])) { - while (child[childIdx] !== -1) { - childIdx = (childIdx + 1) % length; - } - child[childIdx] = parent2[parent2Idx]; - usedIndices.add(parent2[parent2Idx]); - childIdx = (childIdx + 1) % length; + // Otherwise, take half from each parent and repair duplicates + const midpoint = Math.floor(length / 2); + const child = [...parent1.slice(0, midpoint), ...parent2.slice(midpoint)]; + + // Find and fix duplicates + const seen = new Set(); + const duplicatePositions = []; + const allIndices = new Set(parent1.concat(parent2)); + + for (let i = 0; i < child.length; i++) { + if (seen.has(child[i])) { + duplicatePositions.push(i); + } else { + seen.add(child[i]); } } + // Find missing indices + const missing = []; + for (const idx of allIndices) { + if (!seen.has(idx)) { + missing.push(idx); + } + } + + // Replace duplicates with missing values + for (let i = 0; i < duplicatePositions.length && i < missing.length; i++) { + child[duplicatePositions[i]] = missing[i]; + } + return child; } @@ -276,6 +302,12 @@ class GeneticAlgorithm { let child = this.crossover(parent1.indices, parent2.indices); + // Safety: ensure all indices are valid + child = child.map(idx => { + if (idx >= 0 && idx < this.cells.length) return idx; + return Math.floor(Math.random() * this.cells.length); + }); + const usedLabels = new Set(child.map(idx => this.cells[idx].label)); const unusedCells = this.cells.filter(c => !usedLabels.has(c.label));