Add CPU opponents and a table type selector (cash only / friends / CPU)
- Create screen: choose キャッシュオンリー (real cards, chips only), 友人戦 (server deals and settles) or CPU戦 (友人戦 plus 1–8 CPU players); replaces the card toggle. The lobby shows each table's type - Poker AI written from scratch (server/ai.js, no libraries): fast 7-card evaluator (checked against bestHand), Monte Carlo equity, and decisions from hand rank, pot odds, raise count, opponents and a per-CPU style (aggression, bluff rate); continuation bets via the last aggressor. It never sees other players' hole cards - CPU seats: think time of 0.7–1.8s (faster when no human is left in the hand), automatic rebuy, never the host, no new hand unless a human is playing, re-think after pause - Play UI: robot avatars and a "考えています…" status for CPU turns - Tests for the evaluator, equity, legality of AI actions and CPU tables Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66907345ff
commit
0d3a054f0d
12 files changed
+550
-46
No files matched your search
@@ -9,6 +9,8 @@
|
||||
|
||||
const AVATAR_SVG =
|
||||
'<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="4.5"/><path d="M3 21c0-4.4 4-7.5 9-7.5s9 3.1 9 7.5z"/></svg>';
|
||||
const BOT_SVG =
|
||||
'<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="11" y="1.5" width="2" height="3.5" rx="1"/><circle cx="12" cy="1.8" r="1.6"/><path d="M5 7.5A2.5 2.5 0 0 1 7.5 5h9A2.5 2.5 0 0 1 19 7.5v7a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 5 14.5zM9 9.2a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2zm6 0a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2zM2 9.5h2v4H2zm18 0h2v4h-2zM7 18.5h10a3 3 0 0 1 3 3V23H4v-1.5a3 3 0 0 1 3-3z"/></svg>';
|
||||
// U+FE0E: iOS で絵文字として描画されないように
|
||||
const SUIT_SYMBOL = { s: '♠︎', h: '♥︎', d: '♦︎', c: '♣︎' };
|
||||
const ACTION_LABEL = { FOLD: 'Fold', CHECK: 'Check', CALL: 'Call', BET: 'Bet', RAISE: 'Raise', ALLIN: 'All-in' };
|
||||
@@ -234,6 +236,7 @@
|
||||
.map((p, idx) => {
|
||||
const classes = ['seat'];
|
||||
if (p.seat === s.mySeat) classes.push('me');
|
||||
if (p.bot) classes.push('bot');
|
||||
if (!p.connected) classes.push('offline');
|
||||
if (p.inHand && p.folded) classes.push('folded');
|
||||
if (s.toAct === p.seat) classes.push('turn');
|
||||
@@ -273,7 +276,7 @@
|
||||
|
||||
return `
|
||||
<div class="${classes.join(' ')}">
|
||||
<div class="avatar">${AVATAR_SVG}${markers}${cards}</div>
|
||||
<div class="avatar">${p.bot ? BOT_SVG : AVATAR_SVG}${markers}${cards}</div>
|
||||
<div class="nameplate">
|
||||
${tag}
|
||||
<div class="pname">${esc(p.name)}</div>
|
||||
@@ -324,7 +327,10 @@
|
||||
else if (m.canRebuy) status = 'チップがなくなりました';
|
||||
else if (o) status = o.canCheck ? 'あなたの番です' : `あなたの番です(コール ${chips(o.toCall)})`;
|
||||
else if (awaiting) status = m.canAward ? 'カードを見せ合って、勝った人を選んでください' : 'ホストが勝者を選んでいます';
|
||||
else if (h && !h.finished && s.toAct != null) status = `${nameOf(s.toAct)} の番です`;
|
||||
else if (h && !h.finished && s.toAct != null) {
|
||||
const actor = s.seats[s.toAct];
|
||||
status = actor && actor.bot ? `${actor.name}(CPU)が考えています…` : `${nameOf(s.toAct)} の番です`;
|
||||
}
|
||||
else if (h && !h.finished && h.runout) status = 'オールイン — 残りのカードをめくります';
|
||||
else if (h && !h.finished && !seat.inHand) status = '次のハンドから参加します';
|
||||
else if (h && h.finished) status = '次のハンドを準備中…';
|
||||
|
||||
Reference in new issue
Block a user