シャンテン数 小粒プログラミング

#10 国士無双とチートイツのシャンテン数を算出する

Javascriptで遊ぶ麻雀小粒プログラミング
チートイツと国士無双のアガリ判定とシャンテン数を算出する処理を紹介します。

プログラムの解説と実行

前回のお題の通常手のアガリとシャンテン数を算出するに引き続き、国士無双とチートイツのアガリとシャンテン数を求める処理を紹介します。

国士無双のシャンテン数を求める

国士無双は通常手のように「4メンツ+1雀頭」を集める手役ではありません。チートイツと同様、特殊役という扱いになります。したがって通常手とは違う方法でアガリやシャンテン数を求める必要があります。国士無双のシャンテン数は次の計算式で求めることができます。

国士無双のシャンテン数を求める式
シャンテン数 = 13 - ヤオチュウ牌の数 - 雀頭(ダブっているヤオチュウ牌を1枚)

通常手のシャンテン数と同じく、テンパイは「0」でアガリは「-1」になります。

通常の国士無双のテンパイ(雀頭がある)
国士無双十三面待ち(雀頭が無い)

チートイツのシャンテン数を求める

チートイツは7個のトイツが集めるとアガリになる手役です。以下の特徴を押さえてアガリやシャンテン数を算出するプログラムを作ります。

  1. 7種7個のトイツを集める
  2. テンパイは「6種6トイツ+1種単騎待ち」になる
  3. 待ちは必ず単騎待ちになる
  4. 最高のシャンテン数は6シャンテン
  5. 4枚チートイツは基本的に禁止
チートイツのテンパイ。必ず単騎待ちになる

次の手牌はが3枚あるため、単騎待ちのテンパイにはなりません。イーシャンテン扱いになります。トイツ6種と被らない牌を引いてくるまではテンパイになりません。

チートイツで4枚待ちになる手牌例

以上の特徴を踏まえ、チートイツのシャンテン数は次の計算式で求めることができます。

チートイツのシャンテン数を求める式
シャンテン数 = 6 - トイツの数 - (7 - 牌の種類の数)

通常手のシャンテン数と同じく、テンパイは「0」でアガリは「-1」になります。

リンク牌理/牌効率学習ツール




国士無双とチートイツのシャンテン数を求めてみよう

手牌を作ってプログラムの動作を確認してみましょう。操作は簡単です。牌をクリックすると下のパネル内に牌が追加されていくます。[シャンテン数を調べる]ボタンを押すとシャンテン数を調査する手順が表示されます。




手牌:0牌(最大入力数は14牌)
シャンテン数:テンパイは「0」、アガリは「-1」になります。

プログラムの修正や最新版情報は動作チェックとバグ取りをしっかりやろう~通常手編に掲載しています。ここで紹介しているプログラムが新しくなっていることがありますので、チェックしてみてください。



JavaScriptソースコード

本記事で紹介したサンプルプログラムをダウンロードできます。

ダウンロードをする前にお読みください

  • サイトで紹介している記事の内容や公開しているプログラムの動作は100%保障するものではありません。
  • 当プログラム使用による如何なる不具合やトラブル、損害の責任も負いかねます。
  • 当プログラムは断り無く内容が変わることがあります。
  • 当プログラムを別サイトで配布することは禁止します。
  • サポートはいたしません。
  • 自己責任にてご利用くださいませ。

以上をご確認の上、プログラムのダウンロードをお願いいたします。

※今回は麻雀C言語プログラム集様のコードを、ほぼそのままJavaScriptに移植する形になりました。この場を借りてお礼申し上げますm(_ _)m

サンプルプログラムのソースコード

ソースコードは折りたたんであります。[+]を押すと、折りたたまれたソースコードが開きます。[-]を押すと、コードは折りたたまれます。

+ program_010.jsを開く

//============================================================================
//牌の配列:JSON形式
//============================================================================
var paiType = [
	{"No":0,"paiName":"赤五萬","cssSprite":"man0"},
	{"No":1,"paiName":"一萬","cssSprite":"man1"},
	{"No":2,"paiName":"二萬","cssSprite":"man2"},
	{"No":3,"paiName":"三萬","cssSprite":"man3"},
	{"No":4,"paiName":"四萬","cssSprite":"man4"},
	{"No":5,"paiName":"五萬","cssSprite":"man5"},
	{"No":6,"paiName":"六萬","cssSprite":"man6"},
	{"No":7,"paiName":"七萬","cssSprite":"man7"},
	{"No":8,"paiName":"八萬","cssSprite":"man8"},
	{"No":9,"paiName":"九萬","cssSprite":"man9"},

	{"No":10,"paiName":"赤五筒","cssSprite":"pin0"},
	{"No":11,"paiName":"一筒","cssSprite":"pin1"},
	{"No":12,"paiName":"二筒","cssSprite":"pin2"},
	{"No":13,"paiName":"三筒","cssSprite":"pin3"},
	{"No":14,"paiName":"四筒","cssSprite":"pin4"},
	{"No":15,"paiName":"五筒","cssSprite":"pin5"},
	{"No":16,"paiName":"六筒","cssSprite":"pin6"},
	{"No":17,"paiName":"七筒","cssSprite":"pin7"},
	{"No":18,"paiName":"八筒","cssSprite":"pin8"},
	{"No":19,"paiName":"九筒","cssSprite":"pin9"},

	{"No":20,"paiName":"赤五索","cssSprite":"sou0"},
	{"No":21,"paiName":"一索","cssSprite":"sou1"},
	{"No":22,"paiName":"二索","cssSprite":"sou2"},
	{"No":23,"paiName":"三索","cssSprite":"sou3"},
	{"No":24,"paiName":"四索","cssSprite":"sou4"},
	{"No":25,"paiName":"五索","cssSprite":"sou5"},
	{"No":26,"paiName":"六索","cssSprite":"sou6"},
	{"No":27,"paiName":"七索","cssSprite":"sou7"},
	{"No":28,"paiName":"八索","cssSprite":"sou8"},
	{"No":29,"paiName":"九索","cssSprite":"sou9"},

	{"No":30,"paiName":"裏","cssSprite":"ji0"},
	{"No":31,"paiName":"東","cssSprite":"ji1"},
	{"No":32,"paiName":"南","cssSprite":"ji2"},
	{"No":33,"paiName":"西","cssSprite":"ji3"},
	{"No":34,"paiName":"北","cssSprite":"ji4"},
	{"No":35,"paiName":"白","cssSprite":"ji5"},
	{"No":36,"paiName":"發","cssSprite":"ji6"},
	{"No":37,"paiName":"中","cssSprite":"ji7"}
];
//グローバル変数==============================================================
var tehai = new Array(37);//手牌の配列:37種
tehai = [0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0];
//var tempTehai = new Array(37);//tehai配列のクローン用
//============================================================================
//国士無双のアガリ判定とシャンテン数を返す関数
//============================================================================
function syanten_kokusi(){
	var syantenKokusi=13;
	var toitsu_suu=0;//雀頭
	var i;

  //19牌をチェックする処理
	for(i=1;i<30;i++){
		if(i%10==1||i%10==9){//10で割った余りが1または9の場合に実行する
			if(tehai[i]){
				syantenKokusi--;
			}
			////余った19牌を雀頭としてカウント。1個でOK
			if(tehai[i]>=2&&!toitsu_suu){
				toitsu_suu=1;
			}
		}
	}

	//字牌をチェックする処理
	for(i=31;i<38;i++){
		if(tehai[i]){
			syantenKokusi--;
		}
		////余った字牌を雀頭としてカウント。1個でOK
		if(tehai[i]>=2&&!toitsu_suu){
			toitsu_suu=1;
		}
	}

	//雀頭がある場合の処理
	syantenKokusi-=toitsu_suu;
	return syantenKokusi;
}
//============================================================================
//チートイツのアガリ判定とシャンテン数を返す関数:修正版
//============================================================================
function syanten_7toitsu(){
	var toitsu_suu = 0;//対子数
	var syurui_suu = 0;//牌の種類
	var seven7toitsu = 6;//七対子のシャンテン数
	var i;
	var kantsu_count=0;

	for(i=1;i<38;i++){
		if(!tehai[i]){continue;}//牌が無い時は以降の処理を中断して、ループの最初に戻る
		if(tehai[i]===4){kantsu_count++;}
		syurui_suu++;//4枚チートイツを回避するために牌種をカウントしておく
		if(tehai[i]>=2){
			toitsu_suu++;
		}
	}

	if(syurui_suu===7&&toitsu_suu===7){
		return -1;//アガリ判定
	}

	if(syurui_suu>=7&&toitsu_suu===6&&kantsu_count===0){
		return 0;//テンパイ判定
	}

	if(syurui_suu===6&&toitsu_suu===6){
		return 1;//1シャンテン判定
	}

	syanten7toitsu=6-toitsu_suu;//チートイツのシャンテン数を求める計算式

	return syanten7toitsu;
}
//============================================================================
//動作チェック用
$("#SyantenCheckBtn").click(function() {
	var paiTehaiSyantenCheck = $("#paiTehaiSyantenCheck");//要素を変数に格納してキャッシュ
	paiTehaiSyantenCheck.empty();

	var syantenkokusi = syanten_kokusi();//syantenCheck()の動作テスト用関数
	paiTehaiSyantenCheck.append("国士無双のシャンテン数は" + syantenkokusi);

	var syanten7toitsu = syanten_7toitsu();//syantenCheck()の動作テスト用関数
	paiTehaiSyantenCheck.append("<br>チートイツのシャンテン数は" + syanten7toitsu);

});
//============================================================================

+ paiput.jsを開く

var PaiCount = 0;	//入力された牌の枚数

//牌画を挿入する関数
$("span","#haiga").on('click', function(event){
	var paiNo = $(this).attr("name"); //牌の番号を取得
	var paiName = $(this).attr("class"); //CSSスプライト用のクラス名を取得
	//alert(paiNo);
	if(PaiCount<14){
		if(tehai[paiNo]>3){
			alert("5枚以上の同種牌を置くことはできません。");
			return false;
		}else{
			tehai[paiNo] ++;
			paiga = "<span class=\"" + paiName + "\"></span>";//cssスプライトの牌画を配置
			$("#paiTehai").append(paiga);
			PaiCount ++;
			$("#paikazu").empty().append(PaiCount);
		}
	}else{
		alert("これ以上は牌を置けません。");
		return false;
	}
});

//手牌を並べ替える処理
$("#PaiSortBtn").on('click', function(event){
	var i,k,kazu;
	var paiga="";
	$("#paiTehai").empty();
	(function (){
		for(i=0;i<38;i++){
			//通常の手牌表示処理
			if(tehai[i]){
				kazu = tehai[i];
				for(k=0;k<kazu;k++){
					paiga = "<span class=\"" + paiType[i].cssSprite + "\"></span>";//cssスプライトの牌画を配置
					$("#paiTehai").append(paiga);
				}
			}
		}
	})();
});

//手牌を消去する処理
$("#PaiClreaBtn").on('click', function(event){
	$("#paiTehai").empty();
	tehai = [0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0];
	PaiCount = 0;
});

+ program_010.cssを開く

@charset "utf-8";

#paiTehai > span , #haiga > span, .haiga > span{
background-color:transparent;
background-image:url(../images/pai.png);
background-repeat:no-repeat;
background-attachment:scroll;
display:inline-block;
color:#000;
}

.paishadow{
	box-shadow: 0px 0px 5px 2px rgba(0, 256, 256, 1);
}

.man1{background-position:0px 0px;height:47px;width:31px;}
.man2{background-position:-31px 0px;height:47px;width:31px;}
.man3{background-position:-62px 0px;height:47px;width:31px;}
.man4{background-position:-93px 0px;height:47px;width:31px;}
.man5{background-position:-124px 0px;height:47px;width:31px;}
.man6{background-position:-155px 0px;height:47px;width:31px;}
.man7{background-position:-186px 0px;height:47px;width:31px;}
.man8{background-position:-217px 0px;height:47px;width:31px;}
.man9{background-position:-248px 0px;height:47px;width:31px;}
.man0{background-position:-279px 0px;height:47px;width:31px;}
.pin1{background-position:0px -47px;height:47px;width:31px;}
.pin2{background-position:-31px -47px;height:47px;width:31px;}
.pin3{background-position:-62px -47px;height:47px;width:31px;}
.pin4{background-position:-93px -47px;height:47px;width:31px;}
.pin5{background-position:-124px -47px;height:47px;width:31px;}
.pin6{background-position:-155px -47px;height:47px;width:31px;}
.pin7{background-position:-186px -47px;height:47px;width:31px;}
.pin8{background-position:-217px -47px;height:47px;width:31px;}
.pin9{background-position:-248px -47px;height:47px;width:31px;}
.pin0{background-position:-279px -47px;height:47px;width:31px;}
.sou1{background-position:0px -94px;height:47px;width:31px;}
.sou2{background-position:-31px -94px;height:47px;width:31px;}
.sou3{background-position:-62px -94px;height:47px;width:31px;}
.sou4{background-position:-93px -94px;height:47px;width:31px;}
.sou5{background-position:-124px -94px;height:47px;width:31px;}
.sou6{background-position:-155px -94px;height:47px;width:31px;}
.sou7{background-position:-186px -94px;height:47px;width:31px;}
.sou8{background-position:-217px -94px;height:47px;width:31px;}
.sou9{background-position:-248px -94px;height:47px;width:31px;}
.sou0{background-position:-279px -94px;height:47px;width:31px;}
.ji1{background-position:0px -141px;height:47px;width:31px;}
.ji2{background-position:-31px -141px;height:47px;width:31px;}
.ji3{background-position:-62px -141px;height:47px;width:31px;}
.ji4{background-position:-93px -141px;height:47px;width:31px;}
.ji5{background-position:-124px -141px;height:47px;width:31px;}
.ji6{background-position:-155px -141px;height:47px;width:31px;}
.ji7{background-position:-186px -141px;height:47px;width:31px;}
.ji0{background-position:-217px -141px;height:47px;width:31px;}


#haiga{
  background-color:#173B0B;
  padding:5px 15px 3px 15px;
  margin-bottom:5px;
  color:#fff;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;}

おすすめレンタルサーバー

エックスサーバー|高速・高機能レンタルサーバー

オールSSDの快適ハイスペック環境」「国内管理、大容量バックボーン」「独自SSLが無料」「FastCGIなどの高速化機能」「最新のPHP7を実装」など、高機能・高コストパフォーマンスなレンタルサーバーです。※当サイトも「エックスサーバー」で運用しています。

-シャンテン数, 小粒プログラミング
-

© 2020 無料麻雀ツール&アプリ 点数計算ラボ