Simple A* algorithm using C++ 11

///=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/// MIT License
///
/// Copyright (c) 2020 Mikko Romppainen
/// 
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
/// 
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
/// 
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include <algorithm>
#include <map>
#include <vector>

int main() {
	// Typedef for position
	typedef std::pair<int, int> Position;

	// Game level
	static const int LEVEL_WIDTH = 13;
	static const int LEVEL_HEIGHT = 9;
	int level[LEVEL_HEIGHT][LEVEL_WIDTH] = {
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1},
		{1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1},
		{1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1},
		{1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1},
		{1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
	};/*
	int level[LEVEL_HEIGHT][LEVEL_WIDTH] = {
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
		{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
	};*/

	// Start and end positions
	Position start(1, 1);
	Position end(LEVEL_WIDTH - 2, LEVEL_HEIGHT - 2);

	// Lambda for printing level
	auto printLevel = [&level]() {
		for (size_t y = 0; y < LEVEL_HEIGHT; ++y) {
			for (size_t x = 0; x < LEVEL_WIDTH; ++x) {
				printf("%d", level[y][x]);
			}
			printf("\n");
		}
	};

	// Lambda for checking is node walkable
	auto isWalkable = [&level](const Position& pos) -> bool {
		if (pos.first < 0 || pos.second < 0 || pos.first >= LEVEL_WIDTH || pos.second >= LEVEL_HEIGHT) return false;
		return !level[pos.second][pos.first];
	};

	// Lambda for getting adjacent nodes
	auto getAdjacentPositions = [&level, isWalkable](const Position& pos) -> std::vector<Position> {
		std::vector<Position> canditates;
		canditates.push_back(Position(pos.first - 1, pos.second));
		canditates.push_back(Position(pos.first + 1, pos.second));
		canditates.push_back(Position(pos.first, pos.second - 1));
		canditates.push_back(Position(pos.first, pos.second + 1));
		std::vector<Position> res;
		for (Position p : canditates) {
			if (isWalkable(p)) res.push_back(p);
		}
		return res;
	};

	printf("Search level:\n");
	printLevel();

	// Struct for search node
	struct Node {
		Node(Position pos, Node* prev) : position(pos), prevNode(prev) {
		}

		Position position;
		Node* prevNode;

		float getG() const {
			// Just compute previous nodes and return cost to travel from start to this node
			float res = 0.0f;
			const Node* scan = this;
			while (scan->prevNode) {
				res += 1.0f;
				scan = scan->prevNode;
			}
			return res;
		}

		float getH(const Position& end) const {
			// For heurestic, use euclidean length of the vector from this node to end position.
			float dx = float(end.first - position.first);
			float dy = float(end.second - position.second);
			return sqrtf(dx*dx + dy * dy);
		}

		float getF(const Position& end) const {
			// F = G + H
			return getG() + getH(end);
		}
	};

	// Open list
	std::vector<Node*> openList;
	std::map<Position, Node*> closedList;

	// A*
	openList.push_back(new Node(start, 0)); // Add start node to open list
	Node* result = 0; // Save possible A* result here
	while (openList.size() > 0) {
		Node* N = openList[0]; // Pop lowest f-cost from open list
		openList.erase(openList.begin(), openList.begin() + 1); // Remove it from open list
		closedList[N->position] = N; // Add it to the closed list

		// Has reached destination?
		if (N->position.first == end.first && N->position.second == end.second) {
			result = N;
			break; // while(openList.size() > 0)
		}

		std::vector<Position> adjacentPositions = getAdjacentPositions(N->position); // Get neighbour nodes, where we can walk

		for (Position pos : adjacentPositions) {
			// If already at the closed list, ignore it
			if (closedList.find(pos) != closedList.end()) {
				continue;
			}

			// If already at open list?
			auto it = std::find_if(openList.begin(), openList.end(), [pos](const Node* n) -> bool {
				return n->position == pos;
			});
			
			Node* N_ = new Node(pos, N); // We came to N_-node from N-node.

			if (it != openList.end()) {
			    // Found from open list, if new F is smaller than prev route to this position, reset prev node from this route
				if ((*it)->getG() > N_->getG()) {
					(*it)->prevNode = N;
				}
				// Delete N_, because we do not need it anymore
				delete N_;
			} else {
				openList.push_back(N_); // Add new node to the open list
			}
		}
		// Sort open list by ascending order by F-cost.
		std::sort(openList.begin(), openList.end(), [&end](const Node* lhs, const Node* rhs) -> bool {
			return lhs->getF(end) < rhs->getF(end);
		});
	}

	if (result != 0) {
		printf("\nPath found:\n");
		while (result != 0) {
			level[result->position.second][result->position.first] = 2; // Mark path to the level
			result = result->prevNode;
		}
		printLevel();
	}
	else {
		printf("\nPath not found!\n");
	}

    // Remember to cleanup. i.e. call delete to each node in open and closed lists.
	for (auto o : openList) {
		delete o;
	}

	for (auto o : closedList) {
		delete o.second;
	}

	printf("\nPress ENTER to continue...\n");
	getchar();
	return 0;
}