using System; using System.Collections.Generic; using System.Linq; using TMPro; using UnityEngine; namespace Parking { public class ParkingManager : MonoBehaviour { public const float Width = 68; public const float Height = 29; public static ParkingManager Instance; [SerializeField] private int stepTime = 15; [SerializeField] private TextMeshProUGUI timeText; [SerializeField] public TextMeshProUGUI countsText; [SerializeField] public TextMeshProUGUI rejectedText; [SerializeField] public GameObject spotPrefabA; [SerializeField] public GameObject spotPrefabB; [SerializeField] public GameObject spotPrefabC; [SerializeField] public GameObject spotPrefabD; [SerializeField] public GameObject carPrefab; private readonly float[] _spotHeights = {3.5f, 4f, 5f, 7.5f}; public readonly List> SpotMap = new() {new List(), new List(), new List(), new List()}; private static readonly TimeSpan StartTime = TimeSpan.FromHours(5) + TimeSpan.FromMinutes(45); private static readonly TimeSpan EndTime = TimeSpan.FromHours(17) + TimeSpan.FromMinutes(15); private static TimeSpan _currentTime = StartTime; private int[] _rejectedDrivers = {0, 0, 0, 0}; private void Awake() { Instance = this; } private void Start() { timeText.text = _currentTime.ToString(); rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; DataImporter.ReadFile("Assets/Data/v1.csv"); Debug.Log(DataImporter.Drivers); InitialConfigurationGenerator generator = new(); ArrangeSpots(generator.FindSolution()); } public void AdvanceTime() { _currentTime += TimeSpan.FromMinutes(stepTime); if (_currentTime > EndTime) { _currentTime = StartTime; ResetDrivers(); _rejectedDrivers = new int[]{0, 0,0 ,0}; rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; timeText.text = _currentTime.ToString(); return; } timeText.text = _currentTime.ToString(); foreach (Driver driver in DataImporter.Drivers) if (_currentTime <= driver.Times[1].TimeOfDay && _currentTime > driver.Times[0].TimeOfDay && !driver.Parked && !driver.Rejected) { if (!PlaceCarOnParking(driver)) { Debug.Log($"Placing failed for car {driver.Number}"); driver.Rejected = true; _rejectedDrivers[(int)driver.Size]++; rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; } } else if (_currentTime > driver.Times[1].TimeOfDay && driver.Parked) { driver.Reset(); } } private void ResetDrivers() { foreach (Driver driver in DataImporter.Drivers) { driver.Reset(); } } private bool PlaceCarOnParking(Driver driver) { for (var i = 0; i < SpotMap.Count; i++) foreach (Spot spot in SpotMap[i]) if (spot.Size == driver.Size && spot.Free && (spot.ParkingDirection == driver.ParkingPreference || spot.ParkingDirection == ParkingPreference.Any)) { spot.Free = false; driver.Spot = spot; driver.Parked = true; driver.GameObject = Instantiate(carPrefab, spot.GameObject.transform, true); driver.GameObject.GetComponentInChildren().text = driver.Number.ToString(); driver.GameObject.GetComponentInChildren().transform.rotation = Quaternion.Euler(new Vector3(0, 0, spot.Flipped ? 180 : 0)); driver.GameObject.transform.position = spot.GameObject.transform.position; driver.GameObject.transform.rotation = Quaternion.Euler(new Vector3(0, 0, spot.Flipped ? 180 : 0)); return true; } for (var i = 0; i < SpotMap.Count; i++) foreach (Spot spot in SpotMap[i]) if (spot.Size == driver.Size && spot.Free) { spot.Free = false; driver.Spot = spot; driver.Parked = true; driver.GameObject = Instantiate(carPrefab, spot.GameObject.transform, true); driver.GameObject.GetComponentInChildren().text = driver.Number.ToString(); driver.GameObject.GetComponentInChildren().transform.rotation = Quaternion.Euler(new Vector3(0, 0, spot.Flipped ? 180 : 0)); driver.GameObject.transform.position = spot.GameObject.transform.position; driver.GameObject.transform.rotation = Quaternion.Euler(new Vector3(0, 0, spot.Flipped ? 180 : 0)); return true; } return false; } public void UpdateText(string text) { countsText.text = text; } private void ReconfigureSpots() { int[] freeSpots = GetFreeSpotCount(); List nextCars = GetNextCars(10); int[] plannedSpots = new[] {0, 0, 0, 0}; foreach (Driver driver in nextCars) plannedSpots[(int)driver.Size]++; int[] neededSpots = new[] {0, 0, 0, 0}; for (int i = 0; i < neededSpots.Length; i++) neededSpots[i] = Math.Max(freeSpots[i] - plannedSpots[i], 0); if (neededSpots.Max() > 0) { Debug.Log($"Needed spots = {neededSpots[0]} {neededSpots[1]} {neededSpots[2]} {neededSpots[3]}"); Debug.Log($"Attempting reconfiguration..."); } } private int[] GetFreeSpotCount() { int[] freeSpots = new[] {0, 0, 0, 0}; foreach (var t in SpotMap) foreach (Spot spot in t) if (spot.Free) freeSpots[(int)spot.Size]++; return freeSpots; } private List GetNextCars(int n) { List nextCars = new List(); TimeSpan updatedTime = _currentTime + TimeSpan.FromMinutes(stepTime); while (nextCars.Count < n && updatedTime < EndTime) { foreach (Driver driver in DataImporter.Drivers) if (_currentTime + updatedTime <= driver.Times[1].TimeOfDay && _currentTime > driver.Times[0].TimeOfDay && !driver.Parked && !driver.Rejected && !nextCars.Contains(driver)) { nextCars.Add(driver); } updatedTime += TimeSpan.FromMinutes(stepTime); } return nextCars; } private void ArrangeSpots(int[,] spotsCreated) { var spotMap = GenerateSpotMap(spotsCreated); var maxP3 = _spotHeights[(int) spotMap[2].Max().Size]; var maxP2 = _spotHeights[(int) spotMap[1].Max().Size]; for (var i = 0; i < 4; i++) { float currentY; switch (i) { case 0: currentY = -Height / 2.0f + 2f; break; case 1: currentY = Height / 2 - 5.5f - 5f - maxP3 / 2.0f - maxP2; break; case 2: currentY = Height / 2 - 5.5f - 5f - maxP3 / 2.0f; break; case 3: currentY = Height / 2.0f - 2f; break; default: currentY = -10; break; } float currentX; if (i != 0) currentX = -Width / 2f - 2.25f / 2f + 2.25f; else currentX = -Width / 2f + 5.5f - 2.25f / 2f + 2.25f + 1.75f; var flipped = false; var parkingFromTop = i % 2 != 0; for (var j = 0; j < spotMap[i].Count; j++) { spotMap[i][j].Flipped = flipped; var alignTop = i % 2 != 0; switch (spotMap[i][j].Size) { case Size.A: spotMap[i][j].GameObject = Instantiate(Instance.spotPrefabA); spotMap[i][j].GameObject.transform.position = new Vector3(currentX, currentY, 0); break; case Size.B: spotMap[i][j].GameObject = Instantiate(Instance.spotPrefabB); spotMap[i][j].GameObject.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 0.25f, 0); break; case Size.C: spotMap[i][j].GameObject = Instantiate(Instance.spotPrefabC); spotMap[i][j].GameObject.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 0.5f, 0); break; case Size.D: spotMap[i][j].GameObject = Instantiate(Instance.spotPrefabD); spotMap[i][j].GameObject.transform.position = new Vector3(currentX, currentY - 2.0f, 0); break; } spotMap[i][j].GameObject.transform.rotation = Quaternion.Euler(new Vector3(0, 0, spotMap[i][j].Flipped ? 180 : 0)); var frontalParking = !parkingFromTop ^ flipped; spotMap[i][j].ParkingDirection = frontalParking ? ParkingPreference.Front : ParkingPreference.Back; currentX += 2.25f; flipped = !flipped; } } } private List> GenerateSpotMap(int[,] spotsCreated) { for (var i = 0; i < 4; i++) for (var j = 0; j < spotsCreated.GetLength(1); j++) for (var k = 0; k < spotsCreated[i, j]; k++) SpotMap[i].Add(new Spot((Size) j, false)); SpotMap[0].Sort((a, b) => a.Size.CompareTo(b.Size)); // ascending sort SpotMap[1].Sort((a, b) => b.Size.CompareTo(a.Size)); // descending sort SpotMap[2].Sort((a, b) => b.Size.CompareTo(a.Size)); // descending sort SpotMap[3].Sort((a, b) => a.Size.CompareTo(b.Size)); // ascending sort SpotMap[2].Add(new Spot(Size.D, false)); return SpotMap; } } }