More work on cube html

This commit is contained in:
Hamcha 2019-11-22 11:47:27 +01:00
parent 9953f6c289
commit e7e7426da1
Signed by: hamcha
GPG key ID: 44AD3571EB09A39E
3 changed files with 182 additions and 23 deletions

View file

@ -2,11 +2,23 @@ import * as ejs from "ejs";
import { existsSync } from "fs"; import { existsSync } from "fs";
import { createServer } from "http"; import { createServer } from "http";
import { Article, asyncLoadJSON, CardItem, leanArticle, leanCard, MCMDB, onlyUnique } from "../lib"; import {
Article,
asyncLoadJSON,
CardItem,
dictMap,
filterDict,
leanArticle,
leanCard,
MCMDB,
onlyUnique,
spanBy,
} from "../lib";
const colorNames = { const colorNames = {
CL: "Colorless", CL: "Colorless",
MC: "Multicolor", MC: "Multicolor",
L: "Land",
W: "White", W: "White",
U: "Blue", U: "Blue",
B: "Black", B: "Black",
@ -35,13 +47,14 @@ const colorNames = {
}; };
const columns: Record<string, (c: CardItem) => boolean> = { const columns: Record<string, (c: CardItem) => boolean> = {
W: c => colorid(c.colorIdentity) == "W", W: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "W",
U: c => colorid(c.colorIdentity) == "U", U: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "U",
B: c => colorid(c.colorIdentity) == "B", B: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "B",
R: c => colorid(c.colorIdentity) == "R", R: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "R",
G: c => colorid(c.colorIdentity) == "G", G: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "G",
MC: c => c.colorIdentity.length > 0, MC: c => c.types[0] != "Land" && c.colorIdentity.length > 1,
CL: c => colorid(c.colorIdentity) == "CL" CL: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "CL",
L: c => c.types[0] == "Land"
}; };
function wubrg(a: string, b: string) { function wubrg(a: string, b: string) {
@ -58,6 +71,53 @@ function colorid(colors: string[]): string {
return colors.sort(wubrg).join(""); 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;
}
async function run() { async function run() {
if (process.argv.length < 3) { if (process.argv.length < 3) {
console.error("Usage: yarn fetch <uid>"); console.error("Usage: yarn fetch <uid>");
@ -96,15 +156,27 @@ async function run() {
) )
.filter(onlyUnique); .filter(onlyUnique);
// Filter by colors and cmc (nested)
let colorcards = filterDict(valid, columns);
let cmccards = dictMap(colorcards, (cards, col) =>
dictMap(
spanBy(cards, c =>
col == "MC" || col == "L" ? colorid(c.colorIdentity) : c.types[0]
),
typed => spanBy(typed, c => c.convertedManaCost)
)
);
const server = createServer(async (req, res) => { const server = createServer(async (req, res) => {
const template = await ejs.renderFile("templates/cube.ejs", { const template = await ejs.renderFile("templates/cube.ejs", {
user: uid, user: uid,
cards: valid, cards: valid,
cmccards,
columns, columns,
utils: { wubrg, colorNames, colorid } utils: { wubrg, prettyColor, colorid, deepSum, typeSort, abcSort }
}); });
res.end(template); res.end(template);
}); });
server.on("clientError", (err, socket) => { server.on("clientError", (err, socket) => {
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n"); socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
}); });

View file

@ -61,3 +61,41 @@ export function leanArticle(article: Article): LeanArticle {
export function onlyUnique(value: CardItem, index: number, self: CardItem[]) { export function onlyUnique(value: CardItem, index: number, self: CardItem[]) {
return self.findIndex(c => c.name == value.name) == index; return self.findIndex(c => c.name == value.name) == index;
} }
export function filterDict<K extends keyof any, T>(
arr: T[],
list: Record<K, (i: T) => boolean>
): Record<K, T[]> {
let out: Record<K, T[]> = Object.create(null);
for (const key in list) {
out[key] = arr.filter(list[key]);
}
return out;
}
export function spanBy<K extends keyof any, T>(
arr: T[],
spanFn: (i: T) => K
): Record<K, T[]> {
let out: Record<K, T[]> = Object.create(null);
arr.forEach(item => {
let key = spanFn(item);
if (key in out) {
out[key].push(item);
} else {
out[key] = [item];
}
});
return out;
}
export function dictMap<K extends keyof any, A, B>(
dict: Record<K, A>,
mapFn: (i: A, k: K) => B
): Record<K, B> {
let out: Record<K, B> = Object.create(null);
for (let key in dict) {
out[key] = mapFn(dict[key], key);
}
return out;
}

View file

@ -10,7 +10,7 @@
main { main {
background: white; background: white;
max-width: 920px; max-width: 1220px;
margin: 0 auto; margin: 0 auto;
padding: 20pt; padding: 20pt;
} }
@ -33,58 +33,107 @@
.color-section { .color-section {
flex: 1; flex: 1;
margin: 5px;
} }
.color-section header { .color-section header {
text-align: center; text-align: center;
} }
.color-W header { .color-W header.color-header {
background-color: #fff6eb; background-color: #fff6eb;
} }
.color-U header { .color-U header.color-header {
background-color: #49b0fd; background-color: #49b0fd;
} }
.color-B header { .color-B header.color-header {
background-color: #666; background-color: #666;
color: white; color: white;
} }
.color-R header { .color-R header.color-header {
background-color: #ff6868; background-color: #ff6868;
} }
.color-G header { .color-G header.color-header {
background-color: #78ce59; background-color: #78ce59;
} }
.color-MC header { .color-MC header.color-header {
background: linear-gradient(to left, #f3ff6b, #ffb019); background: linear-gradient(to left, #f3ff6b, #ffb019);
} }
.color-CL header { .color-CL header.color-header {
background-color: #d2d2d2; background-color: #d2d2d2;
} }
.color-L header.color-header {
background-color: #eeb445;
}
.type-section {
border: 1px solid #aaa;
border-radius: 5px;
font-size: 10pt;
margin: 5px 0;
}
.type-section header.type-header {
font-size: 12pt;
font-weight: bold;
border-bottom: 1px solid #ccc;
padding: 5px;
}
ul {
margin: 0;
padding: 0;
list-style-type: none;
border-bottom: 1px solid #ccc;
}
ul li {
padding: 5px;
}
ul:last-child {
border-bottom: none;
}
</style> </style>
</head> </head>
<body> <body>
<main> <main>
<header> <header>
<h1>Junk rare cube</h1> <h1>Junk rare cube (<%=cards.length%> cards)</h1>
<h2>Using cards from <a href="https://www.cardmarket.com/en/Magic/Users/<%=user%>"><b><%= user %></b></a> <h2>Using cards from <a href="https://www.cardmarket.com/en/Magic/Users/<%=user%>"><b><%= user %></b></a>
</h2> </h2>
</header> </header>
<section class="columns"> <section class="columns">
<% for (const column in columns) { %> <% Object.entries(cmccards).forEach(([color, colorcards]) => { %>
<section class="color-section color-<%=column%>"> <section class="color-section color-<%=color%>">
<header> <header class="color-header">
<h3><%=utils.colorNames[column]%></h3> <h3><%=utils.prettyColor(color)%> (<%=utils.deepSum(colorcards)%>)</h3>
</header> </header>
<% Object.entries(colorcards).sort((a, b) => color == "MC" || color == "L" ? utils.abcSort(a[0], b[0]) : utils.typeSort(a[0], b[0])).forEach(([type, typecards]) => { %>
<article class="type-section">
<header class="type-header"><%= color == "MC" || color == "L" ? utils.prettyColor(type) : type %>
(<%=utils.deepSum(typecards)%>)</header>
<section class="cards">
<% Object.entries(typecards).forEach(([cmc, finalcards]) => { %>
<ul>
<% finalcards.forEach(card => { %>
<li><%= card.name %></li>
<% }); %>
</ul>
<% }); %>
</section> </section>
<% } %> </article>
<% }); %>
</section>
<% }); %>
</section> </section>
</main> </main>
</body> </body>