Esempio n. 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);
            }
Esempio n. 2
0
 public LinkedNodeSet(LinkedNodeSet toCopy)
 {
     this.Nodes       = new List <LinkedNode>(toCopy.Nodes);
     this.CapsForward = toCopy.CapsForward;
     this.CapsReverse = toCopy.CapsReverse;
     this.Random      = toCopy.Random;
 }
            public override bool Next()
            {
                var caps    = this.Logic.Caps.WithoutKey();
                var closure = LinkedNodeSet.Closure(this.Node, caps, caps, true);

                foreach (var edge in closure.UnlinkedEdges())
                {
                    if (!this.Logic.Map.HoleFree(this.Node.Room, edge.Static.HoleTarget))
                    {
                        continue;
                    }
                    if (this.Logic.Random.Next(5) != 0)
                    {
                        continue;
                    }

                    var possibilities = this.Logic.AvailableNewEdges(caps, caps, e => e.FromNode.ParentRoom.Collectables.Count != 0);
                    foreach (var newEdge in possibilities)
                    {
                        var receipt = ConnectAndMapReceipt.Do(this.Logic, edge, newEdge);
                        if (receipt == null)
                        {
                            continue;
                        }

                        var closure2 = LinkedNodeSet.Closure(receipt.EntryNode, caps, caps, true);
                        var seen     = new HashSet <UnlinkedCollectable>();
                        var options  = new List <Tuple <UnlinkedCollectable, bool> >();
                        foreach (var spot in closure2.UnlinkedCollectables())
                        {
                            seen.Add(spot);
                            options.Add(Tuple.Create(spot, false));
                        }

                        closure2.Extend(caps, null, true);
                        foreach (var spot in closure2.UnlinkedCollectables())
                        {
                            if (seen.Contains(spot))
                            {
                                continue;
                            }
                            options.Add(Tuple.Create(spot, true));
                        }

                        if (options.Count == 0)
                        {
                            receipt.Undo();
                            continue;
                        }

                        var pickedSpotTup = options[this.Logic.Random.Next(options.Count)];
                        var pickedSpot    = pickedSpotTup.Item1;
                        var berry         = pickedSpot.Static.MustFly ? LinkedNode.LinkedCollectable.WingedStrawberry : this.Logic.Settings.Algorithm == LogicType.Endless ? LinkedNode.LinkedCollectable.LifeBerry : LinkedNode.LinkedCollectable.Strawberry;
                        pickedSpot.Node.Collectables[pickedSpot.Static] = Tuple.Create(berry, pickedSpotTup.Item2);
                        break;
                    }
                }

                return(true);
            }
Esempio n. 4
0
            public override bool Next()
            {
                var receipt = this.WorkingPossibility();

                if (receipt == null)
                {
                    return(false);
                }

                this.TriedRooms.Add(receipt.NewRoom.Static);
                this.AddReceipt(receipt);
                var newNode = receipt.NewRoom.Nodes["main"];
                var state   = new FlagSet();

                foreach (var node in LinkedNodeSet.Closure(newNode, this.Logic.Caps.WithFlags(state), null, true).Nodes)
                {
                    foreach (var setter in node.Static.FlagSetters)
                    {
                        TaskPathwayPickRoom.UpdateState(state, setter.Item1, setter.Item2);
                        newNode = node;
                    }
                }
                this.AddNextTask(new TaskPathwayPickEdge(this.Logic, newNode, state, false));
                if (this.Logic.Settings.Strawberries != StrawberryDensity.None)
                {
                    this.AddLastTask(new TaskPathwayBerryOffshoot(this.Logic, newNode, state));
                }

                return(true);
            }
Esempio n. 5
0
        public static LinkedNodeSet Closure(LinkedNode start, Capabilities capsForward, Capabilities capsReverse, bool internalOnly, int maxDistance = 9999)
        {
            var result = new LinkedNodeSet(new List <LinkedNode> {
                start
            });

            result.Extend(capsForward, capsReverse, internalOnly, maxDistance);
            return(result);
        }
Esempio n. 6
0
            public override bool Next()
            {
                if (this.Tries >= 5)
                {
                    Logger.Log("randomizer", $"Failure: took too many tries to place key from {Node.Room.Static.Name}:{Node.Static.Name}");
                    return(false);
                }

                // each attempt we should back further away from the idea that we might add a new room
                bool extendingMap = this.Logic.Random.Next(5) > this.Tries + 1;

                this.Tries++;

                var caps    = this.Logic.Caps.WithoutKey();
                var closure = LinkedNodeSet.Closure(this.Node, caps, caps, this.InternalOnly);

                closure.Shuffle(this.Logic.Random);

                if (!extendingMap)
                {
                    // just try to place a key
                    foreach (var spot in closure.UnlinkedCollectables())
                    {
                        if (spot.Static.MustFly)
                        {
                            continue;
                        }
                        this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedNode.LinkedCollectable.Key));
                        this.OriginalNode.Room.UsedKeyholes.Add(this.KeyholeID);
                        return(true);
                    }
                }

                // see if we can find somewhere to extend the map
                foreach (var outEdge in closure.UnlinkedEdges())
                {
                    if (!this.Logic.Map.HoleFree(outEdge.Node.Room, outEdge.Static.HoleTarget))
                    {
                        continue;
                    }
                    foreach (var toEdge in this.Logic.AvailableNewEdges(caps, caps, (StaticEdge e) => !e.FromNode.ParentRoom.End))
                    {
                        var mapped = ConnectAndMapReceipt.Do(this.Logic, outEdge, toEdge);
                        if (mapped == null)
                        {
                            continue;
                        }
                        this.AddReceipt(mapped);
                        this.AddNextTask(new TaskPathwayPlaceKey(this.Logic, mapped.EntryNode, this.KeyholeID, this.OriginalNode, true, this.Tries));
                        return(true);
                    }
                }

                // try again
                return(this.Next());
            }
Esempio n. 7
0
            public override bool Next()
            {
                int    minCount = LabyrinthMinimums[(int)this.Logic.Settings.Length];
                int    maxCount = LabyrinthMaximums[(int)this.Logic.Settings.Length];
                double progress = (double)(Logic.Map.Count - minCount) / (double)(maxCount - minCount);

                Logger.Log("randomizer", $"Progress: {progress}");
                if (progress > Logic.Random.NextDouble())
                {
                    Logger.Log("randomizer", "No need to proceed");
                    this.Goodwill = 0; // if we need to backtrack go past this
                    return(true);
                }

                if (this.Goodwill <= 0)
                {
                    Logger.Log("randomizer", "Failure: ran out of goodwill");
                    return(false);
                }

                var receipt = this.WorkingPossibility();

                if (receipt == null)
                {
                    Logger.Log("randomizer", "No working possibilities");
                    this.Goodwill = 0; // if we need to backtrack go past this
                    return(true);      // never fail!
                }

                this.AddReceipt(receipt);
                this.TriedEdges.Add(receipt.Edge.CorrespondingEdge(this.Edge.Node));
                var targetNode = receipt.Edge.OtherNode(this.Edge.Node);
                var closure    = LinkedNodeSet.Closure(targetNode, this.Logic.Caps.WithoutKey(), this.Logic.Caps.WithoutKey(), true);

                var any = false;

                foreach (var newedge in closure.UnlinkedEdges())
                {
                    any = true;
                    this.AddNextTask(new TaskLabyrinthContinue(this.Logic, newedge, Math.Min(5, this.Goodwill + 1)));
                }
                if (!any)
                {
                    this.Goodwill = 0;
                }
                else
                {
                    this.Goodwill--;
                }
                return(true);
            }
            private ConnectAndMapReceipt WorkingPossibility()
            {
                var caps          = this.Logic.Caps.WithoutKey(); // don't try to enter a door locked from the other side
                var possibilities = this.Logic.AvailableNewEdges(caps, null, e => RoomFilter(e.FromNode.ParentRoom));

                if (possibilities.Count == 0 && this.IsEnd)
                {
                    throw new GenerationError("No ending rooms available");
                }

                foreach (var edge in possibilities)
                {
                    var result = ConnectAndMapReceipt.Do(this.Logic, this.Edge, edge);
                    if (result != null)
                    {
                        var defaultBerry = this.Logic.Settings.Algorithm == LogicType.Endless ? LinkedNode.LinkedCollectable.LifeBerry : LinkedNode.LinkedCollectable.Strawberry;
                        var closure      = LinkedNodeSet.Closure(result.EntryNode, caps, caps, true);
                        var seen         = new HashSet <UnlinkedCollectable>();
                        foreach (var spot in closure.UnlinkedCollectables())
                        {
                            seen.Add(spot);
                            if (!spot.Static.MustFly && this.Logic.Random.Next(5) == 0)
                            {
                                spot.Node.Collectables[spot.Static] = Tuple.Create(defaultBerry, false);
                            }
                        }

                        closure.Extend(caps, null, true);
                        foreach (var spot in closure.UnlinkedCollectables())
                        {
                            if (!seen.Contains(spot) && !spot.Static.MustFly && this.Logic.Random.Next(10) == 0)
                            {
                                spot.Node.Collectables[spot.Static] = Tuple.Create(defaultBerry, true);
                            }
                        }

                        return(result);
                    }
                }

                return(null);
            }
Esempio n. 9
0
            public override bool Next()
            {
                var receipt = this.WorkingPossibility();

                if (receipt == null)
                {
                    return(false);
                }

                this.TriedRooms.Add(receipt.NewRoom.Static);
                this.AddReceipt(receipt);

                this.AddLastTask(new TaskLabyrinthFinish(this.Logic));

                var closure = LinkedNodeSet.Closure(receipt.NewRoom.Nodes["main"], this.Logic.Caps.WithoutKey(), this.Logic.Caps.WithoutKey(), true);
                var node    = receipt.NewRoom.Nodes["main"];

                foreach (var edge in closure.UnlinkedEdges())
                {
                    this.AddNextTask(new TaskLabyrinthContinue(this.Logic, edge));
                }
                return(true);
            }
Esempio n. 10
0
            public override bool Next()
            {
                var receipt = this.Edge.Static.CustomWarp ? this.WorkingWarpPossibility() : this.WorkingPossibility();

                if (receipt == null)
                {
                    Logger.Log("randomizer", $"Failure: could not find a room that fits on {Edge.Node.Room.Static.Name}:{Edge.Node.Static.Name}:{Edge.Static.HoleTarget}");
                    return(false);
                }

                if (this.FakeEnd && receipt.NewRoom.Static.Level.Entities.Any(x => x.Name == "blackGem"))
                {
                }

                this.AddReceipt(receipt);
                this.TriedRooms.Add(receipt.NewRoom.Static);
                if (!this.IsEnd || this.FakeEnd)
                {
                    var newNode = receipt.Edge.OtherNode(this.Edge.Node);
                    var state   = new FlagSet(this.State);
                    foreach (var node in LinkedNodeSet.Closure(newNode, this.Logic.Caps.WithFlags(this.State), null, true).Nodes)
                    {
                        foreach (var setter in node.Static.FlagSetters)
                        {
                            UpdateState(state, setter.Item1, setter.Item2);
                            newNode = node;
                        }
                    }
                    this.AddNextTask(new TaskPathwayPickEdge(this.Logic, newNode, state, this.FakeEnd));
                    if (this.Logic.Settings.Strawberries != StrawberryDensity.None)
                    {
                        this.AddLastTask(new TaskPathwayBerryOffshoot(this.Logic, newNode, state));
                    }
                }

                return(true);
            }
Esempio n. 11
0
        private void GenerateLabyrinth()
        {
            this.Caps             = this.Caps.WithoutKey();
            this.StartingGemCount = this.Settings.Length == MapLength.Short ? 3 : 0;

            void retry()
            {
                throw new RetryException();
            }

            foreach (var room in RandoLogic.AllRooms)
            {
                if (room.Name == "Celeste/6-Reflection/A/b-00")
                {
                    var lroom = new LabyrinthStartRoom(room);
                    this.Map.AddRoom(lroom);
                    this.RemainingRooms.Remove(room);
                    this.PossibleContinuations.AddRange(LinkedNodeSet.Closure(lroom.Nodes["main"], this.Caps, this.Caps, true).UnlinkedEdges());
                    break;
                }
            }

            while (this.PossibleContinuations.Count != 0)
            {
                //Logger.Log("DEBUG", $"status: rooms={this.Map.Count} queue={this.PossibleContinuations.Count}");
                int idx       = this.Random.Next(this.PossibleContinuations.Count);
                var startEdge = this.PossibleContinuations[idx];
                this.PossibleContinuations.RemoveAt(idx);

                foreach (var toEdge in this.AvailableNewEdges(this.Caps, this.Caps,
                                                              (edge) => edge.FromNode.ParentRoom.ReqEnd is Impossible &&
                                                              edge.FromNode.ParentRoom.Name != "Celeste/7-Summit/A/g-00b" &&
                                                              edge.FromNode.ParentRoom.Nodes.Values.All(node => node.FlagSetters.Count == 0)))
                {
                    var result = ConnectAndMapReceipt.Do(this, startEdge, toEdge);
                    if (result != null)
                    {
                        var closure     = LinkedNodeSet.Closure(result.EntryNode, this.Caps, this.Caps, true);
                        var ue          = closure.UnlinkedEdges();
                        var uc          = new List <Tuple <UnlinkedCollectable, bool> >();
                        var alreadySeen = new HashSet <UnlinkedCollectable>();
                        foreach (var c in closure.UnlinkedCollectables())
                        {
                            alreadySeen.Add(c);
                            uc.Add(Tuple.Create(c, false));
                        }
                        closure.Extend(this.Caps, null, true);
                        foreach (var c in closure.UnlinkedCollectables())
                        {
                            if (alreadySeen.Contains(c))
                            {
                                continue;
                            }
                            uc.Add(Tuple.Create(c, true));
                        }
                        if (ue.Count == 0 && uc.Count == 0)
                        {
                            result.Undo();
                            continue;
                        }
                        else if (ue.Count == 0)
                        {
                            this.PriorityCollectables.AddRange(uc);
                        }
                        else
                        {
                            this.PossibleContinuations.AddRange(ue);
                            this.PossibleCollectables.AddRange(uc);
                        }
                        break;
                    }
                }

                if (this.Map.Count >= LabyrinthMaximums[(int)this.Settings.Length])
                {
                    break;
                }
            }

            while (this.PossibleContinuations.Count != 0)
            {
                //Logger.Log("DEBUG", $"Pruning - {this.PossibleContinuations.Count} remaining");
                var startEdge = this.PossibleContinuations.Last();
                this.PossibleContinuations.RemoveAt(this.PossibleContinuations.Count - 1);

                var closure     = LinkedNodeSet.Closure(startEdge.Node, this.Caps, this.Caps, true);
                var uc          = new List <Tuple <UnlinkedCollectable, bool> >();
                var alreadySeen = new HashSet <UnlinkedCollectable>();
                foreach (var c in closure.UnlinkedCollectables())
                {
                    alreadySeen.Add(c);
                    uc.Add(Tuple.Create(c, false));
                }
                closure.Extend(this.Caps, null, true);
                foreach (var c in closure.UnlinkedCollectables())
                {
                    if (alreadySeen.Contains(c))
                    {
                        continue;
                    }
                    uc.Add(Tuple.Create(c, true));
                }

                if (uc.Count == 0)
                {
                    if (startEdge.Node.Room.Static.Name == "Celeste/6-Reflection/A/b-00")
                    {
                        // DON'T REMOVE THE STARTING ROOM OMG
                        continue;
                    }
                    var edgeCount = 0;
                    foreach (var node in startEdge.Node.Room.Nodes.Values)
                    {
                        edgeCount += node.Edges.Count;
                    }
                    if (edgeCount <= 1)
                    {
                        var nodeNames = new List <string>(startEdge.Node.Room.Nodes.Keys);
                        nodeNames.Sort();
                        foreach (var nodeName in nodeNames)
                        {
                            var node = startEdge.Node.Room.Nodes[nodeName];
                            foreach (var edge in node.Edges)
                            {
                                var otherNode = edge.OtherNode(node);
                                var otherEdge = edge.OtherEdge(node);
                                otherNode.Edges.Remove(edge);
                                this.PossibleContinuations.Add(new UnlinkedEdge(otherNode, otherEdge));
                            }
                        }
                        this.Map.RemoveRoom(startEdge.Node.Room);
                    }
                }
                else
                {
                    this.PriorityCollectables.AddRange(uc);
                    foreach (var c in uc)
                    {
                        this.PossibleCollectables.Remove(c);
                    }
                }
            }

            if (this.Map.Count < LabyrinthMinimums[(int)this.Settings.Length])
            {
                //Logger.Log("DEBUG", "retrying - too short");
                retry();
            }

            if (this.PossibleCollectables.Count + this.PriorityCollectables.Count < (6 - this.StartingGemCount))
            {
                //Logger.Log("DEBUG", "retrying - not enough spots");
                retry();
            }

            for (var gem = LinkedCollectable.Gem1 + this.StartingGemCount; gem <= LinkedCollectable.Gem6; gem++)
            {
                //Logger.Log("DEBUG", $"Placing {gem}");
                var collection = this.PriorityCollectables.Count != 0 ? this.PriorityCollectables : this.PossibleCollectables;
                if (collection.Count == 0)    // just in case
                {
                    retry();
                }
                var idx        = this.Random.Next(collection.Count);
                var spot       = collection[idx].Item1;
                var autoBubble = collection[idx].Item2;
                collection.RemoveAt(idx);

                if (spot.Static.MustFly)
                {
                    //Logger.Log("DEBUG", "...winged berry");
                    if (this.Settings.Strawberries != StrawberryDensity.None)
                    {
                        spot.Node.Collectables[spot.Static] = Tuple.Create(LinkedCollectable.WingedStrawberry, autoBubble);
                    }
                    gem--;
                }
                else
                {
                    spot.Node.Collectables[spot.Static] = Tuple.Create(gem, autoBubble);
                    //Logger.Log("DEBUG", $"Adding gem to {spot}");

                    if (collection == this.PriorityCollectables)
                    {
                        for (int i = 0; i < collection.Count; i++)
                        {
                            if (collection[i].Item1.Node.Room == spot.Node.Room)
                            {
                                collection.RemoveAt(i);
                                i--;
                            }
                        }
                    }
                }
            }

            var defaultBerry = this.Settings.HasLives ? LinkedCollectable.LifeBerry : LinkedCollectable.Strawberry;

            while (this.Settings.Strawberries != StrawberryDensity.None && this.PriorityCollectables.Count != 0)
            {
                //Logger.Log("DEBUG", $"Pruning priority collectables - {this.PriorityCollectables.Count} remaining");
                var spot       = this.PriorityCollectables.Last().Item1;
                var autoBubble = this.PriorityCollectables.Last().Item2;
                this.PriorityCollectables.RemoveAt(this.PriorityCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = Tuple.Create(spot.Static.MustFly ? LinkedCollectable.WingedStrawberry : defaultBerry, autoBubble);
            }

            int targetCount = 0;

            switch (this.Settings.Strawberries)
            {
            case StrawberryDensity.High:
                targetCount = 0;
                break;

            case StrawberryDensity.Low:
                targetCount = this.PossibleCollectables.Count / 3 * 2;
                break;

            case StrawberryDensity.None:
                targetCount = this.PossibleCollectables.Count;
                break;
            }
            this.PossibleCollectables.Shuffle(this.Random);
            while (this.PossibleCollectables.Count > targetCount)
            {
                //Logger.Log("DEBUG", $"Pruning collectables - {this.PossibleContinuations.Count - targetCount} remaining");
                var spot       = this.PossibleCollectables.Last().Item1;
                var autoBubble = this.PossibleCollectables.Last().Item2;
                this.PossibleCollectables.RemoveAt(this.PossibleCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = Tuple.Create(spot.Static.MustFly ? LinkedCollectable.WingedStrawberry : defaultBerry, autoBubble);
            }
        }
Esempio n. 12
0
        private void GenerateLabyrinth()
        {
            this.Caps             = this.Caps.WithoutKey();
            this.StartingGemCount = this.Settings.Length == MapLength.Short ? 3 : 0;

            void retry()
            {
                this.PossibleCollectables.Clear();
                this.PriorityCollectables.Clear();
                this.PossibleContinuations.Clear();
                this.ResetRooms();
                this.Map.Clear();
            }

tryagain:

            foreach (var room in this.RemainingRooms)
            {
                if (room.Name == "Celeste/6-Reflection/A/b-00")
                {
                    var lroom = new LabyrinthStartRoom(room);
                    this.Map.AddRoom(lroom);
                    this.RemainingRooms.Remove(room);
                    this.PossibleContinuations.AddRange(LinkedNodeSet.Closure(lroom.Nodes["main"], this.Caps, this.Caps, true).UnlinkedEdges());
                    break;
                }
            }

            while (this.PossibleContinuations.Count != 0)
            {
                //Logger.Log("DEBUG", $"status: rooms={this.Map.Count} queue={this.PossibleContinuations.Count}");
                int idx       = this.Random.Next(this.PossibleContinuations.Count);
                var startEdge = this.PossibleContinuations[idx];
                this.PossibleContinuations.RemoveAt(idx);

                foreach (var toEdge in this.AvailableNewEdges(this.Caps, this.Caps, (edge) => !edge.FromNode.ParentRoom.End))
                {
                    var result = ConnectAndMapReceipt.Do(this, startEdge, toEdge);
                    if (result != null)
                    {
                        var closure = LinkedNodeSet.Closure(result.EntryNode, this.Caps, this.Caps, true);
                        var ue      = closure.UnlinkedEdges();
                        var uc      = closure.UnlinkedCollectables();
                        if (ue.Count == 0 && uc.Count == 0)
                        {
                            result.Undo();
                            continue;
                        }
                        else if (ue.Count == 0)
                        {
                            this.PriorityCollectables.AddRange(uc);
                        }
                        else
                        {
                            this.PossibleContinuations.AddRange(ue);
                            this.PossibleCollectables.AddRange(uc);
                        }
                        break;
                    }
                }

                if (this.Map.Count >= LabyrinthMaximums[(int)this.Settings.Length])
                {
                    break;
                }
            }

            if (this.Map.Count < LabyrinthMinimums[(int)this.Settings.Length])
            {
                //Logger.Log("DEBUG", "retrying - too short");
                retry();
                goto tryagain;
            }

            if (this.PossibleCollectables.Count + this.PriorityCollectables.Count < (6 - this.StartingGemCount))
            {
                //Logger.Log("DEBUG", "retrying - not enough spots");
                retry();
                goto tryagain;
            }

            for (var gem = LinkedNode.LinkedCollectable.Gem1 + this.StartingGemCount; gem <= LinkedNode.LinkedCollectable.Gem6; gem++)
            {
                var collection = this.PriorityCollectables.Count != 0 ? this.PriorityCollectables : this.PossibleCollectables;
                if (collection.Count == 0)    // just in case
                {
                    retry();
                    goto tryagain;
                }
                var idx  = this.Random.Next(collection.Count);
                var spot = collection[idx];
                collection.RemoveAt(idx);

                if (spot.Static.MustFly)
                {
                    spot.Node.Collectables[spot.Static] = LinkedNode.LinkedCollectable.WingedStrawberry;
                    gem--;
                }
                else
                {
                    spot.Node.Collectables[spot.Static] = gem;
                }
            }

            while (this.PriorityCollectables.Count != 0)
            {
                var spot = this.PriorityCollectables[this.PriorityCollectables.Count - 1];
                this.PriorityCollectables.RemoveAt(this.PriorityCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = spot.Static.MustFly ? LinkedNode.LinkedCollectable.WingedStrawberry : LinkedNode.LinkedCollectable.Strawberry;
            }

            var targetCount = this.PossibleCollectables.Count / 3 * 2;

            this.PossibleCollectables.Shuffle(this.Random);
            while (this.PossibleCollectables.Count > targetCount)
            {
                var spot = this.PossibleCollectables[this.PossibleCollectables.Count - 1];
                this.PossibleCollectables.RemoveAt(this.PossibleCollectables.Count - 1);

                spot.Node.Collectables[spot.Static] = spot.Static.MustFly ? LinkedNode.LinkedCollectable.WingedStrawberry : LinkedNode.LinkedCollectable.Strawberry;
            }
        }
Esempio n. 13
0
            public override bool Next()
            {
                if (this.Tries >= 5)
                {
                    Logger.Log("randomizer", $"Failure: took too many tries to satisfy {this.Req} from {Node.Room.Static.Name}:{Node.Static.Name}");
                    return(false);
                }

                // each attempt we should back further away from the idea that we might add a new room
                int  roll         = this.Logic.Random.Next(5);
                bool extendingMap = roll > this.Tries;

                this.Tries++;

                var caps     = this.Logic.Caps.WithoutKey().WithFlags(this.State);
                int maxSteps = 99999;

                if (!InternalOnly)
                {
                    maxSteps = this.Logic.Random.Next(1, 20);
                }
                var closure = LinkedNodeSet.Closure(this.Node, caps, caps, this.InternalOnly, maxSteps);

                closure.Shuffle(this.Logic.Random);

                if (this.Req is FlagRequirement fr)
                {
                    var curval = this.State.TryGetValue(fr.Flag, out var x) ? x : FlagState.Unset;
                    if (fr.Set && (curval == FlagState.Both || curval == FlagState.Set || curval == FlagState.UnsetToSet))
                    {
                        return(true);
                    }
                    if (!fr.Set && (curval == FlagState.Both || curval == FlagState.Unset || curval == FlagState.SetToUnset))
                    {
                        return(true);
                    }
                }

                if (!extendingMap)
                {
                    switch (this.Req)
                    {
                    case KeyRequirement keyReq: {
                        // just try to place a key
                        foreach (var spot in closure.UnlinkedCollectables())
                        {
                            if (spot.Static.MustFly)
                            {
                                continue;
                            }
                            if (spot.Node.Room == this.OriginalNode.Room)
                            {
                                // don't be boring!
                                continue;
                            }
                            this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedCollectable.Key, false, keyReq.KeyholeID, this.OriginalNode.Room));
                            return(true);
                        }

                        // try again for things that we need to bubble back from
                        var newClosure = new LinkedNodeSet(closure);
                        newClosure.Extend(caps, null, true);
                        foreach (var spot in newClosure.UnlinkedCollectables())
                        {
                            if (spot.Static.MustFly)
                            {
                                continue;
                            }
                            if (spot.Node.Room == this.OriginalNode.Room)
                            {
                                // don't be boring!
                                continue;
                            }
                            this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedCollectable.Key, true, keyReq.KeyholeID, this.OriginalNode.Room));
                            return(true);
                        }

                        // third try: place a room which has a reachable spot
                        var appropriateNodes = this.Logic.RemainingRooms
                                               .Where(x => x.ReqEnd is Impossible)
                                               .SelectMany(r => r.Nodes.Values)
                                               .Where(n => n.Collectables.Count(c => !c.MustFly) != 0)
                                               .ToList();
                        appropriateNodes.Shuffle(this.Logic.Random);
                        foreach (var n in appropriateNodes)
                        {
                            // hack: make a linkednode for each staticnode so that the closure methods can work on it...
                            var edges = LinkedNodeSet
                                        .Closure(new LinkedRoom(n.ParentRoom, Vector2.Zero).Nodes[n.Name], caps, caps, true)
                                        .Shuffle(this.Logic.Random)
                                        .UnlinkedEdges()
                                        .Select(e => e.Static);
                            foreach (var edge in edges)
                            {
                                foreach (var startEdge in closure.UnlinkedEdges())
                                {
                                    var receipt = ConnectAndMapReceipt.Do(this.Logic, startEdge, edge, true);
                                    if (receipt != null)
                                    {
                                        this.AddReceipt(receipt);
                                        var cols = n.Collectables.Where(c => !c.MustFly).ToList();
                                        cols.Shuffle(this.Logic.Random);
                                        this.AddReceipt(PlaceCollectableReceipt.Do(receipt.NewRoom.Nodes[n.Name], cols[this.Logic.Random.Next(cols.Count)], LinkedCollectable.Key, false, keyReq.KeyholeID, this.OriginalNode.Room));
                                        return(true);
                                    }
                                }
                            }
                        }

                        break;
                    }

                    case FlagRequirement flagReq: {
                        var appropriateNodes = this.Logic.RemainingRooms
                                               .SelectMany(r => r.Nodes.Values)
                                               .Where(n => n.FlagSetters.Any(s => s.Item1 == flagReq.Flag && s.Item2 == flagReq.Set))
                                               .ToList();
                        appropriateNodes.Shuffle(this.Logic.Random);
                        foreach (var n in appropriateNodes)
                        {
                            // hack: make a linkednode for each staticnode so that the closure methods can work on it...
                            var edges = LinkedNodeSet
                                        .Closure(new LinkedRoom(n.ParentRoom, Vector2.Zero).Nodes[n.Name], caps, caps, true)
                                        .Shuffle(this.Logic.Random)
                                        .UnlinkedEdges()
                                        .Select(e => e.Static);
                            foreach (var edge in edges)
                            {
                                foreach (var startEdge in closure.UnlinkedEdges())
                                {
                                    var receipt = ConnectAndMapReceipt.Do(this.Logic, startEdge, edge, true);
                                    if (receipt != null)
                                    {
                                        this.AddReceipt(receipt);
                                        // TODO: check for reverse traversability back to orig node
                                        return(true);
                                    }
                                }
                            }
                        }
                        break;
                    }

                    default: {
                        throw new Exception($"Don't know how to satisfy {this.Req}. What?");
                    }
                    }
                }

                // see if we can find somewhere to extend the map
                foreach (var outEdge in closure.UnlinkedEdges())
                {
                    if (!this.Logic.Map.HoleFree(outEdge.Node.Room, outEdge.Static.HoleTarget))
                    {
                        continue;
                    }
                    foreach (var toEdge in this.Logic.AvailableNewEdges(caps, caps, e => e.FromNode.ParentRoom.ReqEnd is Impossible))
                    {
                        var mapped = ConnectAndMapReceipt.Do(this.Logic, outEdge, toEdge, isBacktrack: true);
                        if (mapped == null)
                        {
                            continue;
                        }

                        this.AddReceipt(mapped);
                        this.AddNextTask(new TaskPathwaySatisfyRequirement(this.Logic, mapped.EntryNode, this.Req, this.State, this.OriginalNode, true, this.Tries));
                        return(true);
                    }
                }

                // if we failed to both extend the map or place the capability at the same time, we're f****d!
                if (!extendingMap)
                {
                    return(false);
                }

                // try again
                return(this.Next());
            }
Esempio n. 14
0
            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);
            }
Esempio n. 15
0
            public override bool Next()
            {
                if (this.Tries >= 5)
                {
                    Logger.Log("randomizer", $"Failure: took too many tries to place key from {Node.Room.Static.Name}:{Node.Static.Name}");
                    return(false);
                }

                // each attempt we should back further away from the idea that we might add a new room
                int  roll         = this.Logic.Random.Next(5);
                bool extendingMap = roll > this.Tries;

                this.Tries++;

                var caps     = this.Logic.Caps.WithoutKey();
                int maxSteps = 99999;

                if (!InternalOnly)
                {
                    maxSteps = this.Logic.Random.Next(6, 20);
                }
                var closure = LinkedNodeSet.Closure(this.Node, caps, caps, this.InternalOnly, maxSteps);

                closure.Shuffle(this.Logic.Random);

                if (!extendingMap)
                {
                    // just try to place a key
                    foreach (var spot in closure.UnlinkedCollectables())
                    {
                        if (spot.Static.MustFly)
                        {
                            continue;
                        }
                        if (spot.Node.Room == this.OriginalNode.Room)
                        {
                            // don't be boring!
                            continue;
                        }
                        this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedNode.LinkedCollectable.Key, false));
                        this.OriginalNode.Room.UsedKeyholes.Add(this.KeyholeID);
                        return(true);
                    }

                    // try again for things that we need to bubble back from
                    var newClosure = new LinkedNodeSet(closure);
                    newClosure.Extend(caps, null, true);
                    foreach (var spot in newClosure.UnlinkedCollectables())
                    {
                        if (spot.Static.MustFly)
                        {
                            continue;
                        }
                        if (spot.Node.Room == this.OriginalNode.Room)
                        {
                            // don't be boring!
                            continue;
                        }
                        this.AddReceipt(PlaceCollectableReceipt.Do(spot.Node, spot.Static, LinkedNode.LinkedCollectable.Key, true));
                        this.OriginalNode.Room.UsedKeyholes.Add(this.KeyholeID);
                        return(true);
                    }
                }

                // see if we can find somewhere to extend the map
                foreach (var outEdge in closure.UnlinkedEdges())
                {
                    if (!this.Logic.Map.HoleFree(outEdge.Node.Room, outEdge.Static.HoleTarget))
                    {
                        continue;
                    }
                    foreach (var toEdge in this.Logic.AvailableNewEdges(caps, caps, e => e.FromNode.ParentRoom.ReqEnd is Impossible))
                    {
                        var mapped = ConnectAndMapReceipt.Do(this.Logic, outEdge, toEdge, isBacktrack: true);
                        if (mapped == null)
                        {
                            continue;
                        }
                        this.AddReceipt(mapped);
                        this.AddNextTask(new TaskPathwayPlaceKey(this.Logic, mapped.EntryNode, this.KeyholeID, this.OriginalNode, true, this.Tries));
                        return(true);
                    }
                }

                // try again
                return(this.Next());
            }