Each fish steers by three local rules computed from neighbours within 4.5 units (identical to the 3D version):
separation: flee neighbours closer than 1.6, weight ~1/d
alignment: match the mean neighbour velocity
cohesion: steer toward the neighbour centroid
walls: soft push inside a 6-unit margin (x3)
steer = desired(MAX_SPEED) - v, clamped to 9
Speed is clamped between 2.2 and the max swim speed. Polarization is |mean unit heading| (1 = perfectly aligned, 0 = disordered). Groups are connected clusters of fish closer than the neighbour radius. A pointer acts as a predator that adds a flee force within 9 units.