新年あけましておめでとうございます。
正月といえば(私は)麻雀なんですが、今年はやる機会がなかったので
rubyで麻雀してみました。
考え方
3時間以上かかってしまいましたが、なんとかできました。
ここで使う用語。
・面子(同一 or 連続する牌3枚)
・頭(同一牌2枚)
・待(もう一枚で面子or頭となる牌。1 or 3枚)
上がりとなるパターンは下記2種類と考えます。
・3面子(9枚)+頭(2枚)+待(2枚)
・4面子(12枚)+待(1枚)
なので、この面子となる組み合わせを再帰で探索する方向で
書いてみました。
こんな感じ
ソース
class Majang def mati(haipai) @tenpai = [] mati_check(haipai.split(//).sort) @tenpai.uniq! @tenpai.each{|x| puts x} end private def append_tenpai(mentu, mati) @tenpai << format_tenapi(mentu, mati) end def format_tenapi(mentu, mati) mentu.sort.map{|x| "(#{x})"}.to_s + "[#{mati.to_s}]" end def mati_check(hais, kakutei_mentu = []) #最後の1枚は上がり if hais.size == 1 append_tenpai(kakutei_mentu, hais) and return end #最後の4枚 if hais.size == 4 #全部同じ場合は、役無し return if all_same?(hais) #テンパイ確定後、終了 return if tenpai?(hais, kakutei_mentu) end #面子候補を取得 candidates(hais).each do |cand| next_hais = hais.minus(cand.split(//)) next_kakutei_mentu = kakutei_mentu + cand.to_a mati_check(next_hais, next_kakutei_mentu) end end def all_same?(ar) return false if ar.size == 0 ((ar - ar[0].to_a).size == 0)? true : false end def tenpai?(hais, kakutei_mentu) val = false hais.each_with_index do |hai, i| hai2 = hais[i, 2] # 2枚ずつ取り出す return val unless hai2.size == 2 atama = hai2 mati = hais.minus(atama) if atama?(atama) and mati?(mati) append_tenpai(kakutei_mentu + [atama.to_s], mati) val = true end end val end def atama?(hais) all_same?(hais) end def mati?(hais) (hais[1].to_i - hais[0].to_i) <= 2 ? true : false end def candidates(hais) work = [] hais.each_with_index do |hai, i| #刻子 work << (hai + hais[i+1] + hais[i+2]) if hai == hais[i+1] and hai == hais[i+2] #順子 work << (hai + hai.succ + hai.succ.succ) if hais.include?(hai.succ) and hais.include?(hai.succ.succ) end work.uniq end end class Array def minus(ar) work = self.dup ar.each do |val| work[work.index(val)] = nil if self.include?(val) end work.compact end end m = Majang.new m.mati("1112224588899") puts m.mati("1112223335559") puts m.mati("1223344888999") puts m.mati("1112345678999") puts
結果
$ ruby majang.rb (111)(222)(888)(99)[45] (111)(222)(333)(555)[9] (123)(123)(123)(555)[9] (123)(234)(888)(999)[4] (123)(44)(888)(999)[23] (234)(234)(888)(999)[1] (111)(234)(567)(99)[89] (111)(234)(678)(999)[5] (111)(234)(789)(99)[56] (111)(234)(567)(999)[8] (111)(345)(678)(999)[2] (111)(456)(789)(99)[23] (11)(123)(456)(789)[99] (123)(456)(789)(99)[11] (11)(123)(456)(999)[78] (11)(123)(678)(999)[45] (11)(345)(678)(999)[12]
感想
結構手間取りました。あんまりキレイじゃないなぁ。
他のrubyistのコードも見てみたい。
しかし、麻雀というのは複雑ですね。
これに字牌、捨牌、ドラ、上がり、相手の状況などを瞬時に判断してるんだから
人間すごい!
あと、rubyの配列って引き算が可能(ステキ!)なんですが、
重複する要素は取り除かれてしまいます。
>> ar1 = [1,2,3,1] >> ar2 = [1,2] >> ar1 - ar2 => [3]
今回重複は残したかったので、最後無理矢理Arrayを拡張しています。
もう少しいい書き方がありそうですが、こういうの以外と需要があるんじゃないでしょうか。
class Array def minus(ar) work = self.dup ar.each do |val| work[work.index(val)] = nil if self.include?(val) end work.compact end end