From a038117d95f776ded0203946657e021b59bd057d Mon Sep 17 00:00:00 2001 From: Dawid Pietrykowski Date: Wed, 31 Aug 2022 20:50:59 +0200 Subject: [PATCH] Added spot orientation, refactored code --- Assets/Prefabs/NormalneL.prefab | 85 ++++ Assets/Prefabs/NormalneM.prefab | 85 ++++ Assets/Prefabs/NormalneS.prefab | 85 ++++ Assets/Scenes/SampleScene.unity | 2 - Assets/Scripts/DataImporter.cs | 81 ++-- Assets/Scripts/Driver.cs | 67 +++- .../Scripts/InitialConfigurationGenerator.cs | 221 +++++++++++ .../InitialConfigurationGenerator.cs.meta | 3 + Assets/Scripts/ParkingManager.cs | 375 +++++------------- 9 files changed, 663 insertions(+), 341 deletions(-) create mode 100644 Assets/Scripts/InitialConfigurationGenerator.cs create mode 100644 Assets/Scripts/InitialConfigurationGenerator.cs.meta diff --git a/Assets/Prefabs/NormalneL.prefab b/Assets/Prefabs/NormalneL.prefab index 51a37e7..48b532e 100644 --- a/Assets/Prefabs/NormalneL.prefab +++ b/Assets/Prefabs/NormalneL.prefab @@ -336,6 +336,90 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 +--- !u!1 &1698470686 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1698470687} + - component: {fileID: 1698470688} + m_Layer: 0 + m_Name: Isometric Diamond + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1698470687 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1698470686} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.391, z: 0} + m_LocalScale: {x: 0.29000002, y: 0.13049999, z: 0.6524999} + m_ConstrainProportionsScale: 1 + m_Children: [] + m_Father: {fileID: 13560301962479320} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &1698470688 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1698470686} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 0.5} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 --- !u!1 &13560301962479332 GameObject: m_ObjectHideFlags: 0 @@ -371,6 +455,7 @@ Transform: - {fileID: 378070325} - {fileID: 44517024} - {fileID: 1120243406} + - {fileID: 1698470687} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Prefabs/NormalneM.prefab b/Assets/Prefabs/NormalneM.prefab index 8bb789c..f1d4975 100644 --- a/Assets/Prefabs/NormalneM.prefab +++ b/Assets/Prefabs/NormalneM.prefab @@ -168,6 +168,90 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 +--- !u!1 &1240076081 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1240076082} + - component: {fileID: 1240076083} + m_Layer: 0 + m_Name: Isometric Diamond (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1240076082 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1240076081} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.365, z: 0} + m_LocalScale: {x: 0.29000002, y: 0.145, z: 0.6524999} + m_ConstrainProportionsScale: 1 + m_Children: [] + m_Father: {fileID: 13560301962479320} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &1240076083 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1240076081} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 0.5} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 --- !u!1 &1413974509 GameObject: m_ObjectHideFlags: 0 @@ -371,6 +455,7 @@ Transform: - {fileID: 1413974510} - {fileID: 1859742736} - {fileID: 54299844} + - {fileID: 1240076082} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Prefabs/NormalneS.prefab b/Assets/Prefabs/NormalneS.prefab index 7b0c9b7..19d2790 100644 --- a/Assets/Prefabs/NormalneS.prefab +++ b/Assets/Prefabs/NormalneS.prefab @@ -252,6 +252,90 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 +--- !u!1 &584568173 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 584568174} + - component: {fileID: 584568175} + m_Layer: 0 + m_Name: Isometric Diamond (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &584568174 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584568173} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.36000013, z: 0} + m_LocalScale: {x: 0.29000002, y: 0.16312498, z: 0.6524999} + m_ConstrainProportionsScale: 1 + m_Children: [] + m_Father: {fileID: 13560301962479320} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &584568175 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 584568173} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 1, y: 0.5} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 --- !u!1 &1661884534 GameObject: m_ObjectHideFlags: 0 @@ -371,6 +455,7 @@ Transform: - {fileID: 92791926} - {fileID: 504262852} - {fileID: 1661884535} + - {fileID: 584568174} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 66b8eda..f44c637 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -956,8 +956,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: af24978eff1b496b78bcc0aeccf046ed, type: 3} m_Name: m_EditorClassIdentifier: - width: 68 - height: 29 stepTime: 15 timeText: {fileID: 861769593} countsText: {fileID: 489742541} diff --git a/Assets/Scripts/DataImporter.cs b/Assets/Scripts/DataImporter.cs index 65438ca..32e6c4f 100644 --- a/Assets/Scripts/DataImporter.cs +++ b/Assets/Scripts/DataImporter.cs @@ -1,49 +1,54 @@ -using System; -using System.Collections; using System.Collections.Generic; -using TMPro; -using UnityEngine; +using System.IO; -public class DataImporter +namespace Parking { - public static List Drivers = new List(); - - public static void ReadFile(string path) + public class DataImporter { - string fileData = System.IO.File.ReadAllText(path); - String[] lines = fileData.Split("\n"[0]); - int counter = 0; - foreach (string line in lines) + public static List Drivers = new(); + + public static void ReadFile(string path) { - if (counter == 0) + var fileData = File.ReadAllText(path); + var lines = fileData.Split("\n"[0]); + var counter = 0; + foreach (var line in lines) { + if (counter == 0) + { + counter++; + continue; + } + + var lineData = line.Trim().Split(','); + var preference = lineData[0] == "" + ? ParkingPreference.Any + : lineData[0] == "P" + ? ParkingPreference.Front + : ParkingPreference.Back; + Size size; + switch (lineData[1]) + { + case "Mały": + size = Size.A; + break; + case "Średni": + size = Size.B; + break; + case "Duży": + size = Size.C; + break; + case "Max": + size = Size.D; + break; + default: + size = Size.A; + break; + } + + Drivers.Add(new Driver(size, counter, preference)); counter++; - continue; } - String[] lineData = (line.Trim()).Split(','); - ParkingPreference preference = (lineData[0] == "" ? ParkingPreference.Any : (lineData[0] == "P" ? ParkingPreference.Front : ParkingPreference.Back)); - Size size; - switch (lineData[1]) - { - case "Mały": - size = Size.A; - break; - case "Średni": - size = Size.B; - break; - case "Duży": - size = Size.C; - break; - case "Max": - size = Size.D; - break; - default: - size = Size.A; - break; - } - Drivers.Add(new(size, counter, preference)); - // float.TryParse(lineData[0], x); - counter++; } } } \ No newline at end of file diff --git a/Assets/Scripts/Driver.cs b/Assets/Scripts/Driver.cs index 0447890..60d8311 100644 --- a/Assets/Scripts/Driver.cs +++ b/Assets/Scripts/Driver.cs @@ -1,31 +1,54 @@ using System; -using System.Drawing; -using Unity.Burst.Intrinsics; using Unity.VisualScripting; +using UnityEngine; -public class Driver +namespace Parking { - public Size Size = Size.A; - public int Priority = 0; - public ParkingPreference ParkingPreference = ParkingPreference.Any; - - public Driver(Size size, int priority, ParkingPreference parkingPreference) + public class Driver { - this.Size = size; - this.Priority = priority; - this.ParkingPreference = parkingPreference; + public ParkingPreference ParkingPreference = ParkingPreference.Any; + public int Priority; + public readonly Size Size = Size.A; + + public Driver(Size size, int priority, ParkingPreference parkingPreference) + { + Size = size; + Priority = priority; + ParkingPreference = parkingPreference; + } } -} -public enum Size { - A = 0, - B = 1, - C = 2, - D = 3 -} + public class Spot : IComparable + { + public Size Size; + public bool Flipped; + public GameObject GameObject; -public enum ParkingPreference { - Any = 0, - Front = 1, - Back = 2, + public Spot(Size size, bool flipped) + { + Size = size; + Flipped = flipped; + } + + public int CompareTo(Spot obj) + { + return Size.CompareTo(obj.Size); + } + } + + + public enum Size + { + A = 0, + B = 1, + C = 2, + D = 3 + } + + public enum ParkingPreference + { + Any = 0, + Front = 1, + Back = 2 + } } \ No newline at end of file diff --git a/Assets/Scripts/InitialConfigurationGenerator.cs b/Assets/Scripts/InitialConfigurationGenerator.cs new file mode 100644 index 0000000..d4bddcb --- /dev/null +++ b/Assets/Scripts/InitialConfigurationGenerator.cs @@ -0,0 +1,221 @@ +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 start = 80; + var count = start; + + var 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}"); + + 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..."); + for (var i = 0; i < DataImporter.Drivers.Count && i + fixedCarSpots < carsToPlace; i++) + counts[(int) DataImporter.Drivers[i].Size]++; + + var countsString = $"A: {counts[0]} B: {counts[1]} C: {counts[2]} " + + $"Suma: {counts.Sum()}"; + 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()}"); + + for (var i = 0; i < spotsCreated.GetLength(0); i++) + for (var j = 0; j < spotsCreated.GetLength(1); j++) + spotsCreated[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; + + spotsCreated[laneId, spotSize] += spotsTaken; + + // TODO: Allow modified 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; + // + // 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; + } + + var sum = requiredCounts.Sum(); + if (sum > 0) + return false; + 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]]); + 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); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/InitialConfigurationGenerator.cs.meta b/Assets/Scripts/InitialConfigurationGenerator.cs.meta new file mode 100644 index 0000000..4ad1d8a --- /dev/null +++ b/Assets/Scripts/InitialConfigurationGenerator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3de3cc3a61864323b1208796de3acdc5 +timeCreated: 1661969778 \ No newline at end of file diff --git a/Assets/Scripts/ParkingManager.cs b/Assets/Scripts/ParkingManager.cs index 7d1b47e..ef50927 100644 --- a/Assets/Scripts/ParkingManager.cs +++ b/Assets/Scripts/ParkingManager.cs @@ -1,320 +1,137 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; -using Unity.VisualScripting; using UnityEngine; -public class ParkingManager : MonoBehaviour +namespace Parking { - 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() + public class ParkingManager : MonoBehaviour { - timeText.text = _currentTime.ToString(); - DataImporter.ReadFile("Assets/Data/v1.csv"); - Debug.Log(DataImporter.Drivers); - FindSolution(); - } + public const float Width = 68; + public const float Height = 29; - public void AdvanceTime() - { - _currentTime += TimeSpan.FromMinutes(15); - timeText.text = _currentTime.ToString(); - } + public static ParkingManager Instance; - private void FindSolution() - { - PreProcessCombinations(out var spotCountsPerpendicular, out var spotCountsParallel); + [SerializeField] private int stepTime = 15; + [SerializeField] private TextMeshProUGUI timeText; + [SerializeField] public TextMeshProUGUI countsText; + [SerializeField] public GameObject spotPrefabA; + [SerializeField] public GameObject spotPrefabB; + [SerializeField] public GameObject spotPrefabC; - int[,] spotsCreated = new int[4, 4]; - - int start = 80; - int count = start; + private readonly float[] _spotHeights = {3.5f, 4f, 5f, 7.5f}; - int maxCount = 0; + public readonly List> SpotMap = new() {new(), new(), new(), new()}; + private TimeSpan _currentTime = TimeSpan.FromHours(8); - if (PlaceNCars(30, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) + private void Awake() { - count++; - while (PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) - { - count++; - } - - maxCount = count--; + Instance = this; } - else - { - count--; - while (!PlaceNCars(count, spotCountsPerpendicular, spotCountsParallel, spotsCreated)) - { - count--; - } - maxCount = count; + private void Start() + { + timeText.text = _currentTime.ToString(); + DataImporter.ReadFile("Assets/Data/v1.csv"); + Debug.Log(DataImporter.Drivers); + InitialConfigurationGenerator generator = new(); + ArrangeSpots(generator.FindSolution()); } - 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++) + public void AdvanceTime() { - for (int j = 0; j < spotsCreated.GetLength(1); j++) + _currentTime += TimeSpan.FromMinutes(15); + timeText.text = _currentTime.ToString(); + } + + public void UpdateText(string text) + { + countsText.text = text; + } + + 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++) { - for (int k = 0; k < spotsCreated[i, j]; k++) + float currentY; + switch (i) { - 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); + case 0: + currentY = -Height / 2.0f + 2f; break; - case Size.B: - spot = Instantiate(spotPrefabB); - spot.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 0.25f, 0); + case 1: + currentY = Height / 2 - 5.5f - 5f - maxP3 / 2.0f - maxP2; break; - case Size.C: - spot = Instantiate(spotPrefabC); - spot.transform.position = new Vector3(currentX, currentY + (alignTop ? -1 : 1) * 0.5f, 0); + case 2: + currentY = Height / 2 - 5.5f - 5f - maxP3 / 2.0f; + break; + case 3: + currentY = Height / 2.0f - 2f; break; default: + currentY = -10; break; } - currentX += 2.25f; - } - } - } + 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; - private bool PlaceNCars(int carsToPlace, int[] spotCountsPerpendicular, int[,] spotCountsParallel, int[,] spotsCreated) - { + var flipped = false; - 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}"); + 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); + spotMap[i][j].GameObject.transform.rotation = + Quaternion.Euler(new Vector3(0, 0, spotMap[i][j].Flipped ? 180 : 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); + spotMap[i][j].GameObject.transform.rotation = + Quaternion.Euler(new Vector3(0, 0, spotMap[i][j].Flipped ? 180 : 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); + spotMap[i][j].GameObject.transform.rotation = + Quaternion.Euler(new Vector3(0, 0, spotMap[i][j].Flipped ? 180 : 0)); + break; + } - Debug.Log("Calculating required spot counts..."); - for (int i = 0; i < DataImporter.Drivers.Count && i + fixedCarSpots < carsToPlace; i++) - { - counts[(int) DataImporter.Drivers[i].Size]++; - } + currentX += 2.25f; - 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; + flipped = !flipped; } - // 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) + private List> GenerateSpotMap(int[,] spotsCreated) { - // 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()); + 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)); - return; + 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 + return SpotMap; } - - 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); } } \ No newline at end of file