/// <summary>
        /// Greedily adds only non corridor nodes to the layout.
        /// </summary>
        /// <param name="layout"></param>
        /// <param name="chain"></param>
        /// <param name="updateLayout"></param>
        public override void AddChain(TLayout layout, IList <TNode> chain, bool updateLayout, out int iterationsCount)
        {
            iterationsCount = 0;
            var rooms = chain.Where(x => !LevelDescription.GetRoomDescription(x).IsCorridor);

            foreach (var room in rooms)
            {
                AddNodeGreedily(layout, room, out var addNodeIterations);
                iterationsCount += addNodeIterations;
            }

            if (updateLayout)
            {
                UpdateLayout(layout);
            }
        }
        /// <summary>
        /// Greedily adds corridors from a given chain to the layout.
        /// </summary>
        /// <param name="layout"></param>
        /// <param name="chain"></param>
        /// <returns></returns>
        private bool AddCorridors(TLayout layout, IEnumerable <TNode> chain)
        {
            var clone     = layout.SmartClone();
            var corridors = chain.Where(x => LevelDescription.GetRoomDescription(x).IsCorridor).ToList();

            foreach (var corridor in corridors)
            {
                if (!AddCorridorGreedily(clone, corridor))
                {
                    return(false);
                }
            }

            foreach (var corridor in corridors)
            {
                clone.GetConfiguration(corridor, out var configuration);
                layout.SetConfiguration(corridor, configuration);
            }

            return(true);
        }
        /// <summary>
        /// Tries all shapes and positions from the maximum intersection to find a configuration
        /// with the lowest energy.
        /// </summary>
        /// <param name="layout"></param>
        /// <param name="node"></param>
        public override void AddNodeGreedily(TLayout layout, TNode node, out int iterationsCount)
        {
            iterationsCount = 0;
            var neighborsConfigurations = new List <TConfiguration>();
            var neighbors = LevelDescription.GetGraphWithoutCorridors().GetNeighbours(node);

            foreach (var neighbor in neighbors)
            {
                if (layout.GetConfiguration(neighbor, out var configuration))
                {
                    neighborsConfigurations.Add(configuration);
                }
            }

            // The first node is set to have a random shape and [0,0] position
            if (neighborsConfigurations.Count == 0)
            {
                layout.SetConfiguration(node, CreateConfiguration(RoomShapesHandler.GetRandomShapeWithoutConstraintsDoNotUse(node), new Vector2Int(), node));
                iterationsCount++;
                return;
            }

            var bestEnergy   = float.MaxValue;
            var bestShape    = default(TShapeContainer);
            var bestPosition = new Vector2Int();

            var shapes = RoomShapesHandler.GetPossibleShapesForNode(layout, node, !throwIfRepeatModeNotSatisfied);

            if (shapes.Count == 0)
            {
                if (throwIfRepeatModeNotSatisfied)
                {
                    throw new InvalidOperationException($"It was not possible to assign room shapes in a way that satisfies all the RepeatMode requirements. Moreover, the {nameof(throwIfRepeatModeNotSatisfied)} option is set to true which means that the algorithm did not attempt to find at least some room templates even though not all conditions were satisfied. Please make sure that there are enough room templates to choose from. Problematic room: {node}.");
                }
                else
                {
                    throw new InvalidOperationException($"It was not possible to assign room shapes in a way that satisfies all the RepeatMode requirements.  Please make sure that there are enough room templates to choose from. Problematic room: {node}.");
                }
            }

            shapes.Shuffle(Random);

            // Try all shapes
            foreach (var shape in shapes)
            {
                // var intersection = ConfigurationSpaces.GetMaximumIntersection(CreateConfiguration(shape, new IntVector2(), node), neighborsConfigurations);
                var intersection = simpleConfigurationSpaces.GetMaximumIntersection(CreateConfiguration(shape, new Vector2Int(), node), neighborsConfigurations, out var configurationsSatisfied);

                if (configurationsSatisfied == 0)
                {
                    continue;
                }

                const int maxPoints = 20;
                foreach (var position in intersection.ShuffleAndSamplePositions(maxPoints, Random))
                {
                    iterationsCount++;

                    var energy = constraintsEvaluator
                                 .ComputeNodeEnergy(layout, node, CreateConfiguration(shape, position, node)).Energy;

                    if (energy < bestEnergy)
                    {
                        bestEnergy   = energy;
                        bestShape    = shape;
                        bestPosition = position;
                    }

                    if (bestEnergy <= 0)
                    {
                        break;
                    }
                }
            }

            if (bestEnergy == float.MaxValue)
            {
                throw new ArgumentException($"No shape of the room {node} could be connected to its neighbors. This usually happens if there are pairs of shapes that cannot be connected together in any way (either directly or via corridors). (The mentioned room may not correspond to the actual room as custom types are often mapped to integers to make the computation faster.)");
            }

            var newConfiguration = CreateConfiguration(bestShape, bestPosition, node);

            layout.SetConfiguration(node, newConfiguration);
        }