import * as ejs from "ejs"; import { Article, CardItem, dictMap, filterDict, leanArticle, leanCard, MCMDB, onlyUnique, spanBy } from "."; const colorNames = { CL: "Colorless", MC: "Multicolor", L: "Land", W: "White", U: "Blue", B: "Black", R: "Red", G: "Green", WU: "Azorius", UB: "Dimir", BR: "Rakdos", RG: "Gruul", WG: "Selesnya", WB: "Orzhov", UR: "Izzet", BG: "Golgari", WR: "Boros", UG: "Simic", WUG: "Bant", WUB: "Esper", UBR: "Grixis", BRG: "Jund", WRG: "Naya", WBG: "Abzan", WUR: "Jeskai", UBG: "Sultai", WBR: "Mardu", URG: "Temur" }; const columns: Record boolean> = { W: c => colorid(c.colorIdentity) == "W", U: c => colorid(c.colorIdentity) == "U", B: c => colorid(c.colorIdentity) == "B", R: c => colorid(c.colorIdentity) == "R", G: c => colorid(c.colorIdentity) == "G", MC: c => c.colorIdentity.length > 1, CL: c => colorid(c.colorIdentity) == "CL" }; function wubrg(a: string, b: string) { const order = ["W", "U", "B", "R", "G"]; const indexA = order.indexOf(a); const indexB = order.indexOf(b); return indexA - indexB; } function colorid(colors: string[]): string { if (colors.length < 1) { return "CL"; } return colors.sort(wubrg).join(""); } function prettyColor(color: string) { if (color in colorNames) { return colorNames[color]; } return color; } const allTypes = [ "Creature", "Planeswalker", "Instant", "Sorcery", "Artifact", "Enchantment" ]; function typeSort(a: string, b: string): number { const indexA = allTypes.indexOf(a); const indexB = allTypes.indexOf(b); return indexA - indexB; } function abcSort(a: string, b: string): number { if (a > b) { return 1; } if (b > a) { return -1; } return 0; } // This would be properly typed but as of currently TS does not allow circular references for types function deepSum(dict: any): number { if (Array.isArray(dict)) { return dict.length; } let total = 0; for (let key in dict) { if (Array.isArray(dict[key])) { total += dict[key].length; } else { total += deepSum(dict[key]); } } return total; } export function cubeCards(c: CubeCards): CardItem[] { let cards: CardItem[] = []; dictMap(c, bytyp => dictMap(bytyp, bycmc => dictMap(bycmc, cardlist => (cards = cards.concat(cardlist))) ) ); return cards; } function byRank(a: CardItem, b: CardItem): number { return a.edhrecRank - b.edhrecRank; } export type ByCMC = Record; export type ByType = Record; export type ByColor = Record; export type CubeCards = ByColor>>; export async function genCube( db: MCMDB, articles: Article[] ): Promise { const cards: CardItem[] = articles .filter(art => art.idProduct in db) .map(art => { const card = db[art.idProduct]; return { ...leanArticle(art), ...leanCard(card) }; }); const valid = cards .filter( c => (c.language == "Italian" || c.language == "English") && c.price < 0.2 && (c.rarity == "rare" || c.rarity == "mythic") ) .filter(onlyUnique); const categorized = filterDict(valid, columns); // Cut cards const filtered = dictMap(categorized, (pool, col) => { switch (col) { case "MC": const colors = spanBy(pool, c => colorid(c.colorIdentity)); let cards = []; for (const color in colors) { cards = cards.concat(colors[color].sort(byRank).slice(0, 3)); } return cards; case "CL": return pool.sort(byRank).slice(0, 30); default: return pool.sort(byRank).slice(0, 58); } }); // Filter by colors and cmc (nested) return dictMap(filtered, (cards, col) => dictMap( spanBy(cards, c => col == "MC" || col == "L" ? colorid(c.colorIdentity) : c.types.slice(-1)[0] ), typed => spanBy(typed, c => c.convertedManaCost) ) ); } export async function cubeHTML(user, cubecards: CubeCards) { return await ejs.renderFile("templates/cube.ejs", { user, cards: cubeCards(cubecards), cubecards, columns, utils: { wubrg, prettyColor, colorid, deepSum, typeSort, abcSort } }); }