public int[] GetSolution(IChromosome chromosome) { var shapes = chromosome.GetGenes() .Select(x => (int)x.Value) .Batch(GenesPerShape, x => x.ToArray()) .Index(); // Determine which grid cells each shape would occupy if placed var shapeCells = shapes .Select(x => SolutionFactory.GetIndexes( shapeId: x.Key, shapeValue: x.Value[0], xValue: x.Value[1], yValue: x.Value[2])) .ToArray(); int[] solution = EmptySolutionWithBlockers.ToArray(); int placedShapes = 0; // Try to place each shape if no part of the shape would cover another (already-placed) // shape or blocker. We try to place the more "difficult" shapes first. // Note: Originally overlapping was allowed, but the UI looked a mess and performance // was no better than without overlaps for (int i = 0; i < shapeCells.Length; i++) { if (shapeCells[i].All(x => solution[x] == 0)) { placedShapes++; foreach (int index in shapeCells[i]) { solution[index] = i + ShapeStartIndex; } } } bool IsAdjacent(int i1, int i2) { var(r1, c1) = (i1 / GridWidth, i1 % GridWidth); var(r2, c2) = (i2 / GridWidth, i2 % GridWidth); return(Math.Abs(r1 - r2) + Math.Abs(c1 - c2) == 1); } // The single and double unit shapes are the easiest to place // so handle these last. It's more efficient to find the gaps and // place them there than to wait for mutation to move them to the // correct place. if (placedShapes == GeniusSquareSolver.Shapes) { var empty = solution .Index() .Where(x => x.Value == 0) .Select(x => x.Key) .ToList(); var sol = empty.Permutations().FirstOrDefault(x => IsAdjacent(x[0], x[1])); if (sol != null) { solution[sol[0]] = DoubleUnitShapeIndex; solution[sol[1]] = DoubleUnitShapeIndex; solution[sol[2]] = SingleUnitShapeIndex; } } return(solution); }