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

+52 -31
View File
@@ -7,35 +7,51 @@
const RANKS = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'];
// 強い順。tier が大きいほど強い(レビューでは「一つ上」「二つ上」をこの段階で数える)
// 人数は「自分を含めず、まだ行動していない人の数」(自分より前に誰もコール・レイズしていないとき)。
// 紫枠は後ろが1人(SB で全員が降りて回ってきた)ときだけオープンできる手として、白の一つ下に置く
// 6-max のオープンレンジ(Poker Skill のプリフロップレンジ表、スタック約100BB・全員が降りて回ってきた場面)。
// 強い順。tier が大きいほど強い(レビューでは「一つ上」「二つ上」をこの段階で数える)。
// 各ポジションのレンジは UTG ⊂ HJ ⊂ CO ⊂ SB ⊂ BTN と入れ子になっているので、手ごとに
// 「オープンできる一番狭いポジション」の色を付ける。SB は BTN より狭いので BTN の一つ上に置く。
// 紺は UTG のうち特に強い手
const CATEGORIES = [
{ id: 'navy', name: '紺', label: '8人(強)', tier: 7 },
{ id: 'red', name: '赤', label: '8人(弱)', tier: 6 },
{ id: 'yellow', name: '黄', label: '6〜7人', tier: 5 },
{ id: 'green', name: '緑', label: '4〜5人', tier: 4 },
{ id: 'blue', name: '青', label: '3人', tier: 3 },
{ id: 'white', name: '白', label: '2人', tier: 2 },
{ id: 'purple', name: '紫枠', label: '紫枠', tier: 1 },
{ id: 'navy', name: '紺', label: 'UTG(強)', tier: 6 },
{ id: 'red', name: '赤', label: 'UTG', tier: 5 },
{ id: 'yellow', name: '黄', label: 'HJ', tier: 4 },
{ id: 'green', name: '緑', label: 'CO', tier: 3 },
{ id: 'blue', name: '青', label: 'SB', tier: 2 },
{ id: 'white', name: '白', label: 'BTN', tier: 1 },
{ id: 'gray', name: 'グレー', label: 'フォールド', tier: 0 },
];
// 全169ハンドの分類(載っていないハンドはフォールド)。紫枠はクイズ上の独立した回答
// 全169ハンドの分類(載っていないハンドはフォールド)
const RANGE = {
AA: 'navy', AKs: 'navy', AQs: 'red', AJs: 'red', ATs: 'red', A9s: 'green', A8s: 'green', A7s: 'green', A6s: 'green', A5s: 'green', A4s: 'green', A3s: 'green', A2s: 'green',
AKo: 'navy', KK: 'navy', KQs: 'red', KJs: 'yellow', KTs: 'green', K9s: 'green', K8s: 'white', K7s: 'white', K6s: 'white', K5s: 'white', K4s: 'white', K3s: 'white', K2s: 'white',
AQo: 'red', KQo: 'yellow', QQ: 'navy', QJs: 'yellow', QTs: 'green', Q9s: 'blue', Q8s: 'white', Q7s: 'white', Q6s: 'white', Q5s: 'purple', Q4s: 'purple', Q3s: 'purple', Q2s: 'purple',
AJo: 'yellow', KJo: 'green', QJo: 'blue', JJ: 'red', JTs: 'yellow', J9s: 'blue', J8s: 'white', J7s: 'white', J6s: 'purple',
ATo: 'green', KTo: 'blue', QTo: 'white', JTo: 'blue', TT: 'red', T9s: 'green', T8s: 'blue', T7s: 'purple',
A9o: 'blue', K9o: 'white', Q9o: 'white', J9o: 'white', T9o: 'white', 99: 'red', '98s': 'blue', '97s': 'white', '96s': 'purple',
A8o: 'white', '98o': 'purple', 88: 'yellow', '87s': 'white', '86s': 'purple',
A7o: 'white', 77: 'yellow', '76s': 'white', '75s': 'purple',
A6o: 'purple', 66: 'green', '65s': 'white', '64s': 'purple',
55: 'green', '54s': 'purple',
44: 'blue',
33: 'blue',
22: 'blue',
AA: 'navy', AKs: 'navy', AQs: 'red', AJs: 'red', ATs: 'red', A9s: 'yellow', A8s: 'yellow', A7s: 'yellow', A6s: 'yellow', A5s: 'red', A4s: 'red', A3s: 'red', A2s: 'red',
AKo: 'navy', KK: 'navy', KQs: 'red', KJs: 'red', KTs: 'red', K9s: 'yellow', K8s: 'green', K7s: 'green', K6s: 'green', K5s: 'green', K4s: 'blue', K3s: 'white', K2s: 'white',
AQo: 'red', KQo: 'red', QQ: 'navy', QJs: 'red', QTs: 'red', Q9s: 'yellow', Q8s: 'green', Q7s: 'blue', Q6s: 'blue', Q5s: 'white', Q4s: 'white',
AJo: 'red', KJo: 'yellow', QJo: 'green', JJ: 'red', JTs: 'red', J9s: 'yellow', J8s: 'green', J7s: 'blue', J6s: 'white',
ATo: 'yellow', KTo: 'green', QTo: 'blue', JTo: 'blue', TT: 'red', T9s: 'red', T8s: 'yellow', T7s: 'blue', T6s: 'white',
A9o: 'green', K9o: 'blue', Q9o: 'white', J9o: 'white', T9o: 'white', 99: 'red', '98s': 'red', '97s': 'green', '96s': 'blue', '95s': 'white',
A8o: 'green', K8o: 'white', '98o': 'white', 88: 'red', '87s': 'yellow', '86s': 'blue', '85s': 'white',
A7o: 'blue', 77: 'red', '76s': 'green', '75s': 'white',
A6o: 'blue', 66: 'red', '65s': 'green', '64s': 'white',
A5o: 'blue', 55: 'red', '54s': 'green',
A4o: 'white', 44: 'red',
A3o: 'white', 33: 'red',
A2o: 'white', 22: 'red',
};
// レイズを受けたときの対応(同じレンジ表の「CO のオープンに対する BTN の対応」)。レンジ表では枠で示す。
// 載っていないハンドはフォールド
const FACING_ACTIONS = [
{ id: '3bet', name: '水色枠', label: '3bet' },
{ id: 'call', name: '黄枠', label: 'コール' },
];
const FACING = {
AA: '3bet', KK: '3bet', QQ: '3bet', JJ: '3bet', AKs: '3bet', AKo: '3bet', A5s: '3bet', A4s: '3bet', A3s: '3bet', A2s: '3bet',
KQs: '3bet', KJs: '3bet', '76s': '3bet', '65s': '3bet', '54s': '3bet',
TT: 'call', 99: 'call', 88: 'call', 77: 'call', 66: 'call', 55: 'call', 44: 'call', 33: 'call', 22: 'call',
AQs: 'call', AJs: 'call', ATs: 'call', A9s: 'call', A8s: 'call', A7s: 'call', A6s: 'call', KTs: 'call', K9s: 'call',
QJs: 'call', QTs: 'call', Q9s: 'call', JTs: 'call', J9s: 'call', T9s: 'call', T8s: 'call', '98s': 'call', '87s': 'call',
AQo: 'call', AJo: 'call', KQo: 'call', KJo: 'call', QJo: 'call',
};
const BY_ID = new Map(CATEGORIES.map((c) => [c.id, c]));
@@ -47,6 +63,8 @@
const labelOf = (id) => category(id).label;
const tierOf = (hand) => category(categoryOf(hand)).tier;
const categoryOfTier = (tier) => CATEGORIES.find((c) => c.tier === tier) || BY_ID.get('gray');
const facingOf = (hand) => FACING[hand] || null; // '3bet' / 'call' / null(フォールド)
const facingAction = (id) => FACING_ACTIONS.find((a) => a.id === id) || null;
// ["As", "Kd"] → "AKo"
function handKey(cards) {
@@ -60,16 +78,19 @@
}
// 自分より前に誰もコール・レイズしていないとき、後ろの人数(自分を含めず、まだ行動していない人)で
// 参加してよい一番弱い段階。8人以上は赤(紺は赤の一つ上)。0人(全員降りてBBに回った)は無し
// 参加してよい一番弱い段階。6-max の席に当てはめて、5人以上は UTG の赤(紺は赤の一つ上)、4人は HJ の黄、
// 3人は CO の緑、2人は BTN の白、1人は SB の青。0人(全員降りてBBに回った)は無し
function requiredTier(behind) {
if (behind >= 8) return 6;
if (behind >= 6) return 5;
if (behind >= 4) return 4;
if (behind >= 5) return 5;
if (behind === 4) return 4;
if (behind === 3) return 3;
if (behind === 2) return 2;
if (behind === 1) return 1;
if (behind === 2) return 1;
if (behind === 1) return 2;
return 0;
}
return { RANKS, CATEGORIES, RANGE, handAt, handKey, categoryOf, category, labelOf, tierOf, categoryOfTier, requiredTier };
return {
RANKS, CATEGORIES, RANGE, FACING_ACTIONS, FACING,
handAt, handKey, categoryOf, category, labelOf, tierOf, categoryOfTier, requiredTier, facingOf, facingAction,
};
});
+7 -3
View File
@@ -25,7 +25,12 @@
.cat-green { --c: #4e8051; --fg: #101910; }
.cat-blue { --c: #6aa5c9; --fg: #14191f; }
.cat-white { --c: #f7f7f7; --fg: #222; }
.cat-gray, .cat-purple { --c: #929292; --fg: #222; }
.cat-gray { --c: #929292; --fg: #222; }
/* レイズを受けたときの対応の枠(3bet は水色、コールは黄)。下地の色と紛れないよう内側に暗い線を添える */
.facing-3bet { --frame: #3fd4ff; }
.facing-call { --frame: #ffe23a; }
.range-cell.facing-3bet, .range-cell.facing-call { box-shadow: inset 0 0 0 3px var(--frame), inset 0 0 0 4px rgba(0, 0, 0, 0.45); }
/* 13×13 の表 */
.range-matrix { display: grid; grid-template-columns: repeat(13, 1fr); gap: 2px; }
@@ -44,14 +49,13 @@
cursor: pointer;
position: relative;
}
.range-cell.cat-purple { box-shadow: inset 0 0 0 2px #c12cff; }
.range-cell.selected { outline: 2px solid var(--gold); outline-offset: 1px; z-index: 1; }
/* 凡例 */
.range-legend { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 12px; font-size: 12px; color: var(--muted); }
.range-legend span { display: inline-flex; align-items: center; gap: 6px; }
.swatch-dot { width: 14px; height: 14px; border-radius: 4px; background: var(--c); flex: none; }
.swatch-dot.cat-purple { box-shadow: inset 0 0 0 2px #c12cff; }
.swatch-dot.swatch-frame { background: transparent; box-shadow: inset 0 0 0 3px var(--frame); }
/* 選択したハンド */
.sel-hand { font-size: 28px; font-weight: 900; line-height: 1.1; }
+25 -5
View File
@@ -3,7 +3,7 @@
'use strict';
const $ = (id) => document.getElementById(id);
const { RANKS, CATEGORIES, handAt, categoryOf, labelOf } = window.HandRange;
const { RANKS, CATEGORIES, FACING_ACTIONS, handAt, categoryOf, labelOf, facingOf, facingAction } = window.HandRange;
function swatch(id) {
const s = document.createElement('span');
@@ -11,6 +11,13 @@
return s;
}
// レイズを受けたときの対応の枠(3bet は水色、コールは黄)
function frameSwatch(id) {
const s = document.createElement('span');
s.className = `swatch-dot swatch-frame facing-${id}`;
return s;
}
// ---------- タブ ----------
function showView(name) {
$('view-range').hidden = name !== 'range';
@@ -28,6 +35,11 @@
item.append(swatch(c.id), c.label);
$('legend').append(item);
}
for (const a of FACING_ACTIONS) {
const item = document.createElement('span');
item.append(frameSwatch(a.id), `レイズを受けたら${a.label}`);
$('legend').append(item);
}
for (let r = 0; r < 13; r++) {
for (let c = 0; c < 13; c++) {
@@ -35,9 +47,10 @@
const cat = categoryOf(hand);
const b = document.createElement('button');
b.type = 'button';
b.className = `range-cell cat-${cat}`;
const facing = facingOf(hand);
b.className = `range-cell cat-${cat}${facing ? ` facing-${facing}` : ''}`;
b.textContent = hand;
b.title = `${hand}:${labelOf(cat)}`;
b.title = `${hand}:${labelOf(cat)}${facing ? `/レイズを受けたら${facingAction(facing).label}` : ''}`;
b.addEventListener('click', () => select(b, hand, cat));
$('matrix').append(b);
}
@@ -54,10 +67,17 @@
const label = document.createElement('div');
label.className = 'sel-label';
label.append(swatch(cat), labelOf(cat));
const facing = facingOf(hand);
const vs = document.createElement('div');
vs.className = 'sel-label';
if (facing) vs.append(frameSwatch(facing), `レイズを受けたら${facingAction(facing).label}`);
else vs.append('レイズを受けたらフォールド');
const note = document.createElement('div');
note.className = 'hint';
note.textContent = cat === 'purple' ? '灰色+紫枠。クイズの答えは「紫枠」です。' : 'このハンドの分類です。';
box.append(name, label, note);
note.textContent =
(cat === 'gray' ? 'どのポジションからもオープンしない手です。' : `${labelOf(cat)} からオープンできる一番狭い分類です。`) +
'枠は CO のオープンを BTN で受けたときの対応です。';
box.append(name, label, vs, note);
}
$('start-from-range').addEventListener('click', () => {