I'm writing an AI of a 'police car' that 'patrols' over a 2d grid. Right now, the patrol car randomly selects a destination, finds a path between it's current intersection and the destination intersection, follows the planned path then rinse and repeat. That's the intended behaviour and it works as expected.
Now the A* algorithm I wrote based on Wikipedia's page about it will give me "step style" paths.
And I don't like it in this context. I want the police car to go straight in a direction first (vertical, or up and down, or along the y-axis), then straight in the other direction (i.e. horizontal, or left or right, or along the x-axis) before it reaches its destination.
I though about (and tried) hacking the heuristics method to "inflate" the values for x-axis paths, the idea being that it would find a path for the y-axis first, then for the x-axis, but there was no improvement to the police car behaviour.
In the same idea, I tried to tell the algorithm that x-axis distances were more expensive than the y-axis distances, but I got the same result: there was no improvement to the style of the paths the cars used.
How can I modify the heuristic evaluation method of the A* algorithm so that the police car prefers going all the way through vertically, then horizontally?
Additional information:
- There is no obstacle on the grid.
- All the grids are separated evenly by a floating point value distance.
In my Graph.cpp, I have these two methods that basically find a path between aStart and aEnd. (Sorry for the bad name of "plot".)
// Find a path between aStart and aEnd. std::vector<int> Graph::plot( int aStart, int aEnd ) { std::set<int> closedSet; std::set<int> openSet = {aStart}; std::map<int, int> cameFrom; std::map<int, float> gScore; for ( auto node : mNodes ) gScore[node.first] = std::numeric_limits<float>::infinity(); gScore[aStart] = 0.0f; std::map<int, float> fScore; for ( auto node : mNodes ) fScore[node.first] = std::numeric_limits<float>::infinity(); fScore[aStart] = getHeuristicBetween( aStart, aEnd ); while (openSet.size() > 0) { int current = -1; float currentMinVal = std::numeric_limits<float>::infinity(); for ( auto nodeIndex : openSet ) { if ( fScore[nodeIndex] < currentMinVal ) { current = nodeIndex; currentMinVal = fScore[nodeIndex]; } } if ( current == aEnd ) return reconstructPath(cameFrom, aEnd); openSet.erase( current ); closedSet.insert( current ); for ( auto outArc : mNodes[current]->getOutArcs() ) { int neighbourIndex = outArc->getNodeTo()->getId(); if ( closedSet.find(neighbourIndex) != closedSet.end() ) continue; // The distance will be artificially higher if it's on an horizontal edge: float tentativeGscore = gScore[current] + outArc->getInfluencedDistance(); if ( openSet.find(neighbourIndex) == openSet.end() ) openSet.insert(neighbourIndex); else if ( tentativeGscore >= gScore[neighbourIndex] ) continue; cameFrom[neighbourIndex] = current; gScore[neighbourIndex] = tentativeGscore; fScore[neighbourIndex] = gScore[neighbourIndex] + getHeuristicBetween( neighbourIndex, aEnd ); } } } // Estimates the shortest path between aFromNode and aToNode. float Graph::getHeuristicBetween( int aFromNode, int aToNode ) const { auto nodeStartIt = mNodes.find( aFromNode ); auto nodeEndIt = mNodes.find( aToNode ); if ( nodeStartIt == mNodes.end() || nodeEndIt == mNodes.end() ) return std::numeric_limits<float>::infinity(); float dx = nodeEndIt->second->getX() - nodeStartIt->second->getX(); float dy = nodeEndIt->second->getY() - nodeStartIt->second->getY(); float length = ( glm::vec2( nodeStartIt->second->getX(), nodeStartIt->second->getY() ) - glm::vec2( nodeEndIt->second->getX() , nodeEndIt->second->getY() ) ).length(); if ( dx > dy ) { return length * 0.1; // Artificially inflate the cost of going left-right. } return length; } With or without the modification to the algorithm, I get this kind of behaviour:

