word_search 0.5.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +8 -5
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +3 -2
  6. data/Gemfile +2 -1
  7. data/README.md +26 -4
  8. data/Rakefile +26 -12
  9. data/bin/console +5 -4
  10. data/bin/test +19 -0
  11. data/docs/Gemfile +8 -7
  12. data/docs/Gemfile.lock +31 -31
  13. data/docs/Rakefile +2 -1
  14. data/docs/config.rb +4 -3
  15. data/docs/source/images/logo.png +0 -0
  16. data/docs/source/images/search.png +0 -0
  17. data/docs/source/index.html.haml +49 -7
  18. data/docs/source/layouts/layout.html.haml +7 -9
  19. data/docs/source/stylesheets/site.css.sass +83 -0
  20. data/docs/vendor/cache/activesupport-5.0.1.gem +0 -0
  21. data/docs/vendor/cache/addressable-2.5.0.gem +0 -0
  22. data/docs/vendor/cache/backports-3.6.8.gem +0 -0
  23. data/docs/vendor/cache/coffee-script-2.4.1.gem +0 -0
  24. data/docs/vendor/cache/coffee-script-source-1.12.2.gem +0 -0
  25. data/docs/vendor/cache/compass-import-once-1.0.5.gem +0 -0
  26. data/docs/vendor/cache/concurrent-ruby-1.0.4.gem +0 -0
  27. data/docs/vendor/cache/contracts-0.13.0.gem +0 -0
  28. data/docs/vendor/cache/dotenv-2.1.1.gem +0 -0
  29. data/docs/vendor/cache/em-websocket-0.5.1.gem +0 -0
  30. data/docs/vendor/cache/erubis-2.7.0.gem +0 -0
  31. data/docs/vendor/cache/eventmachine-1.2.1.gem +0 -0
  32. data/docs/vendor/cache/execjs-2.7.0.gem +0 -0
  33. data/docs/vendor/cache/fast_blank-1.0.0.gem +0 -0
  34. data/docs/vendor/cache/fastimage-2.0.1.gem +0 -0
  35. data/docs/vendor/cache/ffi-1.9.14.gem +0 -0
  36. data/docs/vendor/cache/haml-4.0.7.gem +0 -0
  37. data/docs/vendor/cache/hamster-3.0.0.gem +0 -0
  38. data/docs/vendor/cache/hashie-3.4.6.gem +0 -0
  39. data/docs/vendor/cache/http_parser.rb-0.6.0.gem +0 -0
  40. data/docs/vendor/cache/i18n-0.7.0.gem +0 -0
  41. data/docs/vendor/cache/kramdown-1.13.2.gem +0 -0
  42. data/docs/vendor/cache/listen-3.0.8.gem +0 -0
  43. data/docs/vendor/cache/memoist-0.15.0.gem +0 -0
  44. data/docs/vendor/cache/middleman-4.2.0.gem +0 -0
  45. data/docs/vendor/cache/middleman-cli-4.2.0.gem +0 -0
  46. data/docs/vendor/cache/middleman-core-4.2.0.gem +0 -0
  47. data/docs/vendor/cache/middleman-gh-pages-0.3.1.gem +0 -0
  48. data/docs/vendor/cache/middleman-livereload-3.4.6.gem +0 -0
  49. data/docs/vendor/cache/minitest-5.10.1.gem +0 -0
  50. data/docs/vendor/cache/padrino-helpers-0.13.3.3.gem +0 -0
  51. data/docs/vendor/cache/padrino-support-0.13.3.3.gem +0 -0
  52. data/docs/vendor/cache/parallel-1.10.0.gem +0 -0
  53. data/docs/vendor/cache/public_suffix-2.0.5.gem +0 -0
  54. data/docs/vendor/cache/rack-2.0.1.gem +0 -0
  55. data/docs/vendor/cache/rack-livereload-0.3.16.gem +0 -0
  56. data/docs/vendor/cache/rake-12.0.0.gem +0 -0
  57. data/docs/vendor/cache/rb-fsevent-0.9.8.gem +0 -0
  58. data/docs/vendor/cache/rb-inotify-0.9.7.gem +0 -0
  59. data/docs/vendor/cache/sass-3.4.23.gem +0 -0
  60. data/docs/vendor/cache/servolux-0.12.0.gem +0 -0
  61. data/docs/vendor/cache/thor-0.19.4.gem +0 -0
  62. data/docs/vendor/cache/thread_safe-0.3.5.gem +0 -0
  63. data/docs/vendor/cache/tilt-2.0.5.gem +0 -0
  64. data/docs/vendor/cache/tzinfo-1.2.2.gem +0 -0
  65. data/docs/vendor/cache/uglifier-3.0.4.gem +0 -0
  66. data/lib/word_search.rb +25 -24
  67. data/lib/word_search/catalog.rb +1 -0
  68. data/lib/word_search/generator.rb +9 -3
  69. data/lib/word_search/generator/base.rb +50 -2
  70. data/lib/word_search/plane.rb +1 -0
  71. data/lib/word_search/plane/base.rb +25 -2
  72. data/lib/word_search/position_word/base.rb +32 -3
  73. data/lib/word_search/solver.rb +113 -3
  74. data/lib/word_search/three_dimensional/direction.rb +1 -0
  75. data/lib/word_search/three_dimensional/generator.rb +1 -18
  76. data/lib/word_search/three_dimensional/plane.rb +8 -1
  77. data/lib/word_search/three_dimensional/point.rb +5 -0
  78. data/lib/word_search/three_dimensional/position_word.rb +7 -0
  79. data/lib/word_search/two_dimensional/direction.rb +1 -0
  80. data/lib/word_search/two_dimensional/generator.rb +1 -18
  81. data/lib/word_search/two_dimensional/plane.rb +6 -1
  82. data/lib/word_search/two_dimensional/point.rb +5 -0
  83. data/lib/word_search/two_dimensional/position_word.rb +7 -0
  84. data/lib/word_search/version.rb +2 -1
  85. data/lib/word_search/word_bank.rb +4 -3
  86. data/vendor/cache/activemodel-5.0.1.gem +0 -0
  87. data/vendor/cache/activesupport-5.0.1.gem +0 -0
  88. data/vendor/cache/codeclimate-test-reporter-1.0.4.gem +0 -0
  89. data/vendor/cache/concurrent-ruby-1.0.4.gem +0 -0
  90. data/vendor/cache/metaclass-0.0.4.gem +0 -0
  91. data/vendor/cache/minitest-5.10.1.gem +0 -0
  92. data/vendor/cache/mocha-1.2.1.gem +0 -0
  93. data/vendor/cache/parser-2.3.3.1.gem +0 -0
  94. data/vendor/cache/rainbow-2.2.1.gem +0 -0
  95. data/vendor/cache/rake-12.0.0.gem +0 -0
  96. data/vendor/cache/rspec-core-3.5.4.gem +0 -0
  97. data/vendor/cache/rubocop-0.46.0.gem +0 -0
  98. data/vendor/cache/ruby-enum-0.6.0.gem +0 -0
  99. data/vendor/cache/unicode-display_width-1.1.2.gem +0 -0
  100. data/word_search.gemspec +23 -21
  101. metadata +91 -29
  102. data/docs/source/images/github-light.png +0 -0
  103. data/docs/source/images/github.png +0 -0
  104. data/docs/source/stylesheets/site.css.scss +0 -71
  105. data/lib/solution.rb +0 -67
  106. data/vendor/cache/activemodel-5.0.0.1.gem +0 -0
  107. data/vendor/cache/activesupport-5.0.0.1.gem +0 -0
  108. data/vendor/cache/codeclimate-test-reporter-0.6.0.gem +0 -0
  109. data/vendor/cache/concurrent-ruby-1.0.2.gem +0 -0
  110. data/vendor/cache/minitest-5.9.0.gem +0 -0
  111. data/vendor/cache/parser-2.3.1.2.gem +0 -0
  112. data/vendor/cache/rainbow-2.1.0.gem +0 -0
  113. data/vendor/cache/rake-10.5.0.gem +0 -0
  114. data/vendor/cache/rspec-core-3.5.2.gem +0 -0
  115. data/vendor/cache/rubocop-0.42.0.gem +0 -0
  116. data/vendor/cache/ruby-enum-0.5.0.gem +0 -0
  117. data/vendor/cache/unicode-display_width-1.1.0.gem +0 -0
@@ -1,28 +1,57 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  class PositionWord
3
4
  class Base
4
- attr_accessor :plane, :word, :direction, :coordinate
5
+ attr_accessor :plane, :word, :direction, :coordinate, :positions
5
6
 
6
7
  def initialize(plane, word, direction, coordinate)
7
8
  @plane = plane
8
9
  @word = word
9
10
  @direction = direction
10
11
  @coordinate = coordinate
12
+ @positions = {}
11
13
  end
12
14
 
13
15
  def perform
14
16
  return false unless valid?
15
17
 
16
- word.split('').each do |letter|
18
+ letters.each_with_index do |letter, index|
17
19
  place_letter(letter)
20
+ document_position(letter, index)
18
21
  update_coordinates
19
22
  end
23
+
24
+ positions
20
25
  end
21
26
 
22
27
  def valid?
23
28
  valid_coordinates? && valid_letters?
24
29
  end
25
30
 
31
+ def place_letter
32
+ raise NotImplementedError
33
+ end
34
+
35
+ def update_coordinates
36
+ raise NotImplementedError
37
+ end
38
+
39
+ def positive_last_coordinates?
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def last_coordinates_in_plane?
44
+ raise NotImplementedError
45
+ end
46
+
47
+ def letter_at
48
+ raise NotImplementedError
49
+ end
50
+
51
+ def document_position
52
+ raise NotImplementedError
53
+ end
54
+
26
55
  private
27
56
 
28
57
  def last_x
@@ -79,7 +108,7 @@ module WordSearch
79
108
  end
80
109
 
81
110
  def letters
82
- @letters ||= word.split('')
111
+ @letters ||= word.split("")
83
112
  end
84
113
 
85
114
  def x_at(position)
@@ -1,17 +1,127 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  class Solver
3
- attr_accessor :script, :word_bank, :plane
4
+ attr_accessor :script, :word_bank, :plane, :failed
4
5
 
5
6
  def initialize(script, word_list_file, plane_file)
6
7
  @script = script
7
8
  @word_bank = WordBank.new(word_list_file)
8
9
  @plane = Plane.make_from_file(plane_file)
10
+ @failed = false
9
11
  end
10
12
 
11
- def execute
13
+ def perform
14
+ master_solutions # load master solutions so it doesn't effect benchmark
15
+ bm = benchmark_solution
16
+
17
+ return bm if !failed && solved?
18
+
19
+ "Word Search incorrectly solved"
20
+ end
21
+
22
+ def master_solutions
23
+ @master_solutions ||=
24
+ import_solutions(
25
+ if File.exist?("solution_#{plane.digest}")
26
+ File.read("solution_#{plane.digest}")
27
+ else
28
+ generate_master_solution
29
+ end
30
+ )
31
+ end
32
+
33
+ private
34
+
35
+ def benchmark_solution
12
36
  Benchmark.measure do
13
- load(script, true)
37
+ begin
38
+ users_solution
39
+ rescue
40
+ self.failed = true
41
+ end
42
+ end
43
+ end
44
+
45
+ def users_solution
46
+ @users_solution ||=
47
+ import_solutions(File.read(JSON.parse(`ruby #{script}`)))
48
+ end
49
+
50
+ def solved?
51
+ @word_bank.all? { |word| users_solution[word] == master_solutions[word] }
52
+ end
53
+
54
+ def import_solutions(solution_array)
55
+ solution_array.split("---").map do |positions|
56
+ {
57
+ positions.strip.split("\n").map(&:first).join =>
58
+ positions.strip.split("\n").map.with_index do |letter, index|
59
+ [index, JSON.parse(letter[2..-1])]
60
+ end.to_h
61
+ }
62
+ end.reduce({}, :merge)
63
+ end
64
+
65
+ def generate_master_solution
66
+ word_bank.map do |word|
67
+ find_word(word)
68
+ end.join("---")
69
+ end
70
+
71
+ def directions
72
+ @directions ||=
73
+ if plane.two_dimensional?
74
+ WordSearch::TwoDimensional::Direction
75
+ else
76
+ WordSearch::ThreeDimensional::Direction
77
+ end.values
78
+ end
79
+
80
+ def find_word(word)
81
+ string = ""
82
+ plane.catalog[word[0]].find do |point|
83
+ directions.find do |direction|
84
+ next if (spot = find_point(point, word.size - 1, direction)).blank? ||
85
+ not_found?(spot, word, point, direction)
86
+
87
+ string = letter_positions(word, direction, point)
88
+ end
89
+ end
90
+
91
+ string
92
+ end
93
+
94
+ def letter_positions(word, direction, point)
95
+ solution = ""
96
+ word.split("").each_with_index do |letter, index|
97
+ solution += "#{letter} #{point.coordinate}\n"
98
+ next if index == (word.length - 1)
99
+
100
+ point = plane.find_next_point(point, direction)
101
+ end
102
+
103
+ solution
104
+ end
105
+
106
+ def not_found?(spot, word, point, direction)
107
+ !(spot.letter == word[-1] && double_check(word, point, direction))
108
+ end
109
+
110
+ def find_point(point, move, direction)
111
+ plane.dig(
112
+ point.x + (move * direction[0]),
113
+ point.y + (move * direction[1]),
114
+ )
115
+ end
116
+
117
+ def double_check(word, point, direction)
118
+ matching = true
119
+
120
+ (word.length - 2).times do |i|
121
+ matching &&= find_point(point, (1 + i), direction).letter == word[1 + i]
14
122
  end
123
+
124
+ matching
15
125
  end
16
126
  end
17
127
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module ThreeDimensional
3
4
  class Direction
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module ThreeDimensional
3
4
  class Generator < Generator::Base
@@ -9,24 +10,6 @@ module WordSearch
9
10
 
10
11
  private
11
12
 
12
- def place_word(word)
13
- placed = false
14
- until placed || used_coordinates.uniq.count == plane.total_points
15
- placed = position_word(word).present?
16
- end
17
-
18
- word_bank.errors.add(word, 'cannot be placed') if placed.blank?
19
- placed
20
- end
21
-
22
- def position_word(word)
23
- used_coordinates << (coordinate = random_point)
24
-
25
- directions.find do |direction|
26
- PositionWord.new(plane, word, direction, coordinate).perform
27
- end
28
- end
29
-
30
13
  def random_point
31
14
  Point.new(random(plane.x), random(plane.y), random(plane.z))
32
15
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module ThreeDimensional
3
4
  class Plane < Plane::Base
@@ -53,6 +54,12 @@ module WordSearch
53
54
  @directions ||= WordSearch::ThreeDimensional::Direction
54
55
  end
55
56
 
57
+ def find_next_point(point, direction)
58
+ dig(
59
+ point.x + direction[0], point.y + direction[1], point.z + direction[2]
60
+ )
61
+ end
62
+
56
63
  class << self
57
64
  def make_from_file(file)
58
65
  string = File.read(file).split("\n\n").map(&:split)
@@ -74,7 +81,7 @@ module WordSearch
74
81
 
75
82
  string.each_with_index do |slice, z|
76
83
  slice.reverse.each_with_index do |row, y|
77
- row.split('').each_with_index do |letter, x|
84
+ row.split("").each_with_index do |letter, x|
78
85
  plane[x][y][z] = Point.new(x, y, z, letter)
79
86
  add_to_catalog(plane, plane[x][y][z])
80
87
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module ThreeDimensional
3
4
  class Point
@@ -10,6 +11,10 @@ module WordSearch
10
11
  @z = z
11
12
  @letter = letter if letter
12
13
  end
14
+
15
+ def coordinate
16
+ [x, y, z]
17
+ end
13
18
  end
14
19
  end
15
20
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module ThreeDimensional
3
4
  class PositionWord < PositionWord::Base
@@ -24,6 +25,12 @@ module WordSearch
24
25
  def letter_at(position)
25
26
  plane.dig(x_at(position), y_at(position), z_at(position)).letter
26
27
  end
28
+
29
+ def document_position(letter, index)
30
+ positions.merge!("#{letter}-#{index}" => {
31
+ x: coordinate.x, y: coordinate.y, z: coordinate.z
32
+ })
33
+ end
27
34
  end
28
35
  end
29
36
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module TwoDimensional
3
4
  class Direction
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module TwoDimensional
3
4
  class Generator < Generator::Base
@@ -9,24 +10,6 @@ module WordSearch
9
10
 
10
11
  private
11
12
 
12
- def place_word(word)
13
- placed = false
14
- until placed || used_coordinates.uniq.count == plane.total_points
15
- placed = position_word(word).present?
16
- end
17
-
18
- word_bank.errors.add(word, 'cannot be placed') if placed.blank?
19
- placed
20
- end
21
-
22
- def position_word(word)
23
- used_coordinates << (coordinate = random_point)
24
-
25
- directions.find do |direction|
26
- PositionWord.new(plane, word, direction, coordinate).perform
27
- end
28
- end
29
-
30
13
  def random_point
31
14
  Point.new(random(plane.x), random(plane.y))
32
15
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module TwoDimensional
3
4
  class Plane < Plane::Base
@@ -34,6 +35,10 @@ module WordSearch
34
35
  @directions ||= WordSearch::TwoDimensional::Direction
35
36
  end
36
37
 
38
+ def find_next_point(point, direction)
39
+ dig(point.x + direction[0], point.y + direction[1])
40
+ end
41
+
37
42
  class << self
38
43
  def make_from_file(file)
39
44
  string = File.read(file).split("\n").reverse
@@ -50,7 +55,7 @@ module WordSearch
50
55
  plane = new(x_len.first, string.count)
51
56
 
52
57
  string.each_with_index do |row, y|
53
- row.split('').each_with_index do |letter, x|
58
+ row.split("").each_with_index do |letter, x|
54
59
  add_to_catalog(plane, plane[x][y] = Point.new(x, y, letter))
55
60
  end
56
61
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module TwoDimensional
3
4
  class Point
@@ -9,6 +10,10 @@ module WordSearch
9
10
  @y = y
10
11
  @letter = letter if letter
11
12
  end
13
+
14
+ def coordinate
15
+ [x, y]
16
+ end
12
17
  end
13
18
  end
14
19
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  module TwoDimensional
3
4
  class PositionWord < PositionWord::Base
@@ -23,6 +24,12 @@ module WordSearch
23
24
  def letter_at(position)
24
25
  plane.dig(x_at(position), y_at(position)).letter
25
26
  end
27
+
28
+ def document_position(letter, index)
29
+ positions.merge!("#{letter}-#{index}" => {
30
+ x: coordinate.x, y: coordinate.y
31
+ })
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
- VERSION = '0.5.1'
3
+ VERSION = "1.0.0"
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module WordSearch
2
3
  class WordBank < Array
3
4
  include ActiveModel::Validations
@@ -30,15 +31,15 @@ module WordSearch
30
31
  private
31
32
 
32
33
  def word_bank
33
- errors.add(:base, 'Word bank cannot be empty') if blank?
34
+ errors.add(:base, "Word bank cannot be empty") if blank?
34
35
  end
35
36
 
36
37
  def valid_file?(file)
37
- File.file?(file) && File.extname(file) == '.csv'
38
+ File.file?(file) && File.extname(file) == ".csv"
38
39
  end
39
40
 
40
41
  def invalid_file
41
- errors.add(:file, 'is invalid')
42
+ errors.add(:file, "is invalid")
42
43
  false
43
44
  end
44
45
  end
Binary file
Binary file
Binary file
Binary file
Binary file