ポーカーのスターティングハンド表をいい感じに作る【Next.js/D3.js】

# 経緯

フロントエンドの勉強を始める
→息抜きにポーカーの勉強を始める
→とりあえず[エド・ミラーのポーカースクール ──ライブゲームで勝つ](https://www.amazon.co.jp/dp/4775949179/ref=cm_sw_r_tw_dp_PRA5XQYPNNSFN5AP7VFW)を読み始める
→図表が全くないし、文章だけだとスターティングハンド表を覚えにくい
→**ちょうど勉強していたNext.jsで作りました！**

https://startinghands-edmiller.vercel.app/

| PC版 | SP版 |
|:-:|:-:|
| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/fd393476-42ef-c7b9-3fd8-9243ddbc91a2.png" width=100%> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/0bee7e27-ecf3-0f51-04bf-ad22a244a86f.png" width=50%> |

# 目的

ポーカーのスターティングハンド表を作成します。ハンド表はエド・ミラーのポーカースクールを参考にします。
Webアプリとして実装し、**PCやスマホのブラウザからいつでも閲覧できる**ものを作成します。

**参考：ポーカーのスターティングハンドとは**

> ポーカーの一般的なルールの1つであるテキサスホールデムにおいては、**各プレイヤーに最初に配られる2枚の手持ちカード（スターティングハンド）** と、3枚→1枚→1枚の順でめくられる5枚の場のカード（コミュニティカード）の全7枚から5枚を選んで役を作ります。
うち、プリフロップと呼ばれる最初の段階では、配られた2枚のカードだけを元にゲームを降りるか（**フォールド**）、ゲームに参加するか（**コール**）、賭け金を吊り上げてゲームに参加するか（**レイズ**）を決めます。
この段階では場のカードが公開されておらず情報が少ないため、ある程度決まった戦略が確立されています。**スターティングハンド表は、手持ちのハンド2枚をインプットに、初手でFold/Call/Raiseのどれを行うべきかを示した表**となります。

**スターティングハンド表 イメージ図**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/6c31b487-0f31-2774-a1de-18412a673da3.png)

[参考：修羅のポーカー](https://shura-poker.com/texas-holdem-hand-chart-loose/)


# 要件定義（ざっくり）

- 動的コンテンツを含まないため、フロントエンドのみで作成します。`Next.js`で実装し、`Vercel`にデプロイします。APIやDBは建てません。
- スターティングハンド表のデータ部分は「エド・ミラーのポーカースクール ──ライブゲームで勝つ」を情報源とし、jsonファイルに書き起こして一緒にデプロイします。
- 巷のスターティングハンド表には、自分の手番等の情報が考慮されていない、もしくは考慮されていてもその分表が増えて記憶・比較しづらいものなどがあります。本アプリでは、同書籍に倣って **「自分がどの手番か（アーリー/ボタン/...」「前の手番のレイズ有無とその傾向（タイト/ルース）を考慮に入れ、かつラジオ選択により容易に比較できるようにする**ことを目指します。
- 大会の途中でこっそり見ることも考慮し、**スマホ/PC両方に対応**させます。

# 実装

## 1. 簡易データベース作成（SpreadSheet）

本に記載されているプリフロップ戦略をcsvファイルとして書き起こします。今回はGoogle SpreadSheetを使って整理しました。
ポケットの情報（`pocket`列）と自分のポジション（`position`列）をキーとして、前の手番毎の戦略（`sNoRaise`, `sRaiseTight`, ...）を転記します。他、レンダリング位置を決めるのに参照するX, Y座標（`axisX`列, `axisY`列）も記載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/2ea48ec6-fee0-6b12-aaa1-0e04fdb10072.png)


## 2. プロトタイプ作成（d3.js）

データの可視化によく使われるフレームワーク`d3.js`を用いてプロトタイプを作ります。
実装先は[observablehq.com](https://observablehq.com/)としました。ノートブックの公開機能だけでなく、簡単なUIを作成できたり、便利な可視化のテンプレ集があるなど、簡易な可視化プラットフォームとしては十分すぎる機能を持っています。

※本来これができた時点で開発を終了する予定でしたが、凡例配置の難しさやスマホから見る利便性を考えて、結局Webアプリ化することにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/3de232f5-3dee-c5ed-3d0d-4347b0208089.png)
https://observablehq.com/d/bcb9c186f7495591

## 3. 本実装（Next. js）

本番のWebアプリを実装します。以下、詰まったりこだわったりしたポイントを記載します。

- 書き起こしたcsvファイルを`Next.js`で読み込む方法を模索しましたが、結局[CSVからJSONコンバータ](https://www.site24x7.com/ja/tools/csv-to-json.html)を使って事前変換する方針を採用しました。
- cssは`tailwind css`を導入し、jsxファイルの各コンポーネントのクラスにそのまま記載しました。PCの2カラム編成をSPでは1カラムにするレスポンシブ対応は、通常のクラスに`sm`や`lg`プレフィックスをつけることで対応します。

```index.js
{/*  抜粋 */}
<div className='container mx-auto'>
          <div className="grid xs:grid-cols-1 lg:grid-cols-6 lg:gap-3">
            <div className="lg:col-start-1 lg:col-span-5"><Grid/></div>
            <div className="lg:col-start-6 lg:col-span-1"><Form /><Legends /></div>
        </div>
      </div>
{/*  抜粋 */}

```

- 状態管理は`React Hooks`を使いました。`useContext()`と`useState()`を駆使することで、異なるコンポーネント間での状態の連携をpropsリレーなしにシンプルに実装することができます。
- ページ読み込み時のアニメーションは`framer-motion`を使って実装しました。

# 残りの改善点

- 各セルが、スート（同じ柄の手札）かオフスート（別の柄の手札）かどっちなのかがパッと見でわかりにくい。現在は既存の表と同様に右上をスート、左下をオフスートとしているが、より視覚的に分かりやすくなるような工夫をしたい。
- ファビコンをいい感じにしたいが方法がよく分からない

# 終わりに

PokerSnowieや他有料サービスにはもっといい感じのものがあるだろうな、、と思いつつ、やはり書くことで覚えられると信じて作りました。リングゲームの片手間に見ていただけると嬉しいです

**参考資料**

https://www.amazon.co.jp/dp/4775949179/ref=cm_sw_r_tw_dp_PRA5XQYPNNSFN5AP7VFW