knights_tour 0.3.5

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/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
+