-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve A* Pathfinding Efficiency by Addressing Final Segment Penalty #1734
Improve A* Pathfinding Efficiency by Addressing Final Segment Penalty #1734
Conversation
Awesome finding!If I understand this correctly this affects only the case where a path for a ware is searched that has a building as its target. Your "clean" solution detects paths from flag to buildings and deduces the 500 points from the score. Your "dirty" solution detects the path from flag to target building and deduces the 500 points from the score and readds it at the end. I don't really like that as it really litters the generic (road)pathfinder with a special case relying on non-local knowledge. (the 500 is a magic number here defined elsewhere) I think we can improve on that: When the target is a building we search for a route to the flag instead. This avoids the penalty in addition to even considering any other route for an additional, very minor performance bonus. I guess every target for a ware is a building anyway. However we have a similar issue for harbor buildings: The algorithm will still add the 500 points additional penalty there. However I guess this is just an offset to the ship costs that effectively shouldn't matter. We could fix that by changing the So IMO the best solution is to handle the final segment in What do you think? |
Your summary is mostly accurate, but there's one clarification: the actual cost value does matter for replay compatibility. It's difficult to pinpoint the exact source of the hash mismatch in I believe keeping the I recommend updating the We could add another parameter to
In addition to this change, your suggested change to reduce the search scope in |
Even there we just have a comparison against other buildings and the 500 points offset applies to all of them so it shouldn't change their relative order.
The "used incorrectly" part should not happen and could rather be asserted than checked. As for the "misleading" part you are right although we could just accept and document it as I'm not sure it is worth the cost of 2 additional checks and extra parameter. s25client/libs/s25main/pathfinding/RoadPathFinder.cpp Lines 204 to 210 in da659fc
addCosts passed to it was changed at some point, so not great either.
So I'd rather add However we rarely run into this codepath and access the "isHarbor" in the pathfinding right before the costs. So this check is likely cheap in practice. Hence we could do it cleanly:
We can use the (non-local) fact that roads always go from flag to building so the building is What do you think? |
I found a difference:
Columns are Game object ID, Distribution points (more=higher priority), cartesian distance from ware to goal, Estimate is The old code and the one correcting the final "distance" yields the 2nd building while the new code without correction yields the first. This is clearly wrong as with or without the correction the 2nd has a higher score. TLDR: We also need to adjust the max check to check after deducing the 500 points: The issue is caused by the path of the 2nd building getting restricted such that the final score will be higher than the current best score. I.e. |
Hi @Flamefire , I don't completely understand what you mean by the code correcting the final distance. I made a check with the original implementation ( |
I suspect that the calculation in As you wrote, the formula is like this: When points are the same for two buildings, the one with the shorter route will be choosen. But when they are unequal, the path costs will have an increasing impact depending on how far the possible destinations are. I think it would be better to normalize the path costs first and then divide the points by them. Then, different costs values with not change the result of this algorithm. However, this would require to search all pathes and not limit by max length.
|
This exceeds the scope of this pull request, but it may be worth doing a breadth-first-search from the ware to find the |
The penalty would be the same for all paths to buildings. So comparing the routes to different buildings is not affected by this and we can remove it.
We also need to ignore the final path segment for backwards compatibility
Account for the shipping in a single place not two
Breaks the seafaring replay as cost of shipping got higher for wares already in the harbor. This reverts commit abae3f6.
Yes. I tried to remove However with the fix it worked. I made a PR to your branch to discuss the changes and some comments on what I tried.
Might be fine as the points get reduced with arriving/ordered wares so others get preferred for later orders. But yeah, it's a heuristic only...
Maybe. But I think pathfinding is so slow that it is better to have a suboptimal result instead of doing the search more often. Especially as no-one will likely notice this slight change in practice. And the current heuristic isn't too bad:
If either is much higher than the other it skews the results, yes. But at least it is cheap to calculate and so far "good enough", isn't it?
Possible. I also though about using e.g. Dijkstra. But in the end it might not be so easy: For small N repeated A* could be faster already. Additionally we can avoid A* invocations by limiting to the previous best value. Could make this more effective by sorting the possible clients by Cartesian distance ascending. |
Improvements for Pathfinding PR
Thanks for your PR. Your solution seems best to me. Replays are still compatible and the required fix is installed. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for coming up with this fix and PR!
Issue
The A* cost function (
noFlag::GetPunishmentPoints
) adds a 500 point penalty to segments without carrier. This includes the segment from a building's flag to itself, which will never have a carrier. If the building is the destination of the pathfinding, the algorithm cannot find a better segment.As a result, the large penalty causes A* to unnecessarily search a wide area around the final segment, even though no better options exist.
Solutions
I see two approaches to resolve this:
1. Clean solution
Modify
noFlag::GetPunishmentPoints
to handle this special case:This solution directly addresses the issue but changes the behavior of
FindPath
when a max cost value is specified, making it not replay-compatible.2. Dirty/Stable Solution (Implemented)
Make the A* algorithm aware of the issue and re-add the 500 points at the end of the run to make sure that
FindPath
returns consistent cost values. While less elegant, this approach preserves replay compatibility and is implemented in this change.Impact
This fix significantly improves performance. Running games without a UI or using
Test_autoplay
is twice as fast, as pathfinding is a major component of game logic.I prefer the clean implementation. The dirty one serves as proof-of-concept.