Show code
<think>
Okay, let me tackle this problem. I need to create an HTML file that simulates 200 flocking birds following the three rules: separation, alignment, and cohesion. The birds should be drawn as triangles pointing in their direction of travel. The simulation should display the number of distinct flocks and average speed, with wrap-around edges. Also, it has to use requestAnimationFrame and restart when finished without user input. No external libraries, and keep it under 7000 characters.
First, I'll structure the HTML file. The doctype, head with a title, and a canvas element. Then the JavaScript inside a script tag. Since it's self-contained, everything has to be within the HTML.
Starting with the canvas setup. I'll set the canvas size to a reasonable number, maybe 800x600. The context will be 2d. The birds will be stored in an array. Each bird needs position, velocity, and acceleration vectors. The rules will require checking each bird against others within a certain radius for separation, alignment, and cohesion.
Wait, how do I handle the flocking rules? Each bird should avoid others (separation), match their neighbors' direction (alignment), and move towards the average position (cohesion). The separation radius can be a constant, like 50, but maybe needs tuning. The weights for each rule might be important too.
The birds should wrap around the edges. So when their position goes beyond the canvas, they reappear on the opposite side. That means checking x and y positions and mod the canvas dimensions.
Calculating flocks is a bit tricky. Each flock is a group of connected birds. To find the number of flocks, I can use a connected components approach. For each bird, if it hasn't been visited, start a BFS or DFS to mark all connected birds as part of the same flock. The number of times this is done gives the flock count. But with 200 birds, this might be computationally expensive? Maybe using a simpler method, like grouping birds within a certain distance from each other? Wait, the problem states to show the current number of 'distinct' flocks, so a proper connected components approach is needed.
Alternatively, every frame, for each bird, check if they are in a group within a certain radius. But that might not capture all connections. Hmm, perhaps a flood fill algorithm.
But doing this every frame for 200 birds might be slow. Maybe optimize by maintaining a list of flock groups. But that could be complex. Alternatively, every frame, assign each bird to a flock by checking proximity to other birds, but this might not work accurately. Need to think of a way to compute connected components efficiently.
Alternatively, maybe the problem allows a simplified approach where if birds are within a certain radius of each other, they are considered in the same flock. But that might not give accurate count. Hmm. Alternatively, after computing all the rules, when a bird's neighborhood is considered, perhaps each bird's neighbors form a connected graph. To find the number of connected components in this graph, which is the number of flocks.
But how to compute connected components efficiently? For 200 birds, even a O(n^2) approach might be manageable per frame. Let's see. For each bird, check if it's already part of a component. If not, start a BFS to find all connected birds. Each time a component is found, increment the flock count.
So in code, after calculating all the accelerations and updating positions, before drawing, we can run this:
Initialize an array 'visited' of 200 booleans. Initialize flock count to 0.
Loop through each bird:
if not visited[i], then do BFS/DFS to mark all connected birds. Increment flock count.
But what defines 'connected'? Birds are connected if they are within a certain distance (maybe the same as the separation radius?). So in the BFS, the adjacency is based on being within the separation radius.
Wait, but the rules already consider birds within a certain radius. Maybe the separation radius (say, 50) is appropriate for determining flock membership. So birds within that radius are part of the same flock.
Alternatively, maybe a slightly larger radius for flock detection, like 70? Not sure. The exact value might affect flock count, but perhaps the problem allows some flexibility here.
So, the steps each frame:
1. Update each bird's acceleration based on the three rules (separation, alignment, cohesion).
2. Update velocities and positions (with wrap around).
3. Calculate flocks: determine how many connected components exist when birds within separation radius are considered connected.
4. Draw the birds as triangles, pointing in their direction.
5. Display the flock count and average speed.
Starting coding the structure:
- Variables: canvas, ctx, birds array.
Each bird object might have properties:
x, y (position)
vx, vy (velocity)
ax, ay (acceleration)
But maybe we can optimize by storing them in separate arrays for speed? Or maybe objects are okay.
Let me define Bird as an object with those properties.
Initialization: create 200 birds, each with random position and velocity.
The separation, alignment, cohesion rules:
For each bird, loop through all other birds, check distance, and compute the three vectors.
Wait, but for 200 birds, doing O(n^2) is 40,000 operations per frame. That's probably manageable in JS for a canvas animation, but need to be careful.
Alternatively, loop through all pairs once and compute the interactions, but that's O(n^2) anyway.
So code outline for each frame:
Clear the canvas.
Compute for each bird:
Reset acceleration ax, ay.
Loop over all other birds:
if distance < separationRadius, etc.
Compute separation, alignment, cohesion vectors.
Add each to acceleration.
Then clamp the acceleration to a max.
Update velocity, then position with wrap.
Then handle flocks:
Compute connected components.
Then draw each bird as a triangle pointing in direction.
Display flock count and average speed.
Now, the drawing part: each bird is a triangle. To draw a triangle pointing in the direction of velocity, we need to calculate the rotation.
The triangle can be drawn with three points: tip in the direction of the velocity, and two others forming a triangle. To do that, the velocity's heading can be used to rotate the triangle.
Alternatively, the triangle's vertices can be calculated based on the direction vector.
Let me think of the triangle as a small arrow. The main vector is the velocity. The triangle can be a simple three-point shape: tip is at the direction of velocity, and two other points offset perpendicular.
Wait, perhaps it's easier to compute the angle of the velocity vector, then draw the triangle pointing in that direction.
So for each bird, compute the angle theta = Math.atan2(vy, vx). Then draw a triangle with vertices rotated by theta.
The triangle can be, for example, a base of 5 pixels in size. The points could be something like:
The tip is at (x, y), then two other points offset by vectors perpendicular. Maybe:
Let me have a triangle with points like (0, -2), (2, 2), (-2, 2) in some coordinate system, then rotated to face theta.
Alternatively, simpler: the main point is at the center, then a direction vector, then the two sides. Hmm.
Alternatively, using the context's rotation.
So, for each bird:
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
draw a triangle: like a line from (0, -2) to (3,0), then to (0