Live Simulation
20
Sheep
0
Frames
-
Avg Distance to Target
Sheep
Dog
Target
🐑 Flocking Behavior
Cohesion: Sheep move towards the average position of nearby neighbors.
Separation: Sheep avoid getting too close to each other.
Alignment: Sheep match velocity with nearby flock members.
🐕 Dog Strategy
The dog uses a shepherding algorithm to herd sheep towards the target by:
• Finding the furthest sheep from target
• Positioning behind that sheep
• Driving the flock forward
⚙️ Simulation Parameters
Repulsion Radius: 50px
Attraction Radius: 100px
Dog Speed: 5 units/frame
Sheep Speed: 3 units/frame
📝 Complete Python Implementation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.patches import Circle
class Sheep:
"""Individual sheep agent with flocking behavior"""
def __init__(self, x, y):
self.pos = np.array([x, y], dtype=float)
self.vel = np.random.randn(2) * 0.5
self.radius = 0.3
def update(self, sheep_list, dog, target, params, bounds):
"""Update sheep position based on flocking rules and dog repulsion"""
cohesion = np.zeros(2)
separation = np.zeros(2)
neighbor_count = 0
# Flocking behavior with other sheep
for other in sheep_list:
if other is self:
continue
diff = other.pos - self.pos
dist = np.linalg.norm(diff)
# Cohesion: move towards average position of neighbors
if dist < params['attraction_radius']:
cohesion += other.pos
neighbor_count += 1
# Separation: avoid crowding
if 0 < dist < params['repulsion_radius']:
separation -= diff / dist
# Apply cohesion
if neighbor_count > 0:
cohesion = cohesion / neighbor_count - self.pos
self.vel += cohesion * params['cohesion_weight']
# Apply separation
self.vel += separation * params['separation_weight']
# Dog repulsion - flee from dog
dog_diff = self.pos - dog.pos
dog_dist = np.linalg.norm(dog_diff)
if dog_dist < params['dog_influence_radius'] and dog_dist > 0:
repulsion_strength = params['dog_repulsion_weight'] * \
(1 - dog_dist / params['dog_influence_radius'])
self.vel += (dog_diff / dog_dist) * repulsion_strength
# Limit speed
speed = np.linalg.norm(self.vel)
if speed > params['sheep_max_speed']:
self.vel = (self.vel / speed) * params['sheep_max_speed']
# Update position
self.pos += self.vel
# Boundary conditions - bounce off walls
for i in range(2):
if self.pos[i] < bounds[i][0]:
self.pos[i] = bounds[i][0]
self.vel[i] *= -0.5
elif self.pos[i] > bounds[i][1]:
self.pos[i] = bounds[i][1]
self.vel[i] *= -0.5
class Dog:
"""Herding dog agent that drives sheep towards target"""
def __init__(self, x, y):
self.pos = np.array([x, y], dtype=float)
self.vel = np.zeros(2)
self.radius = 0.4
def update(self, sheep_list, target, params):
"""Update dog position using shepherding strategy"""
if not sheep_list:
return
# Calculate center of mass of sheep
sheep_center = np.mean([sheep.pos for sheep in sheep_list], axis=0)
# Find furthest sheep from target
furthest_sheep = max(sheep_list,
key=lambda s: np.linalg.norm(s.pos - target))
# Strategy: position behind the furthest sheep relative to target
to_target = target - furthest_sheep.pos
target_dist = np.linalg.norm(to_target)
if target_dist > 0.1:
# Position dog behind the furthest sheep
behind_pos = furthest_sheep.pos - (to_target / target_dist) * 2.0
# Move towards optimal position
desired_vel = behind_pos - self.pos
dist = np.linalg.norm(desired_vel)
if dist > 0:
desired_vel = (desired_vel / dist) * params['dog_max_speed']
self.vel = desired_vel
else:
# If sheep are at target, move to center
desired_vel = sheep_center - self.pos
dist = np.linalg.norm(desired_vel)
if dist > 0:
self.vel = (desired_vel / dist) * params['dog_max_speed'] * 0.5
# Update position
self.pos += self.vel
class ShepherdingSimulation:
"""Main simulation class"""
def __init__(self, num_sheep=20, bounds=((-10, 10), (-10, 10))):
self.bounds = bounds
# Simulation parameters
self.params = {
'repulsion_radius': 1.5,
'attraction_radius': 3.0,
'dog_influence_radius': 4.0,
'cohesion_weight': 0.03,
'separation_weight': 0.15,
'dog_repulsion_weight': 0.8,
'sheep_max_speed': 0.3,
'dog_max_speed': 0.5
}
# Initialize sheep randomly in one area
self.sheep = [
Sheep(
np.random.uniform(-8, -4),
np.random.uniform(-8, 8)
) for _ in range(num_sheep)
]
# Initialize dog on opposite side
self.dog = Dog(8, 0)
# Target location (where we want to herd sheep)
self.target = np.array([8.0, 0.0])
def update(self):
"""Update one simulation step"""
# Update all sheep
for sheep in self.sheep:
sheep.update(self.sheep, self.dog, self.target,
self.params, self.bounds)
# Update dog
self.dog.update(self.sheep, self.target, self.params)
def animate(self, frames=500, interval=50):
"""Create animation of the simulation"""
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_xlim(self.bounds[0])
ax.set_ylim(self.bounds[1])
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.set_title('Sheep-Dog Shepherding Simulation',
fontsize=14, fontweight='bold')
# Plot elements
sheep_scatter = ax.scatter([], [], c='white', s=200,
edgecolors='black', linewidths=2,
label='Sheep', zorder=3)
dog_scatter = ax.scatter([], [], c='brown', s=300, marker='^',
edgecolors='black', linewidths=2,
label='Dog', zorder=4)
target_scatter = ax.scatter([self.target[0]], [self.target[1]],
c='green', s=500, marker='*',
edgecolors='darkgreen', linewidths=2,
label='Target', zorder=2, alpha=0.6)
# Dog influence radius
dog_circle = Circle(self.dog.pos, self.params['dog_influence_radius'],
fill=False, linestyle='--', color='brown', alpha=0.3)
ax.add_patch(dog_circle)
time_text = ax.text(0.02, 0.98, '', transform=ax.transAxes,
verticalalignment='top', fontsize=10,
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
ax.legend(loc='upper right')
def init():
sheep_scatter.set_offsets(np.empty((0, 2)))
dog_scatter.set_offsets(np.empty((0, 2)))
return sheep_scatter, dog_scatter, dog_circle, time_text
def animate_frame(frame):
self.update()
sheep_pos = np.array([sheep.pos for sheep in self.sheep])
sheep_scatter.set_offsets(sheep_pos)
dog_scatter.set_offsets([self.dog.pos])
dog_circle.center = self.dog.pos
time_text.set_text(f'Time: {frame}')
return sheep_scatter, dog_scatter, dog_circle, time_text
anim = animation.FuncAnimation(fig, animate_frame, init_func=init,
frames=frames, interval=interval,
blit=True, repeat=True)
plt.tight_layout()
return anim
# Run simulation
if __name__ == "__main__":
print("Starting Sheep-Dog Shepherding Simulation...")
sim = ShepherdingSimulation(num_sheep=20)
anim = sim.animate(frames=500, interval=50)
plt.show()