private int PercolateDown(int hole, PQNode <T> data) { while (2 * hole <= _size) { var left = 2 * hole; var right = left + 1; var target = left; if (right > _size || _heap[left].Priority < _heap[right].Priority) { target = left; } else { target = right; } if (_heap[target].Priority >= data.Priority) { break; } _heap[hole] = _heap[target]; hole = target; } return(hole); }
public void Insert(PQNode element) { list.Add(element); if (list.Count > 1) { BubbleUp(); } }
public void PriorotyQueue_CountIsUnchagedAfterPeek() { MaxHeapPriorityQueue pq = new MaxHeapPriorityQueue(); pq.Enqueue(10, 1); PQNode peek = pq.Peek(); Assert.AreEqual(1, pq.Count); }
private int PercolateUp(int hole, PQNode <T> data) { while (hole > 1 && data.Priority < _heap[hole / 2].Priority) { _heap[hole] = _heap[hole / 2]; hole /= 2; } return(hole); }
private void Resize() { var newHeap = new PQNode <T> [_heap.Length * 2]; for (var i = 0; i < _heap.Length; i++) { newHeap[i] = _heap[i]; } _heap = newHeap; }
public void Finding(List <Position> points) { while (astar.IsExistsCandidate()) { // 제일 좋은 후보를 탐색. PQNode candidate = astar.GetBestCandidate(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서 이미 방문 (closed) 된 경우 무시. if (astar.isClosed(candidate.Y, candidate.X)) { continue; } // 방문. astar.Visit(candidate.Y, candidate.X); // 목적지 도착. if (astar.ReachedDestination(candidate.Y, candidate.X)) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인 후 위치 예약. int directionLength = Enum.GetValues(typeof(DirectionEx)).Length; for (int direction = 0; direction < directionLength; ++direction) { int nextY = astar.NextY(candidate.Y, direction); int nextX = astar.NextX(candidate.X, direction); if (Passing(nextY, nextX)) { continue; } // 비용 계산. int g = astar.CalculationG(candidate.G, direction); int h = astar.CalculationH(nextY, nextX); // 현재 비용이 다른 경로로 도달하는 비용보다 좋지않을 경우. if (!astar.IsFasterThanPrev(nextY, nextX, g + h)) { continue; } // 예약 진행. astar.Reservation(nextY, nextX, g, h); astar.SetParent(nextY, nextX, candidate.Y, candidate.X); } } CompletedPath(points); }
//Return a node to be deleted from the queue and //then delete it. If the queue is empty, //the node priority will be zero: public PQNode Dequeue() { PQNode wpqNode = FindMaxNode(); if (wpqNode.Priority == 0) { return(wpqNode); } //Successfully remove Node nodeList.Remove(wpqNode); return(wpqNode); }
public void PriorotyQueue_DequeueSingleNodePQ() { MaxHeapPriorityQueue pq = new MaxHeapPriorityQueue(); for (int i = 0; i < 5; i++) { pq.Enqueue(i * 10, i); } PQNode dequeueNode = pq.Dequeue(); Assert.AreEqual(40, dequeueNode.Priority); }
public void Insert(T data, int priority) { if (_size == _heap.Length - 1) { Resize(); } _size++; var newNode = new PQNode <T>(data, priority); int index = PercolateUp(_size, newNode); _heap[index] = newNode; }
public void Remove() { if (list.Count > 1) { PQNode last = list[list.Count - 1]; list[0] = last; list.RemoveAt(list.Count - 1); SinkDown(); } else { list.RemoveAt(0); } }
public void insert(PQNode node) { nodes.Add(node); int i = nodes.Count - 1; int p = parent(i); PQNode tmp; while (i > 0 && p >= 0 && nodes[i].d < nodes[p].d) { tmp = nodes[p]; nodes[p] = nodes[i]; nodes[i] = tmp; i = p; p = parent(i); } }
private List <PQNode> nodeList = new List <PQNode>(); //Generic List //Add a node to the queue. //Returns true if the node was successfully //added and false if not (duplicate or invalid //priority or item): public bool Enqueue(PQNode newNode) { if (object.Equals(newNode.Item, default(T)) || (newNode.Priority <= 0)) { return(false); } foreach (PQNode wpqNode in nodeList) { if ((wpqNode.Priority == newNode.Priority) || object.Equals(wpqNode.Item, newNode.Item)) { return(false); } } //Operation was Successful //Add new node to the list nodeList.Add(newNode); return(true); }
public void SinkDown() { int index = 0; int length = list.Count; PQNode element = list[0]; while (true) { int leftChildIndex = (2 * index) + 1; int rightChildIndex = (2 * index) + 2; PQNode leftChild = new PQNode(string.Empty, 0); PQNode rightChild = new PQNode(string.Empty, 0); int swap = 0; if (leftChildIndex < length) { leftChild = list[leftChildIndex]; if (leftChild.priority > element.priority) { swap = leftChildIndex; } } if (rightChildIndex < length) { rightChild = list[rightChildIndex]; if (swap == 0 && rightChild.priority > element.priority || swap != 0 && rightChild.priority > leftChild.priority) { swap = rightChildIndex; } } if (swap == 0) { break; } else { list[index] = list[swap]; list[swap] = element; index = swap; } } }
public void BubbleUp() { int index = list.Count - 1; PQNode element = list[index]; while (index > 0) { int parentIndex = (index - 1) / 2; PQNode parent = list[parentIndex]; Console.WriteLine(parentIndex); if (element.priority <= parent.priority) { break; } else { list[parentIndex] = element; list[index] = parent; index = parentIndex; } } }
//Used internally by Dequeue and Peek //to find maximum numerical priority node: private PQNode FindMaxNode() { int maxPriority = -1; int targetIndex = -1; PQNode wpqNode = new PQNode(); if (nodeList.Count == 0) { return(wpqNode); } for (int i = 0; i < nodeList.Count; i++) { if (nodeList[i].Priority > maxPriority) { maxPriority = nodeList[i].Priority; targetIndex = i; } } wpqNode = nodeList[targetIndex]; return(wpqNode); }
// 셀 기반 위치 입력 -> 내부적으로는 _collision 배열기반 위치 -> 셀 기반 위치값을 반환 public List <Vector3Int> FindPath(Vector3Int startCellPos, Vector3Int destCellPos, bool ignoreDestCollision = false) { // ignoreDestCollision 최종 목적지를 충돌처리 할지 여부 // 만약 목적지에 플레이어가 있어서 못간다고 처리하면 안되니깐, 무조건 간다고 처리하게 List <Pos> path = new List <Pos>(); // 점수 매기기 // F = G + H // F = 최종 점수 (작을 수록 좋음, 경로에 따라 달라짐) // G = 시작점에서 해당 좌표까지 이동하는데 드는 비용 (작을 수록 좋음, 경로에 따라 달라짐) // H = 목적지에서 얼마나 가까운지 (작을 수록 좋음, 고정) // (y, x) 이미 방문했는지 여부 (방문 = closed 상태) bool[,] closed = new bool[SizeY, SizeX]; // CloseList // (y, x) 가는 길을 한 번이라도 발견했는지 // 발견X => MaxValue // 발견O => F = G + H int[,] open = new int[SizeY, SizeX]; // OpenList for (int y = 0; y < SizeY; y++) { for (int x = 0; x < SizeX; x++) { open[y, x] = Int32.MaxValue; } } Pos[,] parent = new Pos[SizeY, SizeX]; // 오픈리스트에 있는 정보들 중에서, 가장 좋은 후보를 빠르게 뽑아오기 위한 도구 PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); // CellPos -> ArrayPos Pos pos = Cell2Pos(startCellPos); Pos dest = Cell2Pos(destCellPos); // 시작점 발견 (예약 진행) open[pos.Y, pos.X] = 10 * (Math.Abs(dest.Y - pos.Y) + Math.Abs(dest.X - pos.X)); pq.Push(new PQNode() { F = 10 * (Math.Abs(dest.Y - pos.Y) + Math.Abs(dest.X - pos.X)), G = 0, Y = pos.Y, X = pos.X }); parent[pos.Y, pos.X] = new Pos(pos.Y, pos.X); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다 PQNode node = pq.Pop(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서 이미 방문(closed)된 경우 스킵 if (closed[node.Y, node.X]) { continue; } // 방문한다 closed[node.Y, node.X] = true; // 목적지 도착했으면 바로 종료 if (node.Y == dest.Y && node.X == dest.X) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인해서 예약(open)한다 for (int i = 0; i < _deltaY.Length; i++) { Pos next = new Pos(node.Y + _deltaY[i], node.X + _deltaX[i]); // 유효 범위를 벗어났으면 스킵 // 벽으로 막혀서 갈 수 없으면 스킵 // 목적지를 충돌처리 시킬거면 스킵 if (!ignoreDestCollision || next.Y != dest.Y || next.X != dest.X) { if (CanGo(Pos2Cell(next)) == false) // CellPos { continue; } } // 이미 방문한 곳이면 스킵 if (closed[next.Y, next.X]) { continue; } // 비용 계산 (휴리스틱) // AStar는 최단경로 찾는애가 아님 int g = 0; // node.G + _cost[i]; // 내가 시작한 위치에서 가는 비용 int h = 10 * ((dest.Y - next.Y) * (dest.Y - next.Y) + (dest.X - next.X) * (dest.X - next.X)); // 목적지까지 거리의 절대값만 사용(멘하탄 거리) // 다른 경로에서 더 빠른 길 이미 찾았으면 스킵 if (open[next.Y, next.X] < g + h) { continue; } // 예약 진행 open[dest.Y, dest.X] = g + h; pq.Push(new PQNode() { F = g + h, G = g, Y = next.Y, X = next.X }); parent[next.Y, next.X] = new Pos(node.Y, node.X); } } return(CalcCellPathFromParent(parent, dest)); }
private void AStar() { // U L D R UL DL DR UR int[] deltaY = new int[] { -1, 0, 1, 0 }; int[] deltaX = new int[] { 0, -1, 0, 1 }; int[] cost = new int[] { 10, 10, 10, 10 }; // 점수 매기기 // F = G + H // F = 최종 점수( 적을 수록 좋음, 경로에 따라 달라짐) // G = 시작점에서 해당 좌표까지 이동하는데 드는 비용( 작을수록 좋음) // H = 목적지에서 얼마나 가까운지 (작을 수록 좋음, 고정) // (Y, X) 이미 방문했는지 여부 기록 (방문 = Closed 상태) bool[,] closesd = new bool[_board.Size, _board.Size]; // (Y, X) 가는 길을 한 번이라도 발견했는지 // 발견 X => MaxValue; // 발견 O => F = G + H int[,] open = new int[_board.Size, _board.Size]; for (int y = 0; y < _board.Size; ++y) { for (int x = 0; x < _board.Size; ++x) { open[y, x] = Int32.MaxValue; } } Pos[,] parent = new Pos[_board.Size, _board.Size]; // 오픈 리스트에 있는 정보들 중에서, 가장 좋은 후보를 빠르게 뽑아오기 위한 도구 PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); // 시작점 발견 (에약 진행) open[PosY, PosX] = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)); pq.Push(new PQNode() { F = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)), G = 0, Y = PosY, X = PosX }); parent[PosY, PosX] = new Pos(PosY, PosX); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다 PQNode node = pq.Pop(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서 이미 방문한 경우 스킵 if (closesd[node.Y, node.X]) { continue; } // 방문한다 closesd[node.Y, node.X] = true; // 목적지 도착했으면 바로 종료 if (node.Y == _board.DestY && node.X == _board.DestX) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인해서 예약(Open)한다 for (int i = 0; i < deltaY.Length; ++i) { int nextY = node.Y + deltaY[i]; int nextX = node.X + deltaX[i]; // 유효 범위가 넘어가면 스킵 if (nextX < 0 || nextX >= _board.Size || nextY < 0 || nextY >= _board.Size) { continue; } // 벽으로 막혀있으면 스킵 if (_board.Tile[nextY, nextX] == Board.TileType.Wall) { continue; } // 이미 방문한 곳이면 스킵 if (closesd[nextY, nextX]) { continue; } // 비용 계산 int g = node.G + cost[i]; int h = 10 * (Math.Abs(_board.DestY - nextY) + Math.Abs(_board.DestX - nextX)); // 다른 경로에서 더 빠른 길을 이미 찾았으면 스킵 if (open[nextY, nextX] < g + h) { continue; } // 예약 진행 open[nextY, nextX] = g + h; pq.Push(new PQNode() { F = g + h, G = g, Y = nextY, X = nextX }); parent[nextY, nextX] = new Pos(node.Y, node.X); } } CalcPathFromParent(parent); }
void Astar() { // U L D R UL DL DR UR int[] deltaY = new int[] { -1, 0, 1, 0 }; int[] deltaX = new int[] { 0, -1, 0, 1 }; int[] cost = new int[] { 10, 10, 10, 10 }; // 점수 매기기 // F = G + H // F = 최종 점수 (작을 수록 좋음, 경로에 따라 달라짐) // G = 시작점에서 해당 좌표까지 이동하는데 드는 비용 (작을 수록 좋음, 경로에 따라 달라짐) // H = 목적지에서 얼마나 가까운지 (작을 수록 좋음, 고정) // (y,x) 이미 방문했는지 여부 (방문 = closed 상태) bool[,] closed = new bool[_board.Size, _board.Size]; // CloseList // (y,x) 가는 길을 한번이라도 발견했는지 // 발견 X => MaxValue // 발견 O => F = G + H int[,] open = new int[_board.Size, _board.Size]; // OpenList for (int y = 0; y < _board.Size; y++) { for (int x = 0; x < _board.Size; x++) { open[y, x] = Int32.MaxValue; } } Pos[,] parent = new Pos[_board.Size, _board.Size]; // 오픈리스트에 있는 정보들 중에서, 가장 좋은 후보를 빠르게 뽑아오기 위한 도구. PrioityQueue <PQNode> pq = new PrioityQueue <PQNode>(); // 시작점 발견 (예약을 진행해야 한다) open[PosY, PosX] = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)); // Abs는 절대값을 의미한다. pq.Push(new PQNode() { F = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)), G = 0, Y = PosY, X = PosX }); parent[PosY, PosX] = new Pos(PosY, PosX); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다. PQNode node = pq.Pop(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서 이미 방문(closed)된 경우 스킵 if (closed[node.Y, node.X]) { continue; } // 방문한다 closed[node.Y, node.X] = true; // 목적지에 도착햇으면 바로 종료 if (node.Y == _board.DestY && node.X == _board.DestX) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인해서 예약(open)한다 for (int i = 0; i < deltaY.Length; i++) { int nextY = node.Y + deltaY[i]; int nextX = node.X + deltaX[i]; // 유효 범위를 벗어났을 경우 스킵한다. if (nextX < 0 || nextX >= _board.Size || nextY < 0 || nextY >= _board.Size) { continue; } // 벽으로 막혀서 갈 수 없으면 스킵한다. if (_board.Tile[nextY, nextX] == Part2_Section2MakeMap.TileType.Wall) { continue; } // 이미 방문한 곳이면 스킵한다. if (closed[nextY, nextX]) { continue; } // 비용 계산을 시작한다 // G = 시작하면서 이동하면서 필요한 비용 int g = node.G + cost[i]; // H = 목적지에서 시작점에서 가까운 정도를 나타내는 것 int h = 10 * (Math.Abs(_board.DestY - nextY) + Math.Abs(_board.DestX - nextX)); // 다른 경로에서 더 빠른 길을 이미 찾았으면 스킵을 해야한다 if (open[nextY, nextX] < g + h) { continue; } // 예약을 진행한다 open[nextY, nextX] = g + h; pq.Push(new PQNode() { F = g + h, G = g, Y = nextY, X = nextX }); parent[PosY, PosX] = new Pos(node.Y, node.X); } } CalcPathFromParent(parent); }
public List <Vector3Int> FindPath(Vector3Int startCellPos, Vector3Int destCellPos, bool ignoreDestCollision = false) { List <Pos> path = new List <Pos>(); // 점수 매기기 // F = G + H // F = 최종 점수 (작을수록 좋음.) // G = 시작점에서 해당 좌표까지 비용 // H = 목적지에서 얼마나 가까운지 // 방문 여부 bool[,] closed = new bool[SizeY, SizeX]; // CloseList // (y,x) 가는 길을 한 번이라도 발견했나? // 발견x => maxVal // 발견o => F = G + H int[,] open = new int[SizeY, SizeX]; for (int y = 0; y < SizeY; y++) { for (int x = 0; x < SizeX; x++) { open[y, x] = Int32.MaxValue; } } Pos[,] parent = new Pos[SizeY, SizeX]; // 오픈 리스트에 있는 정보 중에서, 가장 좋은 후보를 빠르게 뽑기 위한 도구 (Heap 대신) PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); Pos pos = Cell2Pos(startCellPos); Pos dest = Cell2Pos(destCellPos); // 시작점 발견 (예약 진행) open[pos.Y, pos.X] = 10 * (Math.Abs(dest.Y - pos.Y) + Math.Abs(dest.X - pos.X)); pq.Push(new PQNode() { F = (Math.Abs(dest.Y - pos.Y) + Math.Abs(dest.X - pos.X)), G = 0, Y = pos.Y, X = pos.X }); parent[pos.Y, pos.X] = new Pos(pos.Y, pos.X); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다. PQNode node = pq.Pop(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인하여 이미 close에 담긴경우 스킵 if (closed[node.Y, node.X]) { continue; } // 방문. closed[node.Y, node.X] = true; // 목적지다. if (node.Y == dest.Y && node.X == dest.X) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인하고 예약 for (int i = 0; i < _deltaY.Length; i++) { Pos next = new Pos(node.Y + _deltaY[i], node.X + _deltaX[i]); // 유효 범위를 벗어나면 스킵 // 벽으로 막혀도. if (!ignoreDestCollision || next.Y != dest.Y || next.X != dest.X) { if (CanGo(Pos2Cell(next)) == false) { continue; } } if (closed[next.Y, next.X]) { continue; } int g = 0; // node.G + _cost[i]; int h = 10 * ((dest.Y - next.Y) * (dest.Y - next.Y) + (dest.X - next.X) * (dest.X - next.X)); // 다른 경로에서 더 빠른 길 찾으면 스킵 if (open[next.Y, next.X] < g + h) { continue; } // 예약 진행 open[dest.Y, dest.X] = g + h; pq.Push(new PQNode() { F = g + h, G = g, Y = next.Y, X = next.X }); parent[next.Y, next.X] = new Pos(node.Y, node.X); } } return(CalcCellPathFromParent(parent, dest)); }
public List <Tile> PathFind(Tile start, Tile target) { InitNode(); PriorityQueue <PQNode> pQueue = new PriorityQueue <PQNode>(); int startH = CalcDistance(target, start); pQueue.Push(new PQNode() { F = 0 + startH, H = startH, G = 0, X = start.X, Y = start.Y }); _openList[start.X, start.Y] = startH; start.PARENT = null; Tile tile = start; Tile tt; while (pQueue.Count > 0) { PQNode node = pQueue.Pop(); if (_closedList[node.X, node.Y]) { continue; } _closedList[node.X, node.Y] = true; if (node.X == target.X && node.Y == target.Y) { break; } // find next tile이웃 노드를 찾고 fcost를 구해 구해진 코스트가 기존의 예약된(openlist의) 코스트보다 작다면 우선순위 큐에 넣는다. for (int i = 0; i < _deltaX.Length; i++) { int neighborX = node.X + _deltaX[i]; int neighborY = node.Y + _deltaY[i]; if (neighborX < 0 || neighborX >= _map._width || neighborY < 0 || neighborY >= _map._height) { continue; } if (CheckType(neighborX, neighborY)) { continue; } if (_closedList[neighborX, neighborY]) { continue; } TileType type = _map.Tiles[neighborX, neighborY].TILETYPE; int delta = 0; if (type == TileType.Monster || type == TileType.Hero) { delta += 1000; } int g = node.G + _cost[i] + delta; int h = CalcDistance(target, _map._tiles[neighborX, neighborY]); if (_openList[neighborX, neighborY] < g + h) { continue; } _openList[neighborX, neighborY] = g + h; pQueue.Push(new PQNode() { F = g + h, H = h, G = g, X = neighborX, Y = neighborY }); _map._tiles[neighborX, neighborY].PARENT = _map._tiles[node.X, node.Y]; } } return(PathTrace(target)); }
//Returns the next node that would be dequeued //without actually removing it. If the queue //is empty, the node priority will be zero: public PQNode Peek() { PQNode wpqNode = FindMaxNode(); return(wpqNode); }
void AStar() { int[] deltaY = new int[] { -1, 0, 1, 0 }; int[] deltaX = new int[] { 0, -1, 0, 1 }; int[] cost = new int[] { 10, 10, 10, 10 }; bool[,] closed = new bool[_board.Size, _board.Size]; // CloseList int[,] open = new int[_board.Size, _board.Size]; // OpenList for (int y = 0; y < _board.Size; y++) { for (int x = 0; x < _board.Size; x++) { open[y, x] = Int32.MaxValue; } } Pos[,] parent = new Pos[_board.Size, _board.Size]; PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); open[PosY, PosX] = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)); pq.Push(new PQNode() { F = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)), G = 0, Y = PosY, X = PosX }); parent[PosY, PosX] = new Pos(PosY, PosX); while (pq.Count > 0) { PQNode node = pq.Pop(); if (closed[node.Y, node.X]) { continue; } closed[node.Y, node.X] = true; if (node.Y == _board.DestY && node.X == _board.DestX) { break; } for (int i = 0; i < deltaY.Length; i++) { int nextY = node.Y + deltaY[i]; int nextX = node.X + deltaX[i]; if (nextX < 0 || nextX >= _board.Size || nextY < 0 || nextY >= _board.Size) { continue; } if (_board.Tile[nextY, nextX] == Board.TileType.Wall) { continue; } if (closed[nextY, nextX]) { continue; } int g = node.G + cost[i]; int h = 10 * (Math.Abs(_board.DestY - nextY) + Math.Abs(_board.DestX - nextX)); if (open[nextY, nextX] < g + h) { continue; } open[nextY, nextX] = g + h; pq.Push(new PQNode() { F = g + h, G = g, Y = nextY, X = nextX }); parent[nextY, nextX] = new Pos(node.Y, node.X); } } CalcPathFromParent(parent); }
void AStar() { // U L D R UL DL DR UR , 대각선 추가 int[] deltaY = new int[] { -1, 0, 1, 0, -1, 1, 1, -1 }; int[] deltaX = new int[] { 0, -1, 0, 1, -1, -1, 1, 1 }; int[] cost = new int[] { 10, 10, 10, 10, 14, 14, 14, 14 }; Pos[,] parent = new Pos[_board.Size, _board.Size]; // 다익스트라와 bfs 모두 사방팔방으로 다 탐색한다. // 하지만, A*는 목적지와 가까워지는 방향으로 우선적으로 탐색한다. // 점수 매기기 // F = G + H // F = 최종 점수( 작을 수록 좋음, 경로에 따라 달라짐) // G = 시작점에서 해당 좌표까지 이동하는데 드는 비용 (작을 수록 좋음, 경로에 따라 달라짐) // H = 목적지에서 얼마나 가까운지( 작을 수록 좋음, 고정) // (y,x) 이미 방문했는지 여부(방문 = closed 상태) bool[,] closed = new bool[_board.Size, _board.Size]; // CloseList // (y,x) 가는 길을 한번이라도 발견 했는지 // 발견X => MaxValue // 발견O => F = G + H int[,] open = new int[_board.Size, _board.Size]; // OpenList for (int y = 0; y < _board.Size; y++) { for (int x = 0; x < _board.Size; x++) { open[y, x] = Int32.MaxValue; } } // 오픈리스트에 있는 정보들 중에서 가장 좋은 후보를 빠르게 뽑아오기 위한 도구 PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); // 시작점 발견 (예약 진행) open[PosY, PosX] = 0 + 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)); pq.Push(new PQNode() { F = 10 * (Math.Abs(_board.DestY - PosY) + Math.Abs(_board.DestX - PosX)), G = 0, Y = PosY, X = PosX }); parent[PosY, PosX] = new Pos(PosY, PosX); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다. PQNode node = pq.Pop(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서 이미 방문(closed)된 경우 스킵 if (closed[node.Y, node.X]) { continue; } // 방문한다. closed[node.Y, node.X] = true; if (node.Y == _board.DestY && node.X == _board.DestX) { break; } // 상하좌우 등 이동할 수 있는 좌표인지 확인해서 예약(open)한다. for (int i = 0; i < deltaY.Length; i++) { int nextY = node.Y + deltaY[i]; int nextX = node.X + deltaX[i]; // 유효 범위를 벗어났으면 스킵 if (nextX < 0 || nextX >= _board.Size || nextY < 0 || nextY >= _board.Size) { continue; } // 벽으로 막혀서 갈수 없으면 스킵 if (_board.Tile[nextY, nextX] == Board.TileType.Wall) { continue; } // 이미 방문한 곳이면 스킵 if (closed[nextY, nextX]) { continue; } // 비용 계산 int g = node.G + cost[i]; int h = 10 * (Math.Abs(_board.DestY - nextY) + Math.Abs(_board.DestX - nextX)); // 다른 경로에서 더 빠른 길을 이미 찾았으면 스킵 if (open[nextY, nextX] < g + h) { continue; } open[nextY, nextX] = g + h; pq.Push(new PQNode() { F = g + h, G = g, Y = nextY, X = nextX }); parent[nextY, nextX] = new Pos(node.Y, node.X); } } CalcPathFromParent(parent); }