Switch to chips-only play: real cards at the table, winners picked on screen
- Remove the deck, hole cards, board, hand evaluator and reveal logic (cards are dealt physically; the site only tracks bets, pots and stacks) - Showdown now waits for a winner per pot (main/side), chosen by a player still in the hand or the host; multiple winners split the pot - All-in with no further betting goes straight to showdown - Remove the 30s action clock / auto sit-out (everyone is at the same table) - UI: winner picker panel (main winner auto-fills side pots), compact table Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8de55f7ca5
commit
b5d84e5f65
11 files changed
+408
-605
No files matched your search
@@ -1,96 +0,0 @@
|
||||
'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 };
|
||||
Reference in new issue
Block a user