あなたのスキルで飯は食えるか 史上最大のコーディングスキル判定
あなたのスキルで飯は食えるか 史上最大のコーディングスキル判定
http://www.itmedia.co.jp/enterprise/articles/1004/03/news002.html
この手の問題を解くのはCマガ電脳倶楽部以来だったので、久々に楽しんで解けました。
コーディング完了までは1時間半。テストで九蓮宝燈が正しく出力されないところで、ようやく実装ミスに気づきましたorz トータルタイム 2時間半。40分で解くってやっぱり凄いな……。
特に工夫点はないのですが、九蓮宝燈が正しく判定されない地点では刻子/順子判定は最初に見つかったものをreturnする実装になっていたのですが、それだと特定パターンしか見つけられないことに気づいたため、offsetでズラして探していくようにして、その過程で見つかったものをyieldで返すようにしておきました。おかげで、修正点は少なくて済んだのがよかった。
とりあえず、九蓮宝燈の結果だけ掲載しておきます。
/home/kirika/ruby% ruby marjang.rb 1112345678999
(111)(234)(567)(999)[8]
(111)(234)(678)(999)[5]
(111)(345)(678)(999)[2]
(111)(234)(567)(99)[89]
(111)(234)(789)(99)[56]
(111)(456)(789)(99)[23]
(11)(123)(456)(999)[78]
(11)(123)(678)(999)[45]
(11)(345)(678)(999)[12]
(11)(123)(456)(789)[99]
(123)(456)(789)(99)[11]
#!/usr/bin/ruby class Marjang # 与えられた配牌から初めに見つかった刻子を取り出す # 見つかったらyieldで返す def kohtsu!(haipai) haipai_bak = haipai.dup offset = 0 # 刻子判定用配列とそれ以外の牌を格納する配列 k = [] l = [] while !haipai.empty? pai = haipai.shift if k.empty? # 刻子判定用配列が空なら、無条件で追加 k.push(pai) elsif k[0] == pai # 刻子判定用配列に入っている値と同じなら、追加 # 判定用配列の長さが3になった地点で、値を戻す k.push(pai) if k.length == 3 haipai.each{|p| l.push(p) } yield k, l haipai = haipai_bak.dup k = [] l = [] offset = offset + 1 offset.times{|n| l.push(haipai.shift) } end else # 上記以外の場合は、刻子ではないため、 # 刻子判定用配列を含めて元に戻す k.each{|p| l.push(p) } k = [] k.push(pai) end end end # 与えられた配牌から初めに見つかった順子を取り出す # 見つかったものはyieldで返す def syuntsu!(haipai) # 順子判定用配列と、それ以外の配列 haipai_bak = haipai.dup offset = 0 s = [] l = [] while !haipai.empty? pai = haipai.shift if s.empty? # 順子判定用配列が空なら、無条件で追加 s.push(pai) elsif s[-1] + 1 > pai # 順子判定用配列に入っている末尾の値 + 1より小さい場合は # 次の牌を読む l.push(pai) elsif s[-1] + 1 == pai # 順子判定用配列に入っている末尾の値 + 1と等しい場合は # 順子判定を継続する。判定配列の長さが3になったら、 # 順子判定用配列と残りを返す s.push(pai) if s.length == 3 haipai.each{|p| l.push(p) } yield s, l # 配牌を元に戻して、offsetをずらす haipai = haipai_bak.dup s = [] l = [] offset = offset + 1 offset.times{|n| l.push(haipai.shift) } end else # 上記以外の場合は、順子ではないため、 # 順子判定用配列を含めて元に戻して新しい値をセットする haipai.unshift(pai) l.push(s.shift) end end end # 与えられた配牌から初めに見つかった対子を取り出す,なければnilを返す def toitsu!(haipai) # 対子判定用配列と、それ以外を格納する配列 t = [] l = [] while !haipai.empty? pai = haipai.shift if t.empty? # 対子用判定配列が空なら無条件に追加 t.push(pai) elsif t[0] == pai # 対子判定配列と同じ値が入って入れば対子確定 t.push(pai) haipai.each{|p| l.push(p) } return t, l else # それ以外の値の場合は対子ではないので判定配列を # 空にして新しい値をセットする t.each{|p| l.push(p) } t = [] t.push(pai) end end # 何も見つからない場合はnilと配牌をreturnする return nil, haipai end # 待ちを探索する def search_wait_pai(haipai, answer_stack) if haipai.length <= 4 # 配牌4個以下になったら待ちを判定する # 刻子、順子が存在する場合は、単騎待ち kohtsu!(haipai.dup){|group, left| make_answer(answer_stack.dup.push(group), [left]) } syuntsu!(haipai.dup){|group, left| make_answer(answer_stack.dup.push(group), [left]) } # 対子が存在する場合 # カンチャン/リャンメン/ペンチャン待ち # もしくはシャンポン待ちが存在すればよい group, left = toitsu!(haipai.dup) unless group.nil? if left[0] == left[-1] #シャンポン待ちのため、groupおよびleftが待ち make_answer(answer_stack.dup.push(group), left) make_answer(answer_stack.dup.push(left), group) elsif left[0] + 1 == left[1] || left[0] + 2 == left[1] # カンチャン/リャンメン待ち make_answer(answer_stack.push(group), left) end end else # それ以外の場合は、順子、刻子のどれかがないかを探す kohtsu!(haipai.dup){|group, left| search_wait_pai(left.dup, answer_stack.dup.push(group)) } syuntsu!(haipai.dup){|group, left| search_wait_pai(left.dup, answer_stack.dup.push(group)) } end end def make_answer(answer_stack, wait_group) if @answer[answer_stack.sort] == true return else @answer[answer_stack.sort] = true end answer_stack.sort.each{|group| print "(#{group})" } print "[#{wait_group}]\n" end def initialize @answer = {} end end def main mj = Marjang.new haipai = ARGV[0] haipai_array = [] for i in 0...haipai.length haipai_array.push(haipai[i].chr.to_i) end mj.search_wait_pai(haipai_array.sort.dup, []) end main()