using System.Collections.Generic; using UnityEngine; public class AStar_Manhattan : MonoBehaviour { public MengaturGrid grid; public Transform pintuDepan; public Transform pintuDapur; public Transform pintuGarasi; public List FindPath(Vector3 startPos, Vector3 targetPos) { grid.visitedNodeCount = 0; 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 newCost = currentNode.gCost + GetDistance(currentNode, neighbour); if (newCost < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newCost; neighbour.hCost = GetManhattanDistance(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 = "") { var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); grid.visitedNodeCount = 0; List path1 = FindPath(start, pintu); Node lastNodePath1 = grid.NodeFromWorldPoint(pintu); List path2 = FindPath(pintu, zonaAman); Node lastNodePath2 = grid.NodeFromWorldPoint(zonaAman); if (path1 == null || path2 == null) return null; path1.AddRange(path2); int panjangJalur = path1.Count; float langkahUnit = 1f; float kecepatanPlayer = 3f; // Penalty float penaltyCost = GetExitPenalty(pintu); // Hitung total cost int totalCost = lastNodePath1.gCost + lastNodePath2.gCost + Mathf.RoundToInt(penaltyCost * 10f); float waktuTempuh = totalCost / (kecepatanPlayer * 10f); stopwatch.Stop(); float waktuKomputasi = stopwatch.ElapsedMilliseconds / 1000f; int jumlahNodeDikunjungi = grid.visitedNodeCount; Debug.Log( $"[Evaluasi A* - {label}]\n" + $"- Waktu Komputasi: {waktuKomputasi:F4} detik\n" + $"- Jumlah Node Dikunjungi: {jumlahNodeDikunjungi}\n" + $"- Panjang Jalur: {panjangJalur} langkah\n" + $"- Total Cost: {totalCost}\n" + $"- Estimasi Waktu Tempuh: {waktuTempuh:F2} detik\n" + $"- Pintu Keluar: {label}" ); return new PathResult { path = path1, totalCost = totalCost, keterangan = label, panjangJalur = panjangJalur, 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.totalCost.CompareTo(b.totalCost)); 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) { return 10 * (Mathf.Abs(a.gridX - b.gridX) + Mathf.Abs(a.gridY - b.gridY)); } int GetManhattanDistance(Node a, Node b) { return 10 * (Mathf.Abs(a.gridX - b.gridX) + Mathf.Abs(a.gridY - b.gridY)); } 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 } }