コーディングスキル判定の麻雀問題を解いてみた

こちら(makeplex salon:あなたのスキルで飯は食えるか? 史上最大のコーディングスキル判定 (1/2) – ITmedia エンタープライズ)の麻雀問題を Ruby で解いてみた。

以前やった迷路探索問題(人生を書き換える者すらいた。: 人材獲得作戦・4 試験問題ほか)の作者の問題らしい。
Gauche 穴掘り法で迷路作成
最短経路探索プログラムの試験問題を解いてみた

麻雀に詳しくないのでクラス名・変数名がおかしいかも。

結果の牌の組み合わせを Machi クラスとして MachiList クラスに保存していく。

Machi クラスには刻子・順子・アタマ・待ちをそれぞれインスタンス変数として別々に保持する。

check メソッドで残り牌の数を調べながら処理を振り分けている。

### 待ち
class Machi
  attr_accessor :kantsu, :juntsu, :atama, :machi
  def initialize(machi = nil)
    if machi.nil?
      @kantsu = []
      @juntsu = []
      @atama = ""
      @machi = ""
    else
      @kantsu = Array.new(machi.kantsu)
      @juntsu = Array.new(machi.juntsu)
      @atama = machi.atama
      @machi = machi.machi
    end
  end

  # 自身と machi との同一性チェック
  def has_same?(machi)
    if (@kantsu.sort <=> machi.kantsu.sort) == 0 and
       (@juntsu.sort <=> machi.juntsu.sort) == 0 and
       @atama == machi.atama and
       @machi == machi.machi
      return true
    else
      return false
    end
  end

  # データを配列にする
  # 待ち牌は [] で囲む
  def to_a
    return @kantsu.sort.concat(@juntsu.sort) << (@atama) << "[#{@machi}]"
  end
end

### 待ちリスト
class MachiList
  attr_accessor :list
  def initialize
    @list = []
  end

  # リストに待ちを追加する
  # 追加時に同一性チェックをする
  def add(machi)
    if is_unique?(machi)
      @list << machi
    end
  end

  # リストを印字する
  def print_list
    @list.each do |m|
      m.to_a.each do |s|
        if s.match(/^\d+$/)
          print "(#{s})"
        else
          print "#{s}"
        end
      end
      print "\n"
    end
  end

  # 待ちを探す
  def check(data, machi)
    c = count_rest(data)
    case c
    when 0
      self.add(machi)
    when 1
      1.upto(9).each do |i|
        if data[i] > 0
          tmp_mdata = Array.new(data)
          tmp_mmachi = Machi.new(machi)
          tmp_mdata[i] -= 1
          tmp_mmachi.machi = "#{i}"
          check(tmp_mdata, tmp_mmachi)
        end
      end
    when 2
      check_machi(data, machi)
    when 4
      check_kantsu(data, machi)
      check_juntsu(data, machi)
      check_atama(data, machi)
    else
      check_kantsu(data, machi)
      check_juntsu(data, machi)
    end
  end

  private

  # 刻子
  def check_kantsu(data, machi)
    1.upto(9).each do |i|
      if has_kantsu?(i, data)
        tmp_kdata = Array.new(data)
        tmp_kdata[i] -= 3
        tmp_kmachi = Machi.new(machi)
        tmp_kmachi.kantsu << "#{i}#{i}#{i}"
        check(tmp_kdata, tmp_kmachi)
      end
    end
  end

  # 順子
  def check_juntsu(data, machi)
    1.upto(9).each do |i|
      if has_juntsu?(i, data)
        tmp_jdata = Array.new(data)
        tmp_jdata[i] -= 1
        tmp_jdata[i+1] -= 1
        tmp_jdata[i+2] -= 1
        tmp_jmachi = Machi.new(machi)
        tmp_jmachi.juntsu << "#{i}#{i+1}#{i+2}"
        check(tmp_jdata, tmp_jmachi)
      end
    end
  end

  # アタマ
  def check_atama(data, machi)
    1.upto(9).each do |i|
      if has_atama?(i, data)
        tmp_adata = Array.new(data)
        tmp_adata[i] -= 2
        tmp_amachi = Machi.new(machi)
        tmp_amachi.atama = "#{i}#{i}"
        check(tmp_adata, tmp_amachi)
      end
    end
  end

  # 待ち
  def check_machi(data, machi)
    1.upto(9).each do |i|
      tmp_mdata = Array.new(data)
      tmp_mmachi = Machi.new(machi)
      if data[i] > 1
        tmp_mdata[i] -= 2
        tmp_mmachi.machi = "#{i}#{i}"
        check(tmp_mdata, tmp_mmachi)
      elsif data[i+1] and data[i] > 0 and data[i+1] > 0
        tmp_mdata[i] -= 1
        tmp_mdata[i+1] -= 1
        tmp_mmachi.machi = "#{i}#{i+1}"
        check(tmp_mdata, tmp_mmachi)
      elsif data[i+2] and data[i] > 0 and data[i+2] > 0
        tmp_mdata[i] -= 1
        tmp_mdata[i+2] -= 1
        tmp_mmachi.machi = "#{i}#{i+2}"
        check(tmp_mdata, tmp_mmachi)
      end
    end
  end

  # 刻子の候補を持っているかどうか
  def has_kantsu?(i, ary)
    ary[i] > 2
  end

  # 順子の候補を持っているかどうか
  def has_juntsu?(i, ary)
    if i < 8
      ary[i] > 0 and ary[i+1] > 0 and ary[i+2] > 0
    else
      false
    end
  end

  # アタマの候補を持っているかどうか
  def has_atama?(i, ary)
    ary[i] > 1
  end

  # 残り牌の数を調べる
  def count_rest(ary)
    count = 0
    1.upto(9).each do |i|
      if ary[i] > 0
        count += ary[i]
      end
    end
    count
  end

  # 重複する待ちがないかどうかをチェック
  def is_unique?(machi)
    @list.each do |m|
      if m.has_same?(machi)
        return false
      end
    end
    return true
  end
end

### ここからテスト

def str2num_array(str)
  ret_array = Array.new(10, 0)
  str.each_char do |c|
    ret_array[c.to_i] += 1
  end
  ret_array
end

def test(str)
  machilist = MachiList.new
  machi = Machi.new
  machilist.check(str2num_array(str), machi)
  machilist.print_list
end

haipai_list = [ "1112224588899",
                "1122335556799",
                "1112223335559",
                "1223344888999",
                "1112345678999" ]

haipai_list.each do |str|
  p str
  test(str)
  puts "------------------------------"
end

実行結果

"1112224588899"
(111)(222)(888)(99)[45]
------------------------------
"1122335556799"
(555)(123)(123)(99)[67]
(123)(123)(567)(55)[99]
(123)(123)(567)(99)[55]
------------------------------
"1112223335559"
(111)(222)(333)(555)[9]
(555)(123)(123)(123)[9]
------------------------------
"1223344888999"
(888)(999)(123)(234)[4]
(888)(999)(123)(44)[23]
(888)(999)(234)(234)[1]
------------------------------
"1112345678999"
(111)(999)(234)(567)[8]
(111)(999)(234)(678)[5]
(111)(999)(345)(678)[2]
(111)(234)(567)(99)[89]
(111)(234)(789)(99)[56]
(111)(456)(789)(99)[23]
(999)(123)(456)(11)[78]
(999)(123)(678)(11)[45]
(999)(345)(678)(11)[12]
(123)(456)(789)(11)[99]
(123)(456)(789)(99)[11]
------------------------------
プログラミング言語 Ruby
プログラミング言語 Ruby

posted with amazlet at 10.04.06
まつもと ゆきひろ David Flanagan
オライリージャパン
売り上げランキング: 91254
«
»