UNPKG

universal-siteswap

Version:

A library for parsing, validating, examining and finding transitions between all types of siteswaps

420 lines 17.1 kB
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 { allPositions, Hand, JugglerBeat, JugglerBeats, Throw, } from './common'; import { Siteswap } from './siteswap'; var JugglerStateBeat = /** @class */ (function () { function JugglerStateBeat(LH, RH) { if (LH === void 0) { LH = 0; } if (RH === void 0) { RH = 0; } this.LH = LH; this.RH = RH; } JugglerStateBeat.prototype.increment = function (hand) { if (hand === Hand.Left) this.LH++; else this.RH++; }; JugglerStateBeat.prototype.decrement = function (hand) { if (hand === Hand.Left) this.LH--; else this.RH--; }; JugglerStateBeat.prototype.val = function (hand) { return hand === Hand.Left ? this.LH : this.RH; }; JugglerStateBeat.prototype.isSync = function () { return this.LH > 0 && this.RH > 0; }; JugglerStateBeat.prototype.isEmpty = function () { return this.LH === 0 && this.RH === 0; }; JugglerStateBeat.prototype.flip = function () { return new JugglerStateBeat(this.RH, this.LH); }; JugglerStateBeat.prototype.isLessOrEqual = function (other) { return this.LH <= other.LH && this.RH <= other.RH; }; JugglerStateBeat.prototype.toString = function (sync) { if (sync) { return "(".concat(this.LH, ",").concat(this.RH, ")"); } else { if (this.isSync()) { throw new Error('Attempt to use async toString on sync beat'); } // One of them must be zero so this is valid. return "".concat(this.LH + this.RH); } }; return JugglerStateBeat; }()); export { JugglerStateBeat }; var JugglerState = /** @class */ (function () { function JugglerState(beats) { this.beats = beats; } JugglerState.Empty = function (maxHeight) { var beats = []; for (var i = 0; i < maxHeight; i++) { beats.push(new JugglerStateBeat()); } return new JugglerState(beats); }; JugglerState.prototype.removeTrailingZeros = function () { while (this.beats.length && this.beats[this.beats.length - 1].isEmpty()) { this.beats.pop(); } }; JugglerState.prototype.isPureAsync = function () { // Async patterns can start with either hand var curHand = -1; for (var _i = 0, _a = this.beats; _i < _a.length; _i++) { var beat = _a[_i]; if (beat.isSync()) return false; if (beat.LH > 0) { if (curHand === Hand.Right) return false; curHand = Hand.Left; } else if (beat.RH > 0) { if (curHand === Hand.Left) return false; curHand = Hand.Right; } if (curHand !== -1) { curHand = 1 - curHand; } } return true; }; JugglerState.prototype.flip = function () { return new JugglerState(this.beats.map(function (j) { return j.flip(); })); }; JugglerState.prototype.toString = function () { var isAsync = this.isPureAsync(); return this.beats.map(function (b) { return b.toString(!isAsync); }).join(''); }; return JugglerState; }()); export { JugglerState }; function siteswapShorter(s1, s2) { return (s1.period < s2.period || (s1.period === s2.period && s1.toString().length < s2.toString().length)); } var State = /** @class */ (function () { function State(jugglers, trimZeros, jugglerDelays) { if (trimZeros === void 0) { trimZeros = true; } this.isGround = false; this.numObjects = 0; this.numJugglers = 0; this.maxHeight = 0; this.jugglers = jugglers; this.numJugglers = this.jugglers.length; this.jugglerDelays = jugglerDelays ? jugglerDelays : new Array(jugglers.length).fill(0); if (trimZeros) this.trimZeros(); this.recalc(); } State.prototype.recalc = function () { this.numObjects = 0; this.maxHeight = 0; this.isGround = true; var len = this.numJugglers > 0 ? this.jugglers[0].beats.length : 0; for (var _i = 0, _a = this.jugglers; _i < _a.length; _i++) { var state = _a[_i]; // All jugglers must be within a margin of 1 this.isGround && (this.isGround = Math.abs(state.beats.length - len) <= 1); this.isGround && (this.isGround = state.isPureAsync()); this.maxHeight = Math.max(this.maxHeight, state.beats.length); for (var _b = 0, _c = state.beats; _b < _c.length; _b++) { var beat = _c[_b]; this.numObjects += beat.LH + beat.RH; this.isGround && (this.isGround = beat.LH + beat.RH === 1); } } }; State.prototype.trimZeros = function () { for (var _i = 0, _a = this.jugglers; _i < _a.length; _i++) { var state = _a[_i]; state.removeTrailingZeros(); } }; State.prototype.at = function (position) { var beat = this.jugglers[position.juggler].beats[position.time]; return beat === undefined ? 0 : beat.val(position.hand); }; State.prototype.inc = function (position) { this.jugglers[position.juggler].beats[position.time].increment(position.hand); }; State.prototype.dec = function (position) { this.jugglers[position.juggler].beats[position.time].decrement(position.hand); }; State.prototype.flip = function () { return new State(this.jugglers.map(function (j) { return j.flip(); })); }; State.prototype.toString = function () { if (this.numJugglers === 1) return this.jugglers[0].toString(); var stateStr = this.jugglers.map(function (j) { return j.toString(); }).join('|'); return "<".concat(stateStr, ">"); }; State.prototype.entry = function (from, sync, allowFlipped) { if (sync === void 0) { sync = false; } if (allowFlipped === void 0) { allowFlipped = true; } if (!from) { from = State.GroundState(this.numObjects, sync, this.numJugglers); } return State.ShortestTransition(from, this, allowFlipped); }; State.prototype.exit = function (to, sync, allowFlipped) { if (sync === void 0) { sync = false; } if (allowFlipped === void 0) { allowFlipped = true; } if (!to) { to = State.GroundState(this.numObjects, sync, this.numJugglers); } return State.ShortestTransition(this, to, allowFlipped); }; State.prototype.globalStateBeat = function (time) { return this.jugglers.map(function (state) { return time < state.beats.length ? state.beats[time] : new JugglerStateBeat(0, 0); }); }; /* makeThrow(beat: JugglerBeat[]) { const front = this.state[0]; const nonZeros = beat.reduce((tot, cur) => tot + (cur != 0 ? 1 : 0), 0); if (nonZeros != front) { throw Error(`${front} non-zero throws expected but ${nonZeros} given.`); } const newState = this.state.slice(1); for (const th of beat) { while (newState.length < th) { newState.push(0); } if (th != 0) { newState[th-1]++; } } return new State(newState); } */ State.GroundState = function (numObjects, sync, numJugglers) { if (sync === void 0) { sync = false; } if (numJugglers === void 0) { numJugglers = 1; } var jugglers = []; var minNumObjects = Math.floor(numObjects / numJugglers); var maxNumObjects = Math.ceil(numObjects / numJugglers); for (var i = 0; i < numJugglers; i++) { var n = i < numObjects % numJugglers ? maxNumObjects : minNumObjects; var state = JugglerState.Empty(n); if (sync) { for (var j = 0; j < n; j += 2) { state.beats[j].increment(Hand.Right); if (n % 2 === 0 || j !== n - 1) { state.beats[j].increment(Hand.Left); } } } else { var curHand = Hand.Left; for (var j = 0; j < n; j++) { state.beats[j].increment(curHand); curHand = 1 - curHand; } } jugglers.push(state); } return new State(jugglers); }; State.Empty = function (numJugglers, maxHeight) { var state = []; for (var j = 0; j < numJugglers; j++) { state.push(JugglerState.Empty(maxHeight)); } return new State(state, /*trimZeros=*/ false); }; State.IsTransitionValid = function (s1, s2) { if (s1.numObjects !== s2.numObjects) { throw Error('States must be for the same number of objects.'); } if (s1.numJugglers !== s2.numJugglers) { throw Error('States must be for the same number of jugglers.'); } // TODO: there really should be a built in way of comparing arrays... if (!s1.jugglerDelays.every(function (v, i) { return v === s2.jugglerDelays[i]; })) { throw Error('Transitions between states with different juggler delays are not supported.'); } }; State.IsShiftValid = function (s1, s2, shift) { var _loop_1 = function (i) { var globalBeat1 = s1.globalStateBeat(shift + i); var globalBeat2 = s2.globalStateBeat(i); if (!globalBeat1.every(function (beat, i) { return beat.isLessOrEqual(globalBeat2[i]); })) { return { value: false }; } }; // Shift is valid when s2 is >= s1 at all points, e.g. // 11011 // 11101 for (var i = 0; i < Math.min(s2.maxHeight, s1.maxHeight - shift); i++) { var state_1 = _loop_1(i); if (typeof state_1 === "object") return state_1.value; } return true; }; State.ShortestTransitionLength = function (s1, s2) { State.IsTransitionValid(s1, s2); var shift = Math.max(0, s1.maxHeight - s2.maxHeight); for (; shift <= s1.maxHeight; shift++) { if (State.IsShiftValid(s1, s2, shift)) { return shift; } } // If they have the same number of objects, we should have found a valid shift. /* istanbul ignore next */ throw Error('Logic Error - this should never happen'); }; State.FindLandings = function (s1, s2, shift) { // Find landing times/positions needed // 11011 // 11101 // Lands: 2, 6 var lands = []; for (var _i = 0, _a = allPositions(s1.numJugglers, s2.maxHeight); _i < _a.length; _i++) { var pos = _a[_i]; var newLanding = s2.at(pos); pos.time += shift; var alreadyLanding = pos.time >= s1.maxHeight ? 0 : s1.at(pos); for (var j = 0; j < newLanding - alreadyLanding; j++) { lands.push(pos); } } return lands; }; State.GetTransition = function (s, length, land_times) { var upto = 0; var jugglers = []; for (var j = 0; j < s.numJugglers; j++) { var jugglerBeats = []; for (var i = 0; i < length; i++) { var beat = [[], []]; for (var _i = 0, _a = [Hand.Right, Hand.Left]; _i < _a.length; _i++) { var hand = _a[_i]; if (s.jugglers[j].beats[i] == null) { continue; } for (var k = 0; k < s.jugglers[j].beats[i].val(hand); k++) { var start = { juggler: j, time: i, hand: hand }; beat[hand].push(Throw.FromPositions(start, land_times[upto])); upto++; } } jugglerBeats.push(new JugglerBeat(beat[0], beat[1])); } jugglers.push(new JugglerBeats(jugglerBeats)); } return new Siteswap(jugglers); }; State.ShortestTransition = function (s1, s2, allowFlipped, allowExtraFlipped) { if (allowExtraFlipped === void 0) { allowExtraFlipped = false; } var best = State.BasicTransition(s1, s2); if (allowFlipped) { var flipA = State.BasicTransition(s1.flip(), s2); var flipB = State.BasicTransition(s1, s2.flip()); var flipAB = State.BasicTransition(s1.flip(), s2.flip()); if (siteswapShorter(flipA, best)) best = flipA; if (siteswapShorter(flipB, best)) best = flipB; if (siteswapShorter(flipAB, best)) best = flipAB; } if (allowExtraFlipped) { // Try flipping jugglers around and flipping hands of some jugglers // TODO: do this } return best; }; State.BasicTransition = function (s1, s2) { var length = State.ShortestTransitionLength(s1, s2); var lands = State.FindLandings(s1, s2, length); // Match landing positions to throw positions to get throws. // 0 -> 2 ::: 2 // 1 -> 6 ::: 5 return State.GetTransition(s1, length, lands); }; State.AllTransitionsOfLength = function (s1, s2, length) { var land_times, perm_length, c, i, k, p, t; return __generator(this, function (_a) { switch (_a.label) { case 0: State.IsTransitionValid(s1, s2); if (!State.IsShiftValid(s1, s2, length)) { return [2 /*return*/]; } land_times = State.FindLandings(s1, s2, length); return [4 /*yield*/, State.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; t = State.GetTransition(s1, length, land_times); return [4 /*yield*/, t]; 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 State; }()); export { State }; //# sourceMappingURL=state.js.map