예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }