麻雀の手牌を表示するプログラム

電脳麻将 は HTML5 + CSS3 + JavaScript で動作しています。手牌もHTML5とCSS3だけで表示しています。今回は以下の手牌を例に、どのように表示しているか説明します。

f:id:xlc:20200809182855p:plain:w600

手順は以下の流れになります。

牌譜形式 → JavaScript形式 に変換

上記の牌姿を電脳麻将の牌譜でも使用している 文字列表記 で表すと、

"m05z11z2,p5550,s550=5,m12-3"

となります。まずこの文字列をプログラム内部で使用している Majiang.Shoupai オブジェクトの形式に変換します。

{				            // 手牌全体
    _bingpai: {                               // 副露していない部分
        m: [1,0,0,0,0,2,0,0,0,0],               // 各牌の枚数(萬子)
        p: [0,0,0,0,0,0,0,0,0,0],               // 各牌の枚数(筒子)
        s: [0,0,0,0,0,0,0,0,0,0],               // 各牌の枚数(索子)
        z: [0,2,1,0,0,0,0,0]                    // 各牌の枚数(字牌)
    },
    _fulou: [ "p5550", "s550=5", "m12-3" ],   // 副露メンツ
    _zimo:  "z2",                             // ツモ牌
    _lizhi: false                             // リーチフラグ
}

詳細は 麻雀の手牌のJavascript表現 - koba::blog で説明しています*1。この変換は以下のプログラムで行うことができます。

let shoupai = Majiang.Shoupai.fromString("m05z11z2,p5550,s550=5,m12-3");

具体的なプログラムは以下の通りです。

JavaScript形式 → HTML に反映

次に、JavaScriptの内部表現をDOMに反映します。

<div class="shoupai">                              <!-- 手牌全体 -->
  <div class="bingpai">	                             <!--  副露していない部分 -->
    <img class="pai" data-pai="m0" src="img/m0.gif">   <!-- 五萬(赤) -->
    <img class="pai" data-pai="m5" src="img/m5.gif">   <!-- 五萬     -->
    <img class="pai" data-pai="z1" src="img/z1.gif">   <!-- 東       -->
    <img class="pai" data-pai="z1" src="img/z1.gif">   <!-- 東       -->
    <span class="zimo">                                <!-- ツモ牌   -->
      <img class="pai" data-pai="z2" src="img/z2.gif">   <!-- 南       -->
    </span>
  </div>
  <div class="fulou">                                <!-- 副露牌全体 -->
    <span class="mianzi">                              <!-- 副露メンツ   -->
      <img class="pai" data-pai="_" src="img/pai.gif">   <!-- (裏向き) -->
      <img class="pai" data-pai="p5" src="img/p5.gif">   <!-- 五筒     -->
      <img class="pai" data-pai="p0" src="img/p0.gif">   <!-- 五筒(赤) -->
      <img class="pai" data-pai="_" src="img/pai.gif">   <!-- (裏向き) -->
    </span>
    <span class="mianzi">                              <!-- 副露メンツ   -->
      <img class="pai" data-pai="s5" src="img/s5.gif">   <!-- 五索     -->
      <span class="rotate">                              <!-- (横向き) -->
        <img class="pai" data-pai="s0" src="img/s0.gif">   <!-- 五索(赤) -->
        <img class="pai" data-pai="s5" src="img/s5.gif">   <!-- 五索     -->
      </span>
      <img class="pai" data-pai="s5" src="img/s5.gif">   <!-- 五索     -->
    </span>
    <span class="mianzi">                              <!-- 副露メンツ   -->
      <span class="rotate">                              <!-- (横向き) -->
        <img class="pai" data-pai="m2" src="img/m2.gif">   <!-- 二萬     -->
      </span>
      <img class="pai" data-pai="m1" src="img/m1.gif">   <!-- 一萬     -->
      <img class="pai" data-pai="m3" src="img/m3.gif">   <!-- 三萬     -->
    </span>
  </div>
</div>

この処理は以下のプログラムで行うことができます。

new Majiang.View.Shoupai($('.shoupai'), shoupai, true).redraw();

$('.shoupai') は手牌を配置するDOMノード、shoupai は先ほど文字列から変換した手牌のJavaScript表現、true は手牌を表向きに表示する指示です。

具体的なプログラムは以下の通りです。

プログラム中では牌画像のURLを意識していません。HTML中に雛形があり、それを参照しています。牌画像のURLを変更する場合、プログラムの変更は不要でHTMLのみを変更すればよいです。

HTML を CSS で実際に表示されるイメージに調整

上のHTMLのままでは牌が順に並んでいるだけですので、CSSで場所を調整します。キモは CSS3で加槓を表現する - koba::blog で説明した牌の回転です*2。これさえできればあとは簡単で、副露牌を float: right; で右側に配置し、副露メンツはその内側でさらに float: right; で右から順に配置すればよいです*3

原理はこれでよいのですが、手牌は様々な大きさで様々な場所に表示する必要があります*4。そこで StylusMixin の機能を使って部品化しました。

shoupai-size($width, $height, $bingpai-height, $fulou-height)

各パラメータの意味は以下の通りです。

$width
表示領域の幅
$height
表示領域の高さ
$bingpai-height
手牌(副露していない部分)の高さ
$fulou-height
副露牌の高さ

Mixin はあたかも既存のCSSプロパティのように透過的に使うことができます。

パラメータを調節すれば以下のような表示が可能になります。

shoupai-size: 520px 50px 49px 35px
副露牌をひとまわり小さくし、表示幅は固定サイズとする。これが通常の表示方法。

f:id:xlc:20200809234952p:plain:w540

shoupai-size: auto 70px 49px 49px
表示領域の幅を auto にすると、手牌と副露牌が接近して表示される。和了役の表示の際はこちらの方が見やすい。

f:id:xlc:20200809235010p:plain:w586

shoupai-size: 460px 120px 42px 42px
表示領域の高さが十分にある場合は、副露牌は下段に表示される。スマートフォン画面での下家・上家など表示領域の幅が不十分なときに適した表示方法。

f:id:xlc:20200809235026p:plain:w480

*1:ver.1.0.0 からリーチしているか否かを示すフラグ _lizhi を追加しました

*2:これが上手くいったので、開発を始めることを決意しました

*3:最初に副露したメンツを一番右に置く

*4:麻雀卓の中でも4人分+和了役の表示。さらには検討モードや牌理、点数計算、何切る解答機など