using System; using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; using Unity.VisualScripting; using UnityEngine; public class ParkingManager : MonoBehaviour { private static readonly float[] AvailableSizesCombinations = {0, 2.5f, 4, 4.5f, 5};//, 7.5f}; private static readonly List combinations = new(3000); [SerializeField] private float width = 68; [SerializeField] private float height = 29; [SerializeField] private int stepTime = 15; [SerializeField] private TextMeshProUGUI timeText; [SerializeField] private TextMeshProUGUI countsText; [SerializeField] private GameObject spotPrefabA; [SerializeField] private GameObject spotPrefabB; [SerializeField] private GameObject spotPrefabC; private TimeSpan _currentTime = TimeSpan.FromHours(8); private float[] _spotHeights = {3.5f, 4f, 5f, 7.5f}; private float _spotWidth = 2.25f; private void Start() { timeText.text = _currentTime.ToString(); DataImporter.ReadFile("Assets/Data/v1.csv"); Debug.Log(DataImporter.Drivers); FindSolution(); } public void AdvanceTime() { _currentTime += TimeSpan.FromMinutes(15); timeText.text = _currentTime.ToString(); } private void FindSolution() { PreProcessCombinations(out var spotCountsPerpendicular, out var spotCountsParallel); int[,] spotsCreated = new int[4, 4]; int start = 80; int count = start; int maxCount = 0; if (PlaceNCars(30, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) { count++; while (PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) { count++; } maxCount = count--; } else { count--; while (!PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) { count--; } maxCount = count; } Debug.Log($"Best solution count {maxCount}"); VisualiseSpots(spotsCreated); } private void VisualiseSpots(int[,] spotsCreated) { List> spotMap = new List>() {new(), new(), new(), new()}; 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++) { spotMap[i].Add((Size) j); } } } spotMap[0].Sort((a, b) => a.CompareTo(b)); // ascending sort spotMap[1].Sort((a, b) => b.CompareTo(a)); // descending sort spotMap[2].Sort((a, b) => b.CompareTo(a)); // descending sort spotMap[3].Sort((a, b) => a.CompareTo(b)); // ascending sort float maxP3 = _spotHeights[(int)spotMap[2].Max()]; float maxP2 = _spotHeights[(int)spotMap[1].Max()]; for (int 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; for (int j = 0; j < spotMap[i].Count; j++) { GameObject spot; bool alignTop = i % 2 != 0; switch (spotMap[i][j]) { case Size.A: spot = Instantiate(spotPrefabA); spot.transform.position = new Vector3(currentX, currentY, 0); break; case Size.B: spot = Instantiate(spotPrefabB); spot.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 0.25f, 0); break; case Size.C: spot = Instantiate(spotPrefabC); spot.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 0.5f, 0); break; default: break; } currentX += 2.25f; } } } private bool PlaceNCars(int carsToPlace, int[] spotCountsPerpendicular, int[,] spotCountsParallel, int[,] spotsCreated) { int[] counts = {0, 0, 0}; counts[(int) Size.A] += 2; counts[(int) Size.B] += 2; // counts[(int) Size.D] += 1; var fixedCarSpots = 0; foreach (var c in counts) fixedCarSpots += c; Debug.Log($"Fixed car spots: {fixedCarSpots}"); Debug.Log("Calculating required spot counts..."); for (int i = 0; i < DataImporter.Drivers.Count && i + fixedCarSpots < carsToPlace; i++) { counts[(int) DataImporter.Drivers[i].Size]++; } string countsString = $"A: {counts[0]} B: {counts[1]} C: {counts[2]} " + $"Suma: {counts.Sum()}"; countsText.text = countsString; Debug.Log(countsString); Debug.Log("Printing top 5 combinations..."); foreach (var comb in combinations) { bool res = TestCombination(comb.ToArray(), spotCountsPerpendicular, spotCountsParallel, counts, spotsCreated); if (res) return true; } return false; } private void PreProcessCombinations(out int[] spotCountsPerpendicular, out int[,] spotCountsParallel) { Debug.Log("Calculating spot counts..."); float[] spotLengthsParallel = {4f, 4.5f, 5f};//, 7.5f}; spotCountsPerpendicular = new[] {0, 0, 0, 0}; spotCountsPerpendicular[0] = Mathf.FloorToInt((width - 5.5f) / _spotWidth); spotCountsPerpendicular[1] = Mathf.FloorToInt((width - 5.5f - 2.5f) / _spotWidth); spotCountsPerpendicular[2] = Mathf.FloorToInt((width - 5.5f - 2.5f) / _spotWidth); spotCountsPerpendicular[3] = Mathf.FloorToInt((width - 5.5f - (3.6f * 2) - (2.5f * 4)) / _spotWidth); spotCountsParallel = new int[4, 4]; for (int i = 0; i < 3; i++) for (int j = 0; j < spotLengthsParallel.Length; j++) spotCountsParallel[i, j] = Mathf.FloorToInt((width - 5.5f) / spotLengthsParallel[j]); for (int j = 0; j < spotLengthsParallel.Length; j++) spotCountsParallel[3, j] = Mathf.FloorToInt((width - 5.5f - (3.6f * 2) - (2.5f * 4)) / spotLengthsParallel[j]); Debug.Log($"" + $"P1: {spotCountsPerpendicular[0]}/ P2: {spotCountsPerpendicular[1]} " + $"P3: {spotCountsPerpendicular[2]} P4: {spotCountsPerpendicular[3]} " + $"Sum: {spotCountsPerpendicular.Sum()}"); Debug.Log("Generating combinations..."); int[] arr = {0, 1, 2, 3, 4}; var n = arr.Length; var r = 4; CombinationRepetition(arr, n, r); Debug.Log($"Found {combinations.Count} available combinations"); Debug.Log("Sorting available combinations..."); combinations.Sort(UsesMoreSpaceComparator); } private bool TestCombination(int[] sizeIds, int[] spotCountsPerpendicularRef, int[,] spotCountsParallelRef, int[] requiredCountsRef, int[,] spotsCreated) { float[] sizes = sizeIds.Select(x => AvailableSizesCombinations[x]).ToArray(); Debug.Log($"Testing: {sizes[0]} {sizes[1]} {sizes[2]} {sizes[3]} sum: {sizes.Sum()}"); for(int i = 0; i < spotsCreated.GetLength(0); i++) for (int j = 0; j < spotsCreated.GetLength(1); j++) spotsCreated[i, j] = 0; int[] requiredCounts = new int[requiredCountsRef.Length]; requiredCountsRef.CopyTo((Span) requiredCounts); int[] spotCountsPerpendicular = new int[spotCountsPerpendicularRef.Length]; spotCountsPerpendicularRef.CopyTo((Span) spotCountsPerpendicular); int[,] spotCountsParallel = new int[spotCountsParallelRef.GetLength(0), spotCountsParallelRef.GetLength(1)]; for(int i = 0; i < spotCountsParallelRef.GetLength(0); i++) for(int j = 0; j < spotCountsParallelRef.GetLength(1); j++) spotCountsParallel[i,j] = spotCountsParallelRef[i, j]; float[] spotSizes = {4, 4.5f, 5}; for (int spotSize = 2; spotSize >= 0; spotSize--) { for (int laneId = 3; laneId >= 0; laneId--) { if (AvailableSizesCombinations[sizeIds[laneId]] <= spotSizes[spotSize] && spotCountsPerpendicular[laneId] != 0) { // parking perpendicular int spotsTaken = Math.Min(requiredCounts[spotSize], spotCountsPerpendicular[laneId]); spotCountsPerpendicular[laneId] -= spotsTaken; requiredCounts[spotSize] -= spotsTaken; spotsCreated[laneId, spotSize] += spotsTaken; // TODO: Allow modified configuration for(int x = 0; x < 3; x++) spotCountsParallel[laneId, x] = 0; } // else if (sizeIds[laneId] == 0 && spotCountsParallel[laneId, spotSize] != 0) { // parking parallel // int spotsTaken = Math.Min(requiredCounts[spotSize], spotCountsParallel[laneId, spotSize]); // // spotCountsParallel[laneId, spotSize] -= spotsTaken; // requiredCounts[spotSize] -= spotsTaken; // // spotsCreated[laneId, spotSize] += spotsTaken; // // // TODO: Allow modified configuration // spotCountsPerpendicular[laneId] = 0; // for(int x = 0; x < 3; x++) // if(x != spotSize) // spotCountsParallel[laneId, x] = 0; // } if(requiredCounts[spotSize] == 0) break; } } int sum = requiredCounts.Sum(); if(sum > 0) return false; else return true; } private int UsesMoreSpaceComparator(int[] a1, int[] a2) { float sum1 = 0; float sum2 = 0; foreach (float val in a1) sum1 += val; foreach (float val in a2) sum2 += val; return -1 * sum1.CompareTo(sum2); } private void CombinationRepetitionUtil(int[] chosen, int[] arr, int index, int r, int start, int end) { if (index == r) { // combinations.Add(new[] {arr[chosen[0]]}); var tempArr = new List(r); for (var i = 0; i < r; i++) tempArr.Add(arr[chosen[i]]); bool hasEnoughSpace = tempArr.Select(x => AvailableSizesCombinations[x]).Sum() < height - 11; bool contains5 = tempArr.Contains(4); tempArr.Sort(); if (hasEnoughSpace && contains5) combinations.Add(tempArr.ToArray()); return; } for (var i = start; i <= end; i++) { chosen[index] = i; CombinationRepetitionUtil(chosen, arr, index + 1, r, i, end); } } private void CombinationRepetition(int[] arr, int n, int r) { var chosen = new int[r + 1]; CombinationRepetitionUtil(chosen, arr, 0, r, 0, n - 1); } }