前回:麻雀で学ぶTypeScript 4 ポン
ソースコード:https://github.com/bonetaro/mahjong-ts
チーは、上家からしかできない、チーする塔子(ターツ)候補が複数の可能性がある(ポン、カンの場合は一種類)など、ポンやカンよりいろいろとやっかいです。その分、いままでの実装では足りない箇所が多くありました。
ポンやカンができるかどうかは、ツモ番のプレイヤーが捨てた牌と同じ牌がポンは2枚、カンは3枚手牌にあるかで判定できますが、チーの場合は、プレイヤーが捨てた牌と同じ色の数牌でかつ、順子が構成可能な塔子(ターツ)があるかを判定する必要があります。現状はこれをそのまま実装していますが、文章でいうよりも実装は面倒(塔子候補の列挙など)な上に、これではTypeScriptらしい?実装ではないのではないかというか、そもそもTypeScriptの型を活かした実装とは何か?に悩んでおります。
export type 萬子 = "m"; // 英語はCharacters
export type 筒子 = "p"; // 英語はWheels
export type 索子 = "s"; // 英語はBamboos
export type 風 = "w"; // Wind
export type 元 = "d"; // Dragon
export type 数牌の数 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
export type 数牌の色 = 萬子 | 筒子 | 索子;
export type 萬子牌 = `${数牌の数}${萬子}`;
export type 筒子牌 = `${数牌の数}${筒子}`;
export type 索子牌 = `${数牌の数}${索子}`;
export type 数牌 = 萬子牌 | 筒子牌 | 索子牌; // Suits
export type 東 = `e${風}`; // EastWind
export type 南 = `s${風}`; // SouthWind
export type 西 = `w${風}`; // WestWind
export type 北 = `n${風}`; // NorthWind
export type 白 = `w${元}`; // WhiteDragon
export type 發 = `g${元}`; // GreenDragon
export type 中 = `r${元}`; // RedDragon
export type 四風牌 = 東 | 南 | 西 | 北; // Winds
export type 三元牌 = 白 | 發 | 中; // Dragons
export type 字牌 = 四風牌 | 三元牌; // Honours
export type 牌 = 数牌 | 字牌;
export type 対子like = [牌, 牌];
export type 刻子like = [牌, 牌, 牌];
export type 槓子like = [牌, 牌, 牌, 牌];
export type 塔子like = [数牌, 数牌];
export type 順子like = [数牌, 数牌, 数牌];
export type 雀頭like = 対子like;
export type 面子like = 順子like | 刻子like | 槓子like;
上記は、麻雀に出てくる用語をTypeScriptのTypeとして独自定義しています。前半は麻雀をそのまま定義している(はず)ので違和感ない(字牌のstringのチョイスは微妙かもですし、そもそも型の宣言に漢字をつかっているのが気持ち悪いというのはご勘弁ください)と思いますが、課題なのは「対子like」以降です。
「対子」は、本来同じ牌(実体)の2つの組み合わせであり、「対子like」のような「牌」という型(= 数牌 | 字牌)としての34種類あるうちの2つの組み合わせとは違います。
export type 対子 = ["1m", "1m"] | ["2m", "2m"] | ["3m", "3m"] | ... | ["gd", "gd"] | ["rd", "rd"];
export type 順子<C extends 数牌の色> =
| [`1${C}`, `2${C}`, `3${C}`]
| [`2${C}`, `3${C}`, `4${C}`]
| [`3${C}`, `4${C}`, `5${C}`]
| [`4${C}`, `5${C}`, `6${C}`]
| [`5${C}`, `6${C}`, `7${C}`]
| [`6${C}`, `7${C}`, `8${C}`]
| [`7${C}`, `8${C}`, `9${C}`];
上記のように、全部列挙すればいいといえばいいのですが、仕事ではそれでいいとしても:-P、趣味としてはそうはいきませんw。TypeScriptで何ができるか、どう使うべきか、を試行錯誤することが学びであり、壁をのりこえようとすることが成長であるはずですよね。
今はまだ分からないことが多いですので、もう少しTypeScriptのお勉強をつづけて次回に一旦の落としどころを出したいと思います。