Example #1
0
            public override bool Next()
            {
                var closure   = LinkedNodeSet.Closure(this.Node, this.Logic.Caps, null, true);
                var available = closure.UnlinkedEdges((UnlinkedEdge u) => !this.TriedEdges.Contains(u.Static) && this.Logic.Map.HoleFree(this.Node.Room, u.Static.HoleTarget));

                if (available.Count == 0)
                {
                    Logger.Log("randomizer", $"Failure: No edges out of {Node.Room.Static.Name}:{Node.Static.Name}");
                    return(false);
                }

                var picked = available[this.Logic.Random.Next(available.Count)];

                this.AddNextTask(new TaskPathwayPickRoom(this.Logic, picked));
                this.TriedEdges.Add(picked.Static);

                var reqNeeded = LinkedNodeSet.TraversalRequires(this.Node, this.Logic.Caps.WithoutKey(), true, picked);

                if (reqNeeded is Possible)
                {
                    // nothing needed
                }
                else if (reqNeeded is KeyRequirement keyReq)
                {
                    Logger.Log("randomizer", $"Need to place a key from {Node.Room.Static.Name}:{Node.Static.Name} to get out of {picked.Static.HoleTarget}");
                    this.AddNextTask(new TaskPathwayPlaceKey(this.Logic, this.Node, keyReq.KeyholeID));
                }
                else
                {
                    throw new Exception("why does this happen? this should not happen");
                }

                return(true);
            }
            public override bool Next()
            {
                var caps      = this.Logic.Caps.WithoutFlags();
                var closure   = LinkedNodeSet.Closure(this.Node, caps, null, true);
                var available = closure.UnlinkedEdges(u => !this.TriedEdges.Contains(u.Static) && (u.Static.HoleTarget == null || (!this.ForceWarp && this.Logic.Map.HoleFree(this.Node.Room, u.Static.HoleTarget))));

                if (available.Count == 0)
                {
                    Logger.Log("randomizer", $"Failure: No edges out of {Node.Room.Static.Name}:{Node.Static.Name}");
                    return(false);
                }

                // stochastic difficulty control
                var picked = available[this.Logic.Random.Next(available.Count)];
                var caps2  = caps.Copy();

                for (int i = 0; ; i++, picked = available[this.Logic.Random.Next(available.Count)])
                {
                    // bias against picking left-facing holes
                    if (picked.Static.HoleTarget != null && picked.Static.HoleTarget.Side == ScreenDirection.Left && this.Logic.Random.Next(4) == 0)
                    {
                        continue;
                    }

                    if (this.Logic.Settings.DifficultyEagerness == DifficultyEagerness.None || this.Logic.Settings.Difficulty == Difficulty.Easy)
                    {
                        break;
                    }
                    if (i >= 4)
                    {
                        return(false);
                    }

                    // pick a lower difficulty level - if we have a higher eagerness it should be more likely we pick a difficulty closer to the current one
                    // to facilitate this we pick a [0,1] sample which biases toward zero as eagerness increases
                    var sample = this.Logic.Random.NextDouble();
                    switch (this.Logic.Settings.DifficultyEagerness)
                    {
                    case DifficultyEagerness.Medium:
                        sample = Math.Pow(sample, 4);
                        break;

                    case DifficultyEagerness.High:
                        sample = Math.Pow(sample, 8);
                        break;
                    }

                    // the off-by-ones here are devious. we want to select a number of steps down which will always step at least once and may step down to one step below easy.
                    sample *= (int)this.Logic.Settings.Difficulty + 1;
                    //Logger.Log("DEBUG", $"Permissiveness sample: {(int) sample} / {(int) this.Logic.Settings.Difficulty}");
                    caps2.PlayerSkill = this.Logic.Settings.Difficulty - ((int)sample + 1);
                    if (caps2.PlayerSkill < 0)
                    {
                        break;
                    }

                    // if the target edge is NOT present in the closure with the lower difficulty, it is hard enough. otherwise, it is too easy.
                    if (!LinkedNodeSet.Closure(this.Node, caps2, null, true).UnlinkedEdges().Contains(picked))
                    {
                        break;
                    }
                    //Logger.Log("DEBUG", "...rejecting edge, too easy");
                }

                var state = new FlagSet(this.State);

                this.AddNextTask(new TaskPathwayPickRoom(this.Logic, picked, state));
                this.TriedEdges.Add(picked.Static);

                var reqNeeded = LinkedNodeSet.TraversalRequires(this.Node, this.Logic.Caps.WithoutKey().WithFlags(this.State), true, picked);

                this.HandleRequirements(reqNeeded, state, this.State);
                var reversible = LinkedNodeSet.Closure(this.Node, null, this.Logic.Caps.WithFlags(state), true).UnlinkedEdges().Contains(picked);

                if (!reversible)
                {
                    CrystallizeState(state);
                }
                // WE NEED TO DO TWO THINGS
                // 1) Update the flags state in the upcoming pickroom task based on the flags we decide we need to handle. CHECK
                // 2) check if this is traversable in reverse. if it's not, update the flags in the upcoming pickroom task. CHECK
                // 3) ummm forgot about this. if we happen to place a room with a switch reachable, update the state
                // BONUS 4) in the requirement satisfying task, add a reachability check to get back to startnode with the new flag set
                return(true);
            }