PyPlayGround |
Powered by |
Pathfinding | Movement Control | Battles | Other |
Pathfinding |
Pathfinding is the task of finding the best (usually shortest) path between two points. This is hardly a trivial task, as between those two points may lie many obstacles. The two points are usually described as origin and destination to describe the expected direction of movement, but these roles may be disregarded by the pathfinder.
Pathfinding algorithms are usually designed to run over a grid of squares, with each square being either passable or not. Some algorithms also allow to specify how hard it is to pass a certain square. A longer path may be taken if it is much easier to pass.
PyPlayground, on the other hand, runs its algorithms in a theoretically infinitely-precise space. While an algorithm may choose to arbitrarily partition space into a grid, it is not necessary.
Good pages about pathfinding:
Simple PathfinderSimple Pathfinder truely is simple. It's simple and dumb, and I placed it here mainly as an easy sample to get you familiar with the framework.
The concept is obvious - move towards the target as long as there is no obstacle. If there is one - tough.
Quite simple.A quick explanation about PyPlayground's design: Each algorithm is a class inheriting from Player. It may implement init and step. init is called when the round starts, and step is then called for every new step (think of it as turns). Here we only needed step. To each step, the current state is provided. The state lets you know where are the obstacles, where are the other players and etc. A quick explanation about the implementation: First we set things up, then we measure the direction we should move in (sgn is the same as normalizing in 1-D), and then we just move in that direction. We don't test for obstacles because if we hit one, we lose anyway: PyPlayground takes care of disqualifying bad players by itself. |
![]() Simple Pathfinder gets lucky. Visualization by PyPlayground |
Wall-TracerWall-Tracer is fairly simple as well: It approaches the target directly, and if it encounters an obstacle on the way, it traces around it until it is free again to move directly at the target. Repeat. The implemenation is a bit odd, so I will explain it. First we define a direction to move in, just like in the simple pathfinder. If we can move in that direction, then hooray. If not, we will try to rotate the direction of movement by 45 degrees and try again. We repeat this process 8 times (until we exhaust all possible directions). This creates the effect of having your robot (your avatar) trace around the obstacles. It does so impressively well, and the only problem is getting stuck in a loop of tracing the same wall all over again. In order to avoid getting stuck in such loops, I also added a record of nodes that's been visited, and made sure never to step on the same node twice. This may cause other bugs, but they are more rare and fixing them would clutter the implmentation (but feel free to try it yourself). The actual implementation is very elegant, and I'm quite proud of it:
Hopefully this code is self-explanatory.Note that actor is a module which has to be imported. |
![]() Wall-Tracer demonstrates remarkable pathfinding skills. Visualization by PyPlayground ![]() Wall-Tracer finds his way out of a sticky situation, by remembering where he visited. |
A* (A-Star)A* (pronounced: A-Star) is the most popular pathfinding algorithm to-date. Providing a good balance between accuracy (finding the best path) and speed, A* is applicable (and applied) in almost any pathfinding field, be it games, robotics, routing, etc. For more reading about A*, check out:
I implemented everything from scratch, trying to use meaningful names (unlike those horribly vague f,g,h symbols) and keeping a logical structure (somewhat influenced by AIMA's excellent implementation, which is more elegant but much less readable than mine, IMHO :-)
Since the entire code is too large to fit in here, the implementation of A* itself can be found here. One last note about the implementation - unlike popular implementations who solve A* in a while-loop, mine is solved by repeated steps. This approach allows for better embedding in code (as a callback, in state-machines, etc.), as most applications wouldn't like to freeze while A* calculates. However, this can be done by simple refactorings in most cases.
And here is the code:
(astar.py can be found here)
|
![]() A* easily finds the best path. Visualization by PyPlayground ![]() This A* is too well-balanced for a game. Let's try to tweak it ![]() Here we gave more weight to g(n), and A* behaved too much like a Breadth-First Search ![]() Here we gave most weight (90%) to h(n), and it found a path very quickly, but it obviously isn't the best one. ![]() Nothing like a healthily-balanced A*. |
Gravity PathfinderAdmittedly, Gravity Pathfinder is not really a pathfinder. It is a naive implementation of movement control, being run in the hope that a correct path will result. Surprisingly, most situations I've tested lead to a correct path, although not the shortest. It has many shortcomings, and is wrong many times. However, its simplicity is beautiful (imho).
Gravity Pathfinder works by a simple concept - the "finder" is attracted towards its target, and is rejected away from its obstacles.
The current formula I use, which seems to produce the best results is:
Current values I use are:
Current implementation of Gravity Pathfinder (using PyPlayground) is:
|
![]() Gravity Pathfinder at work. Visualization by PyPlayground ![]() Another nice job done by gravity. ![]() Yet another nice job done by gravity. ![]() Yet yet another nice job done by gravity. |