Study

Astar C++로 만들기

wnwnovo 2024. 10. 10. 08:47
// Astar.cpp : 이 파일에는 'main' 함수가 포함됩니다. 거기서 프로그램 실행이 시작되고 종료됩니다.
//

#include <iostream>
#include "olcConsoleGameEngine.h"
#include <list>
#include <vector>
#include<iomanip>
#include <map>
#include <cstdio>
#include <cstring>
#include <iterator>
#include<random>
#include <algorithm>
#include<queue>
#define HASH_TABLE_COUNT 11
#define DIR_COUNT 8
using namespace std;

enum NodeDirection : UINT8
{
	N,
	E,
	S,
	W,
	NE,
	NW,
	SE,
	SW
};


struct Vector2
{
	int x;
	int y;

	Vector2(int _x, int _y) : x(_x), y(_y) {}
};

Vector2 nodeDirOffset[] = { Vector2(0,1),Vector2(1,0) ,Vector2(0,-1) ,Vector2(-1,0) ,Vector2(1,1) ,Vector2(-1,1) ,Vector2(1,-1) ,Vector2(-1,-1) };

class Node
{
public:
	//X1, Y1, X2, Y2
	int drawLocation[4] = { 0, 0, 0, 0 };
	int x, y;
	int movedDirection;
	int ID;
	Node* parent;
	Node* neighborNodes[8];
	float costFromStart;
	float costToGoal;
	float totalCost;
	bool isInOpenList;
	bool isInCloseList;
	bool isWall;
	Node()
	{
		ZeroMemory(neighborNodes, sizeof(neighborNodes));
		parent = NULL;
		costFromStart = 0, costToGoal = 0, x = 0, y = 0, ID = 0, totalCost = 0, movedDirection = 0;
		isInOpenList = false;
		isInCloseList = false;
		isWall = false;
	}

	void SetDrawLocation(int x1, int y1, int x2, int y2)
	{
		drawLocation[0] = x1; drawLocation[1] = y1; drawLocation[2] = x2; drawLocation[3] = y2;
	}

	void SetLocation(int X, int Y)
	{
		x = X;
		y = Y;
	}
};

class HashTable
{
public:
	struct stData
	{
		int ID;
		Node* value;
		stData* next;
		stData()
		{
			ID = 0;
			value = 0;
			next = NULL;
		}
	};
	stData* hashList[HASH_TABLE_COUNT];
	int GetHashKey(int ID)
	{
		return ID % HASH_TABLE_COUNT;
	}

	void AddValue(int ID, Node* value)
	{
		stData* data = new stData;
		data->ID = ID;
		data->value = value;
		int index = GetHashKey(ID);
		stData* listData = hashList[index];
		if (hashList[index] != NULL)
		{
			data->next = listData;
			hashList[index] = data;
		}
		else
		{
			hashList[index] = data;
		}
	}

	void RemoveValue(int ID)
	{
		int index = GetHashKey(ID);
		stData* data = hashList[index];
		stData* tempData = data;
		while (data)
		{
			if (data->ID == ID)
			{
				if (hashList[index] == data)
				{
					hashList[index] = data->next;
				}
				else
				{
					tempData->next = data->next;
				}
				break;
			}
			tempData = data;
			data = data->next;
		}
	}

	void DisplayHash()
	{
		printf("\n");
		int i = 0;
		for (int i = 0; i < HASH_TABLE_COUNT; ++i)
		{
			stData* data = hashList[i];
			int size = 0;
			printf("값: ");
			while (data)
			{
				if (data->next != NULL)
				{
					//printf("%d, ", data->value);
				}
				else
				{
					//printf("%d      ", data->value);
				}
				data = data->next;
				++size;
			}
			//printf("\n[%d] 개수 : %d\n\n", i, size);
		}
		printf("\n");
	}
};

struct NodeCostCompare
{
	bool operator()(Node* nod1, Node* nod2)
	{
		if (nod1->totalCost == nod2->totalCost)
		{
			return nod1->costToGoal > nod2->costToGoal;
		}
		return nod1->totalCost > nod2->totalCost;
	}
};

class DrawAstar : public olcConsoleGameEngine
{
private:
	const static int mapWidth = 16;
	const static int mapHeight = 16;
	const static int wallCount = 12;
	int nodeBorder = 3;
	int nodeSizeWidth = 6;
	int nodeSizeHeight = 6;
	HashTable* hashTable;
	vector<Node*> nodeVector;
	Node* map[mapWidth][mapHeight];
	Node* startNode;
	Node* endNode;
	Node* wallNode[wallCount];
	//std::map<int[], NodeDirection> map;

	priority_queue<Node*, vector<Node*>, NodeCostCompare> openList;
	priority_queue<Node*> closeList;

public:
	DrawAstar()
	{
		hashTable = NULL;
		startNode = NULL;
		endNode = NULL;

	}

	Node* GetNodeByDir(NodeDirection dir, Node* node)
	{
		int nodeX = node->x + nodeDirOffset[dir].x;
		int nodeY = node->y + nodeDirOffset[dir].y;
		Node* neighborNode = NULL;

		nodeX = std::clamp(nodeX, 0, mapWidth - 1);
		nodeY = std::clamp(nodeY, 0, mapHeight - 1);

		neighborNode = map[nodeX][nodeY];
		if (neighborNode != node)
		{
			return neighborNode;
		}
		else
		{
			return NULL;
		}
	}

	NodeDirection GetDirByNode(Node* current, Node* goal)
	{
		NodeDirection dir;
		int X = 0;
		if (current->x != goal->x)
		{
			X = current->x > goal->x ? 1 : -1;
		}

		int Y = 0;
		if (current->y != goal->y)
		{
			Y = current->y > goal->y ? 1 : -1;
		}

		int offsetLen = sizeof(nodeDirOffset) / sizeof(Vector2);
			
		for (int i = 0; i < offsetLen; ++i)
		{
			if (nodeDirOffset[i].x == X && nodeDirOffset[i].y == Y)
			{
				return (NodeDirection)i;
			}
		}
	}


	int GetHeuristics(Node* current, Node* goal, Node* parentNode)
	{
		float width = current->x > goal->x ? current->x - goal->x : goal->x - current->x;
		float height = current->y > goal->y ? current->y - goal->y : goal->y - current->y;
		int H = sqrt((width * width) + (height * height)) * 10;
		return H;
	}


	virtual bool OnUserCreate()
	{
		hashTable = new HashTable;
		memset(hashTable->hashList, 0, sizeof(hashTable->hashList));
		int totalSize = mapWidth * mapHeight;
		nodeVector.reserve(totalSize);
		int id = 0;
		for (int i = 0; i < mapWidth; i++)
		{
			for (int k = 0; k < mapHeight; k++)
			{
				Node* node = new Node;
				int X1 = (i + 1) * nodeSizeWidth;
				int X2 = (i + 2) * nodeSizeWidth - nodeBorder;
				int Y1 = (k + 1) * nodeSizeHeight;
				int Y2 = (k + 2) * nodeSizeHeight - nodeBorder;
				node->SetDrawLocation(X1, Y1, X2, Y2);
				node->SetLocation(i, k);
				node->ID = i + k;
				nodeVector.insert(nodeVector.begin() + id, node);
				id++;
				map[i][k] = node;
			}
		}

		NodeDirection dirs[8] = { N, E, S, W, NE, NW, SE, SW };
		for (Node* node : nodeVector)
		{
			for (NodeDirection dir : dirs)
			{
				node->neighborNodes[dir] = GetNodeByDir(dir, node);
			}
		}

		srand(time(NULL));
		std::uniform_int_distribution<int>rand(0, totalSize-1);
		std::random_device device;
		int num1 = rand(device);
		int num2 = rand(device);
		while (num1 == num2)
		{
			num2 = rand(device);
		}



		startNode = nodeVector[num2];
		endNode = nodeVector[num1];
		num2 = rand(device);
		for (int i = 0; i < wallCount; ++i)
		{
			wallNode[i] = map[i][mapHeight/2];
			wallNode[i]->isWall = true;
		}
		//startNode = map[4][3];
		//endNode = map[6][13];
		NodeDirection dir;
		openList.push(startNode);

		Node* parentNode = startNode;

		while (!openList.empty())
		{
			Node* node = openList.top();
			openList.pop();
			if (node->parent != NULL)
			{
				if (node->parent != parentNode)
				{
					while (!openList.empty())
					{
						openList.pop();
					}

					while (!closeList.empty())
					{
						Node* popNode = closeList.top();
						popNode->isInCloseList = false;
						popNode->isInOpenList = true;
						openList.push(popNode);
						closeList.pop();
					}
					Node* n = node;
					while (n->parent)
					{
						closeList.push(n);
						n->isInCloseList = true;
						n->isInOpenList = false;
						n = n->parent;
					}
				}
			}
			node->isInOpenList = false;
			node->isInCloseList = true;
			closeList.push(node);
			if (node == endNode)
			{
				break;
			}
			else
			{
				int index = 0;
				parentNode = node;
				for (Node* neighborNode : node->neighborNodes)
				{
					index++;
					if (neighborNode == NULL)
					{
						continue;
					}

					if (neighborNode->isInCloseList || neighborNode->isWall)
					{
						continue;
					}
					//F = G(StartN->N(진짜)) + H(N->EndNode(추정))
					//Node부터 EndNode까지 추정
					neighborNode->parent = node;
					neighborNode->movedDirection = index;
					int H = GetHeuristics(neighborNode, endNode, node);

					int cost = 10;
					if (index > 3)
					{
						cost = 14;
					}
					neighborNode->costToGoal = H;
					Node* parent = node;
					int G = cost;
					G += parent->costFromStart;
					neighborNode->costFromStart = G;
					int F = G + H;
					neighborNode->totalCost = F;
					if (!neighborNode->isInOpenList)
					{
						neighborNode->isInOpenList = true;
						openList.push(neighborNode);
					}

				}
			}
		}
		return true;
	}

	virtual bool OnUserUpdate(float d)
	{

		Fill(0, 0, ScreenWidth(), ScreenHeight(), L' ');
		vector<Node*> ::iterator n = nodeVector.begin();
		while (n != nodeVector.end())
		{
			Node* node = *n;
			if (node == NULL)
			{
				continue;
			}
			short color = FG_WHITE;
			if (node == startNode)
			{
				color = FG_BLUE;
			}
			else if (node == endNode)
			{
				color = FG_RED;
			}
			if (color == FG_WHITE)
			{
				Fill(node->drawLocation[0], node->drawLocation[1], node->drawLocation[2], node->drawLocation[3], PIXEL_QUARTER, color);
			}
			++n;
		}

		for (int i = 0; i < wallCount; ++i)
		{
			Node* n = wallNode[i];
			if (n == NULL)
			{
				break;
			}
			Fill(n->drawLocation[0], n->drawLocation[1], n->drawLocation[2], n->drawLocation[3], PIXEL_SOLID, FG_CYAN);

		}

		priority_queue<Node*> queue = closeList;
		Node* node = queue.top();
		while (!queue.empty())
		{
			DrawLine(node->drawLocation[0], node->drawLocation[1], node->drawLocation[2], node->drawLocation[3], PIXEL_SOLID, FG_MAGENTA);
			queue.pop();
			if (!queue.empty())
			{
				node = queue.top();
			}
		}
		Fill(startNode->drawLocation[0], startNode->drawLocation[1], startNode->drawLocation[2], startNode->drawLocation[3], PIXEL_SOLID, FG_BLUE);
		Fill(endNode->drawLocation[0], endNode->drawLocation[1], endNode->drawLocation[2], endNode->drawLocation[3], PIXEL_SOLID, FG_RED);

		return true;
	}
};

int main()
{
	DrawAstar* draw = new DrawAstar;
	draw->ConstructConsole(160, 100, 8, 8);
	draw->Start();
}

'Study' 카테고리의 다른 글

하노이의 탑 C++로 만들기  (0) 2024.10.10
MVC, MVP, MVVM  (0) 2024.09.30