tuomas-knights_tour 0.2.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,24 @@
1
+ === 0.2.5 released 2009-01-14
2
+
3
+ * Small code beautifications.
4
+
5
+ === 0.2.4 released 2008-12-10
6
+
7
+ * Small code optimization.
8
+
9
+ === 0.2.3 released 2008-12-11
10
+
11
+ * Small documentation improvements.
12
+ * Refactored code.
13
+
14
+ === 0.2.2 released 2008-12-10
15
+
16
+ * Small socumentation improvements.
17
+
18
+ === 0.2.1 released 2008-12-10
19
+
20
+ * Refactored code.
21
+
22
+ === 0.2.0 released 2008-12-10
23
+
24
+ * First revision that solves the problem correctly. :)
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ CHANGELOG.rdoc
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ bin/knights_tour
6
+ lib/knights_tour.rb
7
+ spec/knights_tour_spec.rb
data/README.rdoc ADDED
@@ -0,0 +1,82 @@
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 the software as a RubyGem from GitHub:
16
+
17
+ $ sudo gem install tuomas-knights_tour --source http://gems.github.com
18
+
19
+ The software is compatible with Ruby 1.9.1.
20
+
21
+ == Usage
22
+
23
+ Change the working directory of your command line shell into the project's
24
+ root directory and enter
25
+
26
+ $ ./bin/knights_tour
27
+
28
+ The command attempts to solve the problem on a board of size 8x8, the knight
29
+ located initially at position 0,0 (the top-left corner). If the program
30
+ finds a solution, it displays a result similar to the following:
31
+
32
+ +---+---+---+---+---+---+---+---+
33
+ | 1| 62| 13| 36| 3| 38| 31| 28|
34
+ +---+---+---+---+---+---+---+---+
35
+ | 14| 35| 2| 63| 32| 29| 4| 39|
36
+ +---+---+---+---+---+---+---+---+
37
+ | 61| 12| 59| 34| 37| 42| 27| 30|
38
+ +---+---+---+---+---+---+---+---+
39
+ | 50| 15| 64| 43| 58| 33| 40| 5|
40
+ +---+---+---+---+---+---+---+---+
41
+ | 11| 60| 49| 54| 41| 24| 45| 26|
42
+ +---+---+---+---+---+---+---+---+
43
+ | 16| 51| 18| 57| 44| 55| 6| 23|
44
+ +---+---+---+---+---+---+---+---+
45
+ | 19| 10| 53| 48| 21| 8| 25| 46|
46
+ +---+---+---+---+---+---+---+---+
47
+ | 52| 17| 20| 9| 56| 47| 22| 7|
48
+ +---+---+---+---+---+---+---+---+
49
+
50
+ The size of the board and the start position of the knight are configurable.
51
+ For all the options, see
52
+
53
+ $ ./bin/knights_tour -h
54
+
55
+ == Contacting
56
+
57
+ Please send feedback by email to Tuomas Kareinen < tkareine (at) gmail (dot)
58
+ com >.
59
+
60
+ == Legal notes
61
+
62
+ This software is licensed under the terms of the "MIT license":
63
+
64
+ Copyright (c) 2008-2009 Tuomas Kareinen.
65
+
66
+ Permission is hereby granted, free of charge, to any person obtaining a copy
67
+ of this software and associated documentation files (the "Software"), to
68
+ deal in the Software without restriction, including without limitation the
69
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
70
+ sell copies of the Software, and to permit persons to whom the Software is
71
+ furnished to do so, subject to the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be included in
74
+ all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
77
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
78
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
79
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
80
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
81
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
82
+ IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require "rubygems"
2
+
3
+ require "hoe"
4
+ require "./lib/knights_tour"
5
+ Hoe.new("knights_tour", KnightsTour::Meta::VERSION.to_s) do |p|
6
+ p.author = "Tuomas Kareinen"
7
+ p.email = "tkareine@gmail.com"
8
+ p.url = "http://github.com/tuomas/knights_tour"
9
+ p.summary =<<-END
10
+ A program that attempts to find a solution to the Knight's Tour problem.
11
+ END
12
+ p.readme_file = "README.rdoc"
13
+ p.history_file = "CHANGELOG.rdoc"
14
+ p.extra_rdoc_files = FileList["*.rdoc", "lib/**/*.rb"]
15
+ p.extra_deps = [["trollop", ">= 1.10.0"]]
16
+ p.extra_dev_deps = [["rspec", ">= 1.2.0"]]
17
+ p.rubyforge_name = "searchable-rec"
18
+ end
19
+
20
+ require "rake/rdoctask"
21
+ require "lib/knights_tour"
22
+ desc "Create documentation."
23
+ Rake::RDocTask.new(:rdoc) do |rd|
24
+ rd.title = "Knight's Tour #{KnightsTour::Meta::VERSION}"
25
+ rd.main = "README.rdoc"
26
+ rd.rdoc_files.include("*.rdoc", "lib/**/*.rb")
27
+ rd.rdoc_dir = "rdoc"
28
+ end
29
+
30
+ require "spec/rake/spectask"
31
+ desc "Run specs."
32
+ Spec::Rake::SpecTask.new(:spec) do |t|
33
+ t.spec_files = FileList["spec/**/*.rb"]
34
+ t.spec_opts = ["--format", "specdoc"]
35
+ #t.warning = true
36
+ end
37
+
38
+ desc "Find code smells."
39
+ task :roodi do
40
+ sh("roodi '**/*.rb'")
41
+ end
42
+
43
+ desc "Search unfinished parts of source code."
44
+ task :todo do
45
+ FileList["**/*.rb"].egrep /#.*(TODO|FIXME)/
46
+ end
47
+
48
+ task :default => :spec
data/bin/knights_tour ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "trollop"
5
+ require File.dirname(__FILE__) << "/../lib/knights_tour"
6
+
7
+ # Parse the command line arguments and invoke the application.
8
+
9
+ include KnightsTour
10
+
11
+ options = Trollop::options do
12
+ version Meta.version
13
+
14
+ banner <<-EOS
15
+ A program that attempts to find a solution to the Knight's Tour problem.
16
+
17
+ Usage:
18
+
19
+ #{File.basename($0)} [OPTIONS] [SIZE]
20
+
21
+ Where SIZE (default: 8,8) sets the size of the board to ROWS x COLS.
22
+
23
+ Options:
24
+ EOS
25
+
26
+ opt :start_at, "Set the initial position of the knight (default: 0,0)",
27
+ :type => :string,
28
+ :required => false
29
+ end
30
+
31
+ begin
32
+ app_params = {}
33
+ app_params[:size] = ARGV.shift unless ARGV.empty?
34
+ app_params[:start_at] = options[:start_at] if options[:start_at]
35
+
36
+ app = Application.new(app_params)
37
+ rescue ArgumentError => e
38
+ Trollop::die e.to_s
39
+ end
40
+
41
+ $stdout.puts app.solve
@@ -0,0 +1,192 @@
1
+ module KnightsTour
2
+ module Meta #:nodoc:
3
+ module VERSION #:nodoc:
4
+ MAJOR = 0
5
+ MINOR = 2
6
+ PATCH = 5
7
+
8
+ def self.to_s
9
+ [ MAJOR, MINOR, PATCH ].join(".")
10
+ end
11
+ end
12
+
13
+ COPYRIGHT = "Copyright (c) Tuomas Kareinen"
14
+
15
+ LICENSE = "Licensed under the terms of the \"MIT license\". See README.rdoc."
16
+
17
+ def self.version
18
+ "#{File.basename($0)} #{Meta::VERSION}\n#{Meta::COPYRIGHT}\n#{Meta::LICENSE}"
19
+ end
20
+ end
21
+
22
+ class Application
23
+ def initialize(params = {})
24
+ @board_size = parse_board_size(params[:size] || [8, 8])
25
+ @knight_starts_at = parse_position_on_board(
26
+ params[:start_at] || [0, 0],
27
+ @board_size)
28
+ end
29
+
30
+ def solve
31
+ @solution ||= StringResult.new(traverse(
32
+ Knight.new(@board_size, @knight_starts_at)))
33
+ end
34
+
35
+ private
36
+
37
+ def parse_pair(param)
38
+ unless param.is_a?(Array)
39
+ param = param.to_s.split(",")
40
+ end
41
+ [param[0].to_i, param[1].to_i]
42
+ end
43
+
44
+ def parse_board_size(size)
45
+ size = parse_pair(size)
46
+ unless size[0] > 0 && size[1] > 0
47
+ raise ArgumentError,
48
+ "Board size must be a pair of positive (non-zero) " \
49
+ "integers, separated by a comma"
50
+ end
51
+ size
52
+ end
53
+
54
+ def parse_position_on_board(position, board_size)
55
+ position = parse_pair(position)
56
+ unless (0...board_size[0]).include?(position[0]) &&
57
+ (0...board_size[1]).include?(position[1])
58
+ raise ArgumentError,
59
+ "Position must be a pair of positive integers within the " \
60
+ "size limits of the board, separated by a comma " \
61
+ "(for example, 0,5 is acceptable for board size 6,6)"
62
+ end
63
+ position
64
+ end
65
+
66
+ # Traverse the knight on the board.
67
+ #
68
+ # The algorithm is a recursive backtracking search for a first solution
69
+ # to the problem. The board is copied and modified by moving the knight
70
+ # to a new position in each recursive step of the algorithm, instead of
71
+ # modifying a single shared board in place.
72
+ def traverse(knight)
73
+ #$stdout.puts StringResult.new(board) # debug
74
+
75
+ unless knight.traversed?
76
+ next_positions = knight.find_next_positions
77
+ next_positions.each do |next_position|
78
+ knight = traverse(knight.dup.traverse_to(next_position))
79
+ unless knight.nil?
80
+ return knight # return the first solution found
81
+ end
82
+ end
83
+ end
84
+
85
+ knight # no solutions found, or already found one
86
+ end
87
+ end
88
+
89
+ class Knight
90
+ ## as [x, y] pairs
91
+ LEGAL_STEPS = [ [-2, 1], [-1, 2], [ 1, 2], [ 2, 1],
92
+ [ 2, -1], [ 1, -2], [-1, -2], [-2, -1] ]
93
+
94
+ attr_reader :board, :steps_taken, :current_position
95
+
96
+ def initialize(board_size, start_at)
97
+ @board = Array.new(board_size[0]) { Array.new(board_size[1], 0) }
98
+ @steps_taken = 0
99
+ traverse_to(start_at)
100
+ end
101
+
102
+ def initialize_copy(other)
103
+ @board = Marshal.load(Marshal.dump(other.board))
104
+ @steps_taken = other.steps_taken
105
+ end
106
+
107
+ def traversed?
108
+ last_step = @board.size * @board[0].size
109
+ @steps_taken == last_step
110
+ end
111
+
112
+ def traverse_to(new_position)
113
+ @steps_taken += 1
114
+ @current_position = new_position
115
+ @board[@current_position[0]][@current_position[1]] = @steps_taken
116
+ self
117
+ end
118
+
119
+ def find_next_positions
120
+ sort_by_warnsdorffs_heuristics(find_next_positions_at(@current_position))
121
+ end
122
+
123
+ private
124
+
125
+ # Optimization by applying Warnsdorff's heuristics: attempt to avoid
126
+ # dead ends by favoring positions with the lowest number of next
127
+ # available positions (thus, isolated positions become visited first).
128
+ #
129
+ # References:
130
+ # <http://mathworld.wolfram.com/KnightsTour.html>
131
+ # <http://web.telia.com/~u85905224/knight/eWarnsd.htm>
132
+ def sort_by_warnsdorffs_heuristics(positions)
133
+ positions.sort_by do |position|
134
+ find_next_positions_at(position).size
135
+ end
136
+ end
137
+
138
+ def find_next_positions_at(position)
139
+ positions = LEGAL_STEPS.map do |step|
140
+ position_after_step(position, step)
141
+ end
142
+ positions.reject { |pos| pos.nil? || (@board[pos[0]][pos[1]] > 0) }
143
+ end
144
+
145
+ def position_after_step(from, step)
146
+ x_pos = from[0] + step[0]
147
+ y_pos = from[1] + step[1]
148
+
149
+ if (0...@board.size).include?(x_pos) &&
150
+ (0...@board[0].size).include?(y_pos)
151
+ [x_pos, y_pos]
152
+ else
153
+ nil
154
+ end
155
+ end
156
+ end
157
+
158
+ class StringResult
159
+ def initialize(result)
160
+ if result.is_a?(Knight)
161
+ @result = board_to_s(result.board, result.steps_taken)
162
+ else
163
+ @result = "No solution found."
164
+ end
165
+ end
166
+
167
+ def to_s
168
+ @result
169
+ end
170
+
171
+ private
172
+
173
+ def board_to_s(board, steps_taken)
174
+ square_width = steps_taken.to_s.length + 1
175
+ separator_str = separator(square_width, board[0].size)
176
+
177
+ output = ""
178
+
179
+ board.each do |row|
180
+ output << separator_str
181
+ row_output = row.map { |step| "%#{square_width}s" % step }.join("|")
182
+ output << "|#{row_output}|\n"
183
+ end
184
+
185
+ output << separator_str
186
+ end
187
+
188
+ def separator(board_width, cols)
189
+ ("+" << "-" * board_width) * cols << "+\n"
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,172 @@
1
+ require File.dirname(__FILE__) << "/../lib/knights_tour"
2
+
3
+ include KnightsTour
4
+
5
+ describe 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 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 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,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tuomas-knights_tour
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.5
5
+ platform: ruby
6
+ authors:
7
+ - Tuomas Kareinen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-28 00:00:00 -07:00
13
+ default_executable: knights_tour
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
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: hoe
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.11.0
44
+ version:
45
+ description: ""
46
+ email: tkareine@gmail.com
47
+ executables:
48
+ - knights_tour
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - Manifest.txt
53
+ - CHANGELOG.rdoc
54
+ - README.rdoc
55
+ - lib/knights_tour.rb
56
+ files:
57
+ - CHANGELOG.rdoc
58
+ - Manifest.txt
59
+ - README.rdoc
60
+ - Rakefile
61
+ - bin/knights_tour
62
+ - lib/knights_tour.rb
63
+ - spec/knights_tour_spec.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/tuomas/knights_tour
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --main
69
+ - README.rdoc
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project: searchable-rec
87
+ rubygems_version: 1.2.0
88
+ signing_key:
89
+ specification_version: 2
90
+ summary: A program that attempts to find a solution to the Knight's Tour problem.
91
+ test_files: []
92
+