using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Parking { public class InitialConfigurationGenerator { private static readonly List Combinations = new(3000); private static readonly float[] AvailableSizesCombinations = {0, 2.5f, 4, 4.5f, 5}; //, 7.5f}; private readonly float _spotWidth = 2.25f; public int[,] FindSolution() { PreProcessCombinations(out var spotCountsPerpendicular, out var spotCountsParallel); var spotsCreated = new int[4, 4]; var count = 77; var maxCount = count; if (PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) { count++; while (PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) count++; maxCount = count - 1; } else { count--; while (!PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) count--; maxCount = count; } Debug.Log($"Best solution count {maxCount}"); int[] counts = new int[] {0, 0, 0, 0}; for (int i = 0; i < spotsCreated.GetLength(0); i++) { for (int j = 0; j < spotsCreated.GetLength(1); j++) { counts[j] += spotsCreated[i, j]; } } var countsString = $"Małe: {counts[0]} Średnie: {counts[1]} Duże: {counts[2]} " + $"Suma: {counts.Sum()}"; ParkingManager.Instance.UpdateText(countsString); Debug.Log(countsString); return spotsCreated; } 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..."); int maxI = 0; List driversLowerPrioritySorted = new List(DataImporter.Drivers.Count); for (var i = 0; i < DataImporter.Drivers.Count && i + fixedCarSpots < carsToPlace; i++) if (DataImporter.Drivers[i].Priority == 1) { counts[(int) DataImporter.Drivers[i].Size]++; maxI = i; } foreach (Driver driver in DataImporter.Drivers) if(driver.Priority != 1) driversLowerPrioritySorted.Add(driver); for(int i = maxI + 1; counts[(int) Size.C] < 20 && i < DataImporter.Drivers.Count; i++) if (DataImporter.Drivers[i].Size == Size.C) { counts[(int) DataImporter.Drivers[i].Size]++; driversLowerPrioritySorted.Remove(DataImporter.Drivers[i]); } driversLowerPrioritySorted.Sort(BiggerSizeComparator); int singlePriorityCars = counts.Sum(); for (var i = 0; i < driversLowerPrioritySorted.Count && i + fixedCarSpots + singlePriorityCars < carsToPlace; i++) counts[(int) driversLowerPrioritySorted[i].Size]++; var countsString = $"Małe: {counts[0]} Średnie: {counts[1]} Duże: {counts[2]} " + $"Suma: {counts.Sum() + 1}"; ParkingManager.Instance.UpdateText(countsString); Debug.Log(countsString); // Debug.Log("Printing top 5 combinations..."); foreach (var comb in Combinations) { var 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((ParkingManager.Width - 5.5f) / _spotWidth); spotCountsPerpendicular[1] = Mathf.FloorToInt((ParkingManager.Width - 5.5f - 2.5f) / _spotWidth); spotCountsPerpendicular[2] = Mathf.FloorToInt((ParkingManager.Width - 5.5f - 2.5f) / _spotWidth); spotCountsPerpendicular[3] = Mathf.FloorToInt((ParkingManager.Width - 5.5f - 3.6f * 2 - 2.5f * 4) / _spotWidth); spotCountsParallel = new int[4, 4]; for (var i = 0; i < 3; i++) for (var j = 0; j < spotLengthsParallel.Length; j++) spotCountsParallel[i, j] = Mathf.FloorToInt((ParkingManager.Width - 5.5f) / spotLengthsParallel[j]); for (var j = 0; j < spotLengthsParallel.Length; j++) spotCountsParallel[3, j] = Mathf.FloorToInt((ParkingManager.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) { var sizes = sizeIds.Select(x => AvailableSizesCombinations[x]).ToArray(); // Debug.Log($"Testing: {sizes[0]} {sizes[1]} {sizes[2]} {sizes[3]} sum: {sizes.Sum()}"); var spotsCreatedTemp = new int[4, 4]; for (var i = 0; i < spotsCreatedTemp.GetLength(0); i++) for (var j = 0; j < spotsCreatedTemp.GetLength(1); j++) spotsCreatedTemp[i, j] = 0; var requiredCounts = new int[requiredCountsRef.Length]; requiredCountsRef.CopyTo((Span) requiredCounts); var spotCountsPerpendicular = new int[spotCountsPerpendicularRef.Length]; spotCountsPerpendicularRef.CopyTo((Span) spotCountsPerpendicular); var spotCountsParallel = new int[spotCountsParallelRef.GetLength(0), spotCountsParallelRef.GetLength(1)]; for (var i = 0; i < spotCountsParallelRef.GetLength(0); i++) for (var j = 0; j < spotCountsParallelRef.GetLength(1); j++) spotCountsParallel[i, j] = spotCountsParallelRef[i, j]; float[] spotSizes = {4, 4.5f, 5}; for (var spotSize = 2; spotSize >= 0; spotSize--) for (var laneId = 3; laneId >= 0; laneId--) { if (AvailableSizesCombinations[sizeIds[laneId]] >= spotSizes[spotSize] && spotCountsPerpendicular[laneId] != 0) { // parking perpendicular var spotsTaken = Math.Min(requiredCounts[spotSize], spotCountsPerpendicular[laneId]); spotCountsPerpendicular[laneId] -= spotsTaken; requiredCounts[spotSize] -= spotsTaken; spotsCreatedTemp[laneId, spotSize] += spotsTaken; // TODO: Allow mixed configuration for (var 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; // // spotsCreatedTemp[laneId, spotSize] += spotsTaken; // // // TODO: Allow mixed configuration // spotCountsPerpendicular[laneId] = 0; // for(int x = 0; x < 3; x++) // if(x != spotSize) // spotCountsParallel[laneId, x] = 0; // } if (requiredCounts[spotSize] == 0) break; } var sum = requiredCounts.Sum(); if (sum > 0) return false; for (var laneId = 3; laneId >= 0; laneId--) { if (spotCountsPerpendicular[laneId] != 0) { if (sizeIds[laneId] == 0) { // empty } else if (sizeIds[laneId] == 1) { // parallel only } else { spotsCreatedTemp[laneId, 0] += spotCountsPerpendicular[laneId]; spotCountsPerpendicular[laneId] = 0; } } } for (var i = 0; i < spotsCreatedTemp.GetLength(0); i++) for (var j = 0; j < spotsCreatedTemp.GetLength(1); j++) spotsCreated[i, j] = spotsCreatedTemp[i, j]; 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 int BiggerSizeComparator(Driver d1, Driver d2) { int sizeComp = d1.Size.CompareTo(d2.Size); if (sizeComp == 0) return d1.Priority.CompareTo(d2.Priority); return sizeComp; } 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]]); var hasEnoughSpace = tempArr.Select(x => AvailableSizesCombinations[x]).Sum() <= ParkingManager.Height - 11; var 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); } } }