Skip to content

Instantly share code, notes, and snippets.

@takehiko
Last active January 11, 2020 01:28
Show Gist options
  • Save takehiko/205a718717bf9a70d439347ec03c5ec8 to your computer and use it in GitHub Desktop.
Save takehiko/205a718717bf9a70d439347ec03c5ec8 to your computer and use it in GitHub Desktop.
Four-8 Puzzle
#!/usr/bin/env ruby
# foureights.rb : "8-((8+8)/8)" Puzzle
# by takehikom
# see also: https://takehikom.hateblo.jp/entry/2020/01/11/062120
# 環境変数LANGの値がja_JP.UTF-8などのときは,
# 日本語文字で出力する(実行時オプションで変更可能)
$option_zenkaku = /jp/i === ENV["LANG"]
class String
TO_J_PATTERN1 = "-+*/()=0123456789"
TO_J_PATTERN2 = "-+×÷()=0123456789"
TO_J_PATTERN3 = "-+×/()=0123456789"
def to_j(option_slash = false)
return self if !$option_zenkaku
tr(TO_J_PATTERN1, option_slash ? TO_J_PATTERN3 : TO_J_PATTERN2)
end
end
class FourNumberPuzzleSolver
def initialize(opt)
@num = opt[:num] || [8, 8, 8, 8]
raise if !(Array === @num) || @num.length < 4
@num = @num[0, 4].map { |item| item.to_i }
@result_only = opt[:res]
@result_h = {} # "number" => [expr, ...]
end
attr_reader :num, :result_h
def start
setup_result(!@result_only)
if !@result_only
puts; puts "=" * 64; puts
print_result_all
puts; puts "=" * 64; puts
end
print_result((0..10).to_a)
end
def op_sym(o)
["+", "-", "*", "/"][o]
end
def op(o, x, y)
return nil if x.nil? || y.nil?
case o
when 0
x + y
when 1
x - y
when 2
x * y
when 3
if y == 0
return nil
end
Rational(x, y)
end
end
def expr(v1, v2, v3, v4, o1, o2, o3, d = 0, opt_j = $option_zenkaku)
case d
when 0
# d == 0: ((8 o1 8) o2 8) o3 8
pat = "((%d%s%d)%s%d)%s%d"
when 1
# d == 1: (8 o1 8) o2 (8 o3 8)
pat = "(%d%s%d)%s(%d%s%d)"
when 2
# d == 2: (8 o1 (8 o2 8)) o3 8
pat = "(%d%s(%d%s%d))%s%d"
when 3
# d == 3: 8 o1 ((8 o2 8) o3 8)
pat = "%d%s((%d%s%d)%s%d)"
when 4
# d == 4: 8 o1 (8 o2 (8 o3 8))
pat = "%d%s(%d%s(%d%s%d))"
else
raise
end
pat % [v1, op_sym(o1), v2, op_sym(o2), v3, op_sym(o3), v4]
end
def eval(v1, v2, v3, v4, o1, o2, o3, d = 0)
case d
when 0
# d == 0: ((8 o1 8) o2 8) o3 8
v = op(o3, op(o2, op(o1, v1, v2), v3), v4)
when 1
# d == 1: (8 o1 8) o2 (8 o3 8)
v = op(o2, op(o1, v1, v2), op(o3, v3, v4))
when 2
# d == 2: (8 o1 (8 o2 8)) o3 8
v = op(o3, op(o1, v1, op(o2, v2, v3)), v4)
when 3
# d == 3: 8 o1 ((8 o2 8) o3 8)
v = op(o1, v1, op(o3, op(o2, v2, v3), v4))
when 4
# d == 4: 8 o1 (8 o2 (8 o3 8))
v = op(o1, v1, op(o2, v2, op(o3, v3, v4)))
else
raise
end
(Rational === v && v.denominator == 1) ? v.numerator : v
end
def setup_result(option_print = false)
(0..4).each do |d|
(0..3).each do |o1|
(0..3).each do |o2|
(0..3).each do |o3|
s = expr(*@num, o1, o2, o3, d)
v = eval(*@num, o1, o2, o3, d)
vs = v.to_s
vs = "NaN" if v.nil?
puts [s.to_j, "=".to_j, vs.to_j(true)].join if option_print
if !@result_h.key?(vs)
@result_h[vs] = []
end
@result_h[vs] << s
end
end
end
end
end
def print_result(output_numbers)
output_numbers.each do |v|
vs = v.to_s
if result_h.key?(vs)
print_freq(vs, result_h[vs].length)
#puts "#{vs} ... #{result_h[vs].length}#{$option_zenkaku ? '通り' : ' expression(s)'}"
puts result_h[vs].map { |s| " " + s.to_j }
else
print_freq(vs, 0)
#puts "#{vs} ... #{$option_zenkaku ? 'なし' : 'nothing'}"
end
end
end
def print_result_all
@result_h.keys.sort.each do |vs|
print_freq(vs, result_h[vs].length)
#puts "#{vs} ... #{result_h[vs].length}#{$option_zenkaku ? '通り' : ' expression(s)'}"
puts @result_h[vs].map { |s| " " + s.to_j }
end
end
def print_freq(vs, freq)
if freq == 0
puts "#{vs} ... #{$option_zenkaku ? 'なし' : 'nothing'}"
else
puts "#{vs} ... #{freq}#{$option_zenkaku ? '通り' : ' expression(s)'}"
end
end
end
if __FILE__ == $0
opt = {}
while /^-/ =~ ARGV.first
case ARGV.shift
when /^-R/
opt[:res] = true
when /^-j/
$option_zenkaku = true
when /^-J/
$option_zenkaku = false
when /-h/ # -h or --help
puts "Usage: #{$0} [-j][-J][-R] number..."
exit
end
end
opt[:num] = ARGV.dup if ARGV.length >= 4
FourNumberPuzzleSolver.new(opt).start
end
__END__
# ruby foureights.rb -J -R
0 ... 44 expression(s)
((8+8)-8)-8
((8-8)+8)-8
((8-8)-8)+8
((8-8)*8)*8
(snip)
1 ... 32 expression(s)
((8+8)-8)/8
((8-8)+8)/8
((8*8)/8)/8
((8/8)+8)-8
(snip)
2 ... 1 expression(s)
(8/8)+(8/8)
3 ... 2 expression(s)
((8+8)+8)/8
(8+(8+8))/8
4 ... 4 expression(s)
(8*8)/(8+8)
(8/(8+8))*8
8/((8+8)/8)
8*(8/(8+8))
5 ... nothing
6 ... 1 expression(s)
8-((8+8)/8)
7 ... 1 expression(s)
((8*8)-8)/8
8 ... 9 expression(s)
((8-8)*8)+8
((8-8)/8)+8
(8*(8-8))+8
8+((8-8)*8)
(snip)
9 ... 2 expression(s)
((8*8)+8)/8
(8+(8*8))/8
10 ... 2 expression(s)
((8+8)/8)+8
8+((8+8)/8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment