for($ in a)a[$[m=n=0]+=$[6]||""]=a[$];i=[];h=c.width=c.height=8E2;b=[7E2,5,9,1E5,3E3,9,99,60];setInterval(onkeyup=onmousemove=function(E){E?(Z={x:m=E.pageX||m,y:n=E.pageY||n,X:0,Y:0,w:0,W:0},A=(E.which|0)-13||++Z.W&i.push(Z),A-19||i.push(Z),b[A+A%2-53>>1]*=.9+A%2/5)(d=Math.abs):a[f="fillStyle"]="rgba(0,0,0,.1)";a.fc(I=0,0,h,h);for(a[f]="tan";I<8;a.fx(b[I++]|0,99,A))a.fx("B|C Ground0D|E Speed0F|G Distance0H|I Flocking0J|K Mouse attr0L|M Obst size0N|O Perch time0P|Q Sight len".split(0)[I],5,A=9*I+9);a[f]="#270";for(a.fc(I=0,b[0],h,h);A=i[I++];)with(A){for(B=K=M=N=O=P=Q=R=0;S=i[K++];a[f]="#888"){(A=d(x-S.x)+d(y-S.y))<b[7]&&(B++,M+=S.x,N+=S.y,Q+=S.X,R+=S.Y);A<S.W*3*b[5]+b[2]&&(O-=S.x-x,P-=S.y-y)}X+=(M/B-x)/b[3]+O/9+(Q/B-X)/15+(m-x)/b[4];y>b[0]|w?(y=b[X=0],Y=-5,w=w?w-1:b[6]|0):Y+=(N/B-y)/b[3]+P/9+(R/B-Y)/15+(n-y)/b[4];C=b[1];X/=d(X)>C?d(X)/C:1;Y/=d(Y)>C?d(Y)/C:1;with(a){ba();strokeStyle="red";W?a(x,y,b[5],0,7,0)&f():m(x,y)&l(x+=X,y+=Y)&s()}}},50)
/*
* Boid simulator
* Lauri Paimen, 2011, http://lauri.paimen.info
* Entry to JS1K #3, http://js1k.com/2011-dysentery/
*
* Boid simulator simulates flocking behavior of bird-like objects.
* Add boid with space, obstacle with enter, control behavior with B-Q keys
* and use your mouse to place obstacles and steer the boids.
* For more information, check http://en.wikipedia.org/wiki/Boids
*
* Controls for increase/decrease values affecting behavior:
* B/C Grass height
* D/E Maximum velocity of boid
* F/G Minimum distance between boids
* H/I Flock "gravity", how tightly boids flock (bigger value less)
* J/K Mouse "gravity", how much mouse pointer attracts boids (bigger less)
* L/M Obstacle size
* N/O Perching time
* P/Q Boid line of sight
*
* Simulated behaviors:
* - Boids try to fly towards other boids (inside line-of-sight)
* - Boids try to keep a small distance between each other
* - Boids try to go to same direction as other boids (inside line-of-sight)
* - Boids try to catch the mouse
* - Boids try to avoid hitting the obstacles
* - Boids have maximum speed
* - Boids perch in the grass for some time
*
* To shrink the file (boids_shim.js):
* 1. Download and extract YUI compressor.
* 2. Run:
* java -jar yuicompressor-2.4.2.jar boids_shim.js \
* | sed -e 's/0\./\./g' \
* -e 's/;$//g' \
* -e 's/){with/)with/' \
* -e 's/}}}}/}}}/' \
* -e 's/00000/E5/g' \
* -e 's/000/E3/g' \
* -e 's/00/E2/g' \
* -e 's/a){/a)/' \
* -e 's/\]}/\];/' \
* -e 's/)){/))/' \
* -e 's/9)}/9);/'
*
*/
// Tested to run on opera 11.10, ff 3.6.16 (bit slow there) and
// chrome 10.0.648.133. Couldn't test on safari (has no mac).
// a: Canvas draw surface (from shim)
// m: Last mouse X coord
// n: Last mouse Y coord
// Set canvas context with shortened names, proudly copied from earlier JS1Ks
// One can squeese 1 byte out by replacing
// a[$[0] + ($[6] || '')] with a[$[0] += $[6] || '']
for($ in a) a[$[m=n=0] += $[6] || ''] = a[$];
// i: item array (contains boids and obstacles)
i = [];
// c: canvas element (from shim)
// h: height and width of draw area. The size doesn't really matter here.
// I picked 800x800. Sorry if its too tall for your screen.
h = c.width = c.height = 800;
// b: settings array:
// # inc dec meaning
// 0: B C Grass height
// 1: D E Maximum velocity
// 2: F G Minimum distance
// 3: H I Flock center "gravity"
// 4: J K Goal (mouse) "gravity"
// 5: L M Obstacle size
// 6: N O Perching time
// 7: P Q Boid line of sight
// 0 1 2 3 4 5 6 7
b = [700, 5, 9, 1E5, 3E3, 9, 99, 60];
// This is the only function in boids simulator. It handles:
// - Mouse events
// - Key events
// - Algorithms
// - Drawing
setInterval(onkeyup = onmousemove =
function(E) {
// E: event
// If we have E, it's mousemove/keyup event
E ? (
// Z: preconstructed item representing boid or obstacle
// Z.x: x coordinate
// Z.y: y coordinate
// Z.X: x velocity
// Z.Y: y velocity
// Z.w: perching time left (0 = not perching)
// Z.W: type; 0 = boid, 1 = obstacle
Z = { // If the event was mouse event (has pageX), update
// last mouse position.
// Also, set the item to be at last mouse position.
x: m = E.pageX || m,
y: n = E.pageY || n,
X: 0,
Y: 0,
w: 0,
W: 0 },
// Handle key event
// A: keycode
// Firefox requires (e.which|0) to make result a number (!NaN)
// Enter(13) -> Add obstacle. Also, A is set to keyCode - 13
A = (E.which|0) - 13 || ++Z.W & i.push(Z),
// space(32 = 19 + 13) -> Add boid
A - 19 || i.push(Z),
// Indexing gives: B(67) and C(68) => 0, D(69) and E(70) => 1, etc
// Multiplier gets either 1.1 to increase or 0.9 to decrease
// the setting value.
// Save 2 bytes with simple addition/deletion: += A % 2 - .5;
b[A + A % 2 - 53 >> 1] *= .9 + A % 2 / 5
// Key and mouse is now handled. To prevent drawing, execute this
// statement. Executing it fails always, equaling to
// returning from handler function.
// Initialize d here, you can assume it to be Math.abs about anywhere
// d: Math.abs
)(d = Math.abs)
// No E => algorithms and drawing
// Draw background (set color to slowly fade to black)
// f: fillstyle
: a[f = "fillStyle"] = "rgba(0,0,0,.1)";
// I: loop helper
a.fc(I = 0, 0, h, h); // fillrect
// Print stats (it takes quite a lot of bytes)
// I: loop index
// Loop increment: round and print stat, increment I
for (a[f] = "tan"; I < 8;a.fx(b[I++]|0, 99, A))
// Stat name
// A: stat y coord
a.fx("B|C Ground0D|E Speed0F|G Distance0H|I Flocking0J|K Mouse attr0L|M Obst size0N|O Perch time0P|Q Sight len".split(0)[I], 5, A=9*I+9)
// Draw grass (drawing at "for" -loop initialization)
a[f] = "#270";
// Calculate, move and draw boids
// I: preset for loop
// A: Temporary helper, needed until with()
// Finish drawing of grass at loop initialization
for (a.fc(I = 0, b[0], h, h); A = i[I++];) // No {}'s
// I: DON'T USE AFTER THIS, POINTS TO NEXT -NOT CURRENT- BOID!
// with() imports properties of boid (Z)
with (A) {
// Calculate boid movement
// B: Amount of boids affecting (inside line of sight)
// K: inner loop index
// M: center X
// N: center Y
// O: dist X
// P: dist Y
// Q: avg velocity X
// R: avg velocity Y
// S: other boid
// Initialize obstacle draw value at increment
for (B = K = M = N = O = P = Q = R = 0; S = i[K++];
a[f] = "#888") {
// K: DON'T USE AFTER THIS, POINTS TO NEXT -NOT CURRENT- BOID!
// A: distance between boids
// Count line-of-sight items
// Pythagorean length calculation takes too many bytes,
// do simplier estimate with box. No-one will notice this.
(A = d(x - S.x) + d(y - S.y)) < b[7] && (
B++,
M += S.x,
N += S.y,
Q += S.X,
R += S.Y
);
// Collision avoidance
// Introduces "feature", real would be (S.W?3*b[5]:b[2])
// I forgot what the "feature" is, sorry. Something useful,
// I guess.
A < S.W * 3 * b[5] + b[2] && (
O -= S.x - x,
P -= S.y - y
)
}
// Calculate velocity, X coord
X +=
// Flock center
(M / B - x) / b[3]
// Collision avoidance with line-in-sight items
+ O / 9
// Average velocity of line-in-sight flock
+ (Q / B - X) / 15
// Mouse gravity
+ (m - x) / b[4];
y > b[0] | w ? (
// Starting perch/rest, or already doing it
// Stop the item and place it onto ground
y = b[X = 0],
// -5 starting velocity, also makes boid "stand" in the ground
Y = -5,
// Reduce or set perch time
w = w ? w - 1 : b[6] | 0 // |0 = floor, w must be integer
) : // Calculate Y velocity (similar to X above)
Y +=
(N / B - y) / b[3]
+ P / 9
+ (R / B - Y) / 15
+ (n - y) / b[4];
// speed limiter
// C: Shortcut for max speed setting
C = b[1];
X /= d(X) > C ? d(X) / C : 1;
Y /= d(Y) > C ? d(Y) / C : 1;
// Move and draw item
// fillStyle is #888 set for obstacle already
// strokeStyle will be red for boid
with(a) {
ba(); // beginPath
strokeStyle = "red"; // Color of stroke (boid)
W ? // Obstacle: arc, fill
a(x, y, b[5], 0, 7, 0) & f()
// Boid: move, lineTo (+ boid move), stroke
: m(x, y) & l(x += X, y += Y) & s();
} // End of with(a)
} // End of with(A)
// end of outer for loop (no {}'s)
}, // End of master function
50); // setInterval interval