This code is my solution to this reddit challenge.
This is the code:
Vector2.h
#pragma once
struct Vector2
{
int x, y;
Vector2(int, int);
Vector2();
Vector2 operator +(const Vector2&) const;
};
Vector2.cpp
#include "Vector2.h"
Vector2::Vector2(int _x, int _y) : x(_x), y(_y)
{ }
Vector2::Vector2()
{ }
Vector2 Vector2::operator+(const Vector2& other) const
{
Vector2 temp;
temp.x = this->x + other.x;
temp.y = this->y + other.y;
return temp;
}
map.h
#pragma once
#include "vector2.h"
#include <vector>
#include <iostream>
#include <random>
#include <algorithm>
class Map
{
Vector2 startPos, endPos;
std::vector<char> data;
std::vector<char> datad;
std::vector<Vector2> directions;
int size;
void fillDangerMap();
Vector2 clamp(int min, int max, Vector2 position) const;
public:
Map(int size, Vector2 startPosition, Vector2 endPosition);
Map();
void setSize(int size);
void fill(char, char, char, char, char);
void setElement(Vector2 position, char element);
void setDangerElement(Vector2 position, char);
void display() const;
char getElement(Vector2 position) const;
char getDangerElement(Vector2 position) const;
int getSize() const;
};
std::mt19937& getRandomEngine();
map.cpp
#include "map.h"
Map::Map(int _size, Vector2 _startPos, Vector2 _endPos) : size(_size), startPos(_startPos), endPos(_endPos)
{
data.resize(size * size);
datad.resize(size * size);
directions.resize(8);
directions[0] = Vector2(-1, 1);
directions[1] = Vector2(-1, 0);
directions[2] = Vector2(-1, -1);
directions[3] = Vector2(0, 1);
directions[4] = Vector2(0, -1);
directions[5] = Vector2(1, 1);
directions[6] = Vector2(1, 0);
directions[7] = Vector2(1, -1);
}
Map::Map()
{ }
Vector2 Map::clamp(int min, int max, Vector2 position) const
{
if (position.y < 0) position.y = 0;
if (position.x < 0) position.x = 0;
if (position.y > size) position.y = size;
if (position.x > size) position.x = size;
return position;
}
void Map::fill(char fillStartWith, char fillEndWith, char fillGravWheelWith, char fillAsteroidWith, char fillElseWith)
{
auto a = (size * size) * 0.1 / 3;
auto b = (size * size) * 0.3 / 3;
for(int i = 0; i < size * size; ++i){
if(i < a) data[i] = fillGravWheelWith;
else if(i < b) data[i] = fillAsteroidWith;
else data[i] = fillElseWith;
}
std::shuffle(data.begin(), data.end(), getRandomEngine());
setElement(startPos, fillStartWith);
setElement(endPos, fillEndWith);
fillDangerMap();
}
void Map::display() const
{
for(int i = 1; i <= size * size; ++i)
{
std::cout << data[i - 1] << " ";
if (!(i % size))
std::cout << "\n";
}
}
void Map::setSize(int _size)
{
size = _size;
data.resize(size * size);
}
char Map::getElement(Vector2 position) const
{
position = clamp(0, size, position);
position.y *= size;
return data[position.x + position.y];
}
char Map::getDangerElement(Vector2 position) const
{
position = clamp(0, size, position);
position.y *= size;
return datad[position.x + position.y];
}
void Map::fillDangerMap()
{
for (int i = 0; i < size * size; ++i) datad[i] = '.';
for(int y = 0; y < size; ++y){
for(int x = 0; x < size; ++x){
Vector2 current(x,y);
if (getElement(current) == 'E') setDangerElement(current, 'E');
else if (getElement(current) == 'S') setDangerElement(current, 'S');
else if (getElement(current) == 'A') setDangerElement(current, 'X');
for (const auto& direction : directions){
auto current2 = current + direction;
if (current2.x < 0 || current2.x > size - 1 || current2.y < 0 || current2.y > size - 1)
continue;
if (getElement(current) == 'G'){
setDangerElement(current, 'X');
setDangerElement(current2, 'X');
}
}
}
}
}
void Map::setElement(Vector2 position, char elem)
{
position = clamp(0, size, position);
position.y *= size;
data[position.x + position. y] = elem;
}
void Map::setDangerElement(Vector2 position, char elem)
{
position = clamp(0, size, position);
position.y *= size;
datad[position.x + position.y] = elem;
}
int Map::getSize() const
{
return size;
}
std::mt19937& getRandomEngine()
{
static std::mt19937 randomEngine(std::random_device{}());
return randomEngine;
}
Node.h
#pragma once
#include "Vector2.h"
#include <cmath>
struct Node
{
Vector2 position;
int G, H, F;
Node* parent = nullptr;
Node();
Node(const Node& other) = default;
Node(Vector2 pos);
void calc(const Vector2&);
bool operator==(const Node&) const;
bool operator!=(const Node&) const;
bool operator<(const Node&) const;
};
Node.cpp
#include "node.h"
Node::Node()
{ }
Node::Node(Vector2 pos) : position(pos)
{ }
void Node::calc(const Vector2& endPos)
{
H = static_cast<int>((abs(static_cast<double>(position.x - endPos.x)) + abs(static_cast<double>(position.y - endPos.y))));
G = parent ? parent->G + 1 : 1;
F = G + H;
}
bool Node::operator==(const Node& other) const
{
return (position.x == other.position.x && position.y == other.position.y);
}
bool Node::operator!=(const Node& other) const
{
return !(*this == other);
}
bool Node::operator<(const Node& other) const
{
return(F < other.F);
}
solver.h
#pragma once
#include "node.h"
#include "map.h"
#include <ctime> //clock_t
#include <list>
class Solver
{
Vector2 startPos, endPos;
Map map;
std::vector<Vector2> directions;
//SETTINGS
bool eachStep;
bool nonDiagonal;
void sleep(unsigned int mseconds);
public:
Solver(const int& size, const Vector2& _startPos, const Vector2& _endPos, bool _eachStep, bool _diagonal);
void displayMap() const;
bool aStar();
};
solver.cpp (Here is the algorithm)
#include "solver.h"
Solver::Solver(const int& size, const Vector2& _startPos, const Vector2& _endPos, bool _eachStep, bool _diagonal)
: startPos(_startPos), endPos(_endPos), eachStep(_eachStep), nonDiagonal(_diagonal)
{
Map temp(size, startPos, endPos);
map = temp;
map.fill('S', 'E', 'G', 'A', '.');
if (nonDiagonal){
directions.resize(4);
directions[0] = Vector2(-1, 0);
directions[1] = Vector2(0, 1);
directions[2] = Vector2(0, -1);
directions[3] = Vector2(1, 0);
}
else{
directions.resize(8);
directions[0] = Vector2(-1, 1);
directions[1] = Vector2(-1, 0);
directions[2] = Vector2(-1, -1);
directions[3] = Vector2(0, 1);
directions[4] = Vector2(0, -1);
directions[5] = Vector2(1, 1);
directions[6] = Vector2(1, 0);
directions[7] = Vector2(1, -1);
}
}
void Solver::sleep(unsigned int mseconds)
{
clock_t goal = mseconds + clock();
while (goal > clock());
}
void Solver::displayMap() const
{
map.display();
}
bool Solver::aStar()
{
Node startNode(startPos);
Node goalNode(Vector2(endPos.x - 1, endPos.y - 1));
if ((map.getDangerElement(startNode.position) == 'X') || map.getDangerElement(goalNode.position) == 'X'){
std::cout << "Either the start of this map is obstructed or so is the end.";
return false;
}
std::list<Node> openList;
std::list<Node> closedList;
startNode.calc(endPos);
openList.push_back(startNode);
while (!openList.empty()){
auto current = Node(*std::min_element(openList.begin(), openList.end()));
current.calc(endPos);
closedList.push_back(current);
openList.remove(current);
for (const auto& direction : directions){
Node successor(direction + current.position);
if (map.getDangerElement(successor.position) == 'X' ||
successor.position.x >= map.getSize() - 1 || successor.position.y >= map.getSize() - 1 ||
successor.position.x < 0 || successor.position.y < 0 ||
std::find(closedList.begin(), closedList.end(), successor) != closedList.end()){
continue;
}
successor.calc(endPos);
auto inOpen = std::find(openList.begin(), openList.end(), successor);
if (inOpen == openList.end()){
auto curr = &closedList.back();
successor.parent = curr;
successor.calc(endPos);
openList.push_back(successor);
}
else{
if (successor.G < inOpen->G){
auto curr = &closedList.back();
successor.parent = curr;
}
}
}
if (eachStep){
for (const auto& display : openList)
map.setElement(display.position, 'O');
for (const auto& display : closedList)
map.setElement(display.position, 'P');
map.display();
std::cout << "\n\n";
sleep(150);
}
if (current == goalNode) break;
}
if (!openList.size()){
std::cout << "There's no solution to this map.";
return false;
}
auto inClosed = std::find(closedList.begin(), closedList.end(), goalNode);
if (inClosed != closedList.end()){
while (*inClosed != startNode){
map.setElement(inClosed->position, 'Y');
map.setDangerElement(inClosed->position, 'Y');
*inClosed = *inClosed->parent;
}
}
return true;
}
Source.cpp
#include "solver.h"
int main()
{
const int SIZE = 21;
const Vector2 startPos(0, 0);
const Vector2 endPos(SIZE - 1, SIZE - 1);
char ans;
bool eachStep;
bool nonDiagonal;
std::cout << "Do you want to see each step? (y/n)\n";
std::cin >> ans;
eachStep = (ans == 'y');
std::cout << "Do you allow diagonal movement? (y/n)\n";
std::cin >> ans;
nonDiagonal = (ans == 'n');
Solver solve(SIZE, startPos, endPos, eachStep, nonDiagonal);
solve.aStar();
std::cout << "\n\n";
solve.displayMap();
std::cin.ignore(2);
return 0;
}