using System; 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; 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 = 20; 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}"); } 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]++; } // Debug.Log($"A: {counts[0]} B: {counts[1]} C: {counts[2]} D: {counts[3]} " + // $"Sum: {counts.Sum()}"); Debug.Log($"A: {counts[0]} B: {counts[1]} C: {counts[2]} " + $"Sum: {counts.Sum()}"); Debug.Log("Printing top 5 combinations..."); var count = 0; foreach (var comb in combinations) { // var sizes = comb.Select(x => AvailableSizesCombinations[x]).ToArray(); // Debug.Log($"{sizes[0]} {sizes[1]} {sizes[2]} {sizes[3]} sum: {sizes.Sum()}"); bool res = TestCombination(comb.ToArray(), spotCountsPerpendicular, spotCountsParallel, counts, spotsCreated); if (res) return true; count++; // if (count >= 5) // return; } // var placingFailed = false; // while (fixedCarSpots < carsToPlace && !placingFailed) break; return false; Application.Quit(); } 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) / _spotWidth); spotCountsPerpendicular[2] = Mathf.FloorToInt((width - 5.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]} 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]; for (int spotSize = 2; spotSize >= 0; spotSize--) { for (int laneId = 0; laneId < 4; laneId++) { if (sizeIds[laneId] == 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]]); if (tempArr.Select(x => AvailableSizesCombinations[x]).Sum() < height - 11) 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); } }