銀河麻雀の待ちを判定するWebアプリを作った

麻雀YouTuberの麻雀カッコイイシリーズが投稿した「銀河麻雀」。

<iframe width="560" height="315" src="https://www.youtube.com/embed/IDaKM7eU7zE?start=820" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

（ルール説明は13:40くらいから）
簡単に言えば、各種類の牌に1枚ずつ「ギャラクシー牌（銀河牌）」と呼ばれる牌種が自由になる牌が入った状態での麻雀だ。
例えば、銀河1mは1p,1sとしても扱える。
銀河東は東南西北、銀河白は發中としても扱える。

銀河牌がたくさん入ると、受け入れや待ちの数が通常よりも多くなる。
銀河牌が沢山入った順子が並べば、とても人間の頭では処理しきれない、まさに銀河が広がるような麻雀となる。

<a href="https://qiita.com/hatamura5121/items/83cdc09343b19cbb415b">前回の記事</a>では「待ちを判定するプログラムを作った」ということを書いたが、この度こちらをWebアプリ化した。
↓こちらから使えるので、ぜひ銀河麻雀のお供に役立てて欲しい。

https://galaxy.hatamura5121.com/

たとえばこんな大変な手牌でも…

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">銀河には人類が発見していない当たり牌&quot;ダークマター&quot;が存在します。ダークマターを見逃し誤ロンやフリテンをしてしまうことが多々あります。このロンを&quot;ダークマターストライク&quot;と名付けます。<br>さて、2mでロンしまったこの人の待ちは？<a href="https://twitter.com/hashtag/%E9%8A%80%E6%B2%B3%E9%BA%BB%E9%9B%80?src=hash&amp;ref_src=twsrc%5Etfw">#銀河麻雀</a><a href="https://twitter.com/hashtag/%E9%BA%BB%E9%9B%80%E6%98%9F%E4%BA%BA?src=hash&amp;ref_src=twsrc%5Etfw">#麻雀星人</a> <a href="https://t.co/YnljdkOXGA">pic.twitter.com/YnljdkOXGA</a></p>&mdash; 肥えD-Koedammit-＠たりんちゅ (@tikinnyaro) <a href="https://twitter.com/tikinnyaro/status/1504348117579481091?ref_src=twsrc%5Etfw">March 17, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

こんな感じで簡単に待ちを調べられる。
（探索には数十秒くらいかかる場合があります）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306681/41b79d51-b048-99d7-1311-2794aec9f913.png)


以下はWebアプリ作成にあたって、前回の記事からプログラムも大幅に見直したので、その辺を書く。
プログラミングとかに興味ないひとはここから先は見なくていいよ。

# バックエンドについて

https://qiita.com/hatamura5121/items/f26b29f4b67f7e1e6e61

以前作ったこのwebアプリのものを流用しているので、こちらを参考にして欲しい。
今回はDynamoDBも必要なく、Lambdaとつなぐだけなので結構あっさり実装できた。

# プログラムの見直しについて
Lambdaでプログラムを実装し直すにあたって、前回のプログラムを大幅にリファクタリングした。

・アガリ判定のロジックを見直した。

https://qiita.com/tomohxx/items/20d886d1991ab89f5522

こちらの方法を参考にして実装した。

・前回はデータの持ち方や受け渡しの部分がグチャグチャになっていたので、それらを大幅に見直した。具体的には、9×3+7列の多重Listで持っていた牌のデータを、34列のListに変えた。また、アガリ判定のフラグも、前回はBoolのリストで管理していたが、bit演算を使ってintで管理するようにした。

・再帰の処理で余計な処理をしている部分がいっぱいあったので、その辺も整理した。


ということで、リファクタ後のclass tehaiはこちら。

```python: lambda_handler.py(一部)
class tehai:
    def __init__(self,hand):
        self.hand = [0 for _ in range(34)]
        for i in range(34):
            self.hand[i] = hand[i]

    def len(self):
        return sum(self.hand)
    
    def agari(self):
        if self.len() % 3 != 2:
            return False
            
        for i in range(34):
            if self.hand[i] > 4:
                return False

        if self.len() == 14:
            f7 = True
            for m in self.hand:
                if m % 2 != 0:
                    f7 = False
            if f7:
                return True 
        
        for i in range(34):
            if self.hand[i] >= 2:
                self.hand[i] -= 2
                
                flag = True
                for j in range(3):
                    suhai = self.hand[j*9:(j+1)*9]
                    flag = flag & mentsu(suhai)
                
                for j in range(7):
                    flag = flag & (self.hand[j+27] % 3 == 0)
                
                self.hand[i] += 2
                if flag:
                    return True
        return False
    
    def machi(self):
        flag = 0
        for i in range(34):
            self.hand[i] += 1
            if self.agari():
                flag = flag | (1 << i)
            self.hand[i] -= 1
        return flag
```

だいぶすっきりした。
webで公開しているアプリなので、全部のコードを晒すことは控えておく。（万が一にも脆弱性とか攻撃とかされたら嫌なので…）

色々工夫を加えたことにより、Lambdaの実行環境においては銀河牌7枚まで1分以内に計算が終わるようになった。
ただ、ここから削っていくには根本的なアルゴリズムの工夫が必要なので、回収はかなり大変そうである。
気が向いたら頑張ることにする。