Rubik's cube 5x5x5 edgeswap added.
[zzandy.git] / tendrils / tendrils.html
blobcecaf8729f35177b2a31b37f9a4ba78988da765c
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml">
3 <head>
4 <title>tendrils</title>
5 <style type="text/css">
6 div.field
9 div.row
12 div.row.odd
14 margin-left: 3px;
16 div.cell
18 display: inline-block;
19 width: 6px;
20 height: 5px;
22 </style>
23 </head>
24 <body>
25 <div id="target">
26 </div>
27 <script type="text/javascript">
29 function rnd(a,b) {
30 switch (arguments.length) {
31 case 0:
32 return Math.random();
33 break;
34 case 1:
35 if(a instanceof Array)
36 return a[Math.floor(Math.random()*a.length)];
38 return Math.random() * a;
39 break;
40 case 2:
41 return a + Math.random() * (b - a);
42 break;
46 function rndi(a,b) {
47 switch (arguments.length) {
48 case 1:
49 return Math.floor(Math.random() * a);
50 break;
51 case 2:
52 return Math.floor(a + Math.random() * (b - a));
53 break;
57 if(!('contains' in Array.prototype))
58 Array.prototype.contains = function(needle){
59 var i=this.length;
60 while(i-->0)if(this[i] == needle)return true;
61 return false;
64 var maxchannel = 0xff;
65 function Color(r, g, b) {
67 function bound(a) {
68 if (a < 0) return 0;
69 else if (a > maxchannel) return maxchannel;
70 return a;
73 function octet(a) {
74 a = Math.floor(a).toString(16);
75 return a.length == 1 ? '0' + a : a;
78 r = bound(r);
79 g = bound(g);
80 b = bound(b);
82 // make color darker - k between 0 and 1
83 this.dim = function (k) {
84 return new Color(r * k, g * k, b * k);
87 // randomly change color - k between 0 and 1
88 this.deviate = function (k) {
89 return new Color(rnd(r * (1 - k), r * (1 + k)), rnd(g * (1 - k), g * (1 + k)), rnd(b * (1 - k), b * (1 + k)));
92 this.toString = function () {
93 return '#' + octet(r) + octet(g) + octet(b);
97 Color.random = function () {
98 var wr = .41;
99 var wg = .37;
100 var wb = .22;
102 var int = rnd(.5, .7);
104 var r = rnd();
105 var g = rnd();
106 var b = rnd();
108 switch (rndi(3)) {
109 case 0: r = (int - wg * g - wb * b) / wr;
110 break;
111 case 1: g = (int - wr * r - wb * b) / wg;
112 break;
113 case 2: b = (int - wg * g - wr * r) / wb;
114 break;
117 return new Color(r * maxchannel, g * maxchannel, b * maxchannel);
120 function mkArray(n, newvalfn) {
121 var map = [];
123 var i = -1;
124 while (++i < n) {
125 map.push(newvalfn(i));
128 return map;
131 function mkMatrix(n, m, newvalfn) {
132 var map = [];
134 var i = -1;
135 while (++i < n) {
136 var j = -1;
137 var row = [];
138 while (++j < m) row.push(newvalfn(i, j));
140 map.push(row);
143 return map;
146 function arci(co, r, a) {
147 return [Math.round(co[0] + r * Math.sin(2 * Math.PI * a)), Math.round(co[1] + r * Math.cos(2 * Math.PI * a))];
150 var dirs2n = [[-1, -1], [-1, 0], [0, 1], [1, 0], [1, -1], [0, -1]];
151 var dirs2n1 = [[-1, 0], [-1, 1], [0, 1], [1, 1], [1, 0], [0, -1]];
153 var numdirs = dirs2n.length;
155 function getDir(id, i, j) {
156 if (id < 0 || id > dirs2n.length) id = rndi(dirs2n.length);
158 var dir = (i % 2) ? dirs2n1[id] : dirs2n[id];
159 return dir;
162 // numdirs = 4;
163 // getDir = function (id, i, j) {
164 // if (id < 0 || id > numdirs) id = rndi(numdirs);
166 // return [[-1, 0], [0, 1], [1, 0], [0, -1]][id];
167 // }
169 var allColor = Color.random();
170 console.log(allColor.toString());
172 var Walker = function (coord, dir, power) {
173 this.i = coord[0];
174 this.j = coord[1];
175 this.dir = dir;
176 this.power = power;
177 this.fill = allColor.deviate(.2);
180 function constant(val){return function(){return val}}
181 function args(){return [].join.call(arguments, 'x')}
183 var t = document.getElementById('target');
185 var aspect = 4/3;
186 var w = 128;
187 var h = Math.floor(w / aspect);
189 var empty = new Color(0,0,0);
191 function renderMap() {
192 var map = mkMatrix(h, w, constant(empty));
194 var getValidNext = function (coord, dirId) {
195 var dir = getDir(dirId, coord[0], coord[1]);
197 var i = coord[0] + dir[0];
198 var j = coord[1] + dir[1];
200 return (i >= 0 && j >= 0 && i < h && j < w && map[i][j] == empty)
201 ? { i: i, j: j }
202 : null;
205 var tryNewDir = function (i, j, fn) {
206 var next = null;
207 var retr = 3;
208 var dir = walker.dir;
210 while (retr-- > 0 && next == null) {
211 var dir = rndi(numdirs);
213 if (!fn || fn(dir))
214 next = getValidNext([i, j], dir);
217 return [next, dir];
220 var branchSmall = .2;
221 var branchBig = .5;
222 var pbig = .2;
224 var numwalkers = numdirs*2;
225 var fillfactor = .25;
226 var startingPower = Math.round(w * h * fillfactor / numwalkers);
228 var o = [Math.floor(h / 2), Math.round(w / 2 + w / 8)];
229 var makeWalker = function (i) { var dir = getDir(i, o[0], o[1]); return new Walker([o[0] + dir[0], o[1] + dir[1]], i, startingPower) };
231 var walkers = mkArray(numwalkers / 2, makeWalker).concat((function () { o = [Math.floor(h / 2), Math.round(w / 2 - w / 8)]; allColor = Color.random(); return mkArray(numwalkers / 2, makeWalker) })());
233 var walker;
234 var pool = 0;
236 while (walker = walkers.pop()) {
238 var i = walker.i;
239 var j = walker.j;
241 if (walker.power <= 0) {
242 map[i][j] = walker.fill.dim(.9);
243 --walker.power; // dead will have -1;
245 else {
247 map[i][j] = walker.fill;
248 --walker.power;
250 if (pool > 0) {
251 var loan = Math.round(rnd(pool));
252 walker.power += loan;
253 pool -= loan;
256 var next = null;
258 if (rnd() < .1) { // random change of direction
259 nextdir = tryNewDir(i, j);
260 next = nextdir[0];
261 dir = nextdir[1];
262 if (next) walker.dir = dir;
265 if (!next) {
266 next = getValidNext([i, j], walker.dir);
268 if (!next) { // forced change of direction
269 nextdir = tryNewDir(i, j);
270 next = nextdir[0];
271 dir = nextdir[1];
273 if (next) walker.dir = dir;
277 if (next) {
278 walker.i = next.i;
279 walker.j = next.j;
280 walker.fill = walker.fill.dim(.97);
282 // continue movement
283 walkers.unshift(walker);
285 var shareRatio = rnd() < pbig ? branchBig : branchSmall;
286 var share = Math.round(rnd(walker.power * shareRatio));
288 if (share > 0) {
289 var nextdir = tryNewDir(i, j, function (dir) { return dir != walker.dir && dir != walker.dir });
290 next = nextdir[0];
291 dir = nextdir[1];
293 if (next) { // add new spawn to the end
294 walkers.push({ i: next.i, j: next.j, dir: dir, power: share, parent: walker, fill: walker.fill });
295 walker.power -= share;
299 else {
301 if (walker.parent) {
302 if (walker.parent.power == -1) {
303 walker.parent.power = walker.power;
304 walkers.push(walker.parent);
306 else {
307 walker.parent.power += walker.power;
310 else {
312 pool += walker.power;
315 walker.power = -1;
321 console.log('wasted: ' + pool);
323 return map;
326 function render() {
327 var map = renderMap();
329 var text = ['<div class="field">'];
331 for (var i = 0; i < h; ++i) {
332 if (i % 2) text.push('<div class="row odd">');
333 else text.push('<div class="row">');
335 for (var j = 0; j < w; ++j) {
336 text.push('<div class="cell" style="background-color:' + map[i][j] + '"></div>');
339 text.push('</div>');
342 text.push('</div>');
343 return text.join('');
346 t.innerHTML = render();
348 </script>
349 </body>
350 </html>