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); }