Switch hand ranges to Poker Skill's 6-max charts with 3bet/call frames

- Replace the Yokosawa ranges with the 6-max opening ranges (UTG, HJ, CO,
  SB, BTN). Colors keep the old order: navy (strong UTG), red UTG, yellow
  HJ, green CO, blue SB, white BTN. SB is narrower than BTN, so blue sits
  one tier above white; the purple-frame category is gone.
- Show the "BTN vs CO open" chart on the range page as frames: light blue
  for 3bet, yellow for call.
- Grade BTN facing an unlimped CO open by those frames in reviews; other
  spots keep the color-tier rule (BB defends down to green).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agomizi1cmandClaude Opus 5.5 committed 2026-10-20 15:21:39 +09:00
1 parent 680aa20d30
commit 3ddb0996e0
8 files changed
+187 -86

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@ const { codes, rangeOf, narrow, rangeEquity, showdownShare } = require('./equity
const STREETS = ['PREFLOP', 'FLOP', 'TURN', 'RIVER'];
// レビューで色に添える説明(クイズ用の分類名がそのままでは分かりにくいもの)
const COLOR_LABEL = { purple: '1人(SBのみ)', gray: 'レンジ外' };
const COLOR_LABEL = { gray: 'レンジ外' };
// 失った期待値がポット(コール後)のこの割合までは誤差として満点にする
const TOLERANCE = 0.03;
+21 -8
View File
@@ -1,19 +1,21 @@
'use strict';
// プリフロップの判定: 配られた手が登録してあるハンドレンジ(ヨコサワハンドレンジ)の何色で、そのとおりに行動できたか。
// プリフロップの判定: 配られた手が登録してあるハンドレンジ(6-max のオープンレンジ)の何色で、そのとおりに行動できたか。
// 自分より前に誰もコール・レイズしていない: 後ろの人数(自分を含めず、まだ行動していない人)に対応する色
// 以上ならレイズ(オープン)、それより弱ければフォールド
// 自分より前にコール・レイズした人がいる: その人のレンジを色で推定し(その人の後ろの人数に対応する色)、
// 自分の手がその色の一つ上ならコール、二つ上ならレイズ、同じかそれより弱ければフォールド
// 自分のレイズにリレイズされた(3bet・4betされた): 相手の推定レンジと同じ色でもコール、二つ上ならレイズ
// BB で 4BB までのオープン(リレイズなし)に直面: 安く参加できるので広く守る。青以上でコール
// (CO のオープンなら白、BTN なら紫枠以上。相手の色の一つ下まで)、三つ上ならレイズ
// BTN で CO のオープン(ほかに参加者なし)に直面: レンジ表の枠のとおり。水色枠は 3bet、黄枠はコール、
// 枠なしはフォールド
// BB で 4BB までのオープン(リレイズなし)に直面: 安く参加できるので広く守る。緑(CO)以上でコール
// (CO のオープンなら青、BTN・SB なら白以上。相手の色の一つ下まで)、三つ上ならレイズ
// 2人目以降に参加した人のレンジも同じ決まりで、コール・レイズできる一番弱い色と推定する
// (ふつうはコールなら直前までで一番強いレンジの一つ上、レイズなら二つ上)。
const HandRange = require('../../admin/range-data');
const TOP_TIER = 7; // 紺
const BB_CALL_TIER = 3; // 青: BB はどこからのオープンにもこの色以上でコールできる
const TOP_TIER = 6; // 紺
const BB_CALL_TIER = 3; // 緑: BB はどこからのオープンにもこの色以上でコールできる
const BB_DEFENSE_MAX_OPEN = 4; // BB で広く守るのはオープンがこの BB 数まで
const ACTION_KIND = { FOLD: 'fold', CHECK: 'check', CALL: 'call', BET: 'raise', RAISE: 'raise' };
@@ -46,7 +48,8 @@ const tierName = (tier) => (tier > TOP_TIER ? 'なし' : HandRange.categoryOfTie
// 前に参加した人がいるときに、コール・レイズできる一番弱い色の段階。rule は
// 'normal': 一つ上でコール、二つ上でレイズ(紺は紺に対してもコールできる)
// 'reraised': 自分のレイズにリレイズされた。同じ色でコール、二つ上でレイズ
// 'bb': BB でオープンに直面。相手の色の一つ下(青まで)でコール、三つ上でレイズ(紫枠より下は参加しない)
// 'bb': BB でオープンに直面。相手の色の一つ下(緑まで)でコール、三つ上でレイズ(白より下は参加しない)
// 'vsCO': BTN で CO のオープンに直面。判定はレンジ表の枠で行い、ここでの段階は推定レンジにだけ使う('normal' と同じ)
function thresholds(rule, ref) {
if (rule === 'reraised') return { call: Math.min(TOP_TIER, ref), raise: ref + 2 };
if (rule === 'bb') return { call: Math.max(1, Math.min(BB_CALL_TIER, ref - 1)), raise: ref + 3 };
@@ -58,6 +61,8 @@ function recommend(tier, situation, entry) {
let rec;
if (situation.kind === 'open') {
rec = situation.behind === 0 ? 'check' : tier >= situation.required ? 'raise' : 'fold';
} else if (situation.rule === 'vsCO') {
rec = situation.facing === '3bet' ? 'raise' : situation.facing === 'call' ? 'call' : 'fold';
} else {
rec = tier >= situation.raise ? 'raise' : tier >= situation.call ? 'call' : 'fold';
}
@@ -76,13 +81,15 @@ function verdictOf(rec, actual) {
// プリフロップの各行動を順に見て、hero の判断ごとに判定する。あわせて各席の推定レンジ(色の段階)を返す
function judgePreflop(hand, hero) {
const tier = HandRange.tierOf(HandRange.handKey(hand.players.get(hero).cards));
const key = HandRange.handKey(hand.players.get(hero).cards);
const tier = HandRange.tierOf(key);
const order = hand.preflopOrder;
const acted = new Set();
const estimated = new Map(); // 席 → 推定レンジの一番弱い色の段階
let maxTier = null;
let maxSeat = null;
let raises = 0;
let entrants = 0; // プリフロップでコール・レイズした回数
const raisers = new Set(); // プリフロップでレイズした席
const judgments = [];
@@ -92,6 +99,7 @@ function judgePreflop(hand, hero) {
// 前に参加した人がいるときの決まり(thresholds を参照)
const ruleOf = (e) => {
if (raisers.has(e.seat)) return 'reraised';
if (entrants === 1 && raises === 1 && positionOf(hand, e.seat) === 'BTN' && positionOf(hand, maxSeat) === 'CO') return 'vsCO';
if (e.seat === hand.bbSeat && raises === 1 && e.currentBet <= BB_DEFENSE_MAX_OPEN * hand.bb) return 'bb';
return 'normal';
};
@@ -105,7 +113,7 @@ function judgePreflop(hand, hero) {
const situation =
maxTier == null
? { kind: 'open', behind: behindOf(hero), required: HandRange.requiredTier(behindOf(hero)) }
: { kind: 'facing', rule, refTier: maxTier, refSeat: maxSeat, ...limits };
: { kind: 'facing', rule, refTier: maxTier, refSeat: maxSeat, ...limits, ...(rule === 'vsCO' && { facing: HandRange.facingOf(key) }) };
const rec = recommend(tier, situation, e);
const verdict = verdictOf(rec, kind);
judgments.push({ index, situation, recommended: rec, actual: kind, verdict, ...VERDICTS[verdict] });
@@ -122,6 +130,7 @@ function judgePreflop(hand, hero) {
maxSeat = e.seat;
}
}
if (kind === 'call' || kind === 'raise') entrants++;
if (kind === 'raise') {
raises++;
raisers.add(e.seat);
@@ -138,6 +147,10 @@ function describeSituation(situation, nameOf) {
return `前に参加者なし・後ろに${situation.behind}人 → ${tierName(situation.required)}以上でオープン`;
}
const who = nameOf(situation.refSeat);
if (situation.rule === 'vsCO') {
const action = HandRange.facingAction(situation.facing);
return `BTNで ${who}(CO)のオープンに直面 → レンジ表の枠(水色枠は3bet、黄枠はコール)。この手は${action ? `${action.name}で${action.label}` : '枠なしでフォールド'}`;
}
const ref = tierName(situation.refTier);
const head =
situation.rule === 'reraised'