143 lines
3.7 KiB
JavaScript
143 lines
3.7 KiB
JavaScript
function filter(type, text) {
|
|
if (text.match(/insert name/i)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function sanitize(type, text) {
|
|
// Trailing periods.
|
|
if ('white' === type) {
|
|
text = text.replace(/(.*)([^.]+)\.+$/, '$1$2');
|
|
}
|
|
// Teh dumb.
|
|
text = text.replace('better then', 'better than');
|
|
text = text.replace('worse then', 'worse than');
|
|
text = text.replace('One in then', 'One in ten');
|
|
text = text.replace('_ then _', '_ than _');
|
|
text = text.replace('pount', 'pound');
|
|
return text;
|
|
}
|
|
|
|
function isCompactJson(json) {
|
|
return !Array.isArray(json);
|
|
}
|
|
|
|
function getCardText(card) {
|
|
return 'string' === typeof card ? card : card.text;
|
|
}
|
|
|
|
function readCompactJsonPacks(json) {
|
|
const packs = [];
|
|
for (let j = 0; j < json.packs.length; ++j) {
|
|
const pack = json.packs[j];
|
|
let tokens = undefined;
|
|
const black = pack.black
|
|
.filter((k) => filter('black', getCardText(json.black[k])))
|
|
.map((k) => sanitize('black', getCardText(json.black[k])));
|
|
black.forEach((text, i) => {
|
|
const matches = text.match(/\[([^\]]+)\]/g);
|
|
if (matches) {
|
|
if (!tokens) {
|
|
tokens = {};
|
|
}
|
|
if (!tokens[i]) {
|
|
tokens[i] = {};
|
|
}
|
|
tokens[i].black = matches.map((match) => match.slice(1, -1));
|
|
}
|
|
});
|
|
const white = pack.white
|
|
.filter((k) => filter('white', getCardText(json.white[k])))
|
|
.map((k) => sanitize('white', getCardText(json.white[k])));
|
|
white.forEach((text, i) => {
|
|
const matches = text.match(/\[([^\]]+)\]/g);
|
|
if (matches) {
|
|
if (!tokens) {
|
|
tokens = {};
|
|
}
|
|
if (!tokens[i]) {
|
|
tokens[i] = {};
|
|
}
|
|
tokens[i].white = matches.map((match) => match.slice(1, -1));
|
|
}
|
|
});
|
|
packs.push({
|
|
...pack,
|
|
black,
|
|
white,
|
|
tokens,
|
|
});
|
|
}
|
|
return packs;
|
|
}
|
|
|
|
function normalizeToCompactJson(json) {
|
|
const normalized = {
|
|
black: [],
|
|
white: [],
|
|
packs: [],
|
|
};
|
|
for (let j = 0; j < json.length; ++j) {
|
|
const pack = json[j];
|
|
normalized.packs.push({
|
|
...pack,
|
|
black: pack.black.map((card) => normalized.black.push(getCardText(card)) - 1),
|
|
white: pack.white.map((card) => normalized.white.push(getCardText(card)) - 1),
|
|
});
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
function readFullJsonPacks(json) {
|
|
return readCompactJsonPacks(normalizeToCompactJson(json));
|
|
}
|
|
|
|
export function readJsonPacks(json) {
|
|
return isCompactJson(json)
|
|
? readCompactJsonPacks(json)
|
|
: readFullJsonPacks(json);
|
|
}
|
|
|
|
export function shuffle(cards) {
|
|
let currentIndex = cards.length, randomIndex;
|
|
while (currentIndex > 0) {
|
|
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
currentIndex--;
|
|
[cards[currentIndex], cards[randomIndex]] = [cards[randomIndex], cards[currentIndex]];
|
|
}
|
|
return cards;
|
|
}
|
|
// Algorithm L(-ish :))
|
|
export function sample(packs, k) {
|
|
const n = packs.reduce((n, {cards: {length}}) => n + length, 0);
|
|
const reservoir = Array(k);
|
|
// Encode the "stream" into a jump table.
|
|
const jump = [];
|
|
let i;
|
|
for (i = 0; i < packs.length; ++i) {
|
|
jump[i] = 0 === i ? 0 : jump[i - 1] + packs[i - 1].cards.length;
|
|
}
|
|
let c = 0, d = 0;
|
|
for (i = 0; i < k; ++i) {
|
|
reservoir[i] = {pack: packs[d].id, card: c};
|
|
if (++c === packs[d].cards.length) {
|
|
c = 0;
|
|
d += 1;
|
|
}
|
|
}
|
|
let W = Math.exp(Math.log(Math.random()) / k);
|
|
let j = d;
|
|
while (i < n) {
|
|
i += Math.floor(Math.log(Math.random()) / Math.log(1 - W)) + 1;
|
|
while (j < jump.length - 1 && i >= jump[j + 1]) {
|
|
j += 1;
|
|
}
|
|
if (i < n) {
|
|
reservoir[Math.floor(Math.random() * k)] = {pack: packs[j].id, card: i - jump[j]};
|
|
W = W * Math.exp(Math.log(Math.random()) / k);
|
|
}
|
|
}
|
|
return reservoir;
|
|
}
|