crucigrama 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,107 @@
1
+ # This module provides methods to query word existence, placement and search on a crossword
2
+ module Crucigrama::Crossword::WordQuery
3
+
4
+
5
+ # @return [String, nil] the word in the crossword at the given coordinates and direction, if there is one, or nil otherwise
6
+ # @param [Hash<Symbol,Integer>] coordinates the coordinates for the crossword cell being queried
7
+ # @option coordinates [Integer] :horizontal a number between 0 and the horizontal dimension of the
8
+ # crossword minus one specifying the row of the cell being queried
9
+ # @option coordinates [Integer] :vertical a number between 0 and the vertical dimension of the
10
+ # crossword minus one specifying the column of the cell being queried
11
+ # @param [:horizontal,:vertical] direction the direction of the word being queried
12
+ def word_at(coordinates, direction)
13
+ other_direction = direction_other_than(direction)
14
+ words.detect do |word|
15
+ (word_positions[word][direction]||=[]).detect do |position|
16
+ position[other_direction] == coordinates[other_direction] and position[direction] <= coordinates[direction] and position[direction] + word.length > coordinates[direction]
17
+ end
18
+ end
19
+ end
20
+
21
+ # @return [Boolean] if the word at the given coordinates and direction on the crossword is the given word
22
+ # @param [Hash<Symbol,Integer>] coordinates the coordinates for the crossword cell being queried
23
+ # @option coordinates [Integer] :horizontal a number between 0 and the horizontal dimension of the
24
+ # crossword minus one specifying the row of the cell being queried
25
+ # @option coordinates [Integer] :vertical a number between 0 and the vertical dimension of the
26
+ # crossword minus one specifying the column of the cell being queried
27
+ # @param [:horizontal,:vertical] direction the direction of the word being queried
28
+ # @param [String] word the word that is or is not the word at the crossword
29
+ def word_at?(coordinates, direction, word)
30
+ word_at(coordinates, direction) == word
31
+ end
32
+
33
+ # @return [Array<Hash<Symbol,Integer>>] the list of black positions (or empty cells) in the crossword
34
+ def black_positions
35
+ @black_positions ||= lines(:horizontal).collect.with_index do |row, vertical_index|
36
+ row.chars.collect.with_index do |cell, horizontal_index|
37
+ cell == self.class::BLACK ? {:horizontal => horizontal_index, :vertical => vertical_index} : nil
38
+ end.compact
39
+ end.flatten
40
+ end
41
+
42
+ # @return [Array<String>] the list of words present in the crossword
43
+ def words
44
+ word_positions.keys
45
+ end
46
+
47
+ # @return [Hash<String,Hash<Symbol, Array<Hash<Symbol, Integer>>>>] a relation of the
48
+ # words in the crosswords and their starting positions in both directions. Words are
49
+ # the primary key in the returned tree, followed by the direction (:horizontal or
50
+ # :vertical). The list of positions is given as an Array of hashes with values for
51
+ # the :horizontal and :vertical coordinates
52
+ # @todo reimplement using lines(direction)
53
+ def word_positions
54
+ @word_positions ||= begin
55
+ result = {}
56
+ [:horizontal, :vertical].each do |direction|
57
+ other_direction = direction_other_than(direction)
58
+ lines(direction).each.with_index do |row, row_index|
59
+ position = 0
60
+ row.split(self.class::BLACK).each do |word|
61
+ unless word.empty?
62
+ result[word]||= {}
63
+ result[word][direction]||= []
64
+ result[word][direction] << {direction => position, other_direction=> row_index}
65
+ end
66
+ position+= word.length + 1
67
+ end
68
+ end
69
+ end
70
+ result
71
+ end
72
+ end
73
+
74
+ # @return [Boolean] if the given word can be set on the crossword on the given coordinates and the given direction.
75
+ # The word can be set if it is not out of bounds on the crossword (that is, the cells it would occupy are defined)
76
+ # and if every crossword cell it would occupy either contains the corresponding word character or is a black
77
+ # position or empty cell.
78
+ def can_set_word?(word, coordinates, direction)
79
+ return false if out_of_bounds(word, coordinates, direction)
80
+ return true if word.empty?
81
+ other_direction = direction_other_than(direction)
82
+ line = line(coordinates[other_direction], direction)
83
+ regexp_str = line[coordinates[direction]..line.length][0..word.length-1].gsub(self.class::BLACK, '.')
84
+ Regexp.new(regexp_str).match(word)
85
+ end
86
+
87
+ private
88
+
89
+ # @return [Boolean] whether the given word would be out of bounds on the
90
+ # crossword on the given coordinates and direction
91
+ # @param [Hash<Symbol,Integer>] coordinates the coordinates for the crossword cell where the queried word would start
92
+ # @option coordinates [Integer] :horizontal a number between 0 and the horizontal dimension of the
93
+ # crossword minus one specifying the row of the cell where the queried word would start
94
+ # @option coordinates [Integer] :vertical a number between 0 and the vertical dimension of the
95
+ # crossword minus one specifying the column of the cell where the queried word would start
96
+ def out_of_bounds(word, coordinates, direction)
97
+ other_direction = direction_other_than(direction)
98
+ coordinates[:horizontal] < 0 or coordinates[:vertical] < 0 or dimensions[other_direction] <= coordinates[other_direction] or dimensions[direction] <= coordinates[direction] + word.length - 1
99
+ end
100
+
101
+ def grid_modified!
102
+ @word_positions = nil
103
+ @black_positions = nil
104
+ super
105
+ end
106
+
107
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/hash_with_indifferent_access'
2
+
3
+ # This class represents a crossword
4
+ # @todo Refactor methods to modules Crucigrama::Crossword::Grid,
5
+ # Crucigrama::Crossword::WordQuery and Crucigrama::Crossword::Definitions
6
+ # @todo Define public API and make everything else private
7
+ class Crucigrama::Crossword
8
+
9
+ # The symbol used to represent empty cells on the crossword, that is, cells occupied by no word characters
10
+ BLACK = '#'
11
+
12
+ require 'crucigrama/crossword/grid'
13
+ require 'crucigrama/crossword/line_query'
14
+ require 'crucigrama/crossword/word_query'
15
+ require 'crucigrama/crossword/definitions'
16
+
17
+ include Grid
18
+ include LineQuery
19
+ include WordQuery
20
+ include Definitions
21
+
22
+ begin
23
+ require 'crucigrama/crossword/pdf_printable'
24
+ include PdfPrintable
25
+ rescue LoadError
26
+ end
27
+
28
+ require 'crucigrama/crossword/serializable'
29
+ include Serializable
30
+
31
+ include Crucigrama::Positionable
32
+
33
+
34
+ def to_s
35
+ "\n#{grid}"
36
+ end
37
+
38
+ end
@@ -0,0 +1,110 @@
1
+ # @todo document everything!
2
+ class Crucigrama::CrosswordBuilder
3
+
4
+ NOT_REPEATABLE_WORD_MINIMUM_LENGTH = 3
5
+
6
+ include Crucigrama::Positionable
7
+
8
+ attr_reader :valid_words, :crossword_class, :crossword
9
+ def initialize(opts = {})
10
+ @valid_words = opts[:valid_words] || []
11
+ @crossword_class = opts[:crossword_class] || Crucigrama::Crossword
12
+ @crossword = @crossword_class.new(opts[:dimensions]||{})
13
+ end
14
+
15
+
16
+ def self.build_crossword(opts = {})
17
+ self.new(opts).build
18
+ end
19
+
20
+ def build
21
+ crossword.black_positions.each do |position|
22
+ set_longest_word_at(position,:horizontal) unless crossword.word_at(position, :horizontal).to_s.length > 1
23
+ set_longest_word_at(position,:vertical) unless crossword.word_at(position, :vertical).to_s.length > 1
24
+ end
25
+ crossword
26
+ end
27
+
28
+ def valid_word?(word)
29
+ word == crossword_class::BLACK || word.length == 1 || valid_words.include?(word)
30
+ end
31
+
32
+ # @todo remove method ?
33
+ def validate
34
+ crossword.words.collect{|word| valid_word?(word)}.include?(false) ? false : true
35
+ end
36
+
37
+ # @todo return both the word and the position it was placed at ?
38
+ def set_longest_word_at(word_position, direction)
39
+ transversal_conditions = transversal_conditions_for(word_position, direction)
40
+ not_word_destructive_condition = not_word_destructive_condition(word_position, direction)
41
+ can_be_set_condition = can_be_set_condition(word_position, direction)
42
+ already_used_words = used_words
43
+ (2..crossword.dimensions[direction]-word_position[direction]).to_a.reverse.each do |length|
44
+ restricting_word_conditions = [can_be_set_condition, *transversal_conditions[0..length-1], not_word_destructive_condition]
45
+ eligible_words = restricting_word_conditions.inject(valid_words_by_length(length) - already_used_words) do |word_list, restricting_condition|
46
+ word_list.select(&restricting_condition)
47
+ end
48
+ if word = eligible_words.sample
49
+ raise "Could not add selected #{word} to crossword:\n#{crossword}" unless crossword.add(word, word_position, direction)
50
+ return word
51
+ end
52
+ end
53
+ nil
54
+ end
55
+
56
+ def adyacent_words_to_position(position, direction)
57
+ return nil if crossword.word_at(position, direction)
58
+ [crossword.word_at(position.merge(direction => position[direction]-1), direction), crossword.word_at(position.merge(direction => position[direction]+1), direction)]
59
+ end
60
+
61
+ def transversal_conditions_for(position, direction)
62
+ (crossword.dimensions[direction] - position[direction]).times.collect do |word_index|
63
+ adyacent_words = adyacent_words_to_position(position.merge(direction => position[direction]+word_index), direction_other_than(direction))
64
+ lambda do |word|
65
+ adyacent_words.nil? || valid_word?("#{adyacent_words[0]}#{word[word_index]}#{adyacent_words[1]}")
66
+ end
67
+ end
68
+ end
69
+
70
+ # @todo Remove ?
71
+ #def word_length_condition(length)
72
+ # lambda do |word|
73
+ # word.length == length
74
+ # end
75
+ #end
76
+
77
+ def valid_words_by_length(length)
78
+ @valid_words_by_length ||={}
79
+ @valid_words_by_length[length] ||= valid_words.select{|word| word.length == length}
80
+ end
81
+
82
+ # @todo rename to not_word_invalidating_condition ?
83
+ def not_word_destructive_condition(coordinates, direction)
84
+ other_direction = direction_other_than(direction)
85
+ line = crossword.line(coordinates[other_direction], direction)
86
+ lambda do |word|
87
+ words_at_line = line.dup.tap{|line| line[coordinates[direction]..coordinates[direction]+word.length-1]=word}.split(crossword_class::BLACK)
88
+ words_at_line.detect{|word| valid_word?(word)==false}.nil?
89
+ end
90
+ end
91
+
92
+ def can_be_set_condition(position, direction)
93
+ other_direction = direction_other_than(direction)
94
+ line = crossword.line(position[other_direction], direction)
95
+ regexp_str = line[position[direction]..line.length].gsub(crossword_class::BLACK, '.')
96
+ length_regexps = regexp_str.length.times.collect {|t| Regexp.new(regexp_str[0..t])}
97
+ lambda do |word|
98
+ if word.length > length_regexps.length
99
+ false
100
+ else
101
+ word.empty? ? true : length_regexps[word.length - 1].match(word)
102
+ end
103
+ end
104
+ end
105
+
106
+ def used_words
107
+ crossword.words.reject{|word| word.length < NOT_REPEATABLE_WORD_MINIMUM_LENGTH}
108
+ end
109
+
110
+ end
@@ -0,0 +1,51 @@
1
+ class Crucigrama::GrillBuilder < Crucigrama::CrosswordBuilder
2
+
3
+ attr_reader :grill_spacing
4
+
5
+ def initialize(opts = {})
6
+ @grill_spacing = opts.delete(:grill_spacing) || 2
7
+ super(opts)
8
+ end
9
+
10
+ def build
11
+ grill_spacing.times do |i|
12
+ (i..crossword.dimensions.values.max-1).step(grill_spacing).each do |coordinate|
13
+ fill_line(coordinate,:horizontal) if crossword.dimensions[:horizontal] > coordinate
14
+ fill_line(coordinate,:vertical) if crossword.dimensions[:vertical] > coordinate
15
+ end
16
+ end
17
+ crossword
18
+ end
19
+
20
+ private
21
+
22
+ def fill_line(index, direction)
23
+ result = []
24
+ other_direction = direction_other_than(direction)
25
+ positions_to_try = crossword.black_positions.select{|position| position[other_direction]==index}
26
+ tried_positions = []
27
+ until positions_to_try.empty? do
28
+ position = positions_to_try.first
29
+ word_position = position_to_start_word(position, direction)
30
+ result << set_longest_word_at(word_position, direction)
31
+ tried_positions << position
32
+ positions_to_try = crossword.black_positions.select{|position| position[other_direction]==index} - tried_positions
33
+ end
34
+ result.compact
35
+ end
36
+
37
+ # @todo Remove first previous_position declaration?
38
+ def position_to_start_word(position, direction)
39
+ return position if position[direction] == 0
40
+ if (position_word = crossword.word_at(position, direction)).nil?
41
+ previous_position = position.merge(direction => position[direction]-1)
42
+ unless position_word = crossword.word_at(previous_position, direction)
43
+ return position
44
+ end
45
+ end
46
+ other_direction = direction_other_than(direction)
47
+ crossword.word_positions[position_word][direction].select do |word_position|
48
+ position[other_direction] == word_position[other_direction] and word_position[direction] <= position[direction]
49
+ end.last
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ # This module defines some methods of use to interact with {Crucigrama::Crossword}s
2
+ module Crucigrama::Positionable
3
+
4
+ # @return [Hash<Symbol,Integer>] a hash with the horizontal and vertical coordinates
5
+ # @param [Integer] x the horizontal coordinate
6
+ # @param [Integer] y the vertical coordinate
7
+ def position(x,y)
8
+ {:horizontal => x, :vertical => y}
9
+ end
10
+
11
+ # @return [Symbol, nil] the direction opposite to the one given,
12
+ # that is, :horizontal for :vertical and :vertical for :horizontal;
13
+ # or nil if neither :horizontal nor :vertical is provided as direction
14
+ # @param [:horizontal, :vertical, nil] direction the direction whose
15
+ # opposite is queried
16
+ def direction_other_than(direction)
17
+ { :horizontal => :vertical, :vertical => :horizontal}[direction]
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+
2
+ # The namespace for the crucigrama library, a ruby library for the generation of crosswords
3
+ module Crucigrama
4
+
5
+ # The version for the Crucigrama library, according to RubyGems Rational Versioning Policy
6
+ VERSION = File.read(File.join(File.dirname(__FILE__), '../../VERSION'))
7
+
8
+ # The major version number for the Crucigrama library, according to RubyGems Rational Versioning Policy
9
+ MAJOR,
10
+ # The minor version number for the Crucigrama library, according to RubyGems Rational Versioning Policy
11
+ MINOR,
12
+ # The patch version number for the Crucigrama library, according to RubyGems Rational Versioning Policy
13
+ PATCH = * VERSION.split('.')
14
+
15
+ end
data/lib/crucigrama.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'crucigrama/version'
2
+ require 'crucigrama/positionable'
3
+ require 'crucigrama/crossword'
4
+ require 'crucigrama/crossword_builder'
5
+ require 'crucigrama/grill_builder'
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Crucigrama" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ if ENV["COVERAGE"]
5
+ require 'simplecov'
6
+ SimpleCov.start do
7
+ add_filter '/spec/'
8
+ add_filter '/config/'
9
+ add_filter '/lib/'
10
+ add_filter '/vendor/'
11
+
12
+ add_group 'Controllers', 'app/controllers'
13
+ add_group 'Models', 'app/models'
14
+ add_group 'Helpers', 'app/helpers'
15
+ add_group 'Mailers', 'app/mailers'
16
+ add_group 'Views', 'app/views'
17
+ end
18
+ end
19
+
20
+ require 'rspec'
21
+ require 'crucigrama'
22
+
23
+ # Requires supporting files with custom matchers and macros, etc,
24
+ # in ./support/ and its subdirectories.
25
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
26
+
27
+ RSpec.configure do |config|
28
+
29
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crucigrama
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - "Pablo Ba\xC3\xB1os L\xC3\xB3pez"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-04-21 00:00:00 +02:00
14
+ default_executable: crucigrama
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 3.1.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_json
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.3
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.3.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: yard
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 0.6.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: cucumber
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: bundler
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 1.0.0
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: jeweler
84
+ requirement: &id007 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.6.4
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: simplecov
95
+ requirement: &id008 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: *id008
104
+ description: "= crucigrama\n\n\
105
+ Crucigrama is a library for the generation of crosswords. The gem includes as well a simple command line tool that, making use of the\n\
106
+ library, can generate and print crosswords.\n\n\
107
+ == Contributing to crucigrama\n \n\
108
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet\n\
109
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it\n\
110
+ * Fork the project\n\
111
+ * Start a feature/bugfix branch\n\
112
+ * Commit and push until you are happy with your contribution\n\
113
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.\n\
114
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.\n\n\
115
+ == Copyright\n\n\
116
+ Copyright (c) 2011-2012 Pablo Ba\xC3\xB1os L\xC3\xB3pez\n\n\
117
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \xE2\x80\x9CSoftware\xE2\x80\x9D), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n\
118
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n\
119
+ THE SOFTWARE IS PROVIDED \xE2\x80\x9CAS IS\xE2\x80\x9D, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
120
+ email: pbl1986@gmail.com
121
+ executables:
122
+ - crucigrama
123
+ extensions: []
124
+
125
+ extra_rdoc_files:
126
+ - LICENSE.txt
127
+ - README.rdoc
128
+ files:
129
+ - Gemfile
130
+ - Gemfile.lock
131
+ - LICENSE.txt
132
+ - README.rdoc
133
+ - Rakefile
134
+ - VERSION
135
+ - bin/crucigrama
136
+ - examples/crosswords/01.json
137
+ - examples/crosswords/02.json
138
+ - examples/crosswords/03.json
139
+ - examples/lemaries/es-1.txt
140
+ - features/crucigrama.feature
141
+ - features/step_definitions/crucigrama_steps.rb
142
+ - features/support/env.rb
143
+ - lib/crucigrama.rb
144
+ - lib/crucigrama/cli.rb
145
+ - lib/crucigrama/cli/generate.rb
146
+ - lib/crucigrama/cli/print.rb
147
+ - lib/crucigrama/crossword.rb
148
+ - lib/crucigrama/crossword/definitions.rb
149
+ - lib/crucigrama/crossword/grid.rb
150
+ - lib/crucigrama/crossword/line_query.rb
151
+ - lib/crucigrama/crossword/pdf_printable.rb
152
+ - lib/crucigrama/crossword/serializable.rb
153
+ - lib/crucigrama/crossword/word_query.rb
154
+ - lib/crucigrama/crossword_builder.rb
155
+ - lib/crucigrama/grill_builder.rb
156
+ - lib/crucigrama/positionable.rb
157
+ - lib/crucigrama/version.rb
158
+ - spec/crucigrama_spec.rb
159
+ - spec/spec_helper.rb
160
+ has_rdoc: true
161
+ homepage: http://github.com/pbanos/crucigrama
162
+ licenses:
163
+ - MIT
164
+ post_install_message:
165
+ rdoc_options: []
166
+
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ hash: -2989028535230256657
175
+ segments:
176
+ - 0
177
+ version: "0"
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ none: false
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: "0"
184
+ requirements: []
185
+
186
+ rubyforge_project:
187
+ rubygems_version: 1.6.2
188
+ signing_key:
189
+ specification_version: 3
190
+ summary: Ruby library for the generation of crosswords
191
+ test_files: []
192
+