/// <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);
        }
示例#2
0
        /// <summary>
        /// Greedily adds non corridor node to the layout.
        /// </summary>
        /// <remarks>
        /// Uses special configuration spaces where shapes are not directly connected
        /// but are rather at a specified distance from each other.
        /// </remarks>
        /// <param name="layout"></param>
        /// <param name="node"></param>
        private void AddNonCorridorGreedily(TLayout layout, TNode node)
        {
            var configurations = GetNeighboursOverCorridors(layout, node);

            if (configurations.Count == 0)
            {
                layout.SetConfiguration(node, CreateConfiguration(ConfigurationSpaces.GetRandomShape(node), new IntVector2()));
                return;
            }

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

            var shapes = ConfigurationSpaces.GetShapesForNode(node).ToList();

            shapes.Shuffle(Random);

            foreach (var shape in shapes)
            {
                var intersection = CorridorConfigurationSpaces.GetMaximumIntersection(CreateConfiguration(shape, new IntVector2()), configurations);

                if (intersection == null)
                {
                    continue;
                }

                intersection.Shuffle(Random);

                foreach (var intersectionLine in intersection)
                {
                    var       tryAll    = true;
                    var       mod       = 1;
                    const int maxPoints = 20;

                    if (intersectionLine.Length > maxPoints)
                    {
                        tryAll = false;
                        mod    = intersectionLine.Length / maxPoints;
                    }

                    var i = 0;

                    foreach (var position in intersectionLine.GetPoints())
                    {
                        if (!tryAll && i % mod != 0 && i != intersectionLine.Length)
                        {
                            continue;
                        }

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

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

                        if (bestEnergy <= 0)
                        {
                            break;
                        }

                        i++;
                    }

                    // There is no point of looking for more solutions when you already reached a valid state
                    // and so no position would be accepted anyway
                    // TODO: What about making it somehow random? If there are more valid positions, choose one at random.
                    if (bestEnergy <= 0)
                    {
                        break;
                    }
                }
            }

            var newConfiguration = CreateConfiguration(bestShape, bestPosition);

            layout.SetConfiguration(node, newConfiguration);
        }