using System.Text.Json.Serialization; using SPTarkov.Common.Annotations; namespace SPTarkov.Server.Core.Helpers; [Injectable] public class ContainerHelper { /// /// Finds a slot for an item in a given 2D container map /// /// List of container with slots filled/free /// Width of item /// Height of item /// Location to place item in container public FindSlotResult FindSlotForItem(int[][] container2D, int itemWidth, int itemHeight) { var rotation = false; var minVolume = (itemWidth < itemHeight ? itemWidth : itemHeight) - 1; var containerY = container2D.Length; var containerX = container2D[0].Length; var limitY = containerY - minVolume; var limitX = containerX - minVolume; // Every x+y slot taken up in container, exit if (container2D.All(x => x.All(y => y == 1))) { return new FindSlotResult(false); } // Down = y for (var y = 0; y < limitY; y++) { if (container2D[y].All(x => x == 1)) // Every item in row is full, skip row { continue; } // Try each slot on the row (across = x) for (var x = 0; x < limitX; x++) { var foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemWidth, itemHeight); if (foundSlot) { return new FindSlotResult(true, x, y, rotation); } // Failed to find slot, rotate item and try again if (!foundSlot && ItemBiggerThan1X1(itemWidth, itemHeight)) { // Bigger than 1x1, try rotating foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemHeight, itemWidth); // Height/Width swapped if (foundSlot) { // Found a slot for it when rotated rotation = true; return new FindSlotResult(true, x, y, rotation); } } } } // Tried all possible holes, nothing big enough for the item return new FindSlotResult(false); } protected static bool ItemBiggerThan1X1(int itemWidth, int itemHeight) { return itemWidth * itemHeight > 1; } /// /// Find a slot inside a container an item can be placed in /// /// Container to find space in /// Container x size /// Container y size /// ??? /// ??? /// Items width /// Items height /// True - slot found protected bool LocateSlot( int[][] container2D, int containerX, int containerY, int x, int y, int itemW, int itemH) { var foundSlot = true; for (var itemY = 0; itemY < itemH; itemY++) { if (foundSlot && y + itemH - 1 > containerY - 1) { foundSlot = false; break; } // Does item fit x-ways across for (var itemX = 0; itemX < itemW; itemX++) { if (foundSlot && x + itemW - 1 > containerX - 1) { foundSlot = false; break; } if (container2D[y + itemY][x + itemX] != 0) { foundSlot = false; break; } } if (!foundSlot) { break; } } return foundSlot; } /// /// Find a free slot for an item to be placed at /// /// Container to place item in /// Container x size /// Container y size /// Items width /// Items height /// is item rotated public void FillContainerMapWithItem( int[][] container2D, int x, int y, int itemW, int itemH, bool rotate) { // Swap height/width if we want to fit it in rotated var itemWidth = rotate ? itemH : itemW; var itemHeight = rotate ? itemW : itemH; for (var tmpY = y; tmpY < y + itemHeight; tmpY++) for (var tmpX = x; tmpX < x + itemWidth; tmpX++) { if (container2D[tmpY][tmpX] == 0) // Flag slot as used { container2D[tmpY][tmpX] = 1; } else { throw new Exception($"Slot at({x}, {y}) is already filled. Cannot fit a {itemW} by {itemH} item"); } } } } public class FindSlotResult { public FindSlotResult(bool success) { Success = success; } public FindSlotResult(bool success, int x, int y, bool rotation) { Success = success; X = x; Y = y; Rotation = rotation; } public FindSlotResult() { } [JsonPropertyName("success")] public bool? Success { get; set; } [JsonPropertyName("x")] public int? X { get; set; } [JsonPropertyName("y")] public int? Y { get; set; } [JsonPropertyName("rotation")] public bool? Rotation { get; set; } }