Skip to content

Commit

Permalink
examples/hugs: Steer the AI using constant-velocity intercepts
Browse files Browse the repository at this point in the history
Instead of moving towards the humans, move the AI towards the point where its
trajectory will intersect with the human's, assuming constant velocities.

The assumption of constant velocities is violated in several ways:
1. the humans have slight, random angular motion,
   rather than going in straight lines;
2. the humans bounce off the sides of the game area;
3. once the AI gets close enough, the humans get scared,
   speeding up themselves and the AI.

As the intercepts are recomputed at each frame, the random perturbations (1) are
smoothly followed by the AI.

Unlike those, (2) and (3) are large, punctual changes in velocity; those result
in slightly-suboptimal steering (i.e. a better AI could catch up slightly
faster) and discontinuities (i.e. the AI suddenly changes direction when its
target bounces or gets close enough to be scared).
  • Loading branch information
nbraud committed Apr 29, 2019
1 parent b7f8888 commit 93ccbc0
Showing 1 changed file with 19 additions and 2 deletions.
21 changes: 19 additions & 2 deletions examples/hugs.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,30 @@ def on_update(self, update, signal):
class AISprite(PlayerSprite):
emoji = 'robot'

def intercept(self, other):
δ = other.position - self.position
v_H = other.velocity

# Calling B and H the bear's and human's initial positions, t and I
# the time and position of intercept, s the bear's speed, we have:
# |BI| = t*s = |δ + t*v_H|; square to get a polynomial equation
# a t² + 2 b t + c
a = v_H*v_H - self.max_speed * self.max_speed
b, c = δ * v_H, δ * δ
Δr = b*b - a*c
if Δr < 0:
# Intercept is impossible
return None

t = max((- b - math.sqrt(Δr))/a, (- b + math.sqrt(Δr))/a)
return other.position + t * other.velocity

def on_update(self, update, signal):
"""Automatically steer the bear towards the closest hooman."""
super().on_update(update, signal)

try:
targets = map(lambda c: c.position,
update.scene.get(kind=RunnerSprite))
targets = map(self.intercept, update.scene.get(kind=RunnerSprite))
self.target = min(targets,
key=lambda p: (p - self.position).length)

Expand Down

0 comments on commit 93ccbc0

Please sign in to comment.