BFS, 다익스트라는 시작점은 있지만 목적지를 향해서 가는 것은 아니다.
break 로 어느 지점에서 멈추라고는 했지만 목적지를 정해놓는 알고리즘은 아니었다.
A* 는 시작점과 더불어 도착점까지 완벽하게 설정하고 길찾기를 시작한다.
사실 이 차이점 외에는 다익스트라와 큰 차이가 없다.
A* 는 출발점에서 내가 가고 있는 길이 얼마나 떨어져있는지(비용) 뿐 만 아니라 도착점과 얼마나 가까워지고 있는지도 판단할 것 이다.
2가지 고려함
최적의 경로를 위해 비용을 계산하는 것은 다익스트라와 비슷하지만 비용을 2가지 방식으로 계산한다는 것이다.
struct PQNode
{
// 우선순위 큐에다가 정보를 넣어서 비교를 할 때에는 f 를 기준으로 비교할것
bool operator<(const PQNode& other) const { return f < other.f; }
bool operator>(const PQNode& other) const { return f > other.f; }
int32 f;
int32 g;
Pos pos;
};
void Player::AStar()
{
// 점수 매기기
// F = G + H
// F = 최종 점수 (작을 수록 좋은걸로 만들거임, 경로에 따라 달라짐)
// G = 시작점에서 해당 좌표까지 이동하는데 드는 비용(작을 수록 좋음. 경로에 따라 다름)
// H = 목적지에서 얼마나 가까운지 ( 작을 수록 좋음. 고정)
Pos start = _pos;
// 목적지 도착하기 전에는 계속 실행
Pos dest = _board->GetExitPos();
enum
{
// 요게 4이면 4방향, 8이면 8방향
DIR_COUNT = 8
};
Pos front[] =
{
Pos {-1,0}, // UP
Pos {0,-1}, // LEFT
Pos {1,0}, // DOWN
Pos {0,1}, // RIGHT
Pos {-1,-1}, // UP_LEFT
Pos { 1, -1}, // DOWN_LEFT
Pos {1, 1}, // DOWN_RIGHT
Pos { -1,1} // UP_RIGHT
};
int32 cost[]
{
10, // UP
10, // LEFT
10, // DOWN
10, // RIGHT
14, // UP_LEFT
14, // DOWN_LEFT
14,
14
};
const int32 size = _board->GetSize();
// 1) 예약 (발견) 시스템 구현
// 정점을 방문할 때 마다 인접한 정점을 발견해서 그 정점들에 대한
// 비용을 계산해서 예약해줄것. 예약 되면 나중에 루프 돌면서
// 예약 중에 제일 좋은 걸로 갈거임.
//
// 2) 근데 뒤늦게 더 좋은 후보가 발견될수있어서 예외처리 꼭 해야함
// 좌표가 어떤 비용을 가지는지 알아야한다.
//CloseList
// close[y][x] 에 방문했는지 여부
// 방문한 친구들 여기서 하ㅘㄱ인 가능
vector<vector<bool>> closed(size, vector<bool>(size, false));
// best[y][x] -> 지금까지 {y,x} 에 대한 가장 좋은 비용 ( 작은 거 )
// f
vector<vector<int32>> best(size, vector<int32>(size, INT32_MAX));
// 부모 추적 용도
map<Pos, Pos> parent;
// 예약 시스템 구현 OpenList 에 해당함 예약된 애들 관리하는거
// 방문은 하지 않았고 발견만한 친구들
priority_queue<PQNode, vector<PQNode>, greater<PQNode>> pq;
// 초기값
{
int32 g = 0;
// 이 공식은 효율적으로 연산량 적게해서 임의로 계산식 만든거고
// 피타고라스 해도 됨..
int32 h = 10 * (abs(dest.y - start.y) + abs(dest.x - start.x));
pq.push(PQNode{ g + h, g, start });
best[start.y][start.x] = g + h;
parent[start] = start;
// closed 이거 true 안한다. 아직 방문 아니다. top 하면 방문이다.
}
while (pq.empty() == false)
{
// 제일 좋은 후보를 찾기
// f 값이 제일 작은 node
// 방문 시작 단계 여기서부터 방문하는 걸 뜯함
PQNode node = pq.top();
pq.pop();
// 동일한 좌표를 여러 경로로 찾아서, 더 빠른 경로로 인해서
// 이미 방문된 경우 스킵
// 선택 CloseList
if (closed[node.pos.y][node.pos.x])
continue;
// 선택 best
if (best[node.pos.y][node.pos.x] < node.f)
continue;
// 방문
// 방문 후에 더 우수한 경로를 찾는것은 불가능한가?
// 수학적으로 이미 증명되었다. 방문 이후에 더 우수한 경로를 찾는것은
// 불가능하다. A* 에서는
closed[node.pos.y][node.pos.x] = true;
// 목적지에 도착했으면 바로 종료
if (node.pos == dest)
break;
for (int32 dir = 0; dir < DIR_COUNT; dir++)
{
// 갈 방향 정하기
Pos nextPos = node.pos + front[dir];
// 막혔으면 continue;
if (CanGo(nextPos) == false)
continue;
// [선택] 이미 방문한 곳 스킵
// if (closed[node.pos.y][node.pos.x])
// continue;
// 이거 있어서 쓸지 말지 정해도 됨 어짜피 위에서 continue 로 걸러짐
if (closed[nextPos.y][nextPos.x])
continue;
// 비용 계산
// 이전 좌표 node.g 에서 현재 좌표 cost[dir]로 가는 비용
//
int32 g = node.g + cost[dir];
int32 h = 10 * (abs(dest.y - nextPos.y) + abs(dest.x - nextPos.x));
// 다른 경로에서 더 빠른 길을 찾았으면 스킵
// 방문은 안했지만 예약은 되었있다면
if (best[nextPos.y][nextPos.x] <= g + h)
continue;
// 예약 진행
best[nextPos.y][nextPos.x] = g + h;
pq.push(PQNode{ g + h,g,nextPos });
parent[nextPos] = node.pos;
}
int a = 3;
}
// 거꾸로 넣어준다.
Pos pos = dest;
_path.clear();
_pathIndex = 0;
while (true)
{
_path.push_back(pos);
// 시작점은 자신이 곧 부모이다.
if (parent[pos] == pos)
break;
pos = parent[pos];
}
std::reverse(_path.begin(), _path.end());
}
'자료구조 알고리즘' 카테고리의 다른 글
버블 정렬 (0) | 2024.05.30 |
---|---|
이진 탐색 (0) | 2024.05.30 |
길찾기3 다익스트라 길찾기 (0) | 2024.05.29 |
길찾기2 BFS 를 이용하기 (0) | 2024.05.29 |
길찾기 1 우수법 (0) | 2024.05.29 |