Skip to content

Commit 95b09aa

Browse files
committed
Add Extra Loops palace style
Based on Reconstructed with some twists: - No regular dead-end rooms (not including item rooms) - Loops pick rooms that are as far away as possible. This is to reduce the likelihood of long dead-end paths that would loop into themselves at the end. (It might still happen if there are very few open rooms.) - No drop rooms This is a small subclass to Reconstructed. I made this instead of having the option to remove drops from all styles, as drops are generally fun in the coordinate palaces.
1 parent f67cff2 commit 95b09aa

6 files changed

Lines changed: 69 additions & 2 deletions

File tree

RandomizerCore/EnumTypes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ public enum PalaceStyle
213213
TOWER,
214214
[Description("Chaos"), DefaultWeight(0)]
215215
CHAOS,
216+
[Description("Extra Loops")]
217+
EXTRA_LOOPS,
216218
[Description("Random"), Metastyle]
217219
RANDOM,
218220
[Description("Random (All Same)"), Metastyle]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace Z2Randomizer.RandomizerCore.Sidescroll;
8+
9+
public class ExtraLoopsPalaceGenerator(CancellationToken ct) : ReconstructedPalaceGenerator(ct)
10+
{
11+
internal override Task<Palace> GeneratePalace(RandomizerProperties props, RoomPool rooms, Random r, int roomCount, int palaceNumber)
12+
{
13+
rooms.RemoveRooms(room => room.HasDrop);
14+
rooms.RemoveRooms(room => !room.IsEntrance && !room.IsBossRoom && !room.HasItem
15+
&& RoomExitTypeExtensions.DEADENDS.Contains(room.CategorizeExits()));
16+
return base.GeneratePalace(props, rooms, r, roomCount, palaceNumber);
17+
}
18+
19+
public override void Consolidate(List<Room> openRooms)
20+
{
21+
Room[] openCopy = new Room[openRooms.Count];
22+
openRooms.CopyTo(openCopy); // shallow copy
23+
foreach (Room r2 in openCopy)
24+
{
25+
var furthestFirst = openRooms.OrderBy(room => -Palace.RoomDistance(r2, room));
26+
foreach (Room r3 in furthestFirst)
27+
{
28+
if (r2 != r3 && openRooms.Contains(r2) && openRooms.Contains(r3))
29+
{
30+
if (AttachToOpen(r2, r3, openRooms))
31+
{
32+
break;
33+
}
34+
}
35+
}
36+
}
37+
}
38+
}

RandomizerCore/Sidescroll/Palace.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,31 @@ public bool IsBossRoomAtLeastMinDistance(int minSteps)
185185
return false; // Boss room not found??
186186
}
187187

188+
public static int RoomDistance(Room room1, Room room2)
189+
{
190+
HashSet<Room> reachedRooms = [];
191+
Queue<(Room, int)> roomsToCheck = [];
192+
roomsToCheck.Enqueue((room1, 0));
193+
while (roomsToCheck.Count > 0)
194+
{
195+
var (room, stepsToRoom) = roomsToCheck.Dequeue();
196+
if (room == room2)
197+
{
198+
return stepsToRoom;
199+
}
200+
201+
// This will return false if the room is already added
202+
if (!reachedRooms.Add(room)) { continue; }
203+
204+
int stepsToNextRoom = stepsToRoom + 1;
205+
if (room.Left != null) { roomsToCheck.Enqueue((room.Left, stepsToNextRoom)); }
206+
if (room.Right != null) { roomsToCheck.Enqueue((room.Right, stepsToNextRoom)); }
207+
if (room.Up != null) { roomsToCheck.Enqueue((room.Up, stepsToNextRoom)); }
208+
if (room.Down != null) { roomsToCheck.Enqueue((room.Down, stepsToNextRoom)); }
209+
}
210+
return -1;
211+
}
212+
188213
/// same algorithm as BossRoomMinDistance except for shapes instead of rooms
189214
public static bool BossRoomMinDistanceShape(Dictionary<Coord, RoomExitType> shape, Coord bossRoom, int minSteps)
190215
{

RandomizerCore/Sidescroll/Palaces.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public async Task<List<Palace>> CreatePalaces(Random r, RandomizerProperties pro
7676
PalaceStyle.TOWER => new TowerCoordinatePalaceGenerator(),
7777
PalaceStyle.RECONSTRUCTED => new ReconstructedPalaceGenerator(ct),
7878
PalaceStyle.CHAOS => new ChaosPalaceGenerator(),
79+
PalaceStyle.EXTRA_LOOPS => new ExtraLoopsPalaceGenerator(ct),
7980
_ => throw new Exception("Unrecognized palace style while generating palaces")
8081
};
8182

RandomizerCore/Sidescroll/ReconstructedPalaceGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ private void ProcessRoom(Palace palace, Room room, List<Room> openRooms)
307307
}
308308
}
309309

310-
public void Consolidate(List<Room> openRooms)
310+
public virtual void Consolidate(List<Room> openRooms)
311311
{
312312
Room[] openCopy = new Room[openRooms.Count];
313313
openRooms.CopyTo(openCopy);
@@ -330,7 +330,7 @@ public void Consolidate(List<Room> openRooms)
330330
/// <param name="room"></param> The room to be attached
331331
/// <param name="open"></param> The room onto which R is attached
332332
/// <returns>Whether or not the room was actually able to be attached.</returns>
333-
private bool AttachToOpen(Room room, Room open, List<Room> openRooms)
333+
protected bool AttachToOpen(Room room, Room open, List<Room> openRooms)
334334
{
335335
bool placed = false;
336336
//Right from open into r

RandomizerCore/Sidescroll/RoomExitType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public enum RoomExitType
4141
public static class RoomExitTypeExtensions
4242
{
4343
public static RoomExitType[] ALL = Enum.GetValues(typeof(RoomExitType)).Cast<RoomExitType>().ToArray();
44+
public static RoomExitType[] DEADENDS = [RoomExitType.DEADEND_EXIT_RIGHT, RoomExitType.DEADEND_EXIT_UP, RoomExitType.DEADEND_EXIT_LEFT, RoomExitType.DEADEND_EXIT_DOWN];
4445

4546
public const int LEFT = 0b00010000;
4647
public const int DOWN = 0b00001000;

0 commit comments

Comments
 (0)