Initial commit: Shelem card game
Full-stack multiplayer Shelem (Iranian trick-taking card game) with Socket.IO, JWT auth, bot players, joker mode, and mobile-friendly UI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+111
@@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
// Generates PNG icons with a Joker card motif on dark green background.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
|
||||
const outDir = path.join(__dirname, 'public', 'icons');
|
||||
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
||||
|
||||
function writePNG(filePath, size) {
|
||||
const channels = 4;
|
||||
const row = size * channels;
|
||||
const raw = Buffer.alloc(size * row);
|
||||
|
||||
// Background: dark forest green #133025
|
||||
const bgR = 0x13, bgG = 0x30, bgB = 0x25;
|
||||
// Gold color for Joker diamond: #f5c518
|
||||
const gR = 0xf5, gG = 0xc5, gB = 0x18;
|
||||
|
||||
const cx = size / 2, cy = size / 2;
|
||||
const dh = size * 0.35; // half-height of diamond
|
||||
const dw = size * 0.28; // half-width
|
||||
|
||||
for (let y = 0; y < size; y++) {
|
||||
for (let x = 0; x < size; x++) {
|
||||
const px = (y * size + x) * channels;
|
||||
const nx = x + 0.5, ny = y + 0.5;
|
||||
|
||||
// Rounded corners
|
||||
const cr = size * 0.2;
|
||||
const inCorner =
|
||||
(nx < cr && ny < cr && Math.hypot(nx - cr, ny - cr) > cr) ||
|
||||
(nx > size - cr && ny < cr && Math.hypot(nx - (size - cr), ny - cr) > cr) ||
|
||||
(nx < cr && ny > size - cr && Math.hypot(nx - cr, ny - (size - cr)) > cr) ||
|
||||
(nx > size - cr && ny > size - cr && Math.hypot(nx - (size - cr), ny - (size - cr)) > cr);
|
||||
if (inCorner) { raw[px + 3] = 0; continue; }
|
||||
|
||||
// Draw a diamond / rhombus shape (Joker card symbol)
|
||||
const dx = Math.abs(nx - cx) / dw;
|
||||
const dy = Math.abs(ny - cy) / dh;
|
||||
const inDiamond = dx + dy <= 1;
|
||||
|
||||
if (inDiamond) {
|
||||
raw[px] = gR;
|
||||
raw[px + 1] = gG;
|
||||
raw[px + 2] = gB;
|
||||
raw[px + 3] = 255;
|
||||
} else {
|
||||
raw[px] = bgR;
|
||||
raw[px + 1] = bgG;
|
||||
raw[px + 2] = bgB;
|
||||
raw[px + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chunks = [];
|
||||
chunks.push(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
||||
|
||||
function crc32(buf) {
|
||||
let c = 0xffffffff;
|
||||
const table = crc32.table || (crc32.table = (() => {
|
||||
const t = new Uint32Array(256);
|
||||
for (let i = 0; i < 256; i++) {
|
||||
let v = i;
|
||||
for (let k = 0; k < 8; k++) v = v & 1 ? 0xedb88320 ^ (v >>> 1) : v >>> 1;
|
||||
t[i] = v;
|
||||
}
|
||||
return t;
|
||||
})());
|
||||
for (let i = 0; i < buf.length; i++) c = table[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
|
||||
return (c ^ 0xffffffff) >>> 0;
|
||||
}
|
||||
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
||||
const tp = Buffer.from(type);
|
||||
const crc = Buffer.alloc(4);
|
||||
crc.writeUInt32BE(crc32(Buffer.concat([tp, data])));
|
||||
return Buffer.concat([len, tp, data, crc]);
|
||||
}
|
||||
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(size, 0); ihdr.writeUInt32BE(size, 4);
|
||||
ihdr[8] = 8; ihdr[9] = 6;
|
||||
chunks.push(chunk('IHDR', ihdr));
|
||||
|
||||
const filtered = Buffer.alloc(size * (row + 1));
|
||||
for (let y = 0; y < size; y++) {
|
||||
filtered[y * (row + 1)] = 0;
|
||||
raw.copy(filtered, y * (row + 1) + 1, y * row, (y + 1) * row);
|
||||
}
|
||||
chunks.push(chunk('IDAT', zlib.deflateSync(filtered)));
|
||||
chunks.push(chunk('IEND', Buffer.alloc(0)));
|
||||
|
||||
fs.writeFileSync(filePath, Buffer.concat(chunks));
|
||||
console.log(`${path.basename(filePath)} written (${size}×${size})`);
|
||||
}
|
||||
|
||||
writePNG(path.join(outDir, 'icon-192.png'), 192);
|
||||
writePNG(path.join(outDir, 'icon-512.png'), 512);
|
||||
|
||||
// SVG icon (scalable, used as favicon)
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<rect width="100" height="100" rx="20" fill="#133025"/>
|
||||
<polygon points="50,10 80,50 50,90 20,50" fill="#f5c518"/>
|
||||
<text x="50" y="56" font-size="22" text-anchor="middle" fill="#133025" font-family="serif" font-weight="bold">J</text>
|
||||
</svg>`;
|
||||
fs.writeFileSync(path.join(outDir, 'icon.svg'), svg);
|
||||
console.log('icon.svg written');
|
||||
console.log('Icons generated.');
|
||||
Reference in New Issue
Block a user