using System; using System.Collections.Generic; using System.Data; using System.Linq; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UI; namespace Parking { public class ParkingManager : MonoBehaviour { public const float Width = 68; public const float Height = 29; public static ParkingManager Instance; 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; [SerializeField] private int stepTime = 15; [SerializeField] private TextMeshProUGUI timeText; [SerializeField] private TextMeshProUGUI dateText; [SerializeField] public TextMeshProUGUI countsText; [SerializeField] public TextMeshProUGUI rejectedText; [SerializeField] public TextMeshProUGUI pathText; [SerializeField] public GameObject startPanel; [SerializeField] public GameObject notFoundPanel; [SerializeField] public GameObject spotPrefabA; [SerializeField] public GameObject spotPrefabB; [SerializeField] public GameObject spotPrefabC; [SerializeField] public GameObject spotPrefabD; [SerializeField] public GameObject carPrefab; [SerializeField] public Transform mainPlanContainer; [SerializeField] public Transform emergencyPlanContainer; [SerializeField] public Toggle reconfigurationToggle; [SerializeField] public string defaultPath = "Assets/Data/Tablica2.csv"; public static readonly float[] SpotHeights = {4f, 4.5f, 5f, 7.5f}; private readonly List> _spotMap = new() {new List(), new List(), new List(), new List()}; private bool _emergencyActivated; private int _initialConfigurationSpotCount; private int[,] _initialSolution; private bool _reconfigurationActive = true; private int[] _rejectedDrivers = {0, 0, 0, 0}; private List _spotPrefabs; private void Awake() { Instance = this; } private void Start() { _spotPrefabs = new List {spotPrefabA, spotPrefabB, spotPrefabC, spotPrefabD}; startPanel.SetActive(true); reconfigurationToggle.onValueChanged.AddListener(ReconfigurationToggle); } public void ReconfigurationToggle(bool value) { _reconfigurationActive = value; Debug.Log($"Reconfiguration {(value ? "activated" : "deactivated")}"); } public void ReadDataButtonClick() { string path = pathText.text.Replace("\u200B", ""); if (path.Length == 0) path = defaultPath; if (!DataImporter.ReadFile(path)) { notFoundPanel.SetActive(true); return; } dateText.text = path.Substring(path.Length - 1 - 4 - 9, 10); InitiateManager(); startPanel.SetActive(false); } private void InitiateManager() { timeText.text = _currentTime.ToString(); rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; GenerateEmergencyPlan(); InitialConfigurationGenerator generator = new(); _initialSolution = generator.FindSolution(); ArrangeSpots(_initialSolution); ReserveInitialSpots(); } public void Emergency() { ResetDrivers(); _emergencyActivated = true; } private void ReserveInitialSpots() { return; for (int j = 3; j >= 0; j--) foreach (Spot spot in _spotMap[j]) { spot.Reserved = false; spot.ReservedPriority = 0; } for (int i = 0; i < Math.Min(DataImporter.Drivers.Count, _initialConfigurationSpotCount); i++) { bool found = false; // Find spot for (int j = 3; j >= 0 && !found; j--) foreach (Spot spot in _spotMap[j]) if (spot.Size == DataImporter.Drivers[i].Size && !spot.Reserved) { spot.Reserved = true; spot.ReservedPriority = DataImporter.Drivers[i].Priority; found = true; break; } } } private void ReserveSpotsInTimeRange(float hours) { for (int j = 3; j >= 0; j--) foreach (Spot spot in _spotMap[j]) { spot.Reserved = false; spot.ReservedPriority = 0; } foreach (Driver driver in DataImporter.Drivers) { driver.Reserved = false; } float rightTime = 12.0f;//(float)_currentTime.TotalHours; float leftTime = 16.0f;//(float)(_currentTime + TimeSpan.FromHours(hours)).TotalHours; TimeSpan maxArrivalTime = _currentTime + TimeSpan.FromHours(hours); List nextDrivers = new List(); foreach (Driver driver in DataImporter.Drivers) { if (driver.RealArrival >= _currentTime && driver.PlannedArrival < maxArrivalTime && !driver.Reserved) { nextDrivers.Add(driver); } } // nextDrivers.Sort(((a, b) => Comparer.Default.Compare(a.PlannedDeparture, b.PlannedDeparture))); nextDrivers.Sort(((a, b) => Comparer.Default.Compare(a.Priority, b.Priority))); //nextDrivers.Sort(((a, b) => Comparer.Default.Compare(a.RealDeparture, b.RealDeparture))); //nextDrivers.Sort(((a, b) => Comparer.Default.Compare(a.PlannedArrival, b.PlannedArrival))); List availableSpots = new List(); foreach (List list in _spotMap) foreach (Spot spot in list) // if (!spot.Reserved && spot.Free) availableSpots.Add(spot); // availableSpots.Reverse(); // leftTime = (float)nextDrivers.Max(d => d.PlannedDeparture).TotalHours; Driver FindOccupyingDriver(Spot spot) { return spot.OccupyingDriver; } for (int i = 0; i < nextDrivers.Count; i++) { Driver driver = nextDrivers[i]; float driverTime = (float)driver.PlannedDeparture.TotalHours; //float driverTime = (float)driver.RealDeparture.TotalHours; float timeProgress = (driverTime - rightTime) / (leftTime - rightTime); float xPosition = Width / 2.0f - (Width * timeProgress); availableSpots.Sort(((a, b) => Comparer.Default.Compare(Mathf.Abs(xPosition - a.Position.x), Mathf.Abs(xPosition - b.Position.x)))); bool useBottomLane = true; for (int j = 0; j < availableSpots.Count; j++) { Spot spot = availableSpots[j]; if (driver.Size == spot.Size && (spot.Lane != 0)) { TimeSpan leaveTime = spot.Free ? TimeSpan.Zero : FindOccupyingDriver(spot).PlannedDeparture; if (((!spot.Reserved && spot.Free) || (!spot.Free && leaveTime < driver.PlannedArrival))) { spot.Reserved = true; driver.Reserved = true; driver.ReservedSpot = spot; break; } } } // if (Mathf.Abs(spot.Position.x - xPosition) > 20.0f) { // if (useBottomLane) // break; // // useBottomLane = true; // // j = -1; // } if(!driver.Reserved) for (int j = 0; j < availableSpots.Count; j++) { Spot spot = availableSpots[j]; if (driver.Size == spot.Size && !spot.Reserved) { spot.Reserved = true; driver.Reserved = true; driver.ReservedSpot = spot; break; } // if (Mathf.Abs(spot.Position.x - xPosition) > 20.0f) { // if (useBottomLane) // break; // // useBottomLane = true; // // j = -1; // } } } // done: // { // } // for (int i = 0; i < Math.Min(DataImporter.Drivers.Count, _initialConfigurationSpotCount); i++) { // bool found = false; // // Find spot // for (int j = 3; j >= 0 && !found; j--) // foreach (Spot spot in _spotMap[j]) // if (spot.Size == DataImporter.Drivers[i].Size && !spot.Reserved) { // spot.Reserved = true; // spot.ReservedPriority = DataImporter.Drivers[i].Priority; // found = true; // break; // } // } } public void AdvanceTime() { // Update time _currentTime += TimeSpan.FromMinutes(stepTime); if (_currentTime > EndTime) { _currentTime = StartTime; timeText.text = _currentTime.ToString(); _rejectedDrivers = new[] {0, 0, 0, 0}; rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; if (_emergencyActivated) { emergencyPlanContainer.gameObject.SetActive(true); mainPlanContainer.gameObject.SetActive(false); } else { ArrangeSpots(_initialSolution); ReserveInitialSpots(); ResetDrivers(); } return; } timeText.text = _currentTime.ToString(); if (_emergencyActivated) return; if (_reconfigurationActive) ReconfigureSpots(); if((_currentTime.Hours - 6) % 3 == 0 && _currentTime.Minutes == 0) ReserveSpotsInTimeRange(3); // if(_currentTime.Hours == 6 && _currentTime.Minutes == 0) // ReserveSpotsInTimeRange(10); UpdateIncomingOutgoingDrivers(); UpdateCounts(); } private void UpdateIncomingOutgoingDrivers() { foreach (Driver driver in DataImporter.Drivers) { // TODO: Check if car can stay before other reservation - may not work for priority-based reservations bool triesToPark = _currentTime <= driver.Times[3].TimeOfDay && _currentTime > driver.Times[2].TimeOfDay; bool leftTheParking = _currentTime > driver.Times[3].TimeOfDay && driver.Parked; bool reservedButTimedout = (_currentTime - driver.Times[0].TimeOfDay).TotalMinutes > driver.UpdateInterval && GetReservedSpotCount(driver.Priority, driver.Size) > 0 && !driver.Parked && !driver.Rejected && _currentTime <= driver.Times[3].TimeOfDay; // if (driver.Reserved && !reservedButTimedout) { // // } if (triesToPark && !driver.Parked && !driver.Rejected) { if (!PlaceCarOnParking(driver)) { if (_reconfigurationActive && TryReconfigureSpotForSize(driver.Size)) { if (!PlaceCarOnParking(driver)) { Debug.Log($"Placing failed for car {driver.Number} size {driver.Size}"); driver.Rejected = true; _rejectedDrivers[(int) driver.Size]++; rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; } } else { Debug.Log($"Placing failed for car {driver.Number} size {driver.Size}"); driver.Rejected = true; _rejectedDrivers[(int) driver.Size]++; rejectedText.text = $"Małe: {_rejectedDrivers[0]} Średnie: {_rejectedDrivers[1]} " + $"Duże: {_rejectedDrivers[2]}"; } } } else if (leftTheParking) { driver.Spot.Reserved = false; driver.Reserved = false; driver.Reset(); } else if (reservedButTimedout) { Debug.Log($"Reserved but timed out driver {driver.Number}"); driver.Rejected = true; } } } private void UpdateCounts() { int[] counts = {0, 0, 0, 0}; for (int i = 0; i < _spotMap.Count; i++) for (int j = 0; j < _spotMap[i].Count; j++) counts[(int) _spotMap[i][j].Size]++; string countsString = $"Małe: {counts[0]} Średnie: {counts[1]} Duże: {counts[2]} " + $"Suma: {counts.Sum()}"; UpdateText(countsString); } private void ResetDrivers() { foreach (Driver driver in DataImporter.Drivers) { if (driver.Spot != null) { driver.Spot.Reserved = false; driver.Spot.ReservedPriority = 0; } driver.Reset(); } } private int GetReservedSpotCount(int priority, Size size) { int count = 0; foreach (List list in _spotMap) foreach (Spot spot in list) if (spot.Size == size && spot.Reserved && spot.ReservedPriority == priority) count++; return count; } private bool FindReservedSpot(Driver driver, out Spot spot) { if (driver.Reserved) { if(driver.ReservedSpot.GameObject == null || !driver.ReservedSpot.Free) Debug.Log("Spot not viable anymore"); else { spot = driver.ReservedSpot; return true; } } // foreach (List list in _spotMap) // foreach (Spot spot1 in list) // if (spot1.Reserved && spot1.ReservedPriority == driver.Priority && driver.Size == spot1.Size) { // spot = spot1; // return true; // } spot = new Spot(); return false; } private bool PlaceCarOnParking(Driver driver) { if (FindReservedSpot(driver, out Spot reservedSpot)) { if (reservedSpot.Free) { PlaceDriverOnSpot(driver, reservedSpot); return true; } Debug.Log("Spot should be reserved but is taken"); } foreach (List t in _spotMap) foreach (Spot spot in t) { bool spotAvailable = spot.Size == driver.Size && spot.Free && (spot.ParkingDirection == driver.ParkingPreference || spot.ParkingDirection == ParkingPreference.Any) && !spot.Reserved; if (spotAvailable) { PlaceDriverOnSpot(driver, spot); return true; } } foreach (List t in _spotMap) foreach (Spot spot in t) if (spot.Size == driver.Size && spot.Free && !spot.Reserved) { PlaceDriverOnSpot(driver, spot); return true; } return false; } private void PlaceDriverOnSpot(Driver driver, Spot spot) { if (spot.GameObject == null) { Debug.LogError("Spot is null"); } spot.Free = false; spot.Reserved = false; spot.OccupyingDriver = driver; driver.Spot = spot; driver.Parked = true; driver.GameObject = Instantiate(carPrefab); driver.GameObject.GetComponentInChildren().text = driver.Number.ToString(); driver.GameObject.GetComponentInChildren().transform.rotation = Quaternion.Euler(new Vector3(0, 0, (spot.Flipped ? 180 : 0) + (!spot.Perpendicular ? -90 : 0))); driver.GameObject.transform.rotation = Quaternion.Euler(new Vector3(0, 0, (spot.Flipped ? 180 : 0) + (!spot.Perpendicular ? 90 : 0))); driver.GameObject.transform.position = spot.GameObject.transform.position; driver.GameObject.transform.SetParent(spot.GameObject.transform, true); driver.GameObject.transform.localPosition += new Vector3(0, 0, -1); } public void UpdateText(string text) { countsText.text = text; } private void ReconfigureSpots() { int[] freeSpots = GetFreeSpotCount(); List nextCars = GetNextCars(1); int[] plannedSpots = {0, 0, 0, 0}; foreach (Driver driver in nextCars) plannedSpots[(int) driver.Size]++; int[] neededSpots = {0, 0, 0, 0}; for (int i = 0; i < neededSpots.Length; i++) neededSpots[i] = Math.Max(plannedSpots[i] - freeSpots[i], 0); if (neededSpots.Sum() > 1) { Debug.Log($"Needed spots = {neededSpots[0]} {neededSpots[1]} {neededSpots[2]} {neededSpots[3]}"); Debug.Log("Attempting reconfiguration..."); List spotlessCars = new(); // Filter drivers by assigning free space foreach (Driver nextCar in nextCars) if (freeSpots[(int) nextCar.Size] != 0) freeSpots[(int) nextCar.Size]--; else spotlessCars.Add(nextCar); int count = 0; foreach (Driver nextCar in spotlessCars) if (TryReconfigureSpotForSize(nextCar.Size)) count++; Debug.Log($"Created {count} out of {spotlessCars.Count}"); } } private bool TryReconfigureSpotForSize(Size newSize) { if (newSize == Size.C) { // Debug.Log("Reconfiguring using new method"); int lane1 = 2; int lane2 = 1; for (int i = _spotMap[lane2].Count - 1; i >= 0; i--) // Check if both spots free if (_spotMap[lane1][i].Free && _spotMap[lane2][i].Free) { // Check if we can make space on lane 1 Spot lowerSpot = _spotMap[lane2][i]; Spot higherSpot = _spotMap[lane1][i]; float lowerBorder = GetNextBorderVectical(lowerSpot.GameObject.transform.position, 1, false, lowerSpot.Size, false); float lowerReconfiguredBorder = Height / 2.0f - 5.0f - 5.5f - 5.0f - 5.0f; if (lowerReconfiguredBorder - lowerBorder >= 5.5f) { // We have space higherSpot.Size = Size.C; lowerSpot.Size = Size.C; Vector3 higherPosition = higherSpot.Position; higherPosition.y = Height / 2.0f - 5.0f - 5.5f - 5.0f / 2.0f; Vector3 lowerPosition = lowerSpot.Position; lowerPosition.y = Height / 2.0f - 5.0f - 5.5f - 5.0f - 5.0f / 2.0f; higherSpot.Position = higherPosition; lowerSpot.Position = lowerPosition; lowerSpot.Reserved = false; higherSpot.Reserved = false; Destroy(lowerSpot.GameObject); Destroy(higherSpot.GameObject); lowerSpot.GameObject = Instantiate(_spotPrefabs[(int)Size.C], lowerPosition, lowerSpot.GameObject.transform.rotation, mainPlanContainer); higherSpot.GameObject = Instantiate(_spotPrefabs[(int)Size.C], higherPosition, higherSpot.GameObject.transform.rotation, mainPlanContainer); lowerSpot.LastReconfiguration = _currentTime; higherSpot.LastReconfiguration = _currentTime; return true; } else { List conflictingSpots = GetConflictingSpotsLower(lowerSpot.GameObject.transform.position, 0, true, lowerSpot.Size, 0.0f); // verify we can delete them int maxASpotsDeleted = GetSpotCountOfSize(Size.A) - 2; int maxBSpotsDeleted = GetSpotCountOfSize(Size.B) - 2; bool allSpotsFree = true; foreach (int conflictingSpotId in conflictingSpots) { Spot conflictingSpot = _spotMap[0][conflictingSpotId]; if (conflictingSpot.Size == Size.A) maxASpotsDeleted--; if (conflictingSpot.Size == Size.B) maxBSpotsDeleted--; if (!conflictingSpot.Free || conflictingSpot.Reserved) allSpotsFree = false; } if(maxASpotsDeleted < 0 || maxBSpotsDeleted < 0) continue; if(!allSpotsFree) continue; // delete them int removed = 0; conflictingSpots.Sort(); foreach (int conflictingSpotId in conflictingSpots) { Destroy(_spotMap[0][conflictingSpotId - removed].GameObject); _spotMap[0].RemoveAt(conflictingSpotId - removed); removed++; } //Adjust new spots higherSpot.Size = Size.C; lowerSpot.Size = Size.C; Vector3 higherPosition = higherSpot.Position; higherPosition.y = Height / 2.0f - 5.0f - 5.5f - 5.0f / 2.0f; Vector3 lowerPosition = lowerSpot.Position; lowerPosition.y = Height / 2.0f - 5.0f - 5.5f - 5.0f - 5.0f / 2.0f; higherSpot.Position = higherPosition; lowerSpot.Position = lowerPosition; lowerSpot.Reserved = false; higherSpot.Reserved = false; Destroy(lowerSpot.GameObject); Destroy(higherSpot.GameObject); lowerSpot.GameObject = Instantiate(_spotPrefabs[(int)Size.C], lowerPosition, lowerSpot.GameObject.transform.rotation, mainPlanContainer); higherSpot.GameObject = Instantiate(_spotPrefabs[(int)Size.C], higherPosition, higherSpot.GameObject.transform.rotation, mainPlanContainer); lowerSpot.LastReconfiguration = _currentTime; higherSpot.LastReconfiguration = _currentTime; return true; } } else { continue; } // Try regeneration before returning false if (RegenerateSpots(newSize)) return true; } foreach (List currentLaneSpots in _spotMap) for (int i = 0; i < currentLaneSpots.Count; i++) { Spot spot = currentLaneSpots[i]; if (!spot.Free || spot.Size == Size.D)// || spot.Reserved) continue; if (GetSpotCountOfSize(Size.A) <= 2 && spot.Size == Size.A) continue; if (GetSpotCountOfSize(Size.B) <= 2 && spot.Size == Size.B) continue; if (spot.LastReconfiguration == _currentTime) continue; int size = (int) newSize; Vector3 position = spot.GameObject.transform.position; Quaternion rotation = spot.GameObject.transform.rotation; float diff = (SpotHeights[(int) spot.Size] - SpotHeights[size]) / 2.0f; if (!spot.AlignToTop) diff *= -1; Vector3 offset = spot.Perpendicular ? new Vector3(0, diff, 0) : new Vector3(-diff, 0, 0); Vector3 newPosition = position + offset; if (spot.Size > newSize) { if (!spot.Perpendicular) { float newX = GetNextBorderHorizontal(position, spot.Lane, spot.Perpendicular, newSize, true) - SpotHeights[(int) spot.Size] / 2.0f - 0.5f; newPosition = new Vector3(newX, newPosition.y, newPosition.z); } else { float newX = GetNextBorderHorizontal(position, spot.Lane, spot.Perpendicular, newSize, true) - 2.25f / 2.0f; if (spot.Lane == 0) { float xRelativeToRight = newX - Width / 2.0f - 2.25f / 2.0f; xRelativeToRight -= (xRelativeToRight + 2.25f / 2.0f) % 2.25f; newX = xRelativeToRight + Width / 2.0f; } else { float xRelativeToLeft = newX + Width / 2.0f; xRelativeToLeft -= (xRelativeToLeft + 2.25f / 2.0f) % 2.25f; newX = xRelativeToLeft - Width / 2.0f; } newPosition = new Vector3(newX, newPosition.y, newPosition.z); } } else if (spot.Size < newSize) { float spotBorder; float thisSizeOffset; if (spot.Perpendicular) thisSizeOffset = SpotHeights[size] / 2.0f; else thisSizeOffset = 2.25f / 2.0f; if (!spot.AlignToTop) spotBorder = newPosition.y + thisSizeOffset; else spotBorder = newPosition.y - thisSizeOffset; if (spot.Perpendicular) { float nextBorder = GetNextBorderVectical(newPosition, spot.Lane, spot.Perpendicular, newSize, !spot.AlignToTop); if (Math.Abs(spotBorder - nextBorder) < 5.5f) continue; } else { float nextBorderLeft = GetNextBorderHorizontal(position, spot.Lane, spot.Perpendicular, spot.Size, false); float nextBorderRight = GetNextBorderHorizontal(position, spot.Lane, spot.Perpendicular, spot.Size, true); if (nextBorderRight - nextBorderLeft < SpotHeights[(int) newSize] + 1f) { // Try perpendicular float bottomBorder = newPosition.y - 2.25f / 2.0f; float topBorder = newPosition.y + 2.25f / 2.0f; bool foundByRemoval = false; if (spot.AlignToTop) { float newBottomBorder = topBorder - SpotHeights[size]; float newTopBorder = topBorder; float nextBorder = GetNextBorderVectical(newPosition, spot.Lane, spot.Perpendicular, newSize, !spot.AlignToTop); if (Math.Abs(nextBorder - newBottomBorder) < 5.5f) { if (i < currentLaneSpots.Count - 1) { Spot nextSpot = currentLaneSpots[i + 1]; if (nextSpot.Free && !nextSpot.Reserved) { Destroy(nextSpot.GameObject); currentLaneSpots.RemoveAt(i + 1); i--; newPosition.x = nextBorderRight - SpotHeights[(int) newSize] - 0.5f; newPosition.y = position.y; foundByRemoval = true; } else continue; } } } else { float newBottomBorder = bottomBorder; float newTopBorder = bottomBorder + SpotHeights[size]; float nextBorder = GetNextBorderVectical(newPosition, spot.Lane, spot.Perpendicular, newSize, !spot.AlignToTop); if (Math.Abs(newTopBorder - nextBorder) < 5.5f) { if (i < currentLaneSpots.Count - 1) { Spot nextSpot = currentLaneSpots[i + 1]; if (nextSpot.Free && !nextSpot.Reserved) { Destroy(nextSpot.GameObject); currentLaneSpots.RemoveAt(i + 1); i--; newPosition.x = nextBorderRight - SpotHeights[(int) newSize] - 0.5f; newPosition.y = position.y; foundByRemoval = true; } else continue; } } } if (!foundByRemoval) { newPosition.y = bottomBorder + SpotHeights[(int) newSize] / 2.0f; float newX = nextBorderRight; if (spot.Lane == 0) { float xRelativeToRight = -(newX - Width / 2.0f); xRelativeToRight -= (xRelativeToRight + 2.25f / 2.0f) % 2.25f; xRelativeToRight += 2.25f; xRelativeToRight = Math.Max(xRelativeToRight, 2.25f / 2.0f); newX = -xRelativeToRight + Width / 2.0f; } else { float xRelativeToLeft = newX + Width / 2.0f; xRelativeToLeft -= (xRelativeToLeft + 2.25f / 2.0f) % 2.25f; newX = xRelativeToLeft - Width / 2.0f; } if (spot.Lane == 0 && Width / 2.0f - newX < 5.5f + 0.5f + 2.25f) { // Try to remove one spot next continue; } newPosition.x = newX; spot.Flipped = currentLaneSpots.FindIndex(spotPredicate => spotPredicate == spot) % 2 == 0; rotation *= Quaternion.Euler(0, 0, spot.Flipped ? 90 : -90); spot.Perpendicular = true; } } else { newPosition.x = nextBorderRight - SpotHeights[(int) newSize] / 2.0f - 0.5f; if (i == 0) newPosition.x += 0.5f; newPosition.y = position.y; // if (i < currentLaneSpots.Count - 1) { // Spot nextSpot = currentLaneSpots[i + 1]; // if (nextSpot.Free && !nextSpot.Reserved) { // Destroy(nextSpot.GameObject); // currentLaneSpots.RemoveAt(i + 1); // i--; // newPosition.x = nextBorderRight - _spotHeights[(int) newSize] - 0.5f; // newPosition.y = position.y; // continue; // } // } // newPosition.x = nextBorderRight - _spotHeights[(int) newSize] - 0.5f; } } } Destroy(spot.GameObject); spot.Size = (Size) size; spot.Reserved = false; spot.GameObject = Instantiate(_spotPrefabs[size], newPosition, rotation, mainPlanContainer); spot.LastReconfiguration = _currentTime; return true; } return false; } private bool RegenerateSpots(Size size) { Vector3 newPosition = Vector3.zero; float lastRightBorder = -Width / 2.0f + 5.5f; float spaceNeeded = SpotHeights[(int)size] + 1.0f; List conflictingSpots = new (); for (int i = 0; i < _spotMap[0].Count; i++) { Spot spot = _spotMap[0][i]; if (spot.Free && !spot.Reserved) { conflictingSpots.Add(i); } } int removed = 0; conflictingSpots.Sort(); foreach (int conflictingSpotId in conflictingSpots) { if(_spotMap[0][conflictingSpotId - removed].Size == Size.A && GetSpotCountOfSize(Size.A) == 2) continue; if(_spotMap[0][conflictingSpotId - removed].Size == Size.B && GetSpotCountOfSize(Size.B) == 2) continue; Destroy(_spotMap[0][conflictingSpotId - removed].GameObject); _spotMap[0].RemoveAt(conflictingSpotId - removed); removed++; } for (int i = _spotMap[0].Count - 1; i >= -1; i--) { float leftBorder; if (i >= 0) { leftBorder = _spotMap[0][i].LeftBorder; } else leftBorder = Width / 2.0f; if (leftBorder - lastRightBorder >= spaceNeeded) { Spot newSpot = new Spot(size, false); //newPosition.x = (lastRightBorder + SpotHeights[(int)size] / 2.0f); newPosition.x = (lastRightBorder + 0.5f + SpotHeights[(int)size] / 2.0f); newPosition.y = (-Height / 2.0f) + 2.25f / 2.0f; newSpot.Perpendicular = false; newSpot.GameObject = Instantiate(_spotPrefabs[(int)size], newPosition, Quaternion.Euler(0, 0, 90), mainPlanContainer); _spotMap[0].Add(newSpot); _spotMap[0].Sort((a, b) => Comparer.Default.Compare(b.Position.x, a.Position.x)); return true; } if (i >= 0) lastRightBorder = _spotMap[0][i].RightBorder; } return false; // newPosition.y = bottomBorder + SpotHeights[(int) newSize] / 2.0f; // float newX = nextBorderRight; // if (spot.Lane == 0) { // float xRelativeToRight = -(newX - Width / 2.0f); // xRelativeToRight -= (xRelativeToRight + 2.25f / 2.0f) % 2.25f; // xRelativeToRight += 2.25f; // xRelativeToRight = Math.Max(xRelativeToRight, 2.25f / 2.0f); // newX = -xRelativeToRight + Width / 2.0f; // } // else { // float xRelativeToLeft = newX + Width / 2.0f; // xRelativeToLeft -= (xRelativeToLeft + 2.25f / 2.0f) % 2.25f; // newX = xRelativeToLeft - Width / 2.0f; // } // // // if (spot.Lane == 0 && Width / 2.0f - newX < 5.5f + 0.5f + 2.25f) { // // Try to remove one spot next // continue; // } // // newPosition.x = newX; // spot.Flipped = // currentLaneSpots.FindIndex(spotPredicate => spotPredicate == spot) % // 2 == 0; // rotation *= Quaternion.Euler(0, 0, spot.Flipped ? 90 : -90); // spot.Perpendicular = true; } private List GetConflictingSpotsLower(Vector3 position, int lane, bool perpendicular, Size size, float margin = 0) { float minX = position.x - (perpendicular ? 2.25f : SpotHeights[(int) size]) - margin; float maxX = position.x + (perpendicular ? 2.25f : SpotHeights[(int) size]) + margin; List allBorders = new(); List targetLane = _spotMap[lane]; List conflictingSpotsIds = new(); for (int i = 0; i < targetLane.Count; i++) { Spot spot1 = targetLane[i]; Vector3 targetSpotPosition = spot1.GameObject.transform.position; if (targetSpotPosition.x < minX || targetSpotPosition.x > maxX) continue; conflictingSpotsIds.Add(i); } return conflictingSpotsIds; } private float GetNextBorderVectical(Vector3 position, int lane, bool perpendicular, Size size, bool up) { float minX = position.x - (perpendicular ? 2.25f : SpotHeights[(int) size]); float maxX = position.x + (perpendicular ? 2.25f : SpotHeights[(int) size]); List allBorders = new(); List targetLane = up ? _spotMap[lane + 1] : _spotMap[lane - 1]; foreach (Spot spot1 in targetLane) { Vector3 targetSpotPosition = spot1.GameObject.transform.position; if (targetSpotPosition.x < minX || targetSpotPosition.x > maxX) continue; float sizeOffset; if (spot1.Perpendicular) sizeOffset = SpotHeights[(int) spot1.Size] / 2.0f; else sizeOffset = 2.25f / 2.0f; if (up) allBorders.Add(targetSpotPosition.y - sizeOffset); else allBorders.Add(targetSpotPosition.y + sizeOffset); } if (12.85 - 2.5 / 2.0f <= maxX && lane == 2 && up) allBorders.Add(Height / 2.0f - 5); if (allBorders.Count == 0) return up ? Height / 2.0f : -Height / 2.0f; float nextBorder = up ? allBorders.Min() : allBorders.Max(); return nextBorder; } private float GetNextBorderHorizontal(Vector3 position, int lane, bool perpendicular, Size size, bool right) { List allBorders = new(); List targetLane = _spotMap[lane]; foreach (Spot spot1 in targetLane) { if (spot1.GameObject.transform.position == position) continue; Vector3 spotPosition = spot1.GameObject.transform.position; float sizeOffset; if (spot1.Perpendicular) sizeOffset = 2.25f / 2.0f; else sizeOffset = SpotHeights[(int) spot1.Size] / 2.0f; if (right) allBorders.Add(spotPosition.x - sizeOffset); else allBorders.Add(spotPosition.x + sizeOffset); } allBorders.Sort(); if (right) { foreach (float adjacentBorder in allBorders) if (adjacentBorder > position.x) return adjacentBorder; } else { allBorders.Reverse(); foreach (float adjacentBorder in allBorders) if (adjacentBorder < position.x) return adjacentBorder; } if (perpendicular) return position.x + (right ? 1 : -1) * SpotHeights[(int) size] / 2.0f; if (right) return Width / 2.0f; return -Width / 2.0f + 5.5f; } private int GetSpotCountOfSize(Size size) { int count = 0; foreach (List t in _spotMap) foreach (Spot spot in t) if (spot.Size == size) count++; return count; } private int[] GetFreeSpotCount() { int[] freeSpots = {0, 0, 0, 0}; foreach (List t in _spotMap) foreach (Spot spot in t) if (spot.Free) freeSpots[(int) spot.Size]++; return freeSpots; } private List GetNextCars(int steps) { List nextCars = new(); TimeSpan updatedTime = _currentTime + TimeSpan.FromMinutes(stepTime * steps); foreach (Driver driver in DataImporter.Drivers) if (updatedTime <= driver.Times[1].TimeOfDay && updatedTime >= driver.Times[0].TimeOfDay && !driver.Parked && !driver.Rejected) nextCars.Add(driver); return nextCars; } private void ArrangeSpots(int[,] spotsCreated) { List> spotMap = GenerateSpotMap(spotsCreated); float[] spotSizes = {4, 4.5f, 5, 0}; float maxP3 = 0; float maxP2 = 0; foreach (Spot spot in spotMap[2]) if (spot.Size != Size.D) maxP3 = Math.Max(maxP3, spot.Perpendicular ? SpotHeights[(int) spot.Size] : 2.25f); foreach (Spot spot in spotMap[1]) if (spot.Size != Size.D) maxP2 = Math.Max(maxP2, spot.Perpendicular ? SpotHeights[(int) spot.Size] : 2.25f); List prefabs = new() {spotPrefabA, spotPrefabB, spotPrefabC, spotPrefabD}; for (int i = 0; i < 4; i++) { float currentY; bool parkingFromTop = i % 2 != 0; switch (i) { case 0: currentY = -Height / 2.0f; break; case 1: currentY = Height / 2.0f - 5.5f - 5.0f - maxP3; break; case 2: currentY = Height / 2.0f - 5.5f - 5.0f - maxP3; break; case 3: currentY = Height / 2.0f; break; default: currentY = -10; break; } float currentX; if (i != 0) currentX = -Width / 2f; else currentX = Width / 2f; // + 5.5f - 2.25f / 2f + 2.25f + 1.75f; bool flipped = false; for (int j = 0; j < spotMap[i].Count; j++) { // currentX += (i != 0 ? 1 : -1) * // (spotMap[i][j].Perpendicular // ? 2.25f / 2.0f // : spotSizes[(int) spotMap[i][j].Size] / 2.0f); if (spotMap[i][j].Size != Size.D) { currentX += (i != 0 ? 1 : -1) * (spotMap[i][j].Perpendicular ? 2.25f / 2.0f : spotSizes[(int) spotMap[i][j].Size] / 2.0f + (j != 0 ? 0.25f : 0)); } else { currentX += (i != 0 ? 1 : -1) * (spotMap[i][j].Perpendicular ? 2.5f / 2.0f : spotSizes[(int) spotMap[i][j].Size] / 2.0f + (j != 0 ? 0.25f : 0)); } spotMap[i][j].Flipped = flipped; bool alignTop = i % 2 != 0; spotMap[i][j].GameObject = Instantiate(prefabs[(int) spotMap[i][j].Size], mainPlanContainer); if (spotMap[i][j].Perpendicular) { spotMap[i][j].GameObject.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * spotSizes[(int) spotMap[i][j].Size] / 2.0f, 0); } else { spotMap[i][j].GameObject.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 2.25f / 2.0f, 0); } spotMap[i][j].AlignToTop = alignTop; if (!spotMap[i][j].Perpendicular) spotMap[i][j].Flipped = alignTop; if (spotMap[i][j].Perpendicular) { spotMap[i][j].GameObject.transform.rotation = Quaternion.Euler(new Vector3(0, 0, spotMap[i][j].Flipped ? 180 : 0)); } else { spotMap[i][j].GameObject.transform.rotation = Quaternion.Euler(new Vector3(0, 0, (spotMap[i][j].Flipped ? 180 : 0) + 90)); } bool frontalParking = !(!parkingFromTop ^ flipped); spotMap[i][j].ParkingDirection = frontalParking ? ParkingPreference.Front : ParkingPreference.Back; spotMap[i][j].Lane = i; if (spotMap[i][j].Size != Size.D) { currentX += (i != 0 ? 1 : -1) * (spotMap[i][j].Perpendicular ? 2.25f / 2.0f : spotSizes[(int) spotMap[i][j].Size] / 2.0f + 0.25f); } else { currentX += (i != 0 ? 1 : -1) * (spotMap[i][j].Perpendicular ? 2.5f / 2.0f : spotSizes[(int) spotMap[i][j].Size] / 2.0f + 0.25f); } flipped = !flipped; } } } private void GenerateEmergencyPlan() { emergencyPlanContainer.gameObject.SetActive(false); for (int i = 0; i < 4; i++) { float currentY; switch (i) { case 0: currentY = -Height / 2.0f; break; case 1: currentY = Height / 2.0f - 5.5f - 5.0f - 5.0f; break; case 2: currentY = Height / 2.0f - 5.5f - 5.0f - 5.0f; break; case 3: currentY = Height / 2.0f; break; default: currentY = -10; break; } float currentX; if (i != 0) currentX = -Width / 2f - 2.25f / 2f + 2.25f; else currentX = Width / 2f - 5.0f / 2f; GameObject spawnedSpot; int[] emergencyMap = {12, 27, 27, 20}; for (int j = 0; j < emergencyMap[i]; j++) if (i == 0) { Vector3 position = new(currentX, currentY + 2.25f / 2.0f, 0); Instantiate(Instance.spotPrefabC, position, Quaternion.Euler(new Vector3(0, 0, 90)), emergencyPlanContainer); currentX -= 5; } else { bool alignTop = i % 2 != 0; Vector3 position = new(currentX, currentY, 0); spawnedSpot = Instantiate(Instance.spotPrefabC, position, Quaternion.Euler(new Vector3(0, 0, 0)), emergencyPlanContainer); spawnedSpot.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 5.0f / 2.0f, 0); currentX += 2.25f; } } } private List> GenerateSpotMap(int[,] spotsCreated) { foreach (List list in _spotMap) { foreach (Spot spot in list) if (spot.GameObject != null) Destroy(spot.GameObject); list.Clear(); } for (int i = 0; i < 4; i++) for (int j = 0; j < spotsCreated.GetLength(1); j++) for (int k = 0; k < spotsCreated[i, j]; k++) { Spot spot = new((Size) (j % 4), false); spot.Perpendicular = j < 4; _spotMap[i].Add(spot); _initialConfigurationSpotCount++; } _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; } } }