(
//generate tiling
~robinson = {arg levels;
	var translate, rotate, putTile,
	tiling, startCross, endCross, arms, tileMap,
	corners, sides, rotates, armIndices,
	putTiles, putCorners, putSides, putAxes,
	curMatrix = [0, 0, 0], matrixStack = [[0, 0, 0]], colorMap;

	translate = {arg x, y; curMatrix = curMatrix + [x, y, 0]};

	rotate = {arg x; curMatrix[2] = (curMatrix[2] + x) % 4};

	putTile = {arg tile;
		tiling[curMatrix[1]][curMatrix[0]] = 4.collect({arg side; tileMap[tile][(side - curMatrix[2]) % 4]});
	};

	tiling = Array.fill2D(pow(2, levels) - 1, pow(2, levels) - 1, {nil});

	startCross = 6;
	endCross = 7;
	arms = [0, 1, 2, 3, 4, 5];

	tileMap = [
		[[0, 0, 0, 1, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0]],
		[[0, 0, 0, 1, 0, 0, 0], [0, 0, -1, -1, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0], [0, 0, 0, -1, -1, 0, 0]],
		[[0, 0, 1, 1, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0], [0, 0, 0, -1, -1, 0, 0], [0, 0, 0, -1, 0, 0, 0]],
		[[0, 0, 1, 1, 0, 0, 0], [0, 0, -1, -1, 0, 0, 0], [0, 0, 0, -1, -1, 0, 0], [0, 0, 0, -1, -1, 0, 0]],
		[[0, 0, 0, 1, 1, 0, 0], [0, 0, 0, -1, 0, 0, 0], [0, 0, -1, -1, 0, 0, 0], [0, 0, 0, -1, 0, 0, 0]],
		[[0, 0, 0, 1, 1, 0, 0], [0, 0, -1, -1, 0, 0, 0], [0, 0, -1, -1, 0, 0, 0], [0, 0, 0, -1, -1, 0, 0]],
		[[0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0]],
		[[0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0]]
	];

	corners = [[1, 1], [-1, 1], [-1, -1], [1, -1]];
	sides = [[0, 1], [-1, 0], [0, -1], [1, 0]];
	rotates = [-1, 0, 1, 2];
	armIndices = [1, 1, 3, 5];

	translate.value((pow(2, levels) / 2).asInteger - 1, (pow(2, levels) / 2).asInteger - 1);

	putTiles = {arg n;
		if(n > 0, {
			putTile.value(startCross);
			putCorners.value(n - 1);
			if(n == 2, {putSides.value()}, {putAxes.value(n)});
		});
	};

	putCorners = {arg n;
		4.do({arg i;
			matrixStack = matrixStack.add(curMatrix);
			translate.value(pow(2, n - 1) * corners[(i + curMatrix[2]) % 4][0], pow(2, n - 1) * corners[(i + curMatrix[2]) % 4][1]);
			rotate.value(rotates[i]);
			if(n == 1, {putTile.value(endCross)}, {putTiles.value(n)});
			curMatrix = matrixStack.pop;
		})
	};

	putSides = {
		4.do({arg i;
			matrixStack = matrixStack.add(curMatrix);
			translate.value(sides[(i + curMatrix[2]) % 4][0], sides[(i + curMatrix[2]) % 4][1]);
			rotate.value(rotates[i]);
			putTile.value(arms[armIndices[i]]);
			curMatrix = matrixStack.pop;
		});
	};

	putAxes = {arg n;
		4.do({arg j;
			(1..(pow(2, n - 1) - 1)).do({arg k;
				matrixStack = matrixStack.add(curMatrix);
				translate.value(sides[(j + curMatrix[2]) % 4][0] * k, sides[(j + curMatrix[2]) % 4][1] * k);
				rotate.value(rotates[j]);
				if(k == (pow(2, n - 2)), {
					case
					{j < 2} {putTile.value(arms[1])}
					{j < 3} {putTile.value(arms[3])}
					{j >= 3} {putTile.value(arms[5])};
				}, {
					case
					{j < 2} {putTile.value(arms[0])}
					{j < 3} {putTile.value(arms[2])}
					{j >= 3} {putTile.value(arms[4])};
				});
				curMatrix = matrixStack.pop
			});
		});
	};

	putTiles.value(levels);

	// add parity tiles and map colors
	colorMap = Dictionary.with(*[
		[ 0, -1, 0, -1, 0, -1, 0 ] -> 0,
		[ 0, -1, 1, 1, 0, -1, 0 ] -> 1,
		[ 0, 1, 0, -1, 0, 1, 0 ] -> 2,
		[ 0, 1, 0, -1, -1, 1, 0 ] -> 3,
		[ 0, 1, 0, 1, 1, 1, 0 ] -> 4,
		[ 0, 1, 1, 1, 0, 1, 0 ] -> 5,
		[ 0, -1, -1, -1, 0, -1, 0 ] -> 6,
		[ 0, -1, 0, 1, 0, -1, 0 ] -> 7,
		[ 0, 1, 0, 1, 0, 1, 0 ] -> 8,
		[ 0, -1, 0, 1, 1, -1, 0 ] -> 9,
		[ 0, -1, 0, -1, -1, -1, 0 ] -> 10,
		[ 0, 1, -1, -1, 0, 1, 0 ] -> 11
	]);

	tiling = tiling.collect({arg row, r;
		row.collect({arg tile, c;
			var resTile = case // add parity tiles
			{((c % 2) == 0) && ((r % 2) == 0)} {tile.collect({arg side; side + [0, 1, 0, 0, 0, 1, 0]})}
			{((c % 2) == 1) && ((r % 2) == 0)} {tile.collect({arg side, s;
				side + if((s % 2) == 0, {[0, -1, 0, 0, 0, -1, 0]}, {[0, 1, 0, 0, 0, 1, 0]})})}
			{((c % 2) == 0) && ((r % 2) == 1)} {tile.collect({arg side, s;
				side + if((s % 2) == 0, {[0, 1, 0, 0, 0, 1, 0]}, {[0, -1, 0, 0, 0, -1, 0]})})}
			{((c % 2) == 1) && ((r % 2) == 1)} {tile.collect({arg side, s; side + [0, -1, 0, 0, 0, -1, 0]})};

			//map colors
			resTile.collect({arg side, s;
				colorMap[switch(s, 0, {side.reverse}, 1, {side}, 2, {side.invert(0)}, 3, {side.reverse.invert(0)})]
			});
		});
	});

	tiling
};

//generate musical data
~robinsonMusicify = {arg tiling, xOffset, yOffset, width, height, size, minDur, maxDur, seed = 11735;
	var subTiling, seqs, secLengths, res;

	thisThread.randSeed = seed;

	subTiling = tiling.slice((yOffset..(yOffset + height)), (xOffset..(xOffset + width - 1)));

	seqs = (16 * 10).collect({arg col;
		var resOuter, max, sec;
		sec = (col / 16).asInteger;
		resOuter = 8.collect({arg row;
			var resInner;
			resInner = (subTiling[row + (sec * 8)][(col % 16) + (sec * 8)][0] * 2).collect({0}) ++
			(([0] ++ (3..15))[subTiling[row + (sec * 8)][(col % 16) + (sec * 8)][1]] * 3).collect({subTiling[row + (sec * 8)][(col % 16) + (sec * 8)][2]});
			resInner
		});

		resOuter = resOuter.collect({
			arg elem;
			var res;
			res = if(elem.flat.size > 0, {elem.separate({ arg a, b; a != b })}, {[[0]]});
			res = if(res.last[0] == 0, {res[..(res.size - 2)].flat}, {res.flat});
		});

		max = resOuter.collect({arg elem; elem.size}).maxItem;
		resOuter = resOuter.collect({arg elem; elem ++ (max - elem.size).collect({0})});
		resOuter
	});

	secLengths = [];
	res = 8.collect({arg part;
		var all, tuples, pair;
		tuples = [[0, 2, 4, 6], [1, 2, 3]].allTuples;

		all = seqs.slice(nil, part);

		all = all.clump(16).collect({arg elem;
			var len = elem.flat.size;
			if(part == 0, {secLengths = secLengths ++ [(len / 16).asInteger + 1]});
			elem.flat ++ (16 - (len % 16)).collect({0}) });

		all.flat.separate({ arg a, b; a != b }).collect({arg elem, e; [
			elem.size,
			part * 2 + 8 + tuples[elem[0]][0],
			if(elem[0] == 0, {0}, {0.125 / (tuples[elem[0]][1] * 2)}), tuples[elem[0]][1]]})
	});

	res = [res, secLengths];
	res
};

//create synths
~robinsonCreateSynths = {arg b;
	Buffer.freeAll(s);
	b = 8.collect({var buf = Buffer.alloc(s, 512, 1); buf.sine1(1.0 / 5.collect({arg i; pow(i + 1, 5.0.rand + 1)}), true, true, true)});

	SynthDef(\robinson, {arg freq, amp = 0.2, del = 5, gate = 1, sustain = 1, buf = 0;
		Out.ar([0, 1], Osc.ar(Select.kr(buf, b), freq, 0, amp) * EnvGen.kr(Env.sine(sustain + 0.01), gate, 1, 0, 1, doneAction: 2));
	}).add;
};


//generate patterns
~robinsonSonify = {arg seqs;
	var finSeqs;
	finSeqs = seqs[0].collect({arg r, i;
		Pbind(\instrument, \robinson,
			\buf, i,
			\freq, Pseq(12.midicps * r.slice(nil, 1)),
			\dur, Pseq(r.slice(nil, 0) * 0.25 * (3/4)),
			\sustain, Pseq(r.slice(nil, 0) * 0.25 * (3/4)),
			\amp, Pseq(r.slice(nil, 2))
		)
	});

	Ppar(finSeqs);
};
)


