I implemented the basic project, with a couple of enhancements. For the basic project I consider one circular robot, and any number of circular obstacles, either static or moving.

Static case:

First I'll explain the case where all the obstacles are static. There is one new data structure, corresponding to each sample in the workspace -- a Configuration, which contains position, and a pointer to its parent Configuration. The work space is split into cells (40 pixels by 40 pixels, meaning there are 15x10 = 150 cells), each of which can contain up to 100 Configurations. A list is kept of all the cells which contain at least one Configuration.

Starting from the root Configuration (the start position of the robot at time 0), a random occupied cell is chosen, and a random Configuration in that cell is chosen to expand. During expansion I try to add up to 5 children to connect (with straight line paths) to this Configuration. The children can be between 0 and 50 pixels away, in a random direction.

A child may fail to be added for 3 reasons: if the cell it would be added to is full, if there is no free path from its parent (this also takes care of the case when the child is in collision with an obstacle), or if it is off the screen. If the child is added successfully, I check for a free straight line path from the child to the goal, and if it exists, I generate the complete path and return success. If I fail to add a child 100,000 times in a row, then I give up and return failure.

Moving case:

Within this framework, it is fairly simple to add the time dimension. When moving obstacles are present, my Configuration now contains a time variable, and my cells are now 40 pixels by 40 pixels by 6 seconds. (All times over 300 seconds are placed in the 50th time cell, so now there are 15x10x50 = 7500 cells, which is the main reason for slower performance).

Now there must also be a concept of robot velocity. However, any path should be achievable if the robot either moves at its maximum velocity, or it doesn't move at all. So now when I child is added, 90% of the time it is the same as before, it moves between 0 and 50 pixels in an arbitrary direction, at the rate of 12 pixels/second (chosen because then the robot only moves 3 pixels per simulation "time_step" and thus appears to be moving smoothly). The other 10% of the time, the child does not move at all, and simply waits for between 0 and 3 seconds. This strategy seems to work well to avoid moving obstacles. Otherwise, the only difference from the static case is in the collision checking.

Collision checking:

To check for collisions of a straight line path, we loop over all the obstacles, and for each obstacle we check the path. For the case of a static obstacle, first check that the two endpoints of the robot are not in collision. Then take the projection of the obstacle position onto the line containing the path segment. If this projection actually hits the path segment, then check the distance of the projection to see if the obstacle collides with the robot.

For a moving obstacle, the collision check is a simple extension of the static case, if you think algePaically instead of geometrically. As the robot moves from A to B, say the obstacle moves from C to D, and parametrize these both over a time interval as t goes from 0 to 1. Now write down the squared distance from the robot to the obstacle at time t, differentiate with respect to t, and set it equal to 0. This finds the closest distance between the robot and obstacle. Simply check this point and the two endpoints for collision.

Path Generation:

When success is returned, we can simply follow the Configuration pointers up to the root Configuration, giving a complete path.

Path Optimization:

The path generated is a sequence of Configurations, but might be very inefficient. To optimize it, we look at all possible straight line short cuts between non-neighboring Configurations, and cut out any steps that we can. We can also draw the optimized and non-optimized path for visual feedback.

Thin Robot:

I also added a different shaped robot, a thin arrow that can get through narrower passages. The arrow always moves in the direction of its tip. Rather than having to rewrite the collision detection entirely, I simply model the thin robot as 4 small circles next to each other, and check for collisions with each of these circles.

Problems:

1. If the environment is very complex, and there is no path, sometimes I can run out of memory instead of returning failure. However, this is reasonable, because it does not crash, and in fact has the same behavior as returning failure.
2. It would be nice to actually have objects which are neither circles nor approximated as circles.
3. The simulation can become fairly slow (15 seconds to compute a path) in crowded environments, which I think could be optimized with some clever memory usage.

Good Points:

1. I think the collision checker for moving obstacles is quite efficient and maybe even elegant.
2. The path optimization and path drawing is quite fun.
3. The extension to the thin robot seems clean and fairly accurate.
4. The simulation seems usually to run quite quickly.