Class: Crucigrama::CrosswordBuilder

Inherits:
Object
  • Object
show all
Includes:
Positionable
Defined in:
lib/crucigrama/crossword_builder.rb

Overview

TODO:

document everything!

Direct Known Subclasses

GrillBuilder

Constant Summary collapse

NOT_REPEATABLE_WORD_MINIMUM_LENGTH =
3

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Positionable

#direction_other_than, #position

Constructor Details

#initialize(opts = {}) ⇒ CrosswordBuilder

Returns a new instance of CrosswordBuilder.



9
10
11
12
13
# File 'lib/crucigrama/crossword_builder.rb', line 9

def initialize(opts = {})
  @valid_words = opts[:valid_words] || []
  @crossword_class = opts[:crossword_class] || Crucigrama::Crossword
  @crossword = @crossword_class.new(opts[:dimensions]||{})
end

Instance Attribute Details

#crosswordObject (readonly)

Returns the value of attribute crossword.



8
9
10
# File 'lib/crucigrama/crossword_builder.rb', line 8

def crossword
  @crossword
end

#crossword_classObject (readonly)

Returns the value of attribute crossword_class.



8
9
10
# File 'lib/crucigrama/crossword_builder.rb', line 8

def crossword_class
  @crossword_class
end

#valid_wordsObject (readonly)

Returns the value of attribute valid_words.



8
9
10
# File 'lib/crucigrama/crossword_builder.rb', line 8

def valid_words
  @valid_words
end

Class Method Details

.build_crossword(opts = {}) ⇒ Object



16
17
18
# File 'lib/crucigrama/crossword_builder.rb', line 16

def self.build_crossword(opts = {})
  self.new(opts).build
end

Instance Method Details

#adyacent_words_to_position(position, direction) ⇒ Object



56
57
58
59
# File 'lib/crucigrama/crossword_builder.rb', line 56

def adyacent_words_to_position(position, direction)
  return nil if crossword.word_at(position, direction)
  [crossword.word_at(position.merge(direction => position[direction]-1), direction), crossword.word_at(position.merge(direction => position[direction]+1), direction)]
end

#buildObject



20
21
22
23
24
25
26
# File 'lib/crucigrama/crossword_builder.rb', line 20

def build
  crossword.black_positions.each do |position|
    set_longest_word_at(position,:horizontal) unless crossword.word_at(position, :horizontal).to_s.length > 1
    set_longest_word_at(position,:vertical) unless crossword.word_at(position, :vertical).to_s.length > 1
  end
  crossword
end

#can_be_set_condition(position, direction) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/crucigrama/crossword_builder.rb', line 92

def can_be_set_condition(position, direction)
  other_direction = direction_other_than(direction)
  line = crossword.line(position[other_direction], direction)
  regexp_str = line[position[direction]..line.length].gsub(crossword_class::BLACK, '.')
  length_regexps = regexp_str.length.times.collect {|t| Regexp.new(regexp_str[0..t])}
  lambda do |word|
    if word.length > length_regexps.length
      false
    else
      word.empty? ? true : length_regexps[word.length - 1].match(word)
    end
  end
end

#not_word_destructive_condition(coordinates, direction) ⇒ Object

TODO:

rename to not_word_invalidating_condition ?



83
84
85
86
87
88
89
90
# File 'lib/crucigrama/crossword_builder.rb', line 83

def not_word_destructive_condition(coordinates, direction)
  other_direction = direction_other_than(direction)
  line = crossword.line(coordinates[other_direction], direction)
  lambda do |word|
    words_at_line = line.dup.tap{|line| line[coordinates[direction]..coordinates[direction]+word.length-1]=word}.split(crossword_class::BLACK)
    words_at_line.detect{|word| valid_word?(word)==false}.nil?
  end
end

#set_longest_word_at(word_position, direction) ⇒ Object

TODO:

return both the word and the position it was placed at ?



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/crucigrama/crossword_builder.rb', line 38

def set_longest_word_at(word_position, direction)
  transversal_conditions = transversal_conditions_for(word_position, direction)
  not_word_destructive_condition = not_word_destructive_condition(word_position, direction)
  can_be_set_condition = can_be_set_condition(word_position, direction)
  already_used_words = used_words
  (2..crossword.dimensions[direction]-word_position[direction]).to_a.reverse.each do |length|
    restricting_word_conditions = [can_be_set_condition, *transversal_conditions[0..length-1], not_word_destructive_condition]
    eligible_words = restricting_word_conditions.inject(valid_words_by_length(length) - already_used_words) do |word_list, restricting_condition|
      word_list.select(&restricting_condition)
    end
    if word = eligible_words.sample
      raise "Could not add selected #{word} to crossword:\n#{crossword}" unless crossword.add(word, word_position, direction)
      return word
    end
  end
  nil
end

#transversal_conditions_for(position, direction) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/crucigrama/crossword_builder.rb', line 61

def transversal_conditions_for(position, direction)
  (crossword.dimensions[direction] - position[direction]).times.collect do |word_index|
    adyacent_words = adyacent_words_to_position(position.merge(direction => position[direction]+word_index), direction_other_than(direction))
    lambda do |word|
      adyacent_words.nil? || valid_word?("#{adyacent_words[0]}#{word[word_index]}#{adyacent_words[1]}")
    end
  end
end

#used_wordsObject



106
107
108
# File 'lib/crucigrama/crossword_builder.rb', line 106

def used_words
  crossword.words.reject{|word| word.length < NOT_REPEATABLE_WORD_MINIMUM_LENGTH}
end

#valid_word?(word) ⇒ Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/crucigrama/crossword_builder.rb', line 28

def valid_word?(word)
  word == crossword_class::BLACK || word.length == 1 || valid_words.include?(word)
end

#valid_words_by_length(length) ⇒ Object

TODO:

Remove ?

def word_length_condition(length)

lambda do |word|
  word.length == length
end

end



77
78
79
80
# File 'lib/crucigrama/crossword_builder.rb', line 77

def valid_words_by_length(length)
  @valid_words_by_length ||={}
  @valid_words_by_length[length] ||= valid_words.select{|word| word.length == length}
end

#validateObject

TODO:

remove method ?



33
34
35
# File 'lib/crucigrama/crossword_builder.rb', line 33

def validate
  crossword.words.collect{|word| valid_word?(word)}.include?(false) ? false : true
end