#include <iostream>
#define MAX_CELL 64
#include <cmath>
#include <vector>
using namespace std;
struct Cell {
int m_Row;
int m_Col;
};
class CellTracker
{
//Establish String to hold all coords, separated by |~|
public: std::vector<Cell> seenCells;
public:
bool seenCell(Cell tCell, bool storeCell)
{
bool isFound = false;
//int length = 0;
for(unsigned int i = 0; i < seenCells.size();i++)
{
if ((seenCells[i].m_Row == tCell.m_Row)&&(seenCells[i].m_Col == tCell.m_Col))
{
isFound = true;
}
}
if (storeCell && !isFound)
{
seenCells.push_back(tCell);
//cout << "test" << endl;
}
return isFound;
}
};
class PathFindingForTest
{
Cell finalCell;
int cMult;//this is an int that will containt either a +1 or -1, controlls movement on Column axis
int rMult;//this is an int that will containt either a +1 or -1, controlls movement on Row axis
float avgCost;//this is just for testing purposes, shows average cost of entire array
float CostSoFar = 0.0F;//stores the length/cost/weight accumulated so far
float cellCost[MAX_CELL][MAX_CELL];//stores original cellCost
CellTracker visited;//stores visited cells, TODO increase weight of visited
CellTracker badCells;//stores cells that I shouldn't visit again, TODO cut arrays once badCell is found
CellTracker final;//stores cells that declare final path
bool moveVert(Cell currCell)//Tells direct path if it should move vertically
{
float cDiff = finalCell.m_Col-currCell.m_Col;//holds the column distance between current cell and end cell
float rDiff = finalCell.m_Row-currCell.m_Row;//holds the row distance between current cell and end cell
cMult = cDiff >= 0? 1:-1;//updates the column multiplier with a positive or negative 1, to tell the shortest path finder which way to go
rMult = rDiff >= 0? 1:-1;//updates the row multiplier with a positive or negative 1, to tell the shortest path finder which way to go
return((abs(cDiff)<abs(rDiff)));//if the vertical distance is more than the lateral distance, we should move vertically, otherwise move horizontally. Return true/false accordingly.
//return true if there is more vertical distance
}
float returnCellWeight(Cell weightedCell)//returns the weight/cost/length of moving into/through a cell
{
return(cellCost[weightedCell.m_Row][weightedCell.m_Col]);//literally just returns the value from the cellCost array, this is just used for convenience.
}
float shortestRoute(Cell currCell)//this method ignores the costs and finds a direct route, it then returns the cost of that direct route TODO:add badCells avoidance for multi-round testing
{
Cell testCell = {currCell.m_Row,currCell.m_Col};
float moves=returnCellWeight(testCell);//set weight of moves to the weight of the new cell
//Setting moves = 0 alternatively makes riskier decisions that can have far better or far worse outcomes, keeping it as is will leave the results consistently better though.
while((testCell.m_Col != finalCell.m_Col)||(testCell.m_Row != finalCell.m_Row))// While current cell and target cell aren't the same (when we haven't reached destination)
{
if(moveVert(testCell))//if we should move vertically (up and down rows)
{
testCell.m_Row+= rMult;// Move up or down rows, see rMult and cMult
}
else
{
testCell.m_Col+= cMult;//otherwise move left and right through the columns
}
moves+=returnCellWeight(testCell);//add the weight of each move
}
return moves;//return weight of this attempt
}
Cell checkSurroundingCells(Cell curCell)/*This is closer to the brains, less of the meat and bones, this function checks the surrounding cells from the current cell, to see which has the cheapest possible route. I've set it like this to assume that the fastest route is generally going to be in the direction of the destination, with some deviation, in a much larger graph, the route could vary quite widely, but in a graph of this size generally curves gently towards a destination on the path of least resistance, if we run this multiple times, reduce the importance of seen cells, and increase the importance of blocked cells we should be able to find the absolutely fastest route, but it would increase the time to process*/
{
float smallestF;//this holds the smallest distance for comparison with current distance
float currentF;//this holds the current distance being compared
Cell smallestC;//this holds the cell to be selected for movement
if((curCell.m_Col > 0)||!(visited.seenCell(Cell{curCell.m_Row,curCell.m_Col-1},false)))//if the current cell isn't on the lowest column and the lower value hasn't been visited, check lower column value/
{
currentF = smallestF =shortestRoute(Cell{curCell.m_Row,curCell.m_Col-1});//set current and smallest f to weight of shortest route
smallestC = {curCell.m_Row,curCell.m_Col-1};//set Cell to current cell
}
if((curCell.m_Col < MAX_CELL-1)||!(visited.seenCell(Cell{curCell.m_Row,curCell.m_Col+1},false)))//make sure we aren't on the max column, same as above
{
currentF = /*returnCellWeight(Cell{curCell.m_Row,curCell.m_Col+1}, cellCost)+*/shortestRoute(Cell{curCell.m_Row,curCell.m_Col+1});//set current to weight of this route
if(currentF<smallestF)//if the current route is shorter, update
{
smallestF=currentF;smallestC={curCell.m_Row,curCell.m_Col+1};//update the lowest weight and cell
}
}
if((curCell.m_Row > 0)||!(visited.seenCell(Cell{curCell.m_Row-1,curCell.m_Col},false)))//same as above, but with rows
{
currentF =shortestRoute(Cell{curCell.m_Row-1,curCell.m_Col});
if(currentF<smallestF)
{
smallestF=currentF;smallestC={curCell.m_Row-1,curCell.m_Col};
}
}
if((curCell.m_Row < MAX_CELL-1)||!(visited.seenCell(Cell{curCell.m_Row+1,curCell.m_Col}, false)))
{
currentF =shortestRoute(Cell{curCell.m_Row+1,curCell.m_Col});
if(currentF<smallestF)
{
smallestF=currentF;smallestC={curCell.m_Row+1,curCell.m_Col};
}
}
if (visited.seenCell(smallestC,true)){/*badCells.seenCell(curCell,true);//TODO re-implement bad cells, check effects on time, still negative, return after final*/}
return smallestC;
}
float avgWeight()//calculate average weight of cells
{
float totalCosts = 0.0F;
for(int i = 0; i < MAX_CELL; i++)// using MAX_CELL since it's already been established
{
for(int j = 0; j < MAX_CELL; j++)//for every cell in array, add weight
{
totalCosts += cellCost[i][j];
}
}
return (totalCosts / (MAX_CELL*MAX_CELL));//using MAX_CELL since it's already been established
// we return the average weight of all cells;
}
public:
int findPath(Cell dest[], int maxDestLength, const Cell* startCell, const Cell* endCell, float cCost[MAX_CELL][MAX_CELL])
// findPath essentially just a copy of bestPath within scope of class. Could have just included it into this class, but wanted to built class entirely first. If bestPath fits better directly into the class for any reason, just change findPath name to bestPath
{
for (int g = 0; g < MAX_CELL; g++)
{
for (int h = 0; h < MAX_CELL; h++)
{
cellCost[g][h] = cCost[g][h];
}
}
finalCell={endCell->m_Row, endCell->m_Col};
//cellCost = cCost;
int length = 0;
Cell workingCell = {startCell->m_Row, startCell->m_Col};
while ((workingCell.m_Row != endCell->m_Row) || (workingCell.m_Col != endCell->m_Col))
{
workingCell = checkSurroundingCells(workingCell);
final.seenCell(workingCell, true);
length++;
//CostSoFar+=returnCellWeight(workingCell);
//ACTIVATE ABOVE LINE AND THE THREE COMMENTED BELOW TO CHECK PERFORMANCE
}
for(int i = 0; i < length; i++)
{
dest[i] = Cell{final.seenCells[i].m_Row,final.seenCells[i].m_Col};
}
//avgCost = avgWeight(); //ACTIVATE THESE THREE LINES
//cout << avgCost << endl; // TO CHECK PERFORMANCE
//cout << CostSoFar/length << endl; // :)
return length;
}
};
int bestPath(Cell dest[], int maxDestLength,
const Cell* start, const Cell* end,
float cellCost[MAX_CELL][MAX_CELL])
{
PathFindingForTest pFinder;
return pFinder.findPath(dest, maxDestLength,start,end,cellCost);
//TODO Add explicit garbage collection, just in case? Should be collected at end of scope of bestpath
}
int main()
{
/* THIS IS JUST FOR TESTING ABOVE CLASS AND FUNCTIONS*/
/* Test in whatever methods suit you best, these were just the quick and dirty */
srand(time(0));
const Cell sCell = {rand()%64,rand()%64};
const Cell eCell = {rand()%64,rand()%64};
float CellCost[64][64];
for(int i = 0; i < MAX_CELL; i++)
{
for(int j = 0; j < MAX_CELL; j++)
{
CellCost[i][j]=(float)(rand()%100);
}
}
Cell dest[64];
int length = bestPath(dest, 64, &sCell, &eCell, CellCost);
cout << "There are " << length << " points between the starting point " << sCell.m_Row<<","<< sCell.m_Col << " and " << eCell.m_Row<<","<< eCell.m_Col << " along this path" << endl;
for (int i = 0; i < length; i++)
{
cout << dest[i].m_Row << "," << dest[i].m_Col << endl;
}
return 0;
}