A new table type, "review" (レビュー付きCPU戦), can only be created by admins. Only the creator and CPUs sit there: friends cannot join, and the table is hidden from non-admin lobbies. After every hand a review opens, and the next hand waits until the creator presses "次のハンドへ". - Preflop: shows which color of the registered hand range the hole cards are and whether the action followed it. With no entrant before you, open with the color for the number of players still to act; facing an entrant, estimate their range from their position, call one color above it and raise two above. Purple ranks just below white (SB only). - Flop onward: equity vs ranges estimated from those colors (narrowed by later bets/calls) gives a rough EV per option. The score is the EV lost against the best option, relative to the pot. It is a guide, not GTO. - Also: pot-odds call check, all-in equity and luck-adjusted P&L, VPIP/PFR/3bet/Cbet/WTSD/W$SD/AF, per-color and per-position compliance, habits to watch, worst-5 hands, a P&L chart and a step-by-step replay on the admin-only /review page. Hand now logs every action for the review. The range data moves to admin/range-data.js, shared by the range page and the server. Reviews are saved under data/reviews/ (newest 50 sessions). Admin scripts and CSS are no longer cached for a day, so updates show up immediately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
3.8 KiB
JavaScript
93 lines
3.8 KiB
JavaScript
'use strict';
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
// プロジェクト直下の .env を読み込む(既存の環境変数は上書きしない)。
|
||
// 秘密情報はソースコードに書かず .env(git 管理外)か環境変数で渡す。
|
||
function loadDotEnv(file) {
|
||
let text;
|
||
try {
|
||
text = fs.readFileSync(file, 'utf8');
|
||
} catch {
|
||
return;
|
||
}
|
||
if (process.platform !== 'win32' && (fs.statSync(file).mode & 0o077) !== 0) {
|
||
console.warn(`[config] ${file} が他ユーザーから読める状態です。chmod 600 してください。`);
|
||
}
|
||
for (const line of text.split(/\r?\n/)) {
|
||
const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*?)\s*$/);
|
||
if (!m || line.trim().startsWith('#')) continue;
|
||
const value = m[2].replace(/^(['"])(.*)\1$/, '$2');
|
||
if (process.env[m[1]] === undefined) process.env[m[1]] = value;
|
||
}
|
||
}
|
||
loadDotEnv(path.join(__dirname, '..', '.env'));
|
||
|
||
const ACCESS_KEY = process.env.ACCESS_KEY;
|
||
if (!ACCESS_KEY || !/^\d{6}$/.test(ACCESS_KEY)) {
|
||
console.error('[config] ACCESS_KEY(6桁の数字)が未設定です。.env か環境変数で指定してください。');
|
||
process.exit(1);
|
||
}
|
||
|
||
// 管理者キー(任意)。入力すると管理者として入室でき、ハンドレンジ学習ページが見える
|
||
const ADMIN_KEY = process.env.ADMIN_KEY || null;
|
||
if (ADMIN_KEY && (!/^\d{6}$/.test(ADMIN_KEY) || ADMIN_KEY === ACCESS_KEY)) {
|
||
console.error('[config] ADMIN_KEY は ACCESS_KEY と異なる6桁の数字にしてください。');
|
||
process.exit(1);
|
||
}
|
||
|
||
module.exports = {
|
||
PORT: Number(process.env.PORT) || 4671,
|
||
|
||
// 既定では自分自身(127.0.0.1)でのみ待ち受け、外部には nginx 等の HTTPS 経由で公開する。
|
||
// どうしても直接公開する場合のみ HOST=0.0.0.0 にする(通信が平文になるので非推奨)。
|
||
HOST: process.env.HOST || '127.0.0.1',
|
||
|
||
// 公開URL(例: https://poker.kanawo.net)。設定するとこのドメイン以外からのアクセスを拒否する。
|
||
// ローカル開発時は未設定のままにする。
|
||
PUBLIC_ORIGIN: process.env.PUBLIC_ORIGIN ? new URL(process.env.PUBLIC_ORIGIN).origin : null,
|
||
|
||
// 参加キー(6桁の数字)。.env の ACCESS_KEY
|
||
ACCESS_KEY,
|
||
ADMIN_KEY,
|
||
|
||
// 1テーブルの最大着席数
|
||
MAX_SEATS: 9,
|
||
|
||
// 同時に存在できるテーブル数の上限
|
||
MAX_TABLES: 30,
|
||
|
||
// 誰も接続していないテーブルを削除するまでの時間
|
||
EMPTY_TABLE_TTL_MS: 30 * 60 * 1000,
|
||
|
||
// オールインで残りの場札をめくる間隔(カード機能あり)
|
||
RUNOUT_STEP_MS: 1500,
|
||
// ハンド終了後、次のハンドまで結果を表示する時間(カードありは手札を見比べられるよう長め)
|
||
SHOWDOWN_MS: 4000,
|
||
CARD_SHOWDOWN_MS: 6000,
|
||
FOLD_WIN_MS: 2500,
|
||
// CPU の考える時間(ランダム)。人間がハンドに残っていないときは BOT_FAST_MS
|
||
BOT_THINK_MIN_MS: 700,
|
||
BOT_THINK_MAX_MS: 1800,
|
||
BOT_FAST_MS: 400,
|
||
// CPU が勝率を見積もるときのシミュレーション回数
|
||
BOT_ITERATIONS: 800,
|
||
// レビューで勝率を見積もるときのシミュレーション回数(1回の判断・選択肢ごと)
|
||
REVIEW_ITERATIONS: 1200,
|
||
// 参加者が2人揃ってから次のハンドを始めるまで
|
||
NEXT_HAND_DELAY_MS: 2000,
|
||
|
||
// 鍵入力のブルートフォース対策
|
||
// IP(IPv6 は /64 単位)ごと: WINDOW 内で MAX_FAILURES 回まで
|
||
AUTH_MAX_FAILURES: 5,
|
||
AUTH_FAILURE_WINDOW_MS: 30 * 60 * 1000,
|
||
// サーバ全体: 1時間に GLOBAL_MAX_FAILURES 回失敗したら新規の鍵入力を一時停止
|
||
// (入室済みの人は Cookie があるので影響なし)
|
||
AUTH_GLOBAL_MAX_FAILURES: 30,
|
||
AUTH_GLOBAL_WINDOW_MS: 60 * 60 * 1000,
|
||
|
||
// Socket.IO のイベント数上限(1接続あたり / 秒)
|
||
SOCKET_EVENTS_PER_SEC: 10,
|
||
};
|