'use strict'; const crypto = require('crypto'); // カードは "As"(スペードのA), "Td"(ダイヤの10)のような2文字 const RANKS = '23456789TJQKA'; const SUITS = 'shdc'; // 暗号論的乱数で Fisher-Yates シャッフル(山札はサーバの外に出さない) function newDeck() { const deck = []; for (const r of RANKS) for (const s of SUITS) deck.push(r + s); for (let i = deck.length - 1; i > 0; i--) { const j = crypto.randomInt(i + 1); [deck[i], deck[j]] = [deck[j], deck[i]]; } return deck; } const rankValue = (card) => RANKS.indexOf(card[0]) + 2; // 5枚の役の強さ。配列を先頭から比較して大きいほうが強い // [役の種類(0〜8), 比較用の数字...] function score5(cards) { const vals = cards.map(rankValue).sort((a, b) => b - a); const flush = cards.every((c) => c[1] === cards[0][1]); let straightHigh = 0; if (new Set(vals).size === 5) { if (vals[0] - vals[4] === 4) straightHigh = vals[0]; else if (vals[0] === 14 && vals[1] === 5) straightHigh = 5; // A-2-3-4-5 } if (straightHigh && flush) return [8, straightHigh]; // 枚数の多い順、同じ枚数なら数字の大きい順 const counts = new Map(); for (const v of vals) counts.set(v, (counts.get(v) || 0) + 1); const groups = [...counts].sort((a, b) => b[1] - a[1] || b[0] - a[0]); const byGroup = groups.map((g) => g[0]); if (groups[0][1] === 4) return [7, ...byGroup]; if (groups[0][1] === 3 && groups[1][1] === 2) return [6, ...byGroup]; if (flush) return [5, ...vals]; if (straightHigh) return [4, straightHigh]; if (groups[0][1] === 3) return [3, ...byGroup]; if (groups[0][1] === 2 && groups[1][1] === 2) return [2, ...byGroup]; if (groups[0][1] === 2) return [1, ...byGroup]; return [0, ...vals]; } function compareScore(a, b) { for (let i = 0; i < Math.max(a.length, b.length); i++) { const d = (a[i] || 0) - (b[i] || 0); if (d) return d; } return 0; } const HAND_NAMES = [ 'ハイカード', 'ワンペア', 'ツーペア', 'スリーカード', 'ストレート', 'フラッシュ', 'フルハウス', 'フォーカード', 'ストレートフラッシュ', ]; function handName(score) { if (score[0] === 8 && score[1] === 14) return 'ロイヤルフラッシュ'; return HAND_NAMES[score[0]]; } // 5〜7枚から最も強い5枚を選ぶ function bestHand(cards) { let best = null; const n = cards.length; const pick = (start, chosen) => { if (chosen.length === 5) { const score = score5(chosen); if (!best || compareScore(score, best.score) > 0) best = { score, cards: chosen.slice() }; return; } for (let i = start; i <= n - (5 - chosen.length); i++) { chosen.push(cards[i]); pick(i + 1, chosen); chosen.pop(); } }; pick(0, []); return best && { score: best.score, cards: best.cards, name: handName(best.score) }; } module.exports = { newDeck, score5, compareScore, bestHand, handName };