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);
        }