Pythonで天鳳のmjlogをcsvに書き直してみる

天鳳のデータを使ってAIのトレーニングをしようと思ったのですが、天鳳のmjlogファイルは特殊なフォーマットなので、それを紐解いていって、csvファイルに書いて行こうと思います。

*鳴きと三麻の場合には未対応なのでスキップして行きます。

##目的
プレイヤー毎の手牌と捨て牌を巡目毎に書き出していく

##Mjlogのフォーマットを理解する。
Mjlogの内容で今回興味があるのは
毎局のはじめに出てくる`<INIT ...>`のタグ
配牌 P1: `hai0="112,124,35,135,10,5,25,85,66,8,43,19,1,33"`
    P2: `hai1="46,96,73,53,128,120,98,89,28,49,41,83,72"`
ツモ P1: `<T[0-9]>`, P2:`<U[0-9]>`, P3: `<V[0-9]>`, P4: `<V[0-9]>`
打牌 P1: `<D[0-9]>`, P2:`<E[0-9]>`, P3: `<F[0-9]>`, P4: `<G[0-9]>`
辺りですのでそれ以外は無視します。

##データの前処理
まずは<INITのタグの位置を入手。

```
          inits = []
          for val in (re.finditer("<INIT", text)):
              inits.append(val.span())
          return inits
```
この<INITの場所を利用して、局を分ける。
曲毎に配牌、ツモ牌、捨て牌を上記したタグで分ける。

```
      """
      Get initial hand from given text
      """
      def retrieveHand(self, text):
          hands = np.zeros((4,34))
          for j in range(4):
              Hand = re.findall('hai' + str(j) + '="(.+?)"',text)
              Hand = [kyoku.split(",") for kyoku in Hand]
              Hand = [[self.haiConverter(int(tile)) for tile in kyoku] for kyoku in Hand]
              for k in range(len(Hand[0])):
                  hands[j][int(Hand[0][k])] += 1
          return hands
 
      """
      Get tsumos of players from given text
      """
      def retrieveTsumo(self, text):
          tsumos = []
          tsumos.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<T\d+',text)])
          tsumos.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<U\d+',text)])
          tsumos.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<V\d+',text)])
          tsumos.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<W\d+',text)])
          return tsumos
      """
      Get discards of players from given text
      """
      def retrieveDiscards(self, text):
         discards = []
         discards.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<D\d+',text)])
         discards.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<E\d+',text)])
         discards.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<F\d+',text)])
         discards.append([self.haiConverter(int(tile[2:])) for tile in re.findall(r'<G\d+',text)])
         return discards
```


##csvに書き出す流れ

```
   def writeToCSV(self, hands, tsumos, discards, csvfile):
         for player in range(4):
             discard = []
             smaller = len(tsumos[player])
             if(len(discards[player]) < len(tsumos[player])):
                 smaller = len(discards[player])
             for k in range(smaller):
                 hands[player][tsumos[player][k]] += 1
                 hands[player][discards[player][k]] -= 1
                 discard.append(discards[player][k])
                 target = self.shanten.calculate_shanten(hands[player])
                 csvfile.write("%d"%target)
                 for m in range(len(discard)):
                     csvfile.write(",%s"%(discard[m]))
                 csvfile.write("\n")
```

hands - 手牌 - 4 x 34の配列で、最初の4がプレイヤーと対応していて、次の34が各牌と対応している
tsumos - ツモ牌 - 4 x nの配列。nは１局における最大ツモ数。鳴きを考慮しないので、最大20としてある。
discards - 捨て牌 - 4 x nの配列。nはツモと同じ数か、一つ少ないかになる。
csvfile - 書き出す先のファイル。

これで書き出せるcsvの形が[シャンテン数、[捨て牌]]のようになっている。
読み込む時に余ったところは-1で埋めることによって次元数の違うベクトルにも対応できるようにしてある。

###github
[mahjongAI](https://github.com/Njima1572/mahjongAI)
こちらにmjlogをcsvに書き出すコードがあるので、自由にお使いください。
あまりにもピンポイントなことをするコードなので、正直使い道がないかもしれませんが。
まだまだ手直しする部分はたくさんあるのですが、初めてのちゃんとしたプロジェクトで浮足立ってるので許してください。

###今後の課題
毎巡csvfileに書き足しているので、ここを綺麗にすれば多少は効率化できるはず。
手牌、ツモ牌、捨て牌の抽出方法が多少強引。もっと効率よく短いコードでできればよし。


####シャンテン数について
[こちら](https://github.com/MahjongRepository/mahjong/blob/master/mahjong/shanten.py)にシャンテン数計算のライブラリがあったので拝借しました。ちょうどサイズ34の配列を使って計算しているので、かなり楽をさせていただきました。