# Re: Getting to 100 (#119)

**URL:** <https://www.ruby-forum.com/t/re-getting-to-100-119/91430>\
**Category:** Ruby\
**Created:** [April 9, 2007, 1:13am UTC](https://www.ruby-forum.com/t/re-getting-to-100-119/91430 "2007-04-09T01:13:37Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Harrison\_R](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/f05b48/32.png) [@Harrison\_R](https://www.ruby-forum.com/u/Harrison_R)\
**Post date:** [April 9, 2007, 1:13am UTC](https://www.ruby-forum.com/t/re-getting-to-100-119/91430/1 "2007-04-09T01:13:37Z")

</div>

On Apr 6, 6:55 am, Ruby Q. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Extra Credit: Write your program to accept an arbitrary number and  
> ordering of digits, an arbitrary set of operators (but allowing the  
> same operator more than once), and an arbitrary target number that  
> the equation is supposed to evaluate to.

I got a bit carried away with this one; it has the extra credit along  
with three on/off switches. I even wrote a little combinatorics  
library (attached) to go with it, which has quite a few more functions  
than I needed, including two string permutors, and an algorithm I  
borrowed from the ASPN Python Cookbook.

As yet another demonstration of the benefits of convention over  
configuration, the array and string permutors had almost no  
difference; I only had to change ‘each’ to ‘each\_byte’.

Note that it doesn’t print every equation it tests, as the extra  
switches make it possible to produce millions of possibilities. To  
have it solve the original example, use either of the following  
commands:

ruby – ./ruby119.rb -R 123456789–+=100  
./ruby119.rb -R 123456789–+=100

Anyway, here it is. Beware of cheap hacks.

#########################

#!/usr/bin/env ruby -w

# ruby119.rb – solves combinatorial arithmetic puzzles

# Released 2007 with no license by Harrison R.

require ‘combinatorics’  
require ‘getoptlong’

def dispense\_advice  
puts “Usage: #{$0} [OPTIONS] digits ops = sum”  
puts " e.g. #{$0} 123456789 + - - = 100"  
puts “equiv: #{$0} 123 - 45 - 67 + 89 = 100”  
puts  
puts “Will attempt to search for a combination of the digits and”  
puts “operators given that produce the sum. Each solution uses”  
puts “each digit once and includes all given operator types.”  
puts  
puts “Operators recognized: +, -, _, /, . (decimal point)"  
puts "Standard operator precedence is used: (_, /) over (+, -)”  
puts  
puts “Options:”  
puts " --help, -h"  
puts " Shows this message."  
puts " --permute-digits, -p"  
puts " Allows the given digits to be combined arbitrarily."  
puts " --no-permute-digits, -P"  
puts " Maintains the given order of the digits. (default)"  
puts " --repeat-ops, -r"  
puts " Uses arbitrary (1…\*) operator multiplicity. (default)"  
puts " --no-repeat-ops, -R"  
puts " Maintains each of the given operators’ multiplicities."  
puts " --unary-minus, -n"  
puts " Allows the use of ‘-’ as negation of the first number."  
puts " --no-unary-minus, -N"  
puts " Disallows the use of the negation operator. (default)"  
exit  
end

opts = GetoptLong.new(  
[‘–help’, ‘-h’, GetoptLong::NO\_ARGUMENT],  
[‘–permute-digits’, ‘-p’, GetoptLong::NO\_ARGUMENT],  
[‘–no-permute-digits’, ‘-P’, GetoptLong::NO\_ARGUMENT],  
[‘–repeat-ops’, ‘-r’, GetoptLong::NO\_ARGUMENT],  
[‘–no-repeat-ops’, ‘-R’, GetoptLong::NO\_ARGUMENT],  
[‘–unary-minus’, ‘-n’, GetoptLong::NO\_ARGUMENT],  
[‘–no-unary-minus’, ‘-N’, GetoptLong::NO\_ARGUMENT]  
)

repeat\_ops = true  
unary\_minus = false  
permute\_digits = false

opts.each do |opt, arg|  
case opt  
when ‘–help’  
dispense\_advice  
when ‘–permute-digits’  
permute\_digits = true  
when ‘–no-permute-digits’  
permute\_digits = false  
when ‘–repeat-ops’  
repeat\_ops = true  
when ‘–no-repeat-ops’  
repeat\_ops = false  
when ‘–unary-minus’  
unary\_minus = true  
when ‘–no-unary-minus’  
unary\_minus = false  
end  
end

dispense\_advice if ARGV.length == 0

digits = []  
operators = []

arg\_str, sum = ARGV.join.split(/=/)  
sum = sum.to\_i  
dispense\_advice if sum == 0

arg\_str.each\_byte do |byte|  
case byte  
when ?0…?9  
digits \<\< byte.chr  
when ?-, ?+, ?\*, ?/  
operators \<\< byte.chr  
else  
dispense\_advice  
end  
end

operators.uniq! if repeat\_ops  
count = 0  
hits = 0

ops\_send\_args = repeat\_ops ? [:each\_tuple, 0] :  
[:each\_unique\_permutation]  
digits\_message = permute\_digits ? :each\_partition :  
:each\_ordered\_partition

digits.send(digits\_message) do |part|  
next unless repeat\_ops or part.length == operators.length + 1  
part = part.map { |x| x.join } if part.first.respond\_to?(:join)

ops\_send\_args[1] = part.length - 1 if repeat\_ops  
operators.send(_ops\_send\_args) do |tuple|  
expr = part.zip(tuple).join(’ ')  
if eval(expr.gsub(/ (\d_) /, ’ \1.0 ‘).gsub(’ . ', ‘.’)) == sum  
puts “#{expr}= #{sum}”  
hits += 1  
end  
count += 1

```
if unary_minus and operators.include?('-')
  num = part[0].to_i
  part[0] = (-num).to_i
  redo if num > 0
end

```

end

if permute\_digits  
part \<\< part.shift  
redo unless part.first == part.min  
end  
end

print "#{count} equation#{count == 1 ? ‘’ : ‘s’} searched, ",  
“#{hits} solution#{hits == 1 ? ‘’ : ‘s’} found.\n”
