Added spot orientation, refactored code

This commit is contained in:
Dawid Pietrykowski 2022-08-31 20:50:59 +02:00
parent 60ec303402
commit a038117d95
9 changed files with 663 additions and 341 deletions

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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<Driver> Drivers = new List<Driver>();
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<Driver> 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++;
}
}
}

View File

@ -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 class Spot : IComparable<Spot>
{
public Size Size;
public bool Flipped;
public GameObject GameObject;
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
}
}
public enum Size {
A = 0,
B = 1,
C = 2,
D = 3
}
public enum ParkingPreference {
Any = 0,
Front = 1,
Back = 2,
}

View File

@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Parking
{
public class InitialConfigurationGenerator
{
private static readonly List<int[]> 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<int>) requiredCounts);
var spotCountsPerpendicular = new int[spotCountsPerpendicularRef.Length];
spotCountsPerpendicularRef.CopyTo((Span<int>) 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<int>(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);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3de3cc3a61864323b1208796de3acdc5
timeCreated: 1661969778

View File

@ -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<int[]> 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];
private readonly float[] _spotHeights = {3.5f, 4f, 5f, 7.5f};
int start = 80;
int count = start;
public readonly List<List<Spot>> SpotMap = new() {new(), new(), new(), new()};
private TimeSpan _currentTime = TimeSpan.FromHours(8);
int maxCount = 0;
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<List<Size>> spotMap = new List<List<Size>>() {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<int>) requiredCounts);
int[] spotCountsPerpendicular = new int[spotCountsPerpendicularRef.Length];
spotCountsPerpendicularRef.CopyTo((Span<int>) 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<List<Spot>> GenerateSpotMap(int[,] spotsCreated)
{
// combinations.Add(new[] {arr[chosen[0]]});
var tempArr = new List<int>(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);
}
}