'use strict'; // 1ハンドから、1人のプレイヤーの統計の材料(数えるだけ。割合は summary.js で出す)を取り出す。 // 離席・退席などで自動で行った行動は本人の判断として数えない。キャッシュオンリー(場札なし)でも同じように数える const { positionOf, ACTION_KIND } = require('../review/preflop'); const STREETS = ['PREFLOP', 'FLOP', 'TURN', 'RIVER']; // スチール(ATS)を試みられる位置: 全員が降りて回ってきた CO・BTN・SB const STEAL_POSITIONS = new Set(['CO', 'BTN', 'SB', 'BTN/SB']); function handStats(hand, seat) { const p = hand.players.get(seat); const mine = hand.log.filter((e) => e.seat === seat && !e.auto); const pre = mine.filter((e) => e.street === 'PREFLOP'); const kind = (e) => ACTION_KIND[e.type]; // フロップを見た: プリフロップで降りておらず、ハンドがフロップまで進んだ(プリフロップのオールインで // 残りをめくったときも含む) const foldedPre = hand.log.some((e) => e.seat === seat && e.street === 'PREFLOP' && e.type === 'FOLD'); const reachedFlop = hand.showdown || hand.board.length >= 3 || hand.log.some((e) => e.street !== 'PREFLOP'); const sawFlop = !foldedPre && reachedFlop; const showdown = Boolean(hand.showdown && !p.folded); const won = p.won > 0; // スチールと 3bet の機会(プリフロップを順に見る) const position = positionOf(hand, seat); let entered = false; // 自分より前に誰かがコール・レイズした let raises = 0; let first = true; let atsOpp = false; let ats = false; let threeBetOpp = false; let threeBet = false; for (const e of hand.log) { if (e.street !== 'PREFLOP') continue; const k = kind(e); if (e.seat === seat && !e.auto) { if (first && !entered && STEAL_POSITIONS.has(position)) { atsOpp = true; ats = k === 'raise'; } first = false; // 3bet の機会: プリフロップでレイズがちょうど1回あった後の判断 if (raises === 1 && !threeBetOpp) { threeBetOpp = true; threeBet = k === 'raise'; } } if (k === 'call' || k === 'raise') entered = true; if (k === 'raise') raises++; } // ストリートごと: 判断した(そのストリートに来た)・降りた・全部の行動のうちベット・レイズ const seen = [0, 0, 0, 0]; const folds = [0, 0, 0, 0]; const actions = [0, 0, 0, 0]; const aggressive = [0, 0, 0, 0]; for (const e of mine) { const i = STREETS.indexOf(e.street); if (i < 0) continue; seen[i] = 1; actions[i]++; if (kind(e) === 'raise') aggressive[i]++; if (e.type === 'FOLD') folds[i] = 1; } const start = hand.startStacks.get(seat); const end = hand.endStacks ? hand.endStacks.get(seat) : start; return { preflop: pre.length > 0, // VPIP・PFR の分母(BB で全員が降りてきたハンドなど、判断の無いハンドは除く) vpip: pre.some((e) => kind(e) === 'call' || kind(e) === 'raise'), pfr: pre.some((e) => kind(e) === 'raise'), atsOpp, ats, threeBetOpp, threeBet, sawFlop, showdown, showdownWon: showdown && won, wonSawFlop: sawFlop && won, won, seen, folds, actions, aggressive, netBB: hand.bb > 0 ? (end - start) / hand.bb : 0, }; } module.exports = { handStats, STREETS };