Ruby
初心者
プログラミングコンテスト
プロコン

【Ruby】「これが解けるような秀才は募集しておりません^^」を解く!

More than 1 year has passed since last update.

はじめに

@Kuniwakさんが投稿していた
「これが解けるような秀才は募集しておりません^^」Prolog「まかせろ」を読みました.

この問題自体は解いたことがあったのですがプログラムにしたことはなかったので,自分自身のスキルアップがてら挑戦してみることにしました!

(Rubyマスターでもなんでもないので,こうした方がいい,こう考えた方がいいなどあれば,是非コメントください!)

問題「これが解けるような秀才は募集しておりません^^」

みなみとかおりの二人が数当てゲームをしていて、さやかが横で見ている。

このゲームは二人が1~13のトランプのカードの中から1枚取り、
先に相手の数字を当てた方が勝ちになるもので、みなみとかおりは自分のカードの数字しか見ることはできないが、さやかは二人のカードの数字を見ている。
3人が次の順で会話したとき、みなみとかおりのカードの数字を答えなさい。

さやか「二人のカードは異なる数字で、どちらも1ではない。片方がもう一方の整数倍の数字になっている」
みなみ「うーん、わからない」
かおり「私もわからない」
みなみ「そうか、今わかった」

プログラム

solution.rb
# 「どちらも1ではないわ」ということなので2からスタート
2.upto(12) do |minami_number|  
  # 「二人のカードは異なる数字」
  # 「片方がもう一方の倍数になっているわよ」
  # 上記条件に合うかおりのカード候補を取得
  candidate_arr = (2..12).select do |kaori_number|
    ((minami_number % kaori_number).zero? || (kaori_number % minami_number).zero?) && minami_number != kaori_number
  end

  # 「うーん、わからないわ」
  # => kaoriの候補番号が2つ以上ある状況
  next if candidate_arr.size < 2
  delete_arr = Array.new
  candidate_arr.each do |num|
    multiple_arr = Array.new
    2.upto(12) do |divisor|
      next if num == divisor

      # kaoriの候補番号の倍数を調べる(1つしかないならkaoriは分かるはず)
      if num % divisor == 0 || divisor % num == 0
        multiple_arr << divisor
      end
    end
    # 「私もわからないわ」
    # => kaoriの候補番号の倍数が1つの場合は「私はわかる」はず
    if multiple_arr.size == 1
      delete_arr << num
    end
  end
  delete_arr.each do |num|
    candidate_arr.delete(num)
  end

  # 「そうか、いま、わかったわ」
  if candidate_arr.size == 1
    puts "minami : #{minami_number}"
    puts "kaori  : #{candidate_arr.first}"
    break
  end  
end

今の自分にはこれが限界のようです…

おわりに

当たり前ですが,本問題では,会話の内容が相手の番号を当てるためのヒント,すなわちプログラム内の条件式となります.

会話の中から条件となる部分を抽出し,プログラムに記述する中で,
「こうやって現実にある問題をプログラムに落とし込んでいくんだなー」と実感しておりました:)

最近,設計に興味を持ち,「エリック・エヴァンスのドメイン駆動設計」を読んでいるのですが,上述した作業がDDDにおける現実の問題をドメインに落とし込む作業に近いんですかね?どうなんですかね?

というわけで,プログラムや最後のDDDっぽいのかな?などの点について,あなたのコメントをお待ちしております!

ありがとうございました.

編集

・2017年12月4日(月) @t2kojima のコメントをもとに修正
修正前

candidate_arr = Array.new
  2.upto(12) do |kaori_number|
    # 「二人のカードは異なる数字」
    next if minami_number == kaori_number
    # 「片方がもう一方の倍数になっているわよ」
    if minami_number % kaori_number == 0 || kaori_number % minami_number == 0
      candidate_arr << kaori_number
    end
  end

修正後

# 「二人のカードは異なる数字」
# 「片方がもう一方の倍数になっているわよ」
# 上記条件に合うかおりのカード候補を取得
candidate_arr = (2..12).select do |kaori_number|
  ((minami_number % kaori_number).zero? || (kaori_number % minami_number).zero?) && minami_number != kaori_number
end

他にもArray.newがあるけど…さて,どう消すべきか