silverua / slay-the-spire-map-in-unity

Implementation of the Slay the Spire Map in Unity3d
MIT License
273 stars 65 forks source link

Question: Forcing a Single Starting Node #16

Open jackyyang09 opened 2 years ago

jackyyang09 commented 2 years ago

Hi Vladimir, thanks for creating this amazing asset!

I've been going through trying to fit it to my needs and found that the NumOfStartingNodes parameter does not consistently work to set the number of starting nodes to 1. I've tried going through the code to see what's going on and I have to admit the path generation logic flies completely over my head.

I'm not sure whether this is a bug or a feature, but I'd appreciate any tips you have on reducing the starting node count to 1.

Thanks

silverua commented 2 years ago

Hi!

Thanks for the kind words about the package. You're right, the NumOfStartingNodes does not work as one would expect it to. If I limit it to (1, 1), I usually get more than 1 node in the generated map. I may look into this in the future, but for now, I think it is solvable with this hack: You can change your GeneratePaths() method in MapGenerator.cs to this (I left some new comments to explain the changes):

        private static void GeneratePaths()
        {
            var finalNode = GetFinalNode();
            var startingNode = new Point(0, 0); // you can try using new Point (1, 0) or new Point(2, 0) here, etc...

            paths = new List<List<Point>>();
            var numOfStartingNodes = config.numOfStartingNodes.GetValue();
            var numOfPreBossNodes = config.numOfPreBossNodes.GetValue();

            var candidateXs = new List<int>();
            for (var i = 0; i < config.GridWidth; i++)
                candidateXs.Add(i);

            candidateXs.Shuffle();
            var preBossXs = candidateXs.Take(numOfPreBossNodes);
            var preBossPoints = (from x in preBossXs select new Point(x, finalNode.y - 1)).ToList();
            var attempts = 0;

            // start by generating paths from each of the preBossPoints to the 1st layer:
            foreach (var point in preBossPoints)
            {
                var path = Path(point, 1, config.GridWidth); // generating the path to layer with index 1 instead of 0
                path.Insert(0, finalNode);
                path.Add(startingNode); // always adding the single starting node at layer 0
                paths.Add(path);
                attempts++;
            }

            while (!PathsLeadToAtLeastNDifferentPoints(paths, numOfStartingNodes) && attempts < 100)
            {
                var randomPreBossPoint = preBossPoints[UnityEngine.Random.Range(0, preBossPoints.Count)];
                var path = Path(randomPreBossPoint, 0, config.GridWidth);
                path.Insert(0, finalNode);
                paths.Add(path);
                attempts++;
            }

            Debug.Log("Attempts to generate paths: " + attempts);
        }
jackyyang09 commented 2 years ago

Appreciate the fast response, your "hack" worked out great! Although there's no way I to confirm that it'll continue to work until I really start field-testing it. So I may return in the future with more bugs to report.

In the meantime though, thanks again!