4つの数で 10 を作る(Ruby)

テンパズル - Wikipedia
1桁の4つの数と四則演算で、10 を作るコードを Ruby で書いてみました。括弧は使ってもよいことにします。

実行例。

$ ruby make_ten.rb
[2, 7, 3, 9] で 10 を作る
(2 + 3) * (9 - 7)
(7 + 9) - (2 * 3)
9 + (7 - (2 * 3))
9 - ((2 * 3) - 7)
7 + (9 - (2 * 3))
7 - ((2 * 3) - 9)
(7 * 3) - (2 + 9)
2 * (9 - (7 - 3))
2 * (9 + (3 - 7))
((7 * 3) - 2) - 9
((7 * 3) - 9) - 2
2 * (3 - (7 - 9))
(9 - 7) * (2 + 3)
2 * (3 + (9 - 7))
2 * ((3 + 9) - 7)
7 - ((3 - 9) / 2)
7 + ((9 - 3) / 2)
((3 * 9) - 7) / 2
$ ruby make_ten.rb 1185
[1, 1, 8, 5] で 10 を作る
8 / (1 - (1 / 5))

括弧を使っているので事実上は同じ演算が重複して出力されてしまいますが、そこはお許しを。
4 / (1 - (3 / 5)) のように分数を使ったものも解けます。

コード。
make_ten.rb

class Integer
  def /(a) Rational(self, a) end
end

def solve(ary)
  if ary.size <= 1
    @ans << ary[0][1..-2] if eval(ary[0]) == 10
  else
    idxs = [*0...ary.size]
    idxs.combination(2) do |i, j|
      a, b = ary[i], ary[j]
      nxt = (idxs - [i, j]).map{|x| ary[x]}
      nums = ["(#{a} + #{b})", "(#{a} - #{b})", "(#{b} - #{a})", "(#{a} * #{b})"]
      nums << "(#{a} / #{b})" if eval(b).nonzero?
      nums << "(#{b} / #{a})" if eval(a).nonzero?
      nums.each {|n| solve(nxt + [n])}
    end
  end
end

@ans = []
given = ARGV[0] ? ARGV[0].chars : Array.new(4) {[*0..9].sample.to_s}
puts given.map(&:to_i).inspect + " で 10 を作る"
solve(given)
puts @ans.uniq

メソッド solve() は solve(["2", "7", "3", "9"]) のように各数字を String にして呼ぶのがミソです。eval の活躍しどころなのです。分数を使った場合に対応するため、演算子/を再定義*1しています(Ruby!)。

例えば 9999 とか 3478 とか、むずかしいのがあるそうですよ。わからなかったら上のプログラムに解かせてみて下さいな。


実際には数字は 1桁でなくともよいですし、4つでなくとも構いません。

*1:Fixnum や Bignum のなくなった Ruby 2.4 以降でしかうまく働きません。それ以前のバージョンでは、再定義を Fixnum, Bignum でおこなう必要があります。