636abcfa9314f91065bba1ed78b2649a0e4045a6 — Tim Morgan 1 year, 4 months ago master
Initial commit
7 files changed, 204 insertions(+), 0 deletions(-)

A .gitignore
A .watchr
A Makefile
A README.md
A bin/boardslam.cr
A lib/boardslam.cr
A spec/boardslam_spec.cr
A  => .gitignore +1 -0
@@ 1,1 @@
+ /bin/boardslam

A  => .watchr +12 -0
@@ 1,12 @@
+ require 'open3'
+ 
+ def spec
+   puts
+   puts '========================================='
+   puts
+   _, stdout, wait_thr = Open3.popen2('crystal spec')
+   print stdout.getc until stdout.eof?
+   wait_thr.value.success?
+ end
+ 
+ watch('\.cr') { spec }

A  => Makefile +8 -0
@@ 1,8 @@
+ build:
+ 	crystal build bin/boardslam.cr -o bin/boardslam
+ 
+ build_release:
+ 	crystal build --static --release bin/boardslam.cr -o bin/boardslam
+ 
+ test:
+ 	crystal spec

A  => README.md +57 -0
@@ 1,57 @@
+ # Boardslam in Crystal
+ 
+ This is a port of my [boardslam.rb script](https://gist.github.com/seven1m/6a36782b93f10fa15a2fc381fd91bfb1) to Crystal.
+ 
+ Explanation here: [seven1m.sdf.org/experiments/boardslam.cgi](http://seven1m.sdf.org/experiments/boardslam.cgi)
+ 
+ ## Build
+ 
+ 1. [Install Crystal](https://crystal-lang.org/docs/installation/)
+ 
+ 2. Run `make build_release`
+ 
+ ## Usage
+ 
+ ```
+ bin/boardslam 1 2 3
+ 
+ 1   + 2   / 3   = 1
+ 1   - 2   + 3   = 2
+ 1   / 2   + 3   = 3
+ 1   + 2   + 3^0 = 4
+ 1   * 2   + 3   = 5
+ 1   + 2   + 3   = 6
+ 1   * 2^2 + 3   = 7
+ 1   - 2   + 3^2 = 8
+ 1   + 2   * 3   = 9
+ 1   * 2^0 + 3^2 = 10
+ 1   * 2   + 3^2 = 11
+ 1   + 2   + 3^2 = 12
+ 1   * 2^2 + 3^2 = 13
+ 1   + 2^2 + 3^2 = 14
+ 1   + 2^2 * 3   = 15
+ 1   + 3   * 2^2 = 16
+ 1   * 2^3 + 3^2 = 17
+ 1   * 2   * 3^2 = 18
+ 1   * 3^3 - 2^3 = 19
+ 1   - 2^3 + 3^3 = 20
+ 2^3 - 1   * 3   = 21
+ 3^3 - 1   - 2^2 = 22
+ 1   * 3^3 - 2^2 = 23
+ 1   - 2^2 + 3^3 = 24
+ 1   * 3^3 - 2   = 25
+ 1   - 2   + 3^3 = 26
+ 1   + 2   * 3^2 = 27
+ 1   * 2^0 + 3^3 = 28
+ 1   * 2   + 3^3 = 29
+ 1   + 2   + 3^3 = 30
+ 1   * 2^2 + 3^3 = 31
+ 1   + 2^2 + 3^3 = 32
+ 2^3 - 1   + 3^3 = 34
+ 1   * 2^3 + 3^3 = 35
+ 1   * 2^2 * 3^2 = 36
+ 
+ missing answers: 33
+ ```
+ 
+ License: MIT

A  => bin/boardslam.cr +13 -0
@@ 1,13 @@
+ require "../lib/boardslam"
+ 
+ args = ARGV.map { |a| a.to_i8 }
+ board = BoardSlam.new(args[0], args[1], args[2])
+ board.results.to_a.sort.each do |result, equation|
+   puts equation.ljust(15) + " = " + result.to_s
+ end
+ puts
+ if board.missing.any?
+   puts "missing answers: #{board.missing.join(", ")}"
+ else
+   puts "all answers possible!"
+ end

A  => lib/boardslam.cr +85 -0
@@ 1,85 @@
+ class BoardSlam
+   BOARD = 1..36
+   OPERATIONS = %w(+ - * /)
+ 
+   def initialize(x : Int8, y : Int8, z : Int8)
+     @numbers = [x, y, z]
+     @answers = {} of Int8 => String
+   end
+ 
+   getter :numbers, :answers
+ 
+   def variants(num)
+     [num, "#{num}^0", "#{num}^2", "#{num}^3"]
+   end
+ 
+   def expand(num)
+     return num if num.is_a?(Int8)
+     num, exp = num.split('^').map { |n| n.to_i }
+     num ** exp
+   end
+ 
+   def results
+     each_order do |x_base, y_base, z_base|
+       each_variant(x_base, y_base, z_base) do |(x_pretty, x), (y_pretty, y), (z_pretty, z)|
+         each_operation_pair do |op1, op2|
+           result1 = op(x.to_i8, op1, y.to_i8)
+           #next if op1 == "/" && x.to_i8 % y.to_i8 != 0
+           result2 = op(result1, op2, z.to_i8)
+           #next if op2 == "/" && result1.to_i8 % z.to_i8 != 0
+           if BOARD.includes?(result2) && !answers.has_key?(result2)
+             answers[result2] = "#{x_pretty.to_s.ljust(3)} #{op1} #{y_pretty.to_s.ljust(3)} #{op2} #{z_pretty.to_s.ljust(3)}"
+           end
+         end
+       end
+     end
+     answers
+   end
+ 
+   def op(n1 : Int8, op : String, n2 : Int8) : Int8
+     case op
+     when "+"
+       n1 + n2
+     when "-"
+       n1 - n2
+     when "*"
+       n1 * n2
+     when "/"
+       n1 / n2
+     else
+       0i8
+     end
+   end
+ 
+   def each_order
+     @numbers.permutations.each do |(x, y, z)|
+       yield x, y, z
+     end
+   end
+ 
+   def each_variant(x_base, y_base, z_base)
+     variants(x_base).each do |x|
+       x_expanded = expand(x)
+       variants(y_base).each do |y|
+         y_expanded = expand(y)
+         variants(z_base).each do |z|
+           z_expanded = expand(z)
+           yield([x, x_expanded], [y, y_expanded], [z, z_expanded])
+         end
+       end
+     end
+   end
+ 
+   def each_operation_pair
+     OPERATIONS.each do |op1|
+       OPERATIONS.each do |op2|
+         yield(op1, op2)
+         yield(op2, op1)
+       end
+     end
+   end
+ 
+   def missing
+     BOARD.to_a - results.keys
+   end
+ end

A  => spec/boardslam_spec.cr +28 -0
@@ 1,28 @@
+ require "spec"
+ 
+ require "../lib/boardslam"
+ 
+ describe BoardSlam do
+   describe "#missing" do
+     context "given 1, 1, 1" do
+       it "returns the missing numbers" do
+         board = BoardSlam.new(1i8, 1i8, 1i8)
+         board.missing.should eq [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]
+       end
+     end
+ 
+     context "given 2, 2, 2" do
+       it "returns the missing numbers" do
+         board = BoardSlam.new(2i8, 2i8, 2i8)
+         board.missing.should eq [19, 21, 22, 23, 25, 26, 27, 29, 35]
+       end
+     end
+ 
+     context "given 3, 5, 1" do
+       it "returns the missing numbers" do
+         board = BoardSlam.new(3i8, 5i8, 1i8)
+         board.missing.should eq [19, 30]
+       end
+     end
+   end
+ end