// NEW
        public override void PerturbShape(TLayout layout, TNode node, bool updateLayout)
        {
            layout.GetConfiguration(node, out var configuration);

            // Return the current layout if a given node cannot be shape-perturbed
            if (!RoomShapesHandler.CanPerturbShapeDoNotUse(node))
            {
                return;
            }

            var possibleShapes = RoomShapesHandler.GetPossibleShapesForNode(layout, node, false);

            if (possibleShapes.Count == 0)
            {
                return;
            }

            var shape            = possibleShapes.GetRandom(Random);
            var newConfiguration = configuration.SmartClone();

            // newConfiguration.ShapeContainer = shape;
            newConfiguration.RoomShape = shape;

            if (updateLayout)
            {
                UpdateLayout(layout, node, newConfiguration);
                return;
            }

            layout.SetConfiguration(node, newConfiguration);
        }
        public void AllowRepeat()
        {
            var roomTemplate1   = GetRoomTemplate(RoomTemplateRepeatMode.AllowRepeat);
            var roomTemplate2   = GetRoomTemplate(RoomTemplateRepeatMode.AllowRepeat);
            var roomTemplate3   = GetRoomTemplate(RoomTemplateRepeatMode.AllowRepeat);
            var roomDescription = new BasicRoomDescription(new List <RoomTemplate>()
            {
                roomTemplate1, roomTemplate2, roomTemplate3
            });

            var mapDescription      = GetMapDescription(roomDescription);
            var configurationSpaces = GetConfigurationSpaces(mapDescription);

            var roomShapesHandler = new RoomShapesHandler <int, Configuration <CorridorsData> >(
                configurationSpaces,
                configurationSpaces.GetIntAliasMapping(),
                mapDescription
                );

            var roomShapes = configurationSpaces.GetIntAliasMapping().Values.ToList();
            var layout     = new Layout <Configuration <CorridorsData> >(mapDescription.GetGraph());

            SetConfiguration(layout, 0, roomShapes[0]);
            SetConfiguration(layout, 1, roomShapes[1]);
            SetConfiguration(layout, 2, roomShapes[2]);

            foreach (var node in mapDescription.GetGraph().Vertices)
            {
                var shapes         = roomShapesHandler.GetPossibleShapesForNode(layout, node);
                var expectedShapes = configurationSpaces.GetShapesForNode(node);
                Assert.That(shapes, Is.EquivalentTo(expectedShapes));
            }
        }
        public void NoImmediate()
        {
            var roomTemplate1   = GetRoomTemplate(RoomTemplateRepeatMode.NoImmediate);
            var roomTemplate2   = GetRoomTemplate(RoomTemplateRepeatMode.NoImmediate);
            var roomTemplate3   = GetRoomTemplate(RoomTemplateRepeatMode.NoImmediate);
            var roomDescription = new BasicRoomDescription(new List <RoomTemplate>()
            {
                roomTemplate1, roomTemplate2, roomTemplate3
            });

            var mapDescription      = GetMapDescription(roomDescription);
            var configurationSpaces = GetConfigurationSpaces(mapDescription);

            var roomShapesHandler = new RoomShapesHandler <int, Configuration <CorridorsData> >(
                configurationSpaces,
                configurationSpaces.GetIntAliasMapping(),
                mapDescription
                );

            var roomShapes = configurationSpaces.GetIntAliasMapping().Values.ToList();
            var layout     = new Layout <Configuration <CorridorsData> >(mapDescription.GetGraph());

            SetConfiguration(layout, 0, roomShapes[0]);
            SetConfiguration(layout, 1, roomShapes[1]);
            SetConfiguration(layout, 2, roomShapes[2]);

            {
                // Node 0
                var shapes         = roomShapesHandler.GetPossibleShapesForNode(layout, 0);
                var expectedShapes = new List <IntAlias <PolygonGrid2D> >()
                {
                    roomShapes[0], roomShapes[2]
                };
                Assert.That(shapes, Is.EquivalentTo(expectedShapes));
            }

            {
                // Node 1
                var shapes         = roomShapesHandler.GetPossibleShapesForNode(layout, 1);
                var expectedShapes = new List <IntAlias <PolygonGrid2D> >()
                {
                    roomShapes[1]
                };
                Assert.That(shapes, Is.EquivalentTo(expectedShapes));
            }

            {
                // Node 2
                var shapes         = roomShapesHandler.GetPossibleShapesForNode(layout, 2);
                var expectedShapes = new List <IntAlias <PolygonGrid2D> >()
                {
                    roomShapes[0], roomShapes[2]
                };
                Assert.That(shapes, Is.EquivalentTo(expectedShapes));
            }
        }
        // NEW
        public override void PerturbShape(TLayout layout, IList <TNode> chain, bool updateLayout)
        {
            var canBePerturbed = chain
                                 .Where(x => !LevelDescription.GetRoomDescription(x).IsCorridor) // TODO: handle better
                                 .Where(x => RoomShapesHandler.CanPerturbShapeDoNotUse(x))
                                 .ToList();

            if (canBePerturbed.Count == 0)
            {
                return;
            }

            PerturbShape(layout, canBePerturbed.GetRandom(Random), updateLayout);
        }
        private void SetupGenerator()
        {
            var mapping       = mapDescription.GetMapping();
            var chainsGeneric = configuration.Chains;

            if (chainsGeneric == null)
            {
                var chainDecomposition = new TwoStageChainDecomposition <TNode>(mapDescriptionOriginal, new BreadthFirstChainDecomposition <TNode>(configuration.ChainDecompositionConfiguration ?? new ChainDecompositionConfiguration()));
                chainsGeneric = chainDecomposition.GetChains(mapDescriptionOriginal.GetGraph());
            }

            var chains = chainsGeneric
                         .Select(x => new Chain <int>(x.Nodes.Select(y => mapping[y]).ToList(), x.Number))
                         .ToList();

            var generatorPlanner = new GeneratorPlanner <Layout <Configuration <CorridorsData> >, int>(configuration.SimulatedAnnealingMaxBranching);

            var configurationSpacesGenerator = new ConfigurationSpacesGenerator(
                new PolygonOverlap(),
                DoorHandler.DefaultHandler,
                new OrthogonalLineIntersection(),
                new GridPolygonUtils());
            var configurationSpaces = configurationSpacesGenerator.GetConfigurationSpaces <Configuration <CorridorsData> >(mapDescription);

            //var corridorConfigurationSpaces = mapDescription.IsWithCorridors ? configurationSpacesGenerator.Generate<TNode, Configuration<CorridorsData>>(mapDescription, mapDescription.CorridorsOffsets) : configurationSpaces;
            var corridorConfigurationSpaces = configurationSpaces;

            var averageSize    = configurationSpaces.GetAverageSize();
            var polygonOverlap = new FastPolygonOverlap();

            var stageOneConstraints =
                new List <INodeConstraint <Layout <Configuration <CorridorsData> >, int, Configuration <CorridorsData>,
                                           CorridorsData> >
            {
                new BasicConstraint <Layout <Configuration <CorridorsData> >, int, Configuration <CorridorsData>,
                                     CorridorsData, IntAlias <PolygonGrid2D> >(
                    new FastPolygonOverlap(),
                    averageSize,
                    configurationSpaces
                    ),
                new CorridorConstraints <Layout <Configuration <CorridorsData> >, int, Configuration <CorridorsData>, CorridorsData, IntAlias <PolygonGrid2D> >(
                    mapDescription,
                    averageSize,
                    corridorConfigurationSpaces
                    ),
            };

            if (!configuration.RoomsCanTouch)
            {
                stageOneConstraints.Add(new TouchingConstraints <Layout <Configuration <CorridorsData> >, int, Configuration <CorridorsData>, CorridorsData, IntAlias <PolygonGrid2D> >(
                                            mapDescription,
                                            polygonOverlap
                                            ));
            }

            var stageOneConstraintsEvaluator = new ConstraintsEvaluator <Layout <Configuration <CorridorsData> >, int, Configuration <CorridorsData>, IntAlias <PolygonGrid2D>, CorridorsData>(stageOneConstraints);

            //if (mapDescription.IsWithCorridors)
            //{
            //    layoutOperations.AddNodeConstraint(new CorridorConstraints<Layout<Configuration<CorridorsData>>, int, Configuration<CorridorsData>, CorridorsData, IntAlias<GridPolygon>>(
            //        mapDescription,
            //        averageSize,
            //        corridorConfigurationSpaces
            //    ));

            //    if (!false) // TODO:
            //    {
            //        var polygonOverlap = new FastPolygonOverlap();
            //        layoutOperations.AddNodeConstraint(new TouchingConstraints<Layout<Configuration<CorridorsData>>, int, Configuration<CorridorsData>, CorridorsData, IntAlias<GridPolygon>>(
            //            mapDescription,
            //            polygonOverlap
            //        ));
            //    }
            //}


            var roomShapesHandler = new RoomShapesHandler <int, Configuration <CorridorsData> >(
                configurationSpaces,
                configurationSpaces.GetIntAliasMapping(),
                mapDescription,
                configuration.RepeatModeOverride
                );

            var layoutOperations = new LayoutOperations <Layout <Configuration <CorridorsData> >, int, Configuration <CorridorsData>, IntAlias <PolygonGrid2D>, CorridorsData>(corridorConfigurationSpaces, configurationSpaces.GetAverageSize(), mapDescription, stageOneConstraintsEvaluator, stageOneConstraintsEvaluator, roomShapesHandler, configuration.ThrowIfRepeatModeNotSatisfied);

            var initialLayout   = new Layout <Configuration <CorridorsData> >(mapDescription.GetGraph());
            var layoutConverter =
                new BasicLayoutConverter <Layout <Configuration <CorridorsData> >, TNode,
                                          Configuration <CorridorsData> >(mapDescription, configurationSpaces,
                                                                          configurationSpaces.GetIntAliasMapping());

            var layoutEvolver =
                new PlatformersEvolver <Layout <Configuration <CorridorsData> >, int,
                                        Configuration <CorridorsData> >(layoutOperations);

            generator = new ChainBasedGenerator <Layout <Configuration <CorridorsData> >, MapLayout <TNode>, int>(initialLayout, generatorPlanner, chains, layoutEvolver, layoutConverter);

            generator.OnRandomInjected += (random) =>
            {
                ((IRandomInjectable)configurationSpaces).InjectRandomGenerator(random);
                ((IRandomInjectable)layoutOperations).InjectRandomGenerator(random);
                ((IRandomInjectable)layoutEvolver).InjectRandomGenerator(random);
                ((IRandomInjectable)layoutConverter).InjectRandomGenerator(random);
            };

            generator.OnCancellationTokenInjected += (token) =>
            {
                ((ICancellable)generatorPlanner).SetCancellationToken(token);
                ((ICancellable)layoutEvolver).SetCancellationToken(token);
            };

            // layoutEvolver.OnEvent += (sender, args) => OnSimulatedAnnealingEvent?.Invoke(sender, args);
            layoutEvolver.OnPerturbed          += (sender, layout) => OnPerturbed?.Invoke(layoutConverter.Convert(layout, false));
            layoutEvolver.OnPerturbed          += (sender, layout) => OnPerturbedInternal?.Invoke(layout);
            layoutEvolver.OnValid              += (sender, layout) => OnPartialValid?.Invoke(layoutConverter.Convert(layout, true));
            generatorPlanner.OnLayoutGenerated += layout => OnValid?.Invoke(layoutConverter.Convert(layout, true));
        }
        /// <summary>
        /// Adds corridor node greedily.
        /// </summary>
        /// <param name="layout"></param>
        /// <param name="node"></param>
        /// <returns></returns>
        public bool AddCorridorGreedily(TLayout layout, TNode node)
        {
            var configurations = new List <TConfiguration>();
            var neighbors      = layout.Graph.GetNeighbours(node);

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

            if (configurations.Count == 0)
            {
                throw new InvalidOperationException();
            }

            var foundValid   = false;
            var bestShape    = default(TShapeContainer);
            var bestPosition = new Vector2Int();

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

            shapes.Shuffle(Random);

            foreach (var shape in shapes)
            {
                // var intersection = ConfigurationSpaces.GetMaximumIntersection(CreateConfiguration(shape, new IntVector2(), node), configurations, out var configurationsSatisfied);

                var intersection = simpleConfigurationSpaces.GetMaximumIntersection(
                    CreateConfiguration(shape, new Vector2Int(), node), configurations,
                    out var configurationsSatisfied);

                if (configurationsSatisfied != 2)
                {
                    continue;
                }

                const int maxPoints = 20;

                foreach (var position in intersection.ShuffleAndSamplePositions(maxPoints, Random))
                {
                    var energyData =
                        constraintsEvaluator.ComputeNodeEnergy(layout, node,
                                                               CreateConfiguration(shape, position, node));

                    if (energyData.IsValid)
                    {
                        bestShape    = shape;
                        bestPosition = position;
                        foundValid   = true;
                        break;
                    }

                    if (foundValid)
                    {
                        break;
                    }
                }
            }

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

            layout.SetConfiguration(node, newConfiguration);

            return(foundValid);
        }
        /// <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);
        }