private void AStar() { // U L D R 4방향 이동만 할 때 int[] deltaY = new int[] { -1, 0, 1, 0 }; int[] deltaX = new int[] { 0, -1, 0, 1 }; int[] cost = new int[] { 10, 10, 10, 10 }; // 방향에따른 이동 비용 // 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 }; // 방향에따른 이동 비용 // 점수 매기기 // F = G + H // F : 최종점수(작을수록 좋음, 경로에 따라 달라짐) // G : 시작점에서 해당 좌표까지 이동하는데 드는 비용(경로에따라 달라짐) // H : 목적지에서 얼마나 가까운지 (고정) // (y,x) 이미 방문했는지 여부 (방문 = closed 상태) bool[,] closed = new bool[_board.Size, _board.Size]; // (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; // 초기값을 MaxValue로 초기화 } } // 부모 노드 기록할 배열 Position[,] parent = new Position[_board.Size, _board.Size]; // 오픈리스트에 있는 정보들 중에서 가장 좋은 후보를 빠르게 뽑아오기 위한 도구 PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); // 시작점 발견(예약 진행) // F = G+H = 시작점이므로 0 + 시작점에서 목적지까지 거리(절대값 (y2,x2) - (y1,x1)) * 비용 10 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 Position(PosY, PosX); while (pq.Count > 0) { // 제일 좋은 후보를 찾는다(Priority Queue사용) PQNode node = pq.Pop(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른경로로 인해 이미 방문(closed)된 경우 skip 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 (nextY < 0 || nextY >= _board.Size || nextX < 0 || nextX >= _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 Position(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]; // 오픈리스트에 있는 정보들 중에서, 가장 좋은 후보를 빠르게 뽑아오기 위한 도구 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(); // 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서 이미 방문(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); }
private void AStar() { // up, left, down, right + UL, DL, DR, UR 4방향 추가 int[] deltaY = new int[] { -1, 0, 1, 0, -1, 1, 1, -1 }; int[] deltaX = new int[] { 0, -1, 0, 1, -1, -1, 1, 1 }; // 상하좌우 1 -> 10 , 대각선 1.4 -> 14 int[] cost = new int[] { 10, 10, 10, 10, 14, 14, 14, 14 }; // 점수 매기기 // F = G + H // F = 최종 점수 (작을 수록 좋음, 경로에 따라 달라짐) // G = 시작점에서 해당 좌표까지 이동하는데 드는 비용 (작을수록 좋다, 경로에 따라 달라짐) // H = 목적지에서 얼마나 가까운지 (작을수록 좋다, 고정값, 가산점) -> 특정 좌표로부터 목적지까지의 거리는 고정임 // (y, x) 이미 방문했는지 여부를 기록 ( 방문 = closed 상태, 이전 visited, found 상태와 동일) bool[,] closed = new bool[_board.Size, _board.Size]; // ClosedList // open 배열에 들어간 최종점수의 의미는? // 1. open 배열에는 어떤 경로에서 배열의 인덱스를 y,x 좌표라 하고 // 2. 이 좌표에서 목적지까지 가는 최종점수(F)를 저장해 놓은 배열이다. // 3. 내가 진행중인 경로 기준으로 계산한 값보다 더 적은 평가점수가 이미 들어가 있다면 // 4. 이전 경로 기준으로 해당 좌표를 접근하는게 더 빠르기 때문에 현재 진행중인 경로에서는 방문 후보 리스트(pq)에 넣지 않는다. 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; } } // (y, x) 가는 길을 한번이라도 발견했는가? // 발견 X => Int.MaxValue // 발견 O => F = G + H Pos[,] parent = new Pos[_board.Size, _board.Size]; // 가장 좋은 값 하나만 뽑는대 최적화된 자료구조임 // 오픈리스트에 있는 정보들 중에서, 가장 좋은 후보를 빠르게 뽑아오기 위한 도구 PriorityQueue <PQNode> pq = new PriorityQueue <PQNode>(); // 시작점 발견 (에약 진행) // 시작점의 G값은 0 (자신이 시작점임) 이므로, F = H 이다. // H값은 맨해튼 거리 공식을 이용한다. // open 배열은 해당 인덱스의 좌표의 F값을 들고있다. // F값을 구할때 왜 10을 곱하냐면 cost(한 칸 이동의 비용)이 1에서 10으로 바뀌었기 때문에 // H값(시작점부터 목적지 까지 가는 거리)에 새롭게 바뀐 이동비용 10을 곱해야 G값과 형평성이 맞다. // G값의 경우 구하는 과정에서 cost를 적용하기 때문에 따로 10을 곱할 필요가 없다. 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) { // 제일 좋은 후보를 찾는다. // 루프돌면서 주변 좌표의 모든 F점수를 비교하는것은 너무 오래걸림 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; } // node.Y, node.X 좌표에 방문완료 // 이거 기준으로 (up, left, down, right + UL, DL, DR, UR 4방향) 총 8방향 확인 // 이동 가능한지 체크하고 우선순위 큐에 추가 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; } // 비용 계산 // 시작점부터 node 위치까지 가는 비용 G + 이후 진행방향별 보정치 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; } // 여기까지 왔으면 nextY, nextX 좌표를 거치면서 목적지로 가는 경로중에 가장 최적의 경로임 open[nextY, nextX] = g + h; // 평가값 갱신 // 경로 후보에 넣는다. pq.Push(new PQNode() { F = g + h, G = g, Y = nextY, X = nextX }); // 부모(직전경로)를 등록해서 쭉 따라가면 최적경로 이동 되는거임 // nextY, nextX 좌표로 가장 빠르게 이동하기 위한 직전 좌표는 node.Y, node.X parent[nextY, nextX] = new Pos(node.Y, node.X); } } // 부모 좌표를 타고 올라가면서 경로를 만든다. CalcPathFromParent(parent); }