using System; using System.Collections.Generic; namespace Ironspore { public enum Hold { Cargo, SafeCase } // Stable content IDs also resolve the existing four gameplay slot indices. public static class InventoryGrid { public const int CargoSize = 4, CaseSize = 2, PodSize = 2; static readonly string[][] Slots = { new[] { "rapid", "coil", "siege" }, new[] { "guided", "pulse" }, new[] { "efficient", "vector" }, new[] { "composite_guard", "reactive_guard" } }; public static string EquipmentId(int slot, int index) => slot >= 0 && slot < Slots.Length && index >= 0 && index < Slots[slot].Length ? Slots[slot][index] : null; public static int Slot(string id) => Array.FindIndex(Slots, group => Array.IndexOf(group, id) >= 0); public static int Index(int slot, string id) => Array.IndexOf(Slots[slot], id); public static bool IsMaterial(string id) => id == "alloy" || id == "cells"; public static bool IsBlueprint(string id) => id != null && id.StartsWith("blueprint_", StringComparison.Ordinal) && id.Length > 10; public static bool Known(string id) => IsMaterial(id) || IsBlueprint(id) || Slot(id) >= 0; public static (string item, int alloy, int cells) Recipe(string blueprint) { switch (blueprint) { case "blueprint_coil_capacitor": return ("coil", 4, 2); case "blueprint_boost_efficient": return ("vector", 3, 2); case "blueprint_reactive_guard": return ("reactive_guard", 4, 1); default: return (null, 0, 0); } } public static void Size(string id, bool rotated, out int width, out int height) { width = 1; height = 1; switch (id) { case "rapid": height = 3; break; case "coil": width = 2; height = 3; break; case "siege": width = 2; height = 4; break; case "pulse": case "reactive_guard": case "vector": width = 2; height = 2; break; case "guided": case "efficient": case "composite_guard": height = 2; break; } if (rotated) (width, height) = (height, width); } public static int Area(CargoStack item) { Size(item.itemId, false, out int w, out int h); return w * h; } public static bool Fits(List items, int size, CargoStack item, int x, int y, bool rotated) { Size(item.itemId, rotated, out int w, out int h); if (x < 0 || y < 0 || x > size - w || y > size - h) return false; foreach (var other in items) { if (other == null) return false; if (ReferenceEquals(item, other)) continue; Size(other.itemId, other.rotated, out int ow, out int oh); if (x < other.x + ow && x + w > other.x && y < other.y + oh && y + h > other.y) return false; } return true; } public static bool Place(List items, int size, CargoStack item, int x, int y, bool rotated) { if (items.Contains(item) || !Fits(items, size, item, x, y, rotated)) return false; item.x = x; item.y = y; item.rotated = rotated; items.Add(item); return true; } public static bool AutoPlace(List items, int size, CargoStack item) { for (int turn = 0; turn < 2; turn++) for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) if (Place(items, size, item, x, y, turn == 1)) return true; return false; } // The pod is small. Try alternate placements so a greedy choice cannot reject a valid manifest. public static bool PodFits(List selection) { if (selection.Count == 0 || selection.Count > PodSize * PodSize) return false; return Pack(0, new List()); bool Pack(int index, List placed) { if (index == selection.Count) return true; var next = new CargoStack(selection[index].itemId, selection[index].count) { uid = selection[index].uid }; for (int turn = 0; turn < 2; turn++) for (int y = 0; y < PodSize; y++) for (int x = 0; x < PodSize; x++) { if (!Place(placed, PodSize, next, x, y, turn == 1)) continue; if (Pack(index + 1, placed)) return true; placed.RemoveAt(placed.Count - 1); } return false; } } } }