universal-siteswap
Version:
A library for parsing, validating, examining and finding transitions between all types of siteswaps
259 lines • 11 kB
JavaScript
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import { intToSS, ssToInt } from "./common";
function sum(arr) {
return arr.reduce(function (a, b) { return a + b; }, 0);
}
var VanillaState = /** @class */ (function () {
function VanillaState(state) {
this.isGround = false;
this.numObjects = 0;
this.maxHeight = 0;
this.state = state;
while (this.state.length > 0) {
var test_1 = this.state.pop();
if (test_1 !== 0) {
this.state.push(test_1);
break;
}
}
this.numObjects = sum(this.state);
this.maxHeight = this.state.length;
this.isGround = this.state.every(function (x) { return x === 1; });
}
VanillaState.prototype.toString = function () {
return this.state.join('');
};
VanillaState.prototype.entry = function (from) {
return VanillaState.ShortestTransition(from ? from : VanillaState.GroundState(this.numObjects), this);
};
VanillaState.prototype.exit = function (to) {
return VanillaState.ShortestTransition(this, to ? to : VanillaState.GroundState(this.numObjects));
};
VanillaState.GroundState = function (numObjects) {
return new VanillaState(new Array(numObjects).fill(1));
};
VanillaState.IsShiftValid = function (s1, s2, shift) {
// Shift is valid when s2 is >= s1 at all points
for (var i = 0; i < Math.min(s2.maxHeight, s1.maxHeight - shift); i++) {
if (i + shift < s1.state.length && s1.state[i + shift] > s2.state[i])
return false;
}
return true;
};
VanillaState.ShortestTransitionLength = function (s1, s2) {
if (s1.numObjects !== s2.numObjects) {
throw Error('States must be for the same number of objects.');
}
var shift = Math.max(0, s1.maxHeight - s2.maxHeight);
while (!this.IsShiftValid(s1, s2, shift))
shift++;
return shift;
};
VanillaState.FindLandings = function (s1, s2, shift) {
var lands = [];
for (var i = 0; i < s2.maxHeight; i++) {
var newLanding = s2.state[i];
var alreadyLanding = i + shift >= s1.maxHeight ? 0 : s1.state[i + shift];
lands.push.apply(lands, Array(newLanding - alreadyLanding).fill(i + shift));
}
return lands;
};
VanillaState.GetTransition = function (s, length, land_times) {
var upto = 0;
var throws = [];
for (var i = 0; i < length; i++) {
var beat = [];
for (var k = 0; k < s.state[i]; k++) {
beat.push(land_times[upto++] - i);
}
throws.push(beat.length > 0 ? beat : [0]);
}
return new VanillaSiteswap(throws);
};
VanillaState.ShortestTransition = function (s1, s2) {
var length = VanillaState.ShortestTransitionLength(s1, s2);
var lands = VanillaState.FindLandings(s1, s2, length);
return VanillaState.GetTransition(s1, length, lands);
};
VanillaState.AllTransitionsOfLength = function (s1, s2, length) {
var land_times, perm_length, c, i, k, p;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (s1.numObjects !== s2.numObjects) {
throw Error('States must be for the same number of objects.');
}
if (!VanillaState.IsShiftValid(s1, s2, length)) {
return [2 /*return*/];
}
land_times = VanillaState.FindLandings(s1, s2, length);
return [4 /*yield*/, VanillaState.GetTransition(s1, length, land_times)];
case 1:
_a.sent();
perm_length = land_times.length;
c = new Array(perm_length).fill(0);
i = 1;
_a.label = 2;
case 2:
if (!(i < perm_length)) return [3 /*break*/, 6];
if (!(c[i] < i)) return [3 /*break*/, 4];
k = i % 2 && c[i];
p = land_times[i];
land_times[i] = land_times[k];
land_times[k] = p;
++c[i];
i = 1;
return [4 /*yield*/, VanillaState.GetTransition(s1, length, land_times)];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4:
c[i] = 0;
++i;
_a.label = 5;
case 5: return [3 /*break*/, 2];
case 6: return [2 /*return*/];
}
});
};
return VanillaState;
}());
export { VanillaState };
var VanillaSiteswap = /** @class */ (function () {
function VanillaSiteswap(throws) {
this.numObjects = 0;
this.period = 0;
this.maxHeight = 0;
this.maxMultiplex = 0;
this.isValid = false;
this.errorMessage = '';
this.throws = throws;
this.state = new VanillaState([]); // Make compiler happy
this.validate();
}
VanillaSiteswap.prototype.validate = function () {
this.isValid = false;
this.errorMessage = '';
this.period = this.throws.length;
this.maxMultiplex = Math.max.apply(Math, this.throws.map(function (j) { return j.length; }));
this.maxHeight = Math.max.apply(Math, this.throws.map(function (j) { return Math.max.apply(Math, j); }));
this.numObjects = sum(this.throws.map(sum)) / this.period;
if (this.numObjects % 1 !== 0) {
this.errorMessage = 'Invalid pattern average';
return false;
}
// `check` stores the number of throws which should land for beat
var check = this.throws.map(function (j) { return j.length; });
var state = new Array(this.maxHeight).fill(0);
for (var i = 0; i < this.period; i++) {
for (var _i = 0, _a = this.throws[i]; _i < _a.length; _i++) {
var th = _a[_i];
var landTime = (i + th) % this.period;
// Check landing position
if (check[landTime] === 0) {
this.errorMessage = "Collision at time ".concat(landTime);
return false;
}
check[landTime]--;
// Add to state, first at original land time, ignoring those landing before the end of the siteswap
for (var curTime = i + th - this.period; curTime >= 0; curTime -= this.period) {
state[curTime]++;
}
}
}
this.state = new VanillaState(state);
this.isValid = true;
return true;
};
VanillaSiteswap.prototype.toString = function () {
return this.throws.map(function (th) { return th.length === 1 ? intToSS(th[0]) : '[' + th.map(intToSS).join('') + ']'; }).join('');
};
VanillaSiteswap.prototype.toStack = function () {
if (this.maxMultiplex > 1) {
throw "Stack notation cannot deal with multiplexes";
}
// For each throw, for each beat before it lands, subtract one if the throw at that beat lands before it
var stack = [];
for (var i = 0; i < this.period; i++) {
var n = this.throws[i][0];
for (var j = 1; j < this.throws[i][0]; j++) {
if (j + this.throws[(i + j) % this.period][0] < this.throws[i][0])
n--;
}
stack.push(n);
}
return stack;
};
VanillaSiteswap.prototype.toStackString = function () {
return this.toStack().map(function (x) { return intToSS(x); }).join('');
};
VanillaSiteswap.ParseStack = function (input) {
var stack = input.replace(/ /g, '');
var throws = [];
for (var i = 0; i < stack.length; i++) {
var ss = ssToInt(stack[i]);
var place = ss;
for (var j = 1; place > 0; j++) {
if (ssToInt(stack[(i + j) % stack.length]) < place)
ss++;
else
place--;
}
throws.push([ss]);
}
return new VanillaSiteswap(throws);
};
VanillaSiteswap.Parse = function (input) {
var multiplex = false;
var throws = [];
var multiplexThrows = [];
for (var _i = 0, _a = input.replace(/ /g, ''); _i < _a.length; _i++) {
var i = _a[_i];
if (i == '[') {
multiplex = true;
multiplexThrows = [];
}
else if (multiplex && i == ']') {
multiplex = false;
throws.push(multiplexThrows);
}
else if (multiplex) {
multiplexThrows.push(ssToInt(i));
}
else {
throws.push([ssToInt(i)]);
}
}
return new VanillaSiteswap(throws);
;
};
return VanillaSiteswap;
}());
export { VanillaSiteswap };
//# sourceMappingURL=vanilla-siteswap.js.map