某研究施設に勤務する研究者です。研究や趣味の麻雀のこと。

天鳳成績管理ツール
最終更新 2011-03-04

当ブログのデータ等を転載したい場合は筆者まで一報願います。

S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
牌譜解析記事まとめ
過去記事へのリンクをまとめています。

カテゴリ
以前の記事
【メモ】 牌譜解析スクリプトでのmjlogから対戦順の評価方法
前回、mjlogから対戦日時を読み込む限界について記事を書いたんだけど、天鳳牌譜解析スクリプトでのmjlogから対戦順の評価方法が分かったので、忘れないうちにメモを残しておきます。

牌譜解析スクリプトで「順位遷移」や「Rateグラフ」のような対戦順にデータをソートする必要のある場面で使っているJavaScriptのソース(log_jun.jsまたはlog_rate.js)を開くと、以下のようなスクリプトが見つかります。

//----------------------------------
// ANALYZER PARAM
//----------------------------------
//var sSortAs"CTimeAscent"|"CTimeDescent"
//※ ソートはOnEnumLogとOnLogBeginの間で行なう
//----------------------------------
var sortAs="CTimeAscent"; // ファイル作成時間の古いものから
//var sortAs="CTimeDescent"; // ファイル作成時間の新しいものから(default)

なるほど、ファイル作成時間の古いものから順にソートして、それを対戦順としているようです。やはり現状ではmjlogファイルはタイムスタンプの情報に頼っているのか。

PCを変えたり、バックアップでファイルをコピーしちゃったりしたらどーすんだよ(^^;)。タイムスタンプ変わっちゃうじゃん。

でも、これで1つ注意しなくちゃいけない重要なことが分かりました。mjlogファイルをコピーしたり、もしくは再度ダウンロードするなど、タイムスタンプを変えてしまうようなことは絶対にしないってコトです(^^;)。少なくとも牌譜解析スクリプト上では対戦順が変わってしまうみたいだからね。

ちなみに今作っているmjlogを読み込む成績管理ツールはタイムスタンプとmjlogファイルのファイル名の両方でソートする仕様にしようと思っているので、万一タイムスタンプが変わってしまったmjlogファイルを使っていたとしても、少なくとも対戦の時間単位までの精度で表示されます。
[PR]
# by doraaka | 2009-04-11 16:39 | 天鳳
【メモ】 mjlogから対戦日時を読み込む限界について
実は一週間ほど前から、天鳳対戦ログの管理について頭を悩ませている問題1つがあります。それは対戦日時に関する情報です。

現在、おさむくんの協力もあって、成績管理ツールをmjlogファイルから直接データを読み込むプログラムを作っているんだけど、対戦日時を正確に判断することができないという問題に直面しちゃいました。

そもそもmjlogファイルはファイルを展開をしなくても、ファイル名から対戦の日付と時間の情報までは得られます(逆に言うと、mjlogファイルの中身を調べても対戦日時に関する情報は書かれていません)。例えば、

2009031123gm-0061-0000-e82aaa78&tw=1.mjlog

というファイル名であれば、2009年の3月11日23時台の対戦であることが分かります。しかし、東風戦など対戦が短時間で終わる場合などは、上の例で言えば23時台をまたがずに次の対戦を始めると、mjlogファイル名の対戦日時情報が書かれている部分は全く同じになってしまい、分単位の情報が得られない以上、どちらが先に行われた対戦かどうか判断することができません。

唯一の方法は、1回対戦が終わるたびに牌譜解析スクリプトで牌譜データをダウンロードすれば、ファイル名からの判断はできませんが、ファイルの作成日時の情報が変わりますので、ここで判断することができます。複数の対戦データを同時にダウンロードした場合、タイムスタンプは全く同じになってしまい判別が付きません。でも、いちいちこんなことやるのは死ぬほど面倒くさいよね(^^;)

いぶしのけんさんがつのさんに確認を取ってくれたようですが、残念ながらこれは仕様で現状では分単位の対戦情報は欠落しているようです。

牌譜解析スクリプトで、[牌譜の内容表示]→[すべての牌譜をファイルに保存]とすると、mjlogのデータがテキストに落とされて新しい順番にソートされるようになっています。僕はこのソートをどのように行っているのか分からないけれど、もし上に書いたような仕組み以上のことが実装されていなければ、厳密には分単位で対戦が前後して表示されることもおきるってことだよね。

何とかならないかな・・・。

牌譜解析スクリプトには、[順位解析]と[Rateグラフ]という項目があって、コイツらはX軸を対戦数にして成績の推移をグラフ化してるよね。でもこの問題があるとすれば、このグラフだってある意味正確とはいえないんじゃないのかな。いや、もしかしたら僕が知らないだけで正確なのかもしれないけど、正確ならどうやってソートしてるのか教えてほしいな。

誰か知っている人がいたら教えて下さい(^^)

現状では、mjlogファイルをリストとして読み込んだ後、ファイルの作成日時の情報でソートをかけ、その後、ファイル名の頭の対戦日時情報の部分で2回目のソートをかけて、対戦の順番を整理するようにしています。多分、現段階で分かっている範囲では、これが一番現実に近いソート方法だと思います。
[PR]
# by doraaka | 2009-04-08 17:55 | 天鳳
【メモ】 mjlogの卓情報を読み込む (Part 2)
Part 1の続きです。

さて、僕のある牌譜ログではこの卓情報の値が137となっているものがありました。では実際にこの137という整数値から卓の種別を割り出してみましょう。ここでも天鳳牌譜解析スクリプトのtenhou.jsを参考にします。

var GT={ // game type
        MULTI:0x0001, // 対人戦
        NOAKA:0x0002, // 赤ナシ
        NOKUI:0x0004, // 喰ナシ
        NAN  :0x0008, // 東南
        SANMA:0x0010, // サンマ
        TOKU :0x0020, // 特上
        SAKU :0x0040, // 速
        HIGH :0x0080, // 上級
        taku:function(w)
          {return (w&0x0020)>>4 | (w&0x0080)>>7;}
          // 0=一般 1=上級 2=特上 3=鳳凰
};

この137という値はある1つの組み合わせでしか成立しないようになっているでしょうから、一番大きな値から順番に切り出していくのがよいでしょう。まずは137を16進数表記に変換します。これは0x0089となりますね。上のスクリプトを見て、この0x0089以下で最も大きい値は「上級」を示す0x0080ですね。つまりこの対局は上級卓ということです。

次に0x0089から0x0080を引くと、0x0009が残ります。同様の方法で条件を探していくと、次は「東南」の0x0008であることが分かりますね。残るは0x0001で、これは「対人戦」データです。また結局、「喰ナシ」の0x0004と「赤ナシ」の0x0002に引っかかりませんでしたので、「喰いタン有り」かつ「赤有り」ということも分かります。

従って、この対局は「上南喰赤」であることが分かりました。めでたしめでたし(^^)。一応、手順を整理しておきます。

(1) GO typeに記載されている整数値を16進数表記に変換する。
(2) その値をXとすると、X以下で最大となる値を上のスクリプトのvar GTから見つける(鳳凰卓は後述)。
(3) その値に該当する卓情報を抜き出す。
(4) Xとその値の差を再びXに代入する。
(5) (2)~(4)をX=0となるまで繰り返す。


以上です。

ちなみに、上のスクリプトには鳳凰卓の16進数表記がないですね。僕は当然鳳凰条件なんて満たしていないので確かめられません。でも何とかなります。牌譜解析スクリプトを片っ端から見ていけばどこかに書いてあるのかもしれないけど、上のスクリプトにも一番最後の行の計算で「3=鳳凰」とあるので、結果が3になる値を逆算すれば出てくるでしょう。

10進数で3ということは2進数表記で11です。この数値のそれぞれの桁を左へ4bitシフト及び7bitシフトさせると、10100000となります。分かりますでしょうか。

0x0020の2進数表記は100000、0x0080の2進数表記は10000000ですから、それぞれwとのビット積を取って、両者のビット和が10100000になるようなwをを導けば、この計算結果が3(=鳳凰)となるってワケです。そのためにはwの値も同じく10100000でなければなりませんね。最後にこれを16進数表記に変換すると、w = 0x00A0となります。つまりこれが鳳凰卓の値となるのではないかと思います。

もしこれが正しいとすれば、「鳳東喰赤速」での卓情報「GO type」の値は225(16進数表記で0x00E1)になっていると思うのですが、どなたか鳳凰条件を満たしている方にチェックしてもらえると嬉しいな。

【追記:2009-04-04】
実際にスクリプトを組んでmjlogを読み込んでみたんだけど、どうやら鳳凰卓の値(0x00A0)が加わったせいで、卓情報の読み込みは少しだけ複雑になっているみたいです。例えば、「上東喰赤速」の値は0x00C1なんだけど、ここから卓情報を割り出そうとすると、一番最初にヒットするのは0x00A0の鳳凰卓になってしまいます。でも、このまま次の情報を読み出すと、0x0020の特上卓になり、卓ランクの情報が2つになってしまいます。これはおかしいですね。なので、卓ランクの値を2重にカウントした場合は鳳凰の値をスキップすれば大丈夫です。この例でもちゃんと0x0080の上級卓がヒットしますね。めでたしめでたし(^^)

[PR]
# by doraaka | 2009-04-03 02:14 | 天鳳
【メモ】 mjlogの卓情報を読み込む (Part 1)
天鳳mjlogファイルをXML形式に変換して適当なビューアで開いてみると牌譜情報がXMLタグに収まっているのを見ることができます。

<mjloggm ver="2.3"><GO type="97"/>

冒頭にログのバージョン情報があって、その次の「GO」にタグが見つかります。ここには「type」というインデックスがあり、ある整数値(上の例だと97)の属性が与えられています。牌譜解析スクリプトのtenhou.jsをテキストビューアで見るとこれはどうやら卓情報のようで、おさむくんのメモにもそう書かれています。そして、これがログ解析の最初の難所でしょうか。

要するに、この整数値が「特東喰赤速」などの卓情報に対応しているってワケです。実は上に示したログの例は「特東喰赤速」の牌譜なので、97という整数はこれに対応しているはずです。ここには、この整数値からどのように卓情報を解析していくか、その過程をメモとして残しておきます。

天鳳牌譜解析スクリプトのtenhou.jsを見ると、以下のような記述があります。

var GT={ // game type
        MULTI:0x0001, // 対人戦
        NOAKA:0x0002, // 赤ナシ
        NOKUI:0x0004, // 喰ナシ
        NAN  :0x0008, // 東南
        SANMA:0x0010, // サンマ
        TOKU :0x0020, // 特上
        SAKU :0x0040, // 速
        HIGH :0x0080, // 上級
        taku:function(w)
          {return (w&0x0020)>>4 | (w&0x0080)>>7;}
          // 0=一般 1=上級 2=特上 3=鳳凰
}; 

ふむふむ、なるほど。なかなか凝ったことやってるなあ。
どうやらルールや卓のランクを16進数で表記していて、その総和で卓情報を表しているようです。実際に「特東喰赤速」を例に計算してみると、

0x0001(対人)+0x0020(特上)+0x0040(速) = 0x0061

となりました。これを10進数表記に変換すると97になりますね。めでたしめでたし(^^)。

でも実際の使い方は、これと逆で整数値から卓の情報を割り出さなくちゃいけないんだよね。上に書いたJaveScriptをもう一度よく見ると、一番最後の行に卓のランク分けに関する記述があるな。一般、上級、特上、鳳凰を0~3で判別しているようです。その計算式をみると、

return (w&0x0020)>>4 | (w&0x0080)>>7

この式の中の&や|、>>はビット演算子なので2進数での計算式ということか。試しにさっきの「特東喰赤速」を表す97で計算してみよう。

まず、97を2進数表記に変換すると、1100001となります。これが上の式のwに代入されます。

次に(w&0x0020)>>4を見ていきます。これは0x0020を2進数に変換(100000)して、wとのビット積(&)を取ります。すると100000という値が得られました。>>は右シフトを表すビット演算子なので、100000を4bit分右にずらすと、10という値になりますね。

同様に(w&0x0080)>>7についても、0x0080を2進数に変換(10000000)して、wとのビット積を取ると、こちらは0になります。ですので当然ビットシフトをかけてもゼロです。

最後に両者(10と0)のビット和(|)を取ると、10という値が得られますね。これを10進数に変換すると2になります。上のスクリプトのコメント欄に「2=特上」とありますね。どうやらこうゆう仕組みのようです。

Part 2に続きます。
[PR]
# by doraaka | 2009-04-03 01:36 | 天鳳
【メモ】 mjlogファイルは常に圧縮されているとは限らない?
今試しに、直接mjlogファイルを読み込むルーチンを作ってみている。
ちなみにmjlogファイルというのは課金版天鳳でプレイすると自動的に保存される牌譜情報のファイルです。

このmjlogファイルは天鳳公式サイトのマニュアルによると、xmlをgz圧縮していると明記されています。

なので、ログが保存されているフォルダ以下にあるmjlogファイルにGZIP解凍をかけるスクリプトを作って走らせてみたところ、ごくわずかのファイルに解凍できないエラーが発生する。どうしてなのかなと、それらのファイルをバイナリエディタで開いてみたところ・・・、

なんと圧縮前のファイル、つまり既にxmlファイルになっているよー!?

調子悪いのは、ファイルの大半はgz圧縮ファイルで、ごくまれに本来のxmlファイルが紛れ込んでいるという状態(^^;)。サーバ側で圧縮をかけるときにエラーでも生じたとき、xmlのままでダウンロードされるってことなのかな?それにしても、gzもxmlも拡張子が同じmjlogだからパッと見て判別できない(圧縮されてないからファイルサイズが大きいけどゲームの長さによるから正確には分からない)。

一応、該当のファイルを移動させて再度ダウンロードしてみたけど結果は同じ・・・、orz。どうして特定のログだけ圧縮されないんだろ?

このために簡単なチェック用のプログラム(checkmjlog)を作ったので、興味あるという奇特な人がもしいたら確かめてみてね(^^;) ← イネーヨ

ちなみに僕のケースでは、

mjlog ファイル数 : 713
-----------------------------------
GZ圧縮ファイル数 : 694
XMLファイル数 : 19
上記以外のファイル数 : 0

という結果になって圧縮されていないXMLファイルが19個も見つかった。

つのさんに聞いてみようかな。
まあ、これが仕様だとすれば、読み込みルーチンは、とりあえず解凍をかけてみて、無理ならアスキーで読んで、xmlのヘッダーとかで牌譜のログであると判別すればいいわけだけど何か気持ち悪いよね(^^;)

どなたか知っている人いませんか?
[PR]
# by doraaka | 2009-04-02 07:30 | 天鳳


その他のジャンル
Twitter
記事ランキング
画像一覧
AX