Previously only the review CPU table paused after each hand; cash-only, friends and CPU tables moved on automatically after a short delay. Now every table keeps the result on screen until the host presses 次のハンドへ, and the next hand is dealt right away. If the host has left, the new host presses it; with no human seated the table still advances on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
3.7 KiB
JavaScript
93 lines
3.7 KiB
JavaScript
'use strict';
|
|
|
|
// node --test で実行(ACCESS_KEY はテスト用のダミー、統計の保存先は一時ディレクトリ)
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
process.env.ACCESS_KEY = process.env.ACCESS_KEY || '000000';
|
|
process.env.STATS_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'holdem-stats-'));
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { Table } = require('../server/tables');
|
|
|
|
const T = (n) => 'q'.repeat(31) + n; // 32文字のトークン
|
|
|
|
function makeTable(n) {
|
|
const t = new Table({
|
|
id: 'x',
|
|
name: 'test',
|
|
startingStack: 1000,
|
|
smallBlind: 10,
|
|
bigBlind: 20,
|
|
blindTimerEnabled: false,
|
|
blindIntervalMin: 10,
|
|
hostToken: T(0),
|
|
mode: 'friends',
|
|
});
|
|
for (let i = 0; i < n; i++) t.join(T(i), 'p' + i);
|
|
return t;
|
|
}
|
|
|
|
// 席の並び(名前)
|
|
const names = (t) => t.seats.filter(Boolean).map((s) => s.name);
|
|
|
|
test('席順: 開始前はすぐに並べ替え、ホスト以外や着席者と合わない指定は受け付けない', () => {
|
|
const t = makeTable(4);
|
|
assert.equal(t.setSeating(T(1), [3, 2, 1, 0]), 'ホストのみ操作できます');
|
|
assert.match(t.setSeating(T(0), [3, 2, 1]), /もう一度/);
|
|
assert.match(t.setSeating(T(0), [3, 3, 1, 0]), /もう一度/);
|
|
assert.match(t.setSeating(T(0), [3, 2, 1, 5]), /もう一度/);
|
|
assert.equal(t.setSeating(T(0), [0, 3, 1, 2]), null);
|
|
assert.deepEqual(names(t), ['p0', 'p3', 'p1', 'p2']);
|
|
assert.equal(t.pendingSeating, null);
|
|
// 席は詰めて座らせ、新しく座った人は空いている次の席(並びの最後)に入る
|
|
t.leave(T(1));
|
|
t.join(T(9), 'p9');
|
|
assert.deepEqual(names(t), ['p0', 'p3', 'p9', 'p2']);
|
|
});
|
|
|
|
test('席順: ハンド中は次のハンドの始めに並べ替え、ボタンは同じ人に付いたまま次へ回る', () => {
|
|
const t = makeTable(4);
|
|
t.start(T(0), 0);
|
|
const h = t.hand;
|
|
const buttonName = t.seats[h.button].name;
|
|
const order = [0, 2, 1, 3]; // p0 → p2 → p1 → p3
|
|
assert.equal(t.setSeating(T(0), order), null);
|
|
assert.deepEqual(names(t), ['p0', 'p1', 'p2', 'p3']); // まだ変わらない
|
|
assert.deepEqual(t.publicState(T(0), new Set()).pendingSeating, order);
|
|
assert.equal(t.publicState(T(1), new Set()).pendingSeating, null); // ホスト以外には送らない
|
|
|
|
// 全員降りてハンドを終わらせ、次のハンドへ
|
|
for (let i = 0; i < 10 && !h.finished; i++) t.act(t.seats[h.toAct].token, 'fold', 0);
|
|
assert.ok(h.finished);
|
|
assert.equal(t.nextHand(T(0), 0), null);
|
|
t.tick(10 ** 6);
|
|
t.tick(10 ** 7);
|
|
assert.equal(t.hand.number, 2);
|
|
assert.deepEqual(names(t), ['p0', 'p2', 'p1', 'p3']);
|
|
assert.equal(t.pendingSeating, null);
|
|
// 前のハンドのボタンの人の、新しい並びでの次の人がボタン
|
|
const after = ['p0', 'p2', 'p1', 'p3'];
|
|
const next = after[(after.indexOf(buttonName) + 1) % 4];
|
|
assert.equal(t.seats[t.hand.button].name, next);
|
|
// 参加者の順もボタンの次から新しい並びどおり
|
|
const seq = t.hand.order.map((s) => t.seats[s].name);
|
|
const k = after.indexOf(next);
|
|
assert.deepEqual(seq, [1, 2, 3, 4].map((d) => after[(k + d) % 4]));
|
|
});
|
|
|
|
test('席順: 決めた後に座った人は最後に加え、いなくなった人は外す', () => {
|
|
const t = makeTable(3);
|
|
t.start(T(0), 0);
|
|
const h = t.hand;
|
|
assert.equal(t.setSeating(T(0), [2, 1, 0]), null);
|
|
t.join(T(5), 'p5'); // ハンド中に新しく座る
|
|
t.leave(T(1)); // ハンド中に退席
|
|
for (let i = 0; i < 10 && !h.finished; i++) t.act(t.seats[h.toAct].token, 'fold', 0);
|
|
assert.equal(t.nextHand(T(0), 0), null);
|
|
t.tick(10 ** 6);
|
|
t.tick(10 ** 7);
|
|
assert.deepEqual(names(t), ['p2', 'p0', 'p5']);
|
|
});
|