knights_tour 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,50 @@
1
+ === 0.3.5 released 2009-09-16
2
+
3
+ * Display proper version info on Ruby 1.8 when using option "-v".
4
+
5
+ === 0.3.4 released 2009-09-03
6
+
7
+ * Slight documentation improvements.
8
+ * Removed dependencies to Rubygems.
9
+
10
+ === 0.3.3 released 2009-04-17
11
+
12
+ * Added missing Rakefile to the distribution gem.
13
+ * Removed rspec as a dependency gem.
14
+
15
+ === 0.3.2 released 2009-03-31
16
+
17
+ * Slight documentation improvements.
18
+
19
+ === 0.3.1 released 2009-03-28
20
+
21
+ * Fixed usage documentation.
22
+
23
+ === 0.3.0 released 2009-03-28
24
+
25
+ * The software is available as a RubyGem from GitHub.
26
+
27
+ === 0.2.5 released 2009-01-14
28
+
29
+ * Small code beautifications.
30
+
31
+ === 0.2.4 released 2008-12-10
32
+
33
+ * Small code optimization.
34
+
35
+ === 0.2.3 released 2008-12-11
36
+
37
+ * Small documentation improvements.
38
+ * Refactored code.
39
+
40
+ === 0.2.2 released 2008-12-10
41
+
42
+ * Small socumentation improvements.
43
+
44
+ === 0.2.1 released 2008-12-10
45
+
46
+ * Refactored code.
47
+
48
+ === 0.2.0 released 2008-12-10
49
+
50
+ * First revision that solves the problem correctly. :)
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008-2009 Tuomas Kareinen.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,66 @@
1
+ = Knight's Tour
2
+
3
+ A program that attempts to find a solution to the
4
+ {Knight's Tour problem}[http://en.wikipedia.org/wiki/Knight%27s_Tour].
5
+ I was inspired to do this by finding
6
+ {a Python implementation}[http://ttsiodras.googlepages.com/knightstour.html].
7
+
8
+ The program's algorithm is a recursive backtracking search that returns the
9
+ first solution to the problem (if the algorithm finds one). It utilizes
10
+ {Warnsdorff's heuristics}[http://mathworld.wolfram.com/KnightsTour.html] to
11
+ avoid dead ends, making the search faster in general.
12
+
13
+ == Installation
14
+
15
+ Install Knight's Tour as a RubyGem from GitHub:
16
+
17
+ $ sudo gem install tuomas-knights_tour --source http://gems.github.com
18
+
19
+ The program is compatible with Ruby 1.9.1.
20
+
21
+ == Usage
22
+
23
+ In the command line, run the program by entering
24
+
25
+ $ knights_tour
26
+
27
+ The command attempts to solve the problem on a board of size 8x8, the knight
28
+ located initially at position 0,0 (the top-left corner). If the program
29
+ finds a solution, it displays a result similar to the following:
30
+
31
+ +---+---+---+---+---+---+---+---+
32
+ | 1| 62| 13| 36| 3| 38| 31| 28|
33
+ +---+---+---+---+---+---+---+---+
34
+ | 14| 35| 2| 63| 32| 29| 4| 39|
35
+ +---+---+---+---+---+---+---+---+
36
+ | 61| 12| 59| 34| 37| 42| 27| 30|
37
+ +---+---+---+---+---+---+---+---+
38
+ | 50| 15| 64| 43| 58| 33| 40| 5|
39
+ +---+---+---+---+---+---+---+---+
40
+ | 11| 60| 49| 54| 41| 24| 45| 26|
41
+ +---+---+---+---+---+---+---+---+
42
+ | 16| 51| 18| 57| 44| 55| 6| 23|
43
+ +---+---+---+---+---+---+---+---+
44
+ | 19| 10| 53| 48| 21| 8| 25| 46|
45
+ +---+---+---+---+---+---+---+---+
46
+ | 52| 17| 20| 9| 56| 47| 22| 7|
47
+ +---+---+---+---+---+---+---+---+
48
+
49
+ The command above is the same as invoking the program with
50
+
51
+ $ knights_tour -s 0,0 8,8
52
+
53
+ The size of the board and the start position of the knight are configurable,
54
+ however. For all the options, see
55
+
56
+ $ knights_tour -h
57
+
58
+ == Contacting
59
+
60
+ Please send feedback by email to Tuomas Kareinen < tkareine (at) gmail (dot)
61
+ com >.
62
+
63
+ == Legal notes
64
+
65
+ Copyright (c) 2008-2009 Tuomas Kareinen. See MIT-LICENSE.txt in this
66
+ directory.
data/Rakefile ADDED
@@ -0,0 +1,94 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
2
+
3
+ require "rubygems"
4
+
5
+ full_name = "Knight's Tour"
6
+ package_name = "knights_tour"
7
+ require "#{package_name}"
8
+ version = KnightsTour::VERSION
9
+
10
+ require "rake/clean"
11
+
12
+ require "rake/gempackagetask"
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = package_name
15
+ s.version = version
16
+ s.homepage = "http://github.com/tuomas/knights_tour"
17
+ s.summary = "Solves Knight's Tour problem."
18
+ s.description = "A program that attempts to find a solution to the Knight's Tour problem."
19
+
20
+ s.author = "Tuomas Kareinen"
21
+ s.email = "tkareine@gmail.com"
22
+
23
+ s.platform = Gem::Platform::RUBY
24
+ s.files = FileList["Rakefile", "MIT-LICENSE.txt", "*.rdoc", "bin/**/*", "lib/**/*", "spec/**/*"].to_a
25
+ s.executables = ["knights_tour"]
26
+
27
+ s.add_dependency("trollop", ">= 1.10.0")
28
+
29
+ s.has_rdoc = true
30
+ s.extra_rdoc_files = FileList["MIT-LICENSE.txt", "*.rdoc"].to_a
31
+ s.rdoc_options << "--title" << "#{full_name} #{version}" \
32
+ << "--main" << "README.rdoc" \
33
+ << "--exclude" << "spec" \
34
+ << "--line-numbers"
35
+ end
36
+
37
+ Rake::GemPackageTask.new(spec) do |pkg|
38
+ pkg.need_zip = false
39
+ pkg.need_tar = true
40
+ end
41
+
42
+ desc "Generate a gemspec file"
43
+ task :gemspec do
44
+ File.open("#{spec.name}.gemspec", "w") do |f|
45
+ f.write spec.to_ruby
46
+ end
47
+ end
48
+
49
+ task :install => [:package] do
50
+ sh %{sudo gem install pkg/#{package_name}-#{version}.gem}
51
+ end
52
+
53
+ task :uninstall => [:clean] do
54
+ sh %{sudo gem uninstall #{package_name}}
55
+ end
56
+
57
+ require "rake/rdoctask"
58
+ desc "Create documentation"
59
+ Rake::RDocTask.new(:rdoc) do |rd|
60
+ rd.rdoc_dir = "rdoc"
61
+ rd.title = "#{full_name} #{version}"
62
+ rd.main = "README.rdoc"
63
+ rd.rdoc_files.include("MIT-LICENSE.txt", "*.rdoc", "lib/**/*.rb")
64
+ rd.options << "--line-numbers"
65
+ end
66
+
67
+ require "spec/rake/spectask"
68
+ desc "Run specs"
69
+ Spec::Rake::SpecTask.new(:spec) do |t|
70
+ t.spec_files = FileList["spec/**/*_spec.rb"]
71
+ t.spec_opts = ["--colour --format progress --loadby mtime"]
72
+ t.warning = true
73
+ t.libs << "lib"
74
+ end
75
+
76
+ desc "Run specs with RCov"
77
+ Spec::Rake::SpecTask.new(:rcov) do |t|
78
+ t.spec_files = FileList["spec/**/*.rb"]
79
+ t.rcov = true
80
+ t.rcov_opts = ["--exclude", "spec"]
81
+ t.libs << "lib"
82
+ end
83
+
84
+ desc "Find code smells"
85
+ task :roodi do
86
+ sh %{roodi "**/*.rb"}
87
+ end
88
+
89
+ desc "Search unfinished parts of source code"
90
+ task :todo do
91
+ FileList["**/*.rb", "**/*.rdoc", "**/*.txt"].egrep /(TODO|FIXME)/
92
+ end
93
+
94
+ task :default => :spec
data/bin/knights_tour ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "trollop"
4
+ require "knights_tour"
5
+
6
+ # Parse the command line arguments and invoke the application.
7
+
8
+ include KnightsTour
9
+
10
+ options = Trollop::options do
11
+ version "#{File.basename($0)} #{KnightsTour::VERSION}"
12
+
13
+ banner <<-EOS
14
+ A program that attempts to find a solution to the Knight's Tour problem.
15
+
16
+ Usage:
17
+
18
+ #{File.basename($0)} [OPTIONS] [SIZE]
19
+
20
+ Where SIZE (default: 8,8) sets the size of the board to ROWS x COLS.
21
+
22
+ Options:
23
+ EOS
24
+
25
+ opt :start_at, "Set the initial position of the knight (default: 0,0)",
26
+ :type => :string,
27
+ :required => false
28
+ end
29
+
30
+ begin
31
+ app_params = {}
32
+ app_params[:size] = ARGV.shift unless ARGV.empty?
33
+ app_params[:start_at] = options[:start_at] if options[:start_at]
34
+
35
+ app = Application.new(app_params)
36
+ rescue ArgumentError => e
37
+ Trollop::die e.to_s
38
+ end
39
+
40
+ puts app.solve
@@ -0,0 +1,172 @@
1
+ module KnightsTour
2
+ VERSION = "0.3.5"
3
+
4
+ class Application
5
+ def initialize(params = {})
6
+ @board_size = parse_board_size(params[:size] || [8, 8])
7
+ @knight_starts_at = parse_position_on_board(
8
+ params[:start_at] || [0, 0],
9
+ @board_size)
10
+ end
11
+
12
+ def solve
13
+ @solution ||= StringResult.new(traverse(
14
+ Knight.new(@board_size, @knight_starts_at)))
15
+ end
16
+
17
+ private
18
+
19
+ def parse_pair(param)
20
+ unless param.is_a?(Array)
21
+ param = param.to_s.split(",")
22
+ end
23
+ [param[0].to_i, param[1].to_i]
24
+ end
25
+
26
+ def parse_board_size(size)
27
+ size = parse_pair(size)
28
+ unless size[0] > 0 && size[1] > 0
29
+ raise ArgumentError,
30
+ "Board size must be a pair of positive (non-zero) " \
31
+ "integers, separated by a comma"
32
+ end
33
+ size
34
+ end
35
+
36
+ def parse_position_on_board(position, board_size)
37
+ position = parse_pair(position)
38
+ unless (0...board_size[0]).include?(position[0]) &&
39
+ (0...board_size[1]).include?(position[1])
40
+ raise ArgumentError,
41
+ "Position must be a pair of positive integers within the " \
42
+ "size limits of the board, separated by a comma " \
43
+ "(for example, 0,5 is acceptable for board size 6,6)"
44
+ end
45
+ position
46
+ end
47
+
48
+ # Traverse the knight on the board.
49
+ #
50
+ # The algorithm is a recursive backtracking search for a first solution
51
+ # to the problem. The board is copied and modified by moving the knight
52
+ # to a new position in each recursive step of the algorithm, instead of
53
+ # modifying a single shared board in place.
54
+ def traverse(knight)
55
+ unless knight.traversed?
56
+ next_positions = knight.find_next_positions
57
+ next_positions.each do |next_position|
58
+ knight = traverse(knight.dup.traverse_to(next_position))
59
+ unless knight.nil?
60
+ return knight # return the first solution found
61
+ end
62
+ end
63
+ end
64
+
65
+ knight # no solutions found, or already found one
66
+ end
67
+ end
68
+
69
+ class Knight
70
+ ## as [x, y] pairs
71
+ LEGAL_STEPS = [ [-2, 1], [-1, 2], [ 1, 2], [ 2, 1],
72
+ [ 2, -1], [ 1, -2], [-1, -2], [-2, -1] ]
73
+
74
+ attr_reader :board, :steps_taken, :current_position
75
+
76
+ def initialize(board_size, start_at)
77
+ @board = Array.new(board_size[0]) { Array.new(board_size[1], 0) }
78
+ @steps_taken = 0
79
+ traverse_to(start_at)
80
+ end
81
+
82
+ def initialize_copy(other)
83
+ @board = Marshal.load(Marshal.dump(other.board))
84
+ @steps_taken = other.steps_taken
85
+ end
86
+
87
+ def traversed?
88
+ last_step = @board.size * @board[0].size
89
+ @steps_taken == last_step
90
+ end
91
+
92
+ def traverse_to(new_position)
93
+ @steps_taken += 1
94
+ @current_position = new_position
95
+ @board[@current_position[0]][@current_position[1]] = @steps_taken
96
+ self
97
+ end
98
+
99
+ def find_next_positions
100
+ sort_by_warnsdorffs_heuristics(find_next_positions_at(@current_position))
101
+ end
102
+
103
+ private
104
+
105
+ # Optimization by applying Warnsdorff's heuristics: attempt to avoid
106
+ # dead ends by favoring positions with the lowest number of next
107
+ # available positions (thus, isolated positions become visited first).
108
+ #
109
+ # References:
110
+ # <http://mathworld.wolfram.com/KnightsTour.html>
111
+ # <http://web.telia.com/~u85905224/knight/eWarnsd.htm>
112
+ def sort_by_warnsdorffs_heuristics(positions)
113
+ positions.sort_by do |position|
114
+ find_next_positions_at(position).size
115
+ end
116
+ end
117
+
118
+ def find_next_positions_at(position)
119
+ positions = LEGAL_STEPS.map do |step|
120
+ position_after_step(position, step)
121
+ end
122
+ positions.reject { |pos| pos.nil? || (@board[pos[0]][pos[1]] > 0) }
123
+ end
124
+
125
+ def position_after_step(from, step)
126
+ x_pos = from[0] + step[0]
127
+ y_pos = from[1] + step[1]
128
+
129
+ if (0...@board.size).include?(x_pos) &&
130
+ (0...@board[0].size).include?(y_pos)
131
+ [x_pos, y_pos]
132
+ else
133
+ nil
134
+ end
135
+ end
136
+ end
137
+
138
+ class StringResult
139
+ def initialize(result)
140
+ if result.is_a?(Knight)
141
+ @result = board_to_s(result.board, result.steps_taken)
142
+ else
143
+ @result = "No solution found."
144
+ end
145
+ end
146
+
147
+ def to_s
148
+ @result
149
+ end
150
+
151
+ private
152
+
153
+ def board_to_s(board, steps_taken)
154
+ square_width = steps_taken.to_s.length + 1
155
+ separator_str = separator(square_width, board[0].size)
156
+
157
+ output = ""
158
+
159
+ board.each do |row|
160
+ output << separator_str
161
+ row_output = row.map { |step| "%#{square_width}s" % step }.join("|")
162
+ output << "|#{row_output}|\n"
163
+ end
164
+
165
+ output << separator_str
166
+ end
167
+
168
+ def separator(board_width, cols)
169
+ ("+" << "-" * board_width) * cols << "+\n"
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,172 @@
1
+ require "knights_tour"
2
+
3
+ include KnightsTour
4
+
5
+ describe KnightsTour::Application do
6
+ it "should accept valid non-default board size" do
7
+ lambda { Application.new(:size => -1) }.should raise_error(ArgumentError)
8
+ lambda { Application.new(:size => 0) }.should raise_error(ArgumentError)
9
+ lambda { Application.new(:size => 1) }.should raise_error(ArgumentError)
10
+ lambda { Application.new(:size => "1") }.should raise_error(ArgumentError)
11
+ lambda { Application.new(:size => [-1, -1]) }.should raise_error(ArgumentError)
12
+ lambda { Application.new(:size => [-1, 0]) }.should raise_error(ArgumentError)
13
+ lambda { Application.new(:size => [0, -1]) }.should raise_error(ArgumentError)
14
+ lambda { Application.new(:size => [0, 0]) }.should raise_error(ArgumentError)
15
+ lambda { Application.new(:size => [0, 1]) }.should raise_error(ArgumentError)
16
+ lambda { Application.new(:size => [1, 0]) }.should raise_error(ArgumentError)
17
+ lambda { Application.new(:size => [1, 1]) }.should_not raise_error(ArgumentError)
18
+ lambda { Application.new(:size => [3, 4]) }.should_not raise_error(ArgumentError)
19
+ lambda { Application.new(:size => [5, 5]) }.should_not raise_error(ArgumentError)
20
+ end
21
+
22
+ it "should accept valid non-default start positions" do
23
+ lambda { Application.new(:size => [1, 1], :start_at => -1) }.should raise_error(ArgumentError)
24
+ lambda { Application.new(:size => [1, 1], :start_at => [ 0, 1]) }.should raise_error(ArgumentError)
25
+ lambda { Application.new(:size => [1, 1], :start_at => [ 1, 0]) }.should raise_error(ArgumentError)
26
+ lambda { Application.new(:size => [1, 1], :start_at => [ 1, 1]) }.should raise_error(ArgumentError)
27
+ lambda { Application.new(:size => [1, 1], :start_at => [ 3, 4]) }.should raise_error(ArgumentError)
28
+ lambda { Application.new(:size => [3, 7], :start_at => [ 4, 2]) }.should raise_error(ArgumentError)
29
+ lambda { Application.new(:size => [3, 7], :start_at => [ 2, 7]) }.should raise_error(ArgumentError)
30
+ lambda { Application.new(:size => [3, 7], :start_at => [ 3, 7]) }.should raise_error(ArgumentError)
31
+ lambda { Application.new(:size => [1, 1], :start_at => [ 0, 0]) }.should_not raise_error(ArgumentError)
32
+ lambda { Application.new(:size => [1, 2], :start_at => [ 0, 1]) }.should_not raise_error(ArgumentError)
33
+ lambda { Application.new(:size => [2, 1], :start_at => [ 1, 0]) }.should_not raise_error(ArgumentError)
34
+ lambda { Application.new(:size => [2, 2], :start_at => [ 0, 0]) }.should_not raise_error(ArgumentError)
35
+ lambda { Application.new(:size => [2, 2], :start_at => [ 1, 1]) }.should_not raise_error(ArgumentError)
36
+ lambda { Application.new(:size => [3, 7], :start_at => [ 2, 4]) }.should_not raise_error(ArgumentError)
37
+ lambda { Application.new(:size => [5, 5], :start_at => [ 4, 4]) }.should_not raise_error(ArgumentError)
38
+ lambda { Application.new(:size => [8, 8], :start_at => [ 7, 6]) }.should_not raise_error(ArgumentError)
39
+ end
40
+
41
+ it "should solve a board with size 1,1" do
42
+ result = Application.new(:size => [1, 1]).solve
43
+ result.to_s.should == <<-END
44
+ +--+
45
+ | 1|
46
+ +--+
47
+ END
48
+ end
49
+
50
+ it "should solve a board with size 5,5" do
51
+ result = Application.new(:size => [5, 5]).solve
52
+ result.to_s.should == <<-END
53
+ +---+---+---+---+---+
54
+ | 1| 14| 9| 20| 3|
55
+ +---+---+---+---+---+
56
+ | 24| 19| 2| 15| 10|
57
+ +---+---+---+---+---+
58
+ | 13| 8| 25| 4| 21|
59
+ +---+---+---+---+---+
60
+ | 18| 23| 6| 11| 16|
61
+ +---+---+---+---+---+
62
+ | 7| 12| 17| 22| 5|
63
+ +---+---+---+---+---+
64
+ END
65
+ end
66
+
67
+ it "should solve a board with size of 5,5, in start position 2,2" do
68
+ result = Application.new(:size => [5, 5], :start_at => [2, 2]).solve
69
+ result.to_s.should == <<-END
70
+ +---+---+---+---+---+
71
+ | 21| 12| 7| 2| 19|
72
+ +---+---+---+---+---+
73
+ | 6| 17| 20| 13| 8|
74
+ +---+---+---+---+---+
75
+ | 11| 22| 1| 18| 3|
76
+ +---+---+---+---+---+
77
+ | 16| 5| 24| 9| 14|
78
+ +---+---+---+---+---+
79
+ | 23| 10| 15| 4| 25|
80
+ +---+---+---+---+---+
81
+ END
82
+ end
83
+
84
+ it "should solve a board with size of 3,7, in start position 2,4" do
85
+ result = Application.new(:size => [3, 7], :start_at => [2, 4]).solve
86
+ result.to_s.should == <<-END
87
+ +---+---+---+---+---+---+---+
88
+ | 11| 14| 17| 20| 3| 8| 5|
89
+ +---+---+---+---+---+---+---+
90
+ | 16| 21| 12| 9| 6| 19| 2|
91
+ +---+---+---+---+---+---+---+
92
+ | 13| 10| 15| 18| 1| 4| 7|
93
+ +---+---+---+---+---+---+---+
94
+ END
95
+ end
96
+
97
+ it "should cache the result" do
98
+ app = Application.new(:size => [1, 1])
99
+ result = []
100
+ result << app.solve
101
+ result << app.solve
102
+ result[0].should == result[1]
103
+ end
104
+ end
105
+
106
+ describe KnightsTour::Knight do
107
+ before(:each) do
108
+ @knight = Knight.new([5, 5], [0, 0])
109
+ # broken board state, but it does not matter for testing
110
+ @knight.instance_variable_set(
111
+ :@board,
112
+ [ [ 1, 0, 0, 12, 3 ],
113
+ [ 0, 11, 2, 7, 18 ],
114
+ [ 0, 0, 0, 0, 13 ],
115
+ [ 10, 15, 6, 0, 8 ],
116
+ [ 0, 19, 9, 14, 5 ] ])
117
+ @knight.instance_variable_set(:@current_position, [1, 4])
118
+ @knight.instance_variable_set(:@steps_taken, 18)
119
+ end
120
+
121
+ it "should find next positions available" do
122
+ next_positions = @knight.send(:find_next_positions_at, @knight.current_position)
123
+ next_positions.sort! # ensure the order is not correct already
124
+ next_positions.should == [ [0, 2], [2, 2], [3, 3] ]
125
+ end
126
+
127
+ it "should sort next positions by Warnsdorff's heuristics" do
128
+ next_positions = @knight.send(:find_next_positions_at, @knight.current_position)
129
+ next_positions.sort! # ensure the order is not correct already
130
+ next_positions.should == [ [0, 2], [2, 2], [3, 3] ]
131
+ next_positions = @knight.send(:sort_by_warnsdorffs_heuristics, next_positions)
132
+ next_positions.should == [ [3, 3], [2, 2], [0, 2] ]
133
+ end
134
+
135
+ it "should find next positions, sorted" do
136
+ next_positions = @knight.find_next_positions
137
+ next_positions.should == [ [3, 3], [2, 2], [0, 2] ]
138
+ end
139
+ end
140
+
141
+ describe KnightsTour::StringResult do
142
+ it "should show the result correctly for a failed result" do
143
+ StringResult.new(nil).to_s.should == "No solution found."
144
+ end
145
+
146
+ it "should show the result correctly for a trivial result" do
147
+ knight = Knight.new([1, 1], [0, 0])
148
+ knight.instance_variable_set(:@board, [[1]])
149
+ result = StringResult.new(knight)
150
+ result.to_s.should == <<-END
151
+ +--+
152
+ | 1|
153
+ +--+
154
+ END
155
+ end
156
+
157
+ it "should show the result correctly for a non-trivial result" do
158
+ # in reality, this is not a solvable board size
159
+ knight = Knight.new([3, 3], [0, 0])
160
+ knight.instance_variable_set(:@board, [[1, 2, 3], [42, 56, 69], [0, 0, 119]])
161
+ knight.instance_variable_set(:@steps_taken, 119)
162
+ StringResult.new(knight).to_s.should == <<-END
163
+ +----+----+----+
164
+ | 1| 2| 3|
165
+ +----+----+----+
166
+ | 42| 56| 69|
167
+ +----+----+----+
168
+ | 0| 0| 119|
169
+ +----+----+----+
170
+ END
171
+ end
172
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knights_tour
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.5
5
+ platform: ruby
6
+ authors:
7
+ - Tuomas Kareinen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-12 00:00:00 +03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: trollop
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.10.0
24
+ version:
25
+ description: A program that attempts to find a solution to the Knight's Tour problem.
26
+ email: tkareine@gmail.com
27
+ executables:
28
+ - knights_tour
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - MIT-LICENSE.txt
33
+ - CHANGELOG.rdoc
34
+ - README.rdoc
35
+ files:
36
+ - Rakefile
37
+ - MIT-LICENSE.txt
38
+ - CHANGELOG.rdoc
39
+ - README.rdoc
40
+ - bin/knights_tour
41
+ - lib/knights_tour.rb
42
+ - spec/knights_tour_spec.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/tuomas/knights_tour
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --title
50
+ - Knight's Tour 0.3.5
51
+ - --main
52
+ - README.rdoc
53
+ - --exclude
54
+ - spec
55
+ - --line-numbers
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Solves Knight's Tour problem.
77
+ test_files: []
78
+