using System.Collections.Generic; using UnityEngine; using System.Diagnostics; public class AStar_Euclidean : MonoBehaviour { public MengaturGridE grid; public Transform pintuDepan; public Transform pintuDapur; public Transform pintuGarasi; public List FindPath(Vector3 startPos, Vector3 targetPos) { Node startNode = grid.NodeFromWorldPoint(startPos); Node targetNode = grid.NodeFromWorldPoint(targetPos); List openSet = new List(); HashSet closedSet = new HashSet(); openSet.Add(startNode); while (openSet.Count > 0) { Node currentNode = openSet[0]; for (int i = 1; i < openSet.Count; i++) { if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)) { currentNode = openSet[i]; } } openSet.Remove(currentNode); closedSet.Add(currentNode); grid.visitedNodeCount++; if (currentNode == targetNode) { return RetracePath(startNode, targetNode); } foreach (Node neighbour in grid.GetNeighbours(currentNode)) { if (!neighbour.walkable || closedSet.Contains(neighbour)) continue; int newMovementCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour); if (newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newMovementCostToNeighbour; neighbour.hCost = GetEuclideanDistance(neighbour, targetNode); neighbour.parent = currentNode; if (!openSet.Contains(neighbour)) openSet.Add(neighbour); } } } return null; } public PathResult FindCombinedPath(Vector3 start, Vector3 pintu, Vector3 zonaAman, string label = "") { System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); List path1 = FindPath(start, pintu); List path2 = FindPath(pintu, zonaAman); if (path1 == null || path2 == null) return null; path1.AddRange(path2); float totalCost = 0f; for (int i = 1; i < path1.Count; i++) { totalCost += GetEuclideanDistance(path1[i - 1], path1[i]); } float penalty = GetExitPenalty(pintu); totalCost += penalty * 10f; float kecepatanPlayer = 5f; float waktuTempuh = totalCost / (kecepatanPlayer * 10f); stopwatch.Stop(); float waktuKomputasi = stopwatch.ElapsedMilliseconds / 1000f; int jumlahNodeDikunjungi = grid.visitedNodeCount; UnityEngine.Debug.Log( $"[Evaluasi A* - {label}]\n" + $"- Waktu Komputasi: {waktuKomputasi:F4} detik\n" + $"- Jumlah Node Dikunjungi: {jumlahNodeDikunjungi}\n" + $"- Panjang Jalur: {path1.Count}\n" + $"- Total Cost: {totalCost:F2}\n" + $"- Pintu Keluar: {label}" ); return new PathResult { path = path1, totalCost = totalCost, keterangan = label, estimasiWaktu = waktuTempuh }; } public List FindMultiplePathsMelaluiPintu(Vector3 startPos, Vector3 zonaAman, List pintuList, int maxJalur, List namaPintu) { List semuaJalur = new List(); for (int i = 0; i < pintuList.Count; i++) { Vector3 pintu = pintuList[i]; string label = (namaPintu != null && i < namaPintu.Count) ? namaPintu[i] : $"Pintu ({pintu.x:F0},{pintu.z:F0})"; PathResult jalur = FindCombinedPath(startPos, pintu, zonaAman, label); if (jalur != null) { semuaJalur.Add(jalur); } } semuaJalur.Sort((a, b) => a.estimasiWaktu.CompareTo(b.estimasiWaktu)); return semuaJalur.GetRange(0, Mathf.Min(maxJalur, semuaJalur.Count)); } List RetracePath(Node startNode, Node endNode) { List path = new List(); Node currentNode = endNode; while (currentNode != startNode) { path.Add(currentNode); currentNode = currentNode.parent; } path.Reverse(); return path; } int GetDistance(Node a, Node b) { int dstX = Mathf.Abs(a.gridX - b.gridX); int dstY = Mathf.Abs(a.gridY - b.gridY); if (dstX > dstY) return 14 * dstY + 10 * (dstX - dstY); return 14 * dstX + 10 * (dstY - dstX); } int GetEuclideanDistance(Node a, Node b) { float dx = a.gridX - b.gridX; float dy = a.gridY - b.gridY; return Mathf.RoundToInt(Mathf.Sqrt(dx * dx + dy * dy) * 10); } float GetExitPenalty(Vector3 pintu) { if (pintuDepan != null && Vector3.Distance(pintu, pintuDepan.position) < 1f) return 2f; // Pintu depan else if (pintuDapur != null && Vector3.Distance(pintu, pintuDapur.position) < 1f) return 1f; // Pintu dapur else if (pintuGarasi != null && Vector3.Distance(pintu, pintuGarasi.position) < 1f) return 1f; // Pintu garasi return 0f; // Tidak ada penalty } }