/** * 스플렌더 component data (docs/games/splendor.md §3.1, §3.2). Kept in a separate data file so the card * list can be swapped for an own list with the same structure (§12). Encoding: `::`. */ export const COLORS = ['W', 'U', 'G', 'R', 'K'] as const; export type Color = (typeof COLORS)[number]; export type TokenColor = Color | 'gold'; export const TOKEN_COLORS: readonly TokenColor[] = [...COLORS, 'gold']; export type Gems = Record; export type Tokens = Record; export type Tier = 1 | 2 | 3; export const TIERS: readonly Tier[] = [1, 2, 3]; export interface DevCard { id: string; tier: Tier; bonus: Color; points: number; cost: Gems; } export interface Noble { id: string; points: 3; req: Gems; } const RAW_CARDS = [ // tier 1 '1W01:0:01111', '1W02:0:01211', '1W03:0:02201', '1W04:0:31001', '1W05:0:00021', '1W06:0:02002', '1W07:0:03000', '1W08:1:00400', '1U01:0:10111', '1U02:0:10121', '1U03:0:10220', '1U04:0:01310', '1U05:0:10002', '1U06:0:00202', '1U07:0:00003', '1U08:1:00040', '1G01:0:11011', '1G02:0:11012', '1G03:0:01022', '1G04:0:13100', '1G05:0:21000', '1G06:0:02020', '1G07:0:00030', '1G08:1:00004', '1R01:0:11101', '1R02:0:21101', '1R03:0:20102', '1R04:0:10013', '1R05:0:02100', '1R06:0:20020', '1R07:0:30000', '1R08:1:40000', '1K01:0:11110', '1K02:0:12110', '1K03:0:22010', '1K04:0:00131', '1K05:0:00210', '1K06:0:20200', '1K07:0:00300', '1K08:1:04000', // tier 2 '2W01:1:00322', '2W02:1:23030', '2W03:2:00142', '2W04:2:00053', '2W05:2:00050', '2W06:3:60000', '2U01:1:02230', '2U02:1:02303', '2U03:2:53000', '2U04:2:20014', '2U05:2:05000', '2U06:3:06000', '2G01:1:30230', '2G02:1:23002', '2G03:2:42001', '2G04:2:05300', '2G05:2:00500', '2G06:3:00600', '2R01:1:20023', '2R02:1:03023', '2R03:2:14200', '2R04:2:30005', '2R05:2:00005', '2R06:3:00060', '2K01:1:32200', '2K02:1:30302', '2K03:2:01420', '2K04:2:00530', '2K05:2:50000', '2K06:3:00006', // tier 3 '3W01:3:03353', '3W02:4:00007', '3W03:4:30036', '3W04:5:30007', '3U01:3:30335', '3U02:4:70000', '3U03:4:63003', '3U04:5:73000', '3G01:3:53033', '3G02:4:07000', '3G03:4:36300', '3G04:5:07300', '3R01:3:35303', '3R02:4:00700', '3R03:4:03630', '3R04:5:00730', '3K01:3:33530', '3K02:4:00070', '3K03:4:00363', '3K04:5:00073', ]; const RAW_NOBLES = [ 'N01:00440', 'N02:04400', 'N03:44000', 'N04:40004', 'N05:00044', 'N06:03330', 'N07:33300', 'N08:33003', 'N09:30033', 'N10:00333', ]; const gems = (digits: string): Gems => ({ W: +digits[0]!, U: +digits[1]!, G: +digits[2]!, R: +digits[3]!, K: +digits[4]! }); export const CARDS: readonly DevCard[] = RAW_CARDS.map((raw) => { const [id, points, cost] = raw.split(':') as [string, string, string]; return { id, tier: Number(id[0]) as Tier, bonus: id[1] as Color, points: Number(points), cost: gems(cost) }; }); export const NOBLES: readonly Noble[] = RAW_NOBLES.map((raw) => { const [id, req] = raw.split(':') as [string, string]; return { id, points: 3, req: gems(req) }; }); const CARD_BY_ID = new Map(CARDS.map((c) => [c.id, c])); const NOBLE_BY_ID = new Map(NOBLES.map((n) => [n.id, n])); export function cardOf(id: string): DevCard { const c = CARD_BY_ID.get(id); if (!c) throw new Error(`unknown card ${id}`); return c; } export function nobleOf(id: string): Noble { const n = NOBLE_BY_ID.get(id); if (!n) throw new Error(`unknown noble ${id}`); return n; } export const isCardId = (id: string): boolean => CARD_BY_ID.has(id); export const emptyGems = (): Gems => ({ W: 0, U: 0, G: 0, R: 0, K: 0 }); export const emptyTokens = (): Tokens => ({ W: 0, U: 0, G: 0, R: 0, K: 0, gold: 0 }); export const sumTokens = (t: Partial>): number => TOKEN_COLORS.reduce((a, c) => a + (t[c] ?? 0), 0);