Spaces:
Running
Running
| export function shuffle(arr, rng = Math.random) { | |
| const a = [...arr]; | |
| for (let i = a.length - 1; i > 0; i--) { | |
| const j = Math.floor(rng() * (i + 1)); | |
| [a[i], a[j]] = [a[j], a[i]]; | |
| } | |
| return a; | |
| } | |
| export class Bag { | |
| constructor(ids, { rng = Math.random, state = null } = {}) { | |
| this.rng = rng; | |
| if (state && Array.isArray(state.bag) && state.bag.length) { | |
| this.ids = [...ids]; | |
| this.bag = state.bag; | |
| this.cursor = state.cursor ?? 0; | |
| this.round = state.round ?? 1; | |
| this.history = []; | |
| this.histPos = -1; | |
| this.lastShown = null; | |
| } else { | |
| this.setIds(ids); | |
| } | |
| } | |
| setIds(ids) { | |
| this.ids = [...ids]; | |
| this.bag = shuffle(this.ids, this.rng); | |
| this.cursor = 0; | |
| this.round = 1; | |
| this.history = []; | |
| this.histPos = -1; | |
| this.lastShown = null; | |
| } | |
| _pop() { | |
| if (this.cursor >= this.bag.length) { | |
| // new round: reshuffle, avoid immediate repeat of lastShown | |
| this.round += 1; | |
| this.cursor = 0; | |
| if (this.ids.length > 1) { | |
| do { this.bag = shuffle(this.ids, this.rng); } | |
| while (this.bag[0] === this.lastShown); | |
| } else { | |
| this.bag = shuffle(this.ids, this.rng); | |
| } | |
| } | |
| return this.bag[this.cursor++]; | |
| } | |
| next() { | |
| // if we stepped back, going next replays forward through history first | |
| if (this.histPos < this.history.length - 1) { | |
| this.histPos += 1; | |
| this.lastShown = this.history[this.histPos]; | |
| return this.lastShown; | |
| } | |
| const id = this._pop(); | |
| this.history.push(id); | |
| this.histPos = this.history.length - 1; | |
| this.lastShown = id; | |
| return id; | |
| } | |
| prev() { | |
| if (this.histPos <= 0) return null; | |
| this.histPos -= 1; | |
| this.lastShown = this.history[this.histPos]; | |
| return this.lastShown; | |
| } | |
| progress() { | |
| return { seen: Math.min(this.cursor, this.bag.length), total: this.bag.length, round: this.round }; | |
| } | |
| serialize() { | |
| return { bag: this.bag, cursor: this.cursor, round: this.round }; | |
| } | |
| } | |