UNPKG

ydeck

Version:

Parse, validate, and classify Yu-Gi-Oh! decklists

280 lines 9.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.classify = void 0; /** * Helper for setCodeClassifer. Copied from ygo-data's CardData.isSetcode. * Note: despite outward appearances, because JavaScript numbers are 64-bit floats, * JavaScript bitwise operations only work on 32-bit integers, and the setcode bit * array is a 64-bit integer, only the first two setcodes are actually checked. * @param card * @param searchSet The specific 16-bit setcode to check for in the card's setcode bit array * @returns */ function hasSetcode(card, searchSet) { let cardSet = card?.setcode || 0; const searchBase = searchSet & 0xfff; // the base bytes of the set we're searching for, ignoring sub-archetypes while (cardSet > 0) { const cardBase = cardSet & 0xfff; if (cardBase === searchBase && // same base archetype (cardSet & searchSet) === searchSet // since we know the base is the same, this only checks the first digit, for sub-archetypes ) { return true; } cardSet >>= 16; } return false; } function setCodeClassifier(setCode, mainThreshold, extraThreshold) { return function (deck, _, data) { // if no extraThreshold, assume mainThreshold applies to whole deck. Set extraThreshold to 0 to suppress this behaviour // we ignore the side deck, fairly safe assumption that it's usually staples // or a smokescreen strategy that isn't part of what the deck is pre-side let main; if (extraThreshold === undefined) { main = [...deck.main, ...deck.extra]; } else { main = [...deck.main]; } const mainCards = main.map(c => data.get(c)); let out = mainCards.filter(c => hasSetcode(c, setCode)).length >= mainThreshold; if (extraThreshold !== undefined) { const extraCards = [...deck.extra].map(c => data.get(c)); out = out && extraCards.filter(c => c && hasSetcode(c, setCode)).length >= extraThreshold; } return out; }; } function cardCodeClassifier(requirements) { // possibly allow ORs? e.g mystic mine is mine AND (dd guide OR wobby OR cannon OR countdown) return function (_, deckVector) { // We're actually computing the vector difference here, but we only care about // whether the requirements multiset is a subset of the deck multiset for (const password in requirements) { if (requirements[password] > (deckVector.get(Number(password)) || 0)) { return false; } } return true; }; } const mekkKnight = setCodeClassifier(0x10c, 5); const mysticMine = cardCodeClassifier({ 76375976: 3, 7153114: 1 // Field Barrier }); const virtualWorld = setCodeClassifier(0x150, 5); const drytron = cardCodeClassifier({ 22398665: 1 // Meteonis Drytron }); const infernoble = (deck, vec, data) => { // noble knight + charles return setCodeClassifier(0x107a, 5)(deck, vec, data) && cardCodeClassifier({ 77656797: 1 })(deck, vec, data); }; const dlink = cardCodeClassifier({ 5969957: 1 // Rokket Recharger }); const dino = cardCodeClassifier({ 18940556: 1 // UCT }); const eldlich = (deck, vec, data) => { // eldlich + eldlixir + golden land return (setCodeClassifier(0x142, 1)(deck, vec, data) && setCodeClassifier(0x143, 1)(deck, vec, data) && setCodeClassifier(0x144, 1)(deck, vec, data)); }; const zoodiac = setCodeClassifier(0xf1, 5); const invoked = (deck, vec, data) => { // invoked + aleister + invoc return (setCodeClassifier(0xf4, 1)(deck, vec, data) && cardCodeClassifier({ 86120751: 1, 74063034: 1 })(deck, vec, data)); }; const dogma = (deck, vec, data) => { // dogma + nadir servant return setCodeClassifier(0x146, 2)(deck, vec, data) && cardCodeClassifier({ 1984618: 1 })(deck, vec, data); }; const pk = cardCodeClassifier({ 26692769: 1 // The Phantom Knights of Rusty Bardiche }); const pranks = setCodeClassifier(0x120, 5); const blader = cardCodeClassifier({ 11790356: 1, 86240887: 1, 32104431: 1, 33280639: 1 // Synchro trap }); const striker = cardCodeClassifier({ 63166095: 1 // Sky Striker Mobilize - Engage! }); const geist = setCodeClassifier(0x103, 5); const dolls = setCodeClassifier(0x9d, 5); const tribe = setCodeClassifier(0x14f, 5); const lyrilusc = setCodeClassifier(0xf7, 5); const maju = cardCodeClassifier({ 36584821: 1 // Gren Maju }); const ba = setCodeClassifier(0xb1, 5); const salad = setCodeClassifier(0x119, 5); const guru = setCodeClassifier(0xed, 5); const adam = setCodeClassifier(0x140, 5); const dolche = setCodeClassifier(0x71, 5); const numer = setCodeClassifier(0x14b, 5); const amazement = cardCodeClassifier({ 94821366: 1 // Amazement Administrator Arlekino }); const plant = cardCodeClassifier({ 21200905: 1 // Aromaseraphy Jasmine }); const paleo = setCodeClassifier(0xd4, 5); const fluffal = cardCodeClassifier({ 70245411: 1 // Toy Vendor }); const plunder = cardCodeClassifier({ 31374201: 1 // Whitebeard, the Plunder Patroll Helm }); const orcust = cardCodeClassifier({ 30741503: 1 // Galatea, the Orcust Automaton }); const swordsoul = cardCodeClassifier({ 14821890: 1 // Swordsoul Blackout }); const flunder = cardCodeClassifier({ 18940725: 1 // Floowandereeze & Robina }); const twins = cardCodeClassifier({ 36609518: 1, 9205573: 1 // Evil★Twin Ki-sikil }); const peng = cardCodeClassifier({ 14761450: 1 // Penguin Squire }); const branded = cardCodeClassifier({ 44362883: 1 // Branded Fusion }); const despia = cardCodeClassifier({ 62962630: 1 // Aluber the Jester of Despia }); const therion = cardCodeClassifier({ 10604644: 1 // Therion "King" Regulus }); const sunavalon = cardCodeClassifier({ 27520594: 1 // Sunseed Genius Loci }); const brave = cardCodeClassifier({ 3285551: 1 // Rite of Aramesir }); const punk = cardCodeClassifier({ 55920742: 1 // Noh-P.U.N.K. Foxy Tune }); const predap = cardCodeClassifier({ 44932065: 1 // Predaplant Byblisp }); const spright = cardCodeClassifier({ 76145933: 1 // Spright Blue }); const tearlaments = cardCodeClassifier({ 37961969: 1 // Tearlaments Havnis }); const ghoti = cardCodeClassifier({ 46037983: 1 // Paces, Light of the Ghoti }); const vernusylph = cardCodeClassifier({ 62133026: 1 // Vernusylph of the Flowering Fields }); const exosister = cardCodeClassifier({ 37343995: 1 // Exosister Martha }); const mathmech = cardCodeClassifier({ 36521307: 1 // Mathmech }); const labrynth = cardCodeClassifier({ 5380979: 1 // Welcome Labrynth }); const runick = cardCodeClassifier({ 55990317: 1 // Hugin the Runick Wings }); const vaylantz = setCodeClassifier(0x17e, 5); const ishizu = cardCodeClassifier({ 63542003: 1 // Keldo the Sacred Protector }); const bystial = cardCodeClassifier({ 33854624: 1 // Bystial Magnamhut }); const dracoslayer = cardCodeClassifier({ 92332424: 1 // Majesty Pegasus, the Dracoslayer }); const naturia = cardCodeClassifier({ 29942771: 1 // Naturia Camellia }); const darkworld = cardCodeClassifier({ 33017655: 1 // The Gates of Dark World }); const classifiers = { "Mekk-Knight": mekkKnight, "Mystic Mine": mysticMine, "Virtual World": virtualWorld, Drytron: drytron, "Infernoble Knight": infernoble, "Dragon Link": dlink, Dinosaur: dino, Eldlich: eldlich, Zoodiac: zoodiac, Invoked: invoked, Dogmatika: dogma, "Phantom Knight": pk, "Prank-Kids": pranks, "Buster Blader": blader, "Sky Striker": striker, Altergeist: geist, Shaddoll: dolls, "Tri-Brigade": tribe, Lyrilusc: lyrilusc, "Gren Maju": maju, "Burning Abyss": ba, Salamangreat: salad, Subterror: guru, Adamancipator: adam, Madolche: dolche, Numeron: numer, Amazement: amazement, Plant: plant, Paleozoic: paleo, Fluffal: fluffal, "Plunder Patroll": plunder, Orcust: orcust, Swordsoul: swordsoul, Floowandereeze: flunder, "Evil★Twin": twins, Penguin: peng, Branded: branded, Despia: despia, Therion: therion, Sunavalon: sunavalon, Adventurer: brave, "P.U.N.K.": punk, Predaplant: predap, Spright: spright, Tearlaments: tearlaments, Ghoti: ghoti, Vernusylph: vernusylph, Exosister: exosister, Mathmech: mathmech, Labrynth: labrynth, Runick: runick, Vaylantz: vaylantz, Ishizu: ishizu, Bystial: bystial, Dracoslayer: dracoslayer, Naturia: naturia, "Dark World": darkworld }; function classify(deck, vector, data) { return Object.entries(classifiers) .filter(([, match]) => match(deck, vector, data)) .map(([theme]) => theme); } exports.classify = classify; //# sourceMappingURL=classify.js.map