【Ruby】和了判定プログラムを作成する。(3)-刻子判定【麻雀】【最重要!!】

前回記事は、こちらです。↓

刻子を判定する。

今回は、同じ牌が3枚あるときの判定処理を作成します。

3枚同じ牌だからと言って、必ずしも「刻子」とは限らない。

同じ牌が3枚集まると、「刻子」となり、ひとつの面子になるのですが、3枚組が必ずしも刻子となるわけではありません。

下の手牌を見てください。

一萬:麻雀王国一萬:麻雀王国二萬:麻雀王国二萬:麻雀王国三萬:麻雀王国三萬:麻雀王国三萬:麻雀王国四萬:麻雀王国五萬:麻雀王国一筒:麻雀王国二筒:麻雀王国三筒:麻雀王国九筒:麻雀王国九筒:麻雀王国

この手牌は三萬:麻雀王国が3枚ありますが、三萬:麻雀王国を刻子とすると、和了りになりません。

この様に、3枚組に対しては、それが刻子の場合と、そうではない場合で分けて判定する必要があります。

3枚組の数が増えると、それぞれの3枚組に対してこの判定を行う必要があります。

力技による刻子判定

ただ、この3枚組も最大で4つしかないと考えると、下のプログラムになります。

#雀頭候補毎にcheck
jt=[]	#雀頭候補格納配列
tehai_check.each_with_index do |v,i|
	if v>=2 then
		jt << i
	end
end
jt.each_with_index do |v,i|
	tmp=tehai_check.dup #tehai_checkを複製
	amtmp=[]  #和了りの手牌を格納する配列
	#刻子判定
	kohtsu_list = []  #刻子候補リスト
	
	#刻子をチェック
	tmp.each_with_index do |vv,ii|
		kohtsu_list << ii if vv>=3
	end
	#刻子候補洗い出し
	kohtsu_ptn = []
	kohtsu_ptn_bit=[]
	
	case kohtsu_list.size
	when 4 then
		kohtsu_ptn_bit=[
			[1,1,1,1],
			[1,1,1,0],
			[1,1,0,1],
			[1,1,0,0],
			[1,0,1,1],
			[1,0,1,0],
			[1,0,0,1],
			[1,0,0,0],
			[0,1,1,1],
			[0,1,1,0],
			[0,1,0,1],
			[0,1,0,0],
			[0,0,1,1],
			[0,0,1,0],
			[0,0,0,1],
			[0,0,0,0]
		]
		kohtsu_ptn_bit.each do |vv|
			kpb_tmp=[]
			kpb_tmp=[
				vv[0]*kohtsu_list[0],
				vv[1]*kohtsu_list[1],
				vv[2]*kohtsu_list[2],
				vv[3]*kohtsu_list[3]
			]
			kpb_tmp.delete(0)
			kohtsu_ptn << kpb_tmp
		end
	when 3 then
		kohtsu_ptn_bit=[
			[1,1,1],
			[1,1,0],
			[1,0,1],
			[1,0,0],
			[0,1,1],
			[0,1,0],
			[0,0,1],
			[0,0,0]
		]
		kohtsu_ptn_bit.each do |vv|
			kpb_tmp=[]
			kpb_tmp=[
				vv[0]*kohtsu_list[0],
				vv[1]*kohtsu_list[1],
				vv[2]*kohtsu_list[2]
			]
			kpb_tmp.delete(0)
			kohtsu_ptn << kpb_tmp
		end
	when 2 then
		kohtsu_ptn_bit=[
			[1,1],
			[1,0],
			[0,1],
			[0,0]
		]
		kohtsu_ptn_bit.each do |vv|
			kpb_tmp=[]
			kpb_tmp=[
				vv[0]*kohtsu_list[0],
				vv[1]*kohtsu_list[1]
			]
			kpb_tmp.delete(0)
			kohtsu_ptn << kpb_tmp
		end
	when 1 then
		kohtsu_ptn_bit=[
			[1],
			[0]
		]
		kohtsu_ptn_bit.each do |vv|
			kpb_tmp=[]
			kpb_tmp=[
				vv[0]*kohtsu_list[0]
			]
			kpb_tmp.delete(0)
			kohtsu_ptn << kpb_tmp
		end
	end
	
	#刻子パターンごとに刻子を抜く
	kohtsu_ptn.each_with_index do |vv,ii|
		tmp2=tmp.dup
		amtmp2=amtmp.dup
		vv.each do |vvv|
			amtmp2 << [vvv,vvv,vvv]
			tmp2[vvv]-=3
		end
	
		#順子を抜く(未実装)
	end
	
	#順子を抜く(刻子候補なし)(未実装)
end

これは、何をしているプログラムなのかと言うと、

  1. 3枚組の数毎に条件分岐をした後に、(22行目のcase以降)
  2. 3枚組の数分に刻子であれば1、そうでなければ0として、(kohtsu_ptn_bit)
  3. 刻子候補の牌を表す数値を掛けることで、刻子候補リストを作成し、(kpb_tmp)
  4. それを基に刻子を抜く。(105行目以降)

と言う形になっています。

この後で、順子を抜いて牌が残らなければ、「和了り」。残ったら「ノーテン(和了っていない)」と言うわけです。

これでも、動くことは動くのですが、あまりに冗長です。

このプログラムを「ビット(2進数)」を使用したプログラムに書き換えます。

ビットを使用した刻子判定

ビットを使用すると、以下のプログラムになります。

#雀頭候補毎にcheck
jt=[]	#雀頭候補格納配列
tehai_check.each_with_index do |v,i|
	if v>=2 then
		jt << i
	end
end
jt.each_with_index do |v,i|
	tmp=tehai_check.dup #tehai_checkを複製
	amtmp=[]  #和了りの手牌を格納する配列
	#刻子判定
	kohtsu_list = []  #刻子候補リスト
	
	#刻子をチェック
	tmp.each_with_index do |vv,ii|
		kohtsu_list << ii if vv>=3
	end
	#刻子候補洗い出し
	kohtsu_ptn = []
	num = 2**kohtsu_list.size
	
	bit = kohtsu_list.size
	
	if kohtsu_list.size != 0 then
		kohtsu_ptn_bit=Array.new(num)
	
		#ここからビット(2進数)を使用
		(num-1).downto(0) do |i|
			kpb_tmp=Array.new(bit - 1)
			(bit-1).downto(0) do |ii|
				kpb_tmp[ii]= i[ii]!=0 ? kohtsu_list[ii] : 0 #ビットを活用した判定
			end
			kpb_tmp.delete(0)
			kohtsu_ptn << kpb_tmp
		end
		#ここまで
	end
	#刻子パターンごとに刻子を抜く
	kohtsu_ptn.each_with_index do |vv,ii|
		tmp2=tmp.dup
		amtmp2=amtmp.dup
		vv.each do |vvv|
			amtmp2 << [vvv,vvv,vvv]
			tmp2[vvv]-=3
		end
	
		#順子を抜く(未実装)
	end
	#順子を抜く(刻子なし)(未実装)
end

このプログラムは31行目でRubyの以下の機能を活用しています。

i = 15
ii=0
puts i[ii]  #iをビット(2進数)にした時の、1の位の数値(0あるいは1)
#=> 1

このように、整数に配列の添え字である「[]」をつけると、その整数をビット(2進数)であらわした時の、添え字の位の値を表示します(上のコードでは、1の位)。上のプログラムの27行目から36行目で使用することで、整理されたプログラムになりました。

これで、刻子判定のプログラムが完成しましたので、次回は順子判定のプログラムを作成します。

次回記事は、こちらです。↓

コメント

タイトルとURLをコピーしました