scrambler 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/scrambler.rb CHANGED
@@ -5,4 +5,7 @@ require "scrambler/cube"
5
5
  require "scrambler/pyraminx"
6
6
  require "scrambler/clock"
7
7
  require "scrambler/square1"
8
- require "scrambler/megaminx"
8
+ require "scrambler/megaminx"
9
+
10
+ require "scrambler/random_state/solver"
11
+ require "scrambler/random_state/two_by_two"
@@ -1,6 +1,6 @@
1
1
  module Scrambler
2
2
  class Clock
3
- def scramble
3
+ def scramble(length = 0)
4
4
  pins = %w(U d)
5
5
  states = %w(UUdd dUdU ddUU UdUd dUUU UdUU UUUd UUdU UUUU dddd)
6
6
  scramble = states.map do |state|
@@ -0,0 +1,51 @@
1
+ # TODO Connascence of position...
2
+ # Up:
3
+ # 3 0
4
+ # 2 1
5
+ # Down
6
+ # - 4
7
+ # 6 5
8
+ module Scrambler
9
+ module RandomState
10
+ class CornerOrientation
11
+ def initialize(orientation = [0] * 7)
12
+ case orientation
13
+ when Array
14
+ @orientation = orientation.clone
15
+ else
16
+ @orientation = convert_to_array orientation
17
+ end
18
+ end
19
+
20
+ def to_a
21
+ @orientation
22
+ end
23
+
24
+ def to_i
25
+ n = -1
26
+ @orientation.inject(0) { |sum, i| n += 1; sum + i * (3**n) }
27
+ end
28
+
29
+ def turn!(move)
30
+ o = @orientation
31
+ case move
32
+ when :R
33
+ @orientation = [(o[1] + 1) % 3, (o[5] + 2) % 3, o[2], o[3], (o[0] + 2) % 3, (o[4] + 1) % 3, o[6]]
34
+ when :F
35
+ @orientation = [o[0], (o[2] + 1) % 3, (o[6] + 2) % 3, o[3], o[4], (o[1] + 2) % 3, (o[5] + 1) % 3]
36
+ when :U
37
+ @orientation = [o[3], o[0], o[1], o[2], o[4], o[5], o[6]]
38
+ end
39
+ self
40
+ end
41
+
42
+ private
43
+ def convert_to_array(number)
44
+ short = number.to_s(3).reverse.split(//).map do |s|
45
+ s.to_i
46
+ end
47
+ short + [0] * (7 - short.size)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ module Scrambler
2
+ module RandomState
3
+ class CornerPermutation
4
+ def initialize(permutation = [0, 1, 2, 3, 4, 5, 6])
5
+ case permutation
6
+ when Array
7
+ @permutation = permutation.clone
8
+ else
9
+ @permutation = convert_to_array(permutation)
10
+ end
11
+ end
12
+
13
+ def to_a
14
+ @permutation
15
+ end
16
+
17
+ def to_i
18
+ @permutation.map do |piece|
19
+ piece.to_s
20
+ end.join.to_i
21
+ end
22
+
23
+ def turn!(move)
24
+ p = @permutation
25
+ case move
26
+ when :R
27
+ @permutation = [p[1], p[5], p[2], p[3], p[0], p[4], p[6]]
28
+ when :F
29
+ @permutation = [p[0], p[2], p[6], p[3], p[4], p[1], p[5]]
30
+ when :U
31
+ @permutation = [p[3], p[0], p[1], p[2], p[4], p[5], p[6]]
32
+ end
33
+ self
34
+ end
35
+
36
+ private
37
+ def convert_to_array(number)
38
+ result = number.to_s.split(//).map { |s| s.to_i }
39
+ result.size < 7 ? [0] + result : result
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ module Scrambler
2
+ module RandomState
3
+ class CubeState
4
+ attr_reader :orientation, :permutation
5
+
6
+ def initialize
7
+ @orientation = [0] * 8
8
+ @permutation = [0, 1, 2, 3, 4, 5, 6, 7]
9
+ end
10
+
11
+ def turn!(layer)
12
+ o = @orientation.clone
13
+ p = @permutation.clone
14
+ case layer
15
+ when :R
16
+ @permutation = [p[1], p[5], p[2], p[3], p[0], p[4], p[6], p[7]]
17
+ @orientation = [(o[1] + 1) % 3, (o[5] + 2) % 3, o[2], o[3], (o[0] + 2) % 3, (o[4] + 1) % 3, o[6], o[7]]
18
+ when :F
19
+ @permutation = [p[0], p[2], p[6], p[3], p[4], p[1], p[5], p[7]]
20
+ @orientation = [o[0], (o[2] + 1) % 3, (o[6] + 2) % 3, o[3], o[4], (o[1] + 2) % 3, (o[5] + 1) % 3, o[7]]
21
+ when :U
22
+ @permutation = [p[3], p[0], p[1], p[2], p[4], p[5], p[6], p[7]]
23
+ @orientation = [o[3], o[0], o[1], o[2], o[4], o[5], o[6], o[7]]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,98 @@
1
+ require "scrambler/random_state/corner_orientation"
2
+ require "scrambler/random_state/corner_permutation"
3
+
4
+ module Scrambler
5
+ module RandomState
6
+ class Solver
7
+ SOLVED_PERMUTATION = [0, 1, 2, 3, 4, 5, 6]
8
+ SOLVED_ORIENTATION = [0] * 7
9
+ TURNS = [:R, :U, :F]
10
+ MODS = ["", "2", "'"]
11
+
12
+ def initialize
13
+ @permutation_map = []
14
+ SOLVED_PERMUTATION.permutation.each do |p|
15
+ @permutation_map[CornerPermutation.new(p).to_i] = { :R => CornerPermutation.new(p).turn!(:R).to_i,
16
+ :U => CornerPermutation.new(p).turn!(:U).to_i,
17
+ :F => CornerPermutation.new(p).turn!(:F).to_i,
18
+ :length => -1
19
+ }
20
+ end
21
+
22
+ @permutation_map[123456][:length] = 0
23
+ 7.times do |l|
24
+ SOLVED_PERMUTATION.permutation.each do |p|
25
+ if @permutation_map[CornerPermutation.new(p).to_i][:length] == l
26
+ [:R, :U, :F].each do |m|
27
+ q = CornerPermutation.new(p).to_i
28
+ 3.times do |c|
29
+ q = @permutation_map[q][m]
30
+ @permutation_map[q][:length] = l + 1 if @permutation_map[q][:length] == -1
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ @orientation_map = []
38
+ (3**7).times do |i|
39
+ @orientation_map[i] = { :R => CornerOrientation.new(i).turn!(:R).to_i,
40
+ :U => CornerOrientation.new(i).turn!(:U).to_i,
41
+ :F => CornerOrientation.new(i).turn!(:F).to_i,
42
+ :length => -1
43
+ }
44
+ end
45
+
46
+ @orientation_map[0][:length] = 0
47
+ 6.times do |l|
48
+ (3**7).times do |o|
49
+ if @orientation_map[o][:length] == l
50
+ [:R, :U, :F].each do |m|
51
+ q = o
52
+ 3.times do |c|
53
+ q = @orientation_map[q][m]
54
+ @orientation_map[q][:length] = l + 1 if @orientation_map[q][:length] == -1
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def solve(corner_permutation, corner_orientation)
63
+ @solution = []
64
+ move_limit = -1
65
+ begin
66
+ move_limit += 1
67
+ end while !search(corner_permutation.to_i, corner_orientation.to_i, move_limit)
68
+ @solution.map { |t| t[0].to_s + t[1] }.join " "
69
+ end
70
+
71
+ private
72
+
73
+ def search(current_permutation, current_orientation, limit, depth = 0, last_move = nil)
74
+ if current_permutation == 123456 and current_orientation == 0
75
+ return true
76
+ elsif limit == 0
77
+ return false
78
+ else
79
+ return false if @permutation_map[current_permutation][:length] > limit or @orientation_map[current_orientation][:length] > limit
80
+ TURNS.each do |turn|
81
+ if last_move != turn
82
+ next_permutation = current_permutation
83
+ next_orientation = current_orientation
84
+ MODS.each do |modifier|
85
+ next_permutation = @permutation_map[next_permutation][turn]
86
+ next_orientation = @orientation_map[next_orientation][turn]
87
+ @solution[depth] = [turn, modifier]
88
+ return true if search(next_permutation, next_orientation, limit - 1, depth + 1, turn)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ return false
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,21 @@
1
+ require "scrambler/random_state/corner_orientation"
2
+ require "scrambler/random_state/corner_permutation"
3
+ require "scrambler/random_state/solver"
4
+
5
+ module Scrambler
6
+ module RandomState
7
+ class TwoByTwo
8
+ def initialize
9
+ @solver = Solver.new
10
+ end
11
+
12
+ def scramble
13
+ permutation = Solver::SOLVED_PERMUTATION.shuffle
14
+ orientation = Array.new(6) { rand 3 }
15
+ orientation += [(3 - (orientation.inject { |sum, i| sum + i } % 3)) % 3] # fix orientation parity
16
+
17
+ @solver.solve(CornerPermutation.new(permutation), CornerOrientation.new(orientation))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,6 @@
1
1
  module Scrambler
2
2
  class Square1
3
- def scramble
3
+ def scramble(length = 0)
4
4
  scramble = []
5
5
  up_layer = (0..7).map{|i| i%2 == 0 ? 30 : 60}
6
6
  down_layer = [up_layer].flatten!
@@ -1,3 +1,3 @@
1
1
  module Scrambler
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe Scrambler::RandomState::CornerOrientation do
4
+ include Scrambler::RandomState
5
+
6
+ it "should have all corners oriented right after initialization" do
7
+ subject.to_a.should == [0] * 7
8
+ subject.to_i.should == 0
9
+ end
10
+
11
+ it "should have orientation [1, 2, 0, 0, 2, 1, 0] after R turn" do
12
+ co = subject.turn!(:R)
13
+ co.to_a.should == [1, 2, 0, 0, 2, 1, 0]
14
+ co.to_i.should == 412
15
+ end
16
+
17
+ it "should have orientation [0, 1, 2, 0, 0, 2, 1] after F turn" do
18
+ subject.turn!(:F).to_a.should == [0, 1, 2, 0, 0, 2, 1]
19
+ end
20
+
21
+ it "should have orientation [0, 0, 0, 0, 0, 0, 0] after U turn" do
22
+ subject.turn!(:U).to_a.should == [0, 0, 0, 0, 0, 0, 0]
23
+ subject.turn!(:U).to_i.should == 0
24
+ end
25
+
26
+ it "should have orientation [0, 1, 2, 0, 2, 1, 0] after R U turns" do
27
+ subject.turn!(:R).turn!(:U).to_a.should == [0, 1, 2, 0, 2, 1, 0]
28
+ end
29
+
30
+ it "should have all corners oriented after a U-Perm" do
31
+ subject.turn!(:R).turn!(:R).turn!(:U).turn!(:U).turn!(:U).
32
+ turn!(:R).turn!(:R).turn!(:R).turn!(:U).turn!(:U).turn!(:U).
33
+ turn!(:R).turn!(:U).turn!(:R).turn!(:U).turn!(:R).
34
+ turn!(:U).turn!(:U).turn!(:U).turn!(:R).to_a.should == [0] * 7
35
+ end
36
+
37
+ it "should accept numbers and arrays as initial value" do
38
+ CornerOrientation.new(248).to_a.should == [2, 1, 0, 0, 0, 1, 0]
39
+ CornerOrientation.new([2, 1, 0, 0, 0, 1, 0]).to_i.should == 248
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ require "spec_helper"
2
+
3
+ describe Scrambler::RandomState::CornerPermutation do
4
+ include Scrambler::RandomState
5
+
6
+ it "should return solved permutation after initialization" do
7
+ subject.to_a.should == [0, 1, 2, 3, 4, 5, 6]
8
+ subject.to_i.should == 123456
9
+ end
10
+
11
+ it "should have permutation [1, 5, 2, 3, 0, 4, 6] after R turn" do
12
+ subject.turn!(:R).to_a.should == [1, 5, 2, 3, 0, 4, 6]
13
+ end
14
+
15
+ it "should have permutation [0, 2, 6, 3, 4, 1, 5] after F turn" do
16
+ subject.turn!(:F).to_a.should == [0, 2, 6, 3, 4, 1, 5]
17
+ end
18
+
19
+ it "should have permutation [3, 0, 1, 2, 4, 5, 6] after U turn" do
20
+ subject.turn!(:U).to_a.should == [3, 0, 1, 2, 4, 5, 6]
21
+ end
22
+
23
+ it "should return proper number representation" do
24
+ CornerPermutation.new([1, 0, 2, 4, 5, 6, 3]).to_i.should == 1024563
25
+ end
26
+
27
+ it "should accept numbers and convert them to arrays" do
28
+ CornerPermutation.new(1024563).to_a.should == [1, 0, 2, 4, 5, 6, 3]
29
+ CornerPermutation.new(123456).to_a.should == [0, 1, 2, 3, 4, 5, 6]
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+ require "scrambler/random_state/cube_state"
3
+
4
+ describe Scrambler::RandomState::CubeState do
5
+ include Scrambler::RandomState
6
+
7
+ Given(:cube) { CubeState.new }
8
+
9
+ context "when new" do
10
+ Then { cube.orientation.should == [0] * 8 }
11
+ Then { cube.permutation.should == [0, 1, 2, 3, 4, 5, 6, 7] }
12
+ end
13
+
14
+ context "when turned R" do
15
+ When { cube.turn! :R }
16
+
17
+ Then { cube.orientation.should == [1, 2, 0, 0, 2, 1, 0, 0] }
18
+ Then { cube.permutation.should == [1, 5, 2, 3, 0, 4, 6, 7] }
19
+ end
20
+
21
+ context "when turned F" do
22
+ When { cube.turn! :F }
23
+
24
+ Then { cube.orientation.should == [0, 1, 2, 0, 0, 2, 1, 0] }
25
+ Then { cube.permutation.should == [0, 2, 6, 3, 4, 1, 5, 7] }
26
+ end
27
+
28
+ context "when turned U" do
29
+ When { cube.turn! :U }
30
+
31
+ Then { cube.orientation.should == [0] * 8 }
32
+ Then { cube.permutation.should == [3, 0, 1, 2, 4, 5, 6, 7] }
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ describe Scrambler::RandomState::Solver do
4
+ include Scrambler::RandomState
5
+
6
+ before(:all) do
7
+ @solver ||= Solver.new
8
+ end
9
+
10
+ it "should solve scramble R U optimally" do
11
+ @solver.solve(CornerPermutation.new([3, 1, 5, 2, 0, 4, 6]), CornerOrientation.new([0, 1, 2, 0, 2, 1, 0])).should == "U' R'"
12
+ end
13
+
14
+ it "should solve scramble U R U' R' optimally" do
15
+ @solver.solve(CornerPermutation.new([3, 5, 2, 0, 4, 1, 6]), CornerOrientation.new([0, 1, 0, 1, 0, 1, 0])).should == "R U R' U'"
16
+ end
17
+
18
+ it "should solve scramble F U R U' R' F' optimally" do
19
+ @solver.solve(CornerPermutation.new([3, 2, 1, 0, 4, 5, 6]), CornerOrientation.new([0, 0, 2, 1, 0, 0, 0])).should == "F R U R' U' F'"
20
+ end
21
+
22
+ it "should solve 8 move scramble optimally" do
23
+ @solver.solve(CornerPermutation.new([0, 1, 4, 2, 5, 6, 3]), CornerOrientation.new([2, 1, 2, 2, 0, 2, 0])).should == "U R' U2 F R' F U2 F'"
24
+ end
25
+
26
+ it "should solve 9 move scramble optimally" do
27
+ @solver.solve(CornerPermutation.new([0, 5, 6, 3, 2, 1, 4]), CornerOrientation.new([2, 1, 1, 2, 0, 2, 1])).should == "R U' F' R2 F R' F U2 R'"
28
+ end
29
+
30
+ it "should solve 10 move scramble optimally" do
31
+ @solver.solve(CornerPermutation.new([2, 5, 0, 6, 1, 4, 3]), CornerOrientation.new([2, 1, 2, 1, 1, 2, 0])).should == "U2 F U' R2 U F2 U R' U' R2"
32
+ end
33
+
34
+ it "should solve 11 move scramble optimally" do
35
+ @solver.solve(CornerPermutation.new([3, 2, 1, 0, 4, 5, 6]), CornerOrientation.new([0, 0, 0, 0, 0, 0, 0])).should == "R U2 R' U' F U2 R' F' R U' F2"
36
+ end
37
+ end
38
+
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe Scrambler::RandomState::TwoByTwo do
4
+ include Scrambler::RandomState
5
+
6
+ let(:valid_turns) { %w(R R' R2 U U' U2 F F' F2) }
7
+
8
+ it "should not have more than 11 moves" do
9
+ subject.scramble.split(" ").size.should be <= 11
10
+ end
11
+
12
+ it "should only contain moves out or <R, F, U>" do
13
+ subject.scramble.split(" ").each do |turn|
14
+ valid_turns.should include(turn)
15
+ end
16
+ end
17
+
18
+ it "should return different scrambles (almost) every time" do
19
+ subject.scramble.should_not == subject.scramble
20
+ end
21
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "rubygems"
2
1
  require "bundler/setup"
2
+ Bundler.require(:default)
3
3
 
4
- Bundler.require(:default)
4
+ require "rspec/given"
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: scrambler
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.1.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tim Habermaas
@@ -23,6 +23,17 @@ dependencies:
23
23
  version: 2.6.0
24
24
  type: :development
25
25
  version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec-given
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :development
36
+ version_requirements: *id002
26
37
  description: Scrambler for cubes and other puzzles
27
38
  email: t.habermaas@gmail.com
28
39
  executables: []
@@ -37,19 +48,29 @@ files:
37
48
  - lib/scrambler/cube.rb
38
49
  - lib/scrambler/megaminx.rb
39
50
  - lib/scrambler/pyraminx.rb
51
+ - lib/scrambler/random_state/corner_orientation.rb
52
+ - lib/scrambler/random_state/corner_permutation.rb
53
+ - lib/scrambler/random_state/cube_state.rb
54
+ - lib/scrambler/random_state/solver.rb
55
+ - lib/scrambler/random_state/two_by_two.rb
40
56
  - lib/scrambler/square1.rb
41
57
  - lib/scrambler/version.rb
42
58
  - lib/scrambler.rb
43
59
  - spec/scrambler/clock_spec.rb
44
- - spec/scrambler/five_by_five_spec.rb
45
- - spec/scrambler/four_by_four_spec.rb
60
+ - spec/scrambler/cubes/five_by_five_spec.rb
61
+ - spec/scrambler/cubes/four_by_four_spec.rb
62
+ - spec/scrambler/cubes/seven_by_seven_spec.rb
63
+ - spec/scrambler/cubes/six_by_six_spec.rb
64
+ - spec/scrambler/cubes/three_by_three_spec.rb
65
+ - spec/scrambler/cubes/two_by_two_spec.rb
46
66
  - spec/scrambler/megaminx_spec.rb
47
67
  - spec/scrambler/pyraminx_spec.rb
48
- - spec/scrambler/seven_by_seven_spec.rb
49
- - spec/scrambler/six_by_six_spec.rb
68
+ - spec/scrambler/random_state/corner_orientation_spec.rb
69
+ - spec/scrambler/random_state/corner_permutation_spec.rb
70
+ - spec/scrambler/random_state/cube_state_spec.rb
71
+ - spec/scrambler/random_state/solver_spec.rb
72
+ - spec/scrambler/random_state/two_by_two_spec.rb
50
73
  - spec/scrambler/square1_spec.rb
51
- - spec/scrambler/three_by_three_spec.rb
52
- - spec/scrambler/two_by_two_spec.rb
53
74
  - spec/spec_helper.rb
54
75
  - Gemfile
55
76
  homepage: https://github.com/timhabermaas/scrambler
@@ -75,9 +96,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
96
  requirements: []
76
97
 
77
98
  rubyforge_project:
78
- rubygems_version: 1.8.10
99
+ rubygems_version: 1.8.15
79
100
  signing_key:
80
101
  specification_version: 3
81
102
  summary: Scrambler for cubes and other puzzles
82
103
  test_files: []
83
104
 
105
+ has_rdoc: