Extends Boids flocking simulations to periodic 2D domains via rectangular lattice projections, eliminating edge effects through Mod-wrapped position updates and Nearest-driven force calculations across domain copies; integrates dynamic parameter controls (mobility rates, inertia, speed) via sliders, precomputed neighbor lookups for global friend/foe interactions, and memory-efficient inertial averaging for 1000+ agents.
In response to the request here: https://mathematica.stackexchange.com/q/312395​
See other part here:
​Boids 1: 3D dynamics, velocity-color mapping, and inertia-driven coordination​
​Boids 2: spherical projection with dynamic viewpoint tracking

Function definitions

Make periodic copies of a position
In[]:=
perioidLocations[position_,domain:{length_,height_}]:={{0,0},{-length,-height},{-length,0},{-length,height},{0,-height},{0,height},{length,-height},{length,0},{length,height}}+Threaded[position]
Use Nearest to find which copy to interact with
In[]:=
boidForce[me_,other_,domain_]:=Block{copies=perioidLocations[other,domain],closest,distance,delta},​​{closest,distance}=Nearest[copies->{"Element","Distance"}][me][[1]];​​
closest-me
.0001+distance
​​
Same thing, but with a precomputed nearest function
In[]:=
nearestForce[location_,nf_]:=​​Block{closest,distance},​​{closest,distance}=nf[location][[1]];​​
closest-location
.0001+distance
​​

Initialize

In[]:=
numberBoids=500;​​boidLocations=RandomPoint[boundary=Rectangle[{0,0},{length,height}={2,.5}],numberBoids];​​{friend,enemy}=RandomInteger[{1,numberBoids},{2,numberBoids}];​​{everyonesFriend,everyonesEnemy}=RandomInteger[{1,numberBoids},2]​​velocity=ConstantArray[{0,0},numberBoids];​​friendMobility=2;​​enemyMobility=1;​​globalFriendMobility=.02;​​globalEnemyMobility=0.01;​​mobilityFactor=.01;​​inertialFactor=0.5;
Out[]=
{1,233}

View in a separate window

In[]:=
CreateDocument@Dynamic[Graphics[{FaceForm[],EdgeForm[Black],boundary,Point[Flatten[perioidLocations[#,{length,height}]&/@boidLocations,1]]},ImageSize->Large,PlotRange->{{-length,2length},{-height,2height}}]];

Some controls for parameters (this should all be in a DynamicModule)

In[]:=
Grid[​​{​​{​​Row[{Style["Friend Mobility",12],Slider[Dynamic[friendMobility],{0,4}]}],​​Row[{Style["Enemy Mobility",12],Slider[Dynamic[enemyMobility],{0,4}]}]​​},​​{​​Row[{Style["Everyone's Friend Mobility",12],Slider[Dynamic[globalFriendMobility],{0,1}]}],​​Row[{Style["Everyone's Enemy Mobility",12],Slider[Dynamic[globalEnemyMobility],{0,1}]}]​​},​​{​​Row[{Style["Inertia Factor",12],Slider[Dynamic[inertialFactor],{0,1}]}],​​Row[{Style["Speed",12],Slider[Dynamic[mobilityFactor],{0,.1}]}]​​}​​},​​Alignment->Left,Frame->All​​]
Out[]=
Friend Mobility
Enemy Mobility
Everyone's Friend Mobility
Everyone's Enemy Mobility
Inertia Factor
Speed

Run simulation

In[]:=
Do[​​If[RandomReal[]<.02,With[{switchMe=RandomInteger[{1,numberBoids}]},{friend[[switchMe]],enemy[[switchMe]]}=RandomInteger[{1,numberBoids},2]]​​];​​If[RandomReal[]<.05,{everyonesFriend,everyonesEnemy}=RandomInteger[{1,numberBoids},2]​​];​​vLast=velocity;​​​​nfEveryonesFriend=Nearest[perioidLocations[boidLocations[[everyonesFriend]],{length,height}]->{"Element","Distance"}];​​nfEveryonesEnemy=Nearest[perioidLocations[boidLocations[[everyonesEnemy]],{length,height}]->{"Element","Distance"}];​​​​velocity=mobilityFactor(​​friendMobilityMapThread[boidForce[#1,#2,{length,height}]&,{boidLocations,Extract[boidLocations,List/@friend]}]-​​enemyMobilityMapThread[boidForce[#1,#2,{length,height}]&,{boidLocations,Extract[boidLocations,List/@enemy]}]+​​globalFriendMobilityMap[nearestForce[#,nfEveryonesFriend]&,boidLocations]-​​globalEnemyMobilityMap[nearestForce[#,nfEveryonesEnemy]&,boidLocations]​​);​​vMean=(1-inertialFactor)velocity+inertialFactorvLast;​​​​boidLocations=Mod[#,{length,height}]&/@(boidLocations+vMean),​​​​10^6​​]

CITE THIS NOTEBOOK

Boids 3: periodic domain eliminates edge effects via rectangular lattice projections​
by W. Craig Carter​
Wolfram Community, STAFF PICKS, June 24, 2025
​https://community.wolfram.com/groups/-/m/t/3485229