대학생활/수업

게임인공지능 7주차 - Shortest Path, Dijkstra, A* Algorithm

se.jeon 2023. 10. 17. 13:08
728x90
반응형

Edge Relax (에지완화)

알고리즘이 진행되는 동안 시작 노드에서 목적 노드까지 Best Path Found So Far (BFSP)에 대한 정보 수집 및 갱신

Dijkstra's Algorithm

비용기반 방향 그래프 G = (V, E)에서 시작 노드에서 다른 모든 노드까지의 최단 경로를 구할 수 있다.
- SPT(Shortest Path Tree) 관리 : 시작 노드 s로부터의 최종 최단경로 가중치가 이미 결정된 정점들을
관리한다.
- 우선순위 큐(Priority Queue)를 이용한다.
 : 최단 경로 추정 가중치가 가장 작은 정점 u ∈ V – S를 선택
 : u 를 S에 추가
 : u 와 연결된 모든 간선을 완화

https://youtu.be/dhvf9KCAsVg?si=de46BllxT84Xa6pP 

실습 진행

#include "Dijkstra.h"


void Dijkstra::extractMin(point& choicePos)
{
	int min = INT_MAX;
	int width = m_gameMap->getWidth();
	int height = m_gameMap->getHeight();

	int curX, curY;
	list<point>::reverse_iterator curPos;

	for (curPos = m_visitNode.rbegin(); curPos != m_visitNode.rend(); curPos++)
	{
		// 현재 노드를 기준으로 8방향을 스캐닝
		for (int ty = -1; ty <= 1; ty++)
		{
			for (int tx = -1; tx <= 1; tx++)
			{
				curX = curPos->x + tx;
				curY = curPos->y + ty;

				if (curX < 0 || curX > width - 1 || curY < 0 || curY > height - 1 || (tx == 0 && ty == 0))
				{
					continue;
				}

				if (m_gameMap->getMapVal(curX, curY) < min && m_gameMap->getVisitInfo(curX, curY) == false)
				{
					min = m_gameMap->getMapVal(curX, curY);
					choicePos = { curX, curY };
				}
			}
		}
	}
}

bool Dijkstra::findPath(int sx, int sy, int dx, int dy)
{
	m_bFound = false;
	int width = m_gameMap->getWidth();
	int height = m_gameMap->getHeight();

	point** parent;
	parent = new point * [height];

	for (int i = 0; i < height; i++)
	{
		parent[i] = new point[width];
	}

	point choicePos;
	m_gameMap->setMapVal(sx, sy, 0); // 시작 위치의 비용을 0으로 설정
	choicePos = { sx, sy };
	parent[sy][sx] = choicePos;

	for (int i = 0; i < width * height; i++)
	{
		m_gameMap->setVisitInfo(choicePos.x, choicePos.y, true);
		m_visitNode.push_back(choicePos);

		// 목표 노드를 만나면 종료
		if (choicePos.x == dx && choicePos.y == dy)
		{
			m_bFound = true;
			break;
		}

		// 에지완화 알고리즘 적용
		for (int ty = -1; ty <= 1; ty++)
		{
			for (int tx = -1; tx <= 1; tx++)
			{
				int nx = choicePos.x + tx;
				int ny = choicePos.y + ty;
				int dist;

				if (nx < 0 || nx > width - 1 || ny < 0 || ny > height - 1)
				{
					continue;
				}

				if (m_gameMap->getVisitInfo(nx, ny) == false)
				{
					dist = (tx == 0 || ty == 0) ? 10 : 14;
					int tcost = m_gameMap->getMapVal(choicePos.x, choicePos.y) + dist;

					// 에지완화 처리
					if (m_gameMap->getMapVal(nx, ny) > tcost)
					{
						m_gameMap->setMapVal(nx, ny, tcost);
						parent[ny][nx] = choicePos;
					}
				}
			}
		}
		extractMin(choicePos);
		draw();
	}

	if (m_bFound)
	{
		point p;
		p = { dx, dy };

		m_path.push(p);

		while (p.x != sx || p.y != sy) 
		{
			p = parent[p.y][p.x];
			m_path.push(p);
		}
		return true;
	}

	return false;
}

void Dijkstra::draw()
{
	m_gameMap->draw();

	if (m_bFound) 
	{
		point p;

		do 
		{
			p = m_path.top();
			cout << "(" << p.x << "," << p.y << ") ==> ";
			m_path.pop();
		} while (!m_path.empty());
		cout << "최단 경로를 찾았습니다..." << endl;

	}
}

Shortest Path - A* Algorithm

배경 화면 상의 두 지점이 존재할 때, 시작점에서 목표점을 연결하는 가장 효율적인 경로를 찾는 목표 지향성 알고리즘.

현재의 위치에서 이동 가능한 노드로 간 후 목표점까지의 비용으로 길을 결정한다.

둘은 큰 줄기가 같다. dijkstra 알고리즘에서 extractMin만 다른 형태.

 

void AStar::extractMin(point& choicePos, int dx, int dy)
{
	int min = INT_MAX;
	int width = m_gameMap->getWidth();
	int height = m_gameMap->getHeight();

	int curX, curY;
	list<point>::reverse_iterator curPos;

	for (curPos = m_visitNode.rbegin(); curPos != m_visitNode.rend(); curPos++)
	{
		// 현재 노드를 기준으로 8방향을 스캐닝
		for (int ty = -1; ty <= 1; ty++)
		{
			for (int tx = -1; tx <= 1; tx++)
			{
				curX = curPos->x + tx;
				curY = curPos->y + ty;

				if (curX < 0 || curX > width - 1 || curY < 0 || curY > height - 1 || (tx == 0 && ty == 0))
				{
					continue;
				}
				
				// 현재 노드에서 목표노드까지의 추정 거리 계산
				int hx = abs(dx - curX) * 10;				
				int hy = abs(dy - curY) * 10;
				int hdist = hx + hy;
				//hdist = sqrt((dx - curX) * (dx = curX) + (dy - curY) * (dy - curY));

				if (m_gameMap->getMapVal(curX, curY) + hdist < min && m_gameMap->getVisitInfo(curX, curY) == false)
				{
					min = m_gameMap->getMapVal(curX, curY) + hdist;
					choicePos = { curX, curY };
				}
			}
		}
	}
}

 

중간평가 과제

FSM과 A* 알고리즘을 적용하여 팩맨게임을 구현한다.

 

다음의 조건을 만족해야 한다.

- 고스트가 팩맨에게 먹히면 리스폰된다.

- 리스폰된 고스트는 In-Box에서 다시 시작한다.

- 고스트가 Hunter 상태일 때 팩맨이 근처에 있으면 팩맨을 쫒아간다

- 팩맨을 쫒아갈 때에는 최단경로찾기 알고리즘을 적용한다. (A* algorithm)

- 고스트가 Hunter 상태일 때 팩맨이 근처에 없는 경우 배회한다.

- 팩맨이 파워 쿠키를 먹으면 고스트는 Hunted 상태로 바뀌고 팩맨으로부터 도망간다.

- Hunted 상태의 고스트는 일정 시간이 지나면 Hunter 상태로 바뀐다.

- 고스트가 Hunted 상태일 때 팩맨에게 먹히면 리스폰 장소로 되돌아간다.

- 고스트의 FSM은 state pattern으로 구현한다.

 

11월 05일 일요일까지

Source code 1set, 보고서 (pdf)를 lms 과제란에 업로드한다.

- no copy

- no delay

- no engine

- 미완성인 경우에도 미완성 내용을 보고서에 포함하여 code와 함께 제출

- 부분 점수 있음

728x90
반응형