word_search 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +29 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1166 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/Rakefile +14 -0
- data/bin/console +8 -0
- data/bin/coverage +1 -0
- data/bin/setup +6 -0
- data/lib/word_search/generator/base.rb +28 -0
- data/lib/word_search/generator.rb +60 -0
- data/lib/word_search/plane/base.rb +52 -0
- data/lib/word_search/plane.rb +22 -0
- data/lib/word_search/position_word/base.rb +98 -0
- data/lib/word_search/solver.rb +15 -0
- data/lib/word_search/three_dimensional/direction.rb +37 -0
- data/lib/word_search/three_dimensional/generator.rb +35 -0
- data/lib/word_search/three_dimensional/plane.rb +89 -0
- data/lib/word_search/three_dimensional/point.rb +15 -0
- data/lib/word_search/three_dimensional/position_word.rb +29 -0
- data/lib/word_search/two_dimensional/direction.rb +17 -0
- data/lib/word_search/two_dimensional/generator.rb +35 -0
- data/lib/word_search/two_dimensional/plane.rb +57 -0
- data/lib/word_search/two_dimensional/point.rb +14 -0
- data/lib/word_search/two_dimensional/position_word.rb +28 -0
- data/lib/word_search/version.rb +3 -0
- data/lib/word_search/word_bank.rb +45 -0
- data/lib/word_search.rb +30 -0
- data/vendor/cache/activemodel-5.0.0.1.gem +0 -0
- data/vendor/cache/activesupport-5.0.0.1.gem +0 -0
- data/vendor/cache/ast-2.2.0.gem +0 -0
- data/vendor/cache/codeclimate-test-reporter-0.6.0.gem +0 -0
- data/vendor/cache/coderay-1.1.1.gem +0 -0
- data/vendor/cache/concurrent-ruby-1.0.2.gem +0 -0
- data/vendor/cache/diff-lcs-1.2.5.gem +0 -0
- data/vendor/cache/docile-1.1.5.gem +0 -0
- data/vendor/cache/i18n-0.7.0.gem +0 -0
- data/vendor/cache/json-2.0.2.gem +0 -0
- data/vendor/cache/method_source-0.8.2.gem +0 -0
- data/vendor/cache/minitest-5.9.0.gem +0 -0
- data/vendor/cache/parser-2.3.1.2.gem +0 -0
- data/vendor/cache/powerpack-0.1.1.gem +0 -0
- data/vendor/cache/pry-0.10.4.gem +0 -0
- data/vendor/cache/rainbow-2.1.0.gem +0 -0
- data/vendor/cache/rake-10.5.0.gem +0 -0
- data/vendor/cache/rspec-3.5.0.gem +0 -0
- data/vendor/cache/rspec-core-3.5.2.gem +0 -0
- data/vendor/cache/rspec-expectations-3.5.0.gem +0 -0
- data/vendor/cache/rspec-mocks-3.5.0.gem +0 -0
- data/vendor/cache/rspec-support-3.5.0.gem +0 -0
- data/vendor/cache/rubocop-0.42.0.gem +0 -0
- data/vendor/cache/ruby-enum-0.5.0.gem +0 -0
- data/vendor/cache/ruby-progressbar-1.8.1.gem +0 -0
- data/vendor/cache/simplecov-0.12.0.gem +0 -0
- data/vendor/cache/simplecov-html-0.10.0.gem +0 -0
- data/vendor/cache/slop-3.6.0.gem +0 -0
- data/vendor/cache/thread_safe-0.3.5.gem +0 -0
- data/vendor/cache/tzinfo-1.2.2.gem +0 -0
- data/vendor/cache/unicode-display_width-1.0.5.gem +0 -0
- data/word_search.gemspec +35 -0
- metadata +248 -0
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.0
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 npezza93
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# ![Word Search](https://raw.githubusercontent.com/google/material-design-icons/master/action/drawable-xxxhdpi/ic_search_black_18dp.png)Word Search Generator and Solver
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/npezza93/word_search.svg?branch=master)](https://travis-ci.org/npezza93/word_search)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/npezza93/word_search/badges/gpa.svg)](https://codeclimate.com/github/npezza93/word_search)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/npezza93/word_search/badges/coverage.svg)](https://codeclimate.com/github/npezza93/word_search/coverage)
|
6
|
+
|
7
|
+
## Install
|
8
|
+
Add WordSearch to your `Gemfile` and `bundle install`:
|
9
|
+
|
10
|
+
`gem 'word_search'`
|
11
|
+
|
12
|
+
Alternatively, you can install the gem from [rubygems.org](https://rubygems.org/):
|
13
|
+
|
14
|
+
`gem install word_search`
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
To create a plane with just random letters in each position:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
❯ plane = WordSearch::Plane.new(5, 5)
|
22
|
+
❯ plane.add_letters
|
23
|
+
# To traverse the cartesian plane:
|
24
|
+
❯ plane[0][3]
|
25
|
+
=> #<WordSearch::TwoDimensional::Point:0x007facf1d8d7e0 @letter="u", @x=0, @y=3>
|
26
|
+
❯ plane.pto_s
|
27
|
+
nvqgy
|
28
|
+
uhsit
|
29
|
+
zqloh
|
30
|
+
muudd
|
31
|
+
himyj
|
32
|
+
|
33
|
+
# To print to a file(without a filename defaults to 'crossword')
|
34
|
+
❯ plane.print(file_name)
|
35
|
+
|
36
|
+
# When printing a 3D crossword there are two spaces between z slices. The top slice is z = 0.
|
37
|
+
❯ plane = WordSearch::Plane.new(3, 3, 2)
|
38
|
+
❯ plane.add_letters
|
39
|
+
❯ plane.pto_s
|
40
|
+
bxv
|
41
|
+
lud
|
42
|
+
agp
|
43
|
+
|
44
|
+
esj
|
45
|
+
era
|
46
|
+
utg
|
47
|
+
```
|
48
|
+
|
49
|
+
To create plane filled with words supplied by a word bank:
|
50
|
+
```ruby
|
51
|
+
❯ generator = WordSearch::Generator.new('words.csv', 5, 5) # or add a z param to get a 3D word search
|
52
|
+
❯ generator.perform
|
53
|
+
❯ generator.word_bank
|
54
|
+
=> ["word", "hello", "bye"]
|
55
|
+
❯ generator.pto_s
|
56
|
+
ghsii
|
57
|
+
eelwt
|
58
|
+
ylcon
|
59
|
+
blarz
|
60
|
+
yoydt
|
61
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
task :console do
|
6
|
+
require 'pry'
|
7
|
+
require 'word_searcher'
|
8
|
+
ARGV.clear
|
9
|
+
Pry.start
|
10
|
+
end
|
11
|
+
|
12
|
+
RuboCop::RakeTask.new
|
13
|
+
RSpec::Core::RakeTask.new(:spec)
|
14
|
+
task default: [:spec, :rubocop]
|
data/bin/console
ADDED
data/bin/coverage
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
open coverage/index.html
|
data/bin/setup
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module WordSearch
|
2
|
+
class Generator
|
3
|
+
class Base
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
attr_accessor :plane, :word_bank, :used_coordinates
|
7
|
+
delegate :to_s, :pto_s, :print, to: :plane
|
8
|
+
|
9
|
+
def initialize(plane, word_bank)
|
10
|
+
@plane = plane
|
11
|
+
@word_bank = word_bank
|
12
|
+
@used_coordinates = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform
|
16
|
+
word_bank.each do |word|
|
17
|
+
place_word(word)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def random(number)
|
24
|
+
SecureRandom.random_number number
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module WordSearch
|
2
|
+
class Generator < SimpleDelegator
|
3
|
+
include ActiveModel::Validations
|
4
|
+
|
5
|
+
validate :can_words_fit?
|
6
|
+
validate :valid_plane?
|
7
|
+
validate :valid_word_bank?
|
8
|
+
|
9
|
+
def initialize(file, x, y, z = nil)
|
10
|
+
plane = Plane.new(x, y, z)
|
11
|
+
obj =
|
12
|
+
if z.present?
|
13
|
+
ThreeDimensional::Generator.new(plane, WordBank.new(file))
|
14
|
+
else
|
15
|
+
TwoDimensional::Generator.new(plane, WordBank.new(file))
|
16
|
+
end
|
17
|
+
|
18
|
+
super obj
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform
|
22
|
+
super
|
23
|
+
|
24
|
+
if valid?
|
25
|
+
plane.add_letters
|
26
|
+
plane
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def object
|
33
|
+
__getobj__
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def can_words_fit?
|
39
|
+
errors.add(:base, words_too_long) if plane.max < word_bank.longest_length
|
40
|
+
end
|
41
|
+
|
42
|
+
def words_too_long
|
43
|
+
"#{word_bank.longest_words.join(' and ')} "\
|
44
|
+
"#{'is'.pluralize(word_bank.longest_words.count)} "\
|
45
|
+
'too long for the word search'
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid_plane?
|
49
|
+
plane.errors.full_messages.each do |msg|
|
50
|
+
errors.add(:base, msg)
|
51
|
+
end unless plane.valid?
|
52
|
+
end
|
53
|
+
|
54
|
+
def valid_word_bank?
|
55
|
+
word_bank.errors.full_messages.each do |msg|
|
56
|
+
errors.add(:base, msg)
|
57
|
+
end unless word_bank.valid?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module WordSearch
|
2
|
+
class Plane
|
3
|
+
class Base < Hash
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
attr_accessor :x, :y
|
7
|
+
|
8
|
+
validates :x, :y, numericality: { greater_than_or_equal_to: 2 }
|
9
|
+
|
10
|
+
def print(file_name = nil)
|
11
|
+
file = File.open(file_name || 'crossword', 'w')
|
12
|
+
file.write(to_s)
|
13
|
+
file
|
14
|
+
end
|
15
|
+
|
16
|
+
def random_letter
|
17
|
+
('a'..'z').to_a[SecureRandom.random_number(26)]
|
18
|
+
end
|
19
|
+
|
20
|
+
def two_dimensional?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def three_dimensional?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_letters
|
29
|
+
x.times do |x_point|
|
30
|
+
y.times do |y_point|
|
31
|
+
yield(x_point, y_point)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pto_s
|
37
|
+
puts to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def initialize_plane
|
43
|
+
x.times do |x_point|
|
44
|
+
self[x_point] = {}
|
45
|
+
y.times do |y_point|
|
46
|
+
yield(x_point, y_point)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WordSearch
|
2
|
+
class Plane < SimpleDelegator
|
3
|
+
def initialize(x, y, z = nil)
|
4
|
+
obj =
|
5
|
+
if z.present? && z > 1
|
6
|
+
ThreeDimensional::Plane.new(x, y, z)
|
7
|
+
else
|
8
|
+
TwoDimensional::Plane.new(x, y)
|
9
|
+
end
|
10
|
+
|
11
|
+
super obj
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
if invalid?
|
16
|
+
errors.full_messages.join("\n")
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module WordSearch
|
2
|
+
class PositionWord
|
3
|
+
class Base
|
4
|
+
attr_accessor :plane, :word, :direction, :coordinate
|
5
|
+
|
6
|
+
def initialize(plane, word, direction, coordinate)
|
7
|
+
@plane = plane
|
8
|
+
@word = word
|
9
|
+
@direction = direction
|
10
|
+
@coordinate = coordinate
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform
|
14
|
+
return false unless valid?
|
15
|
+
|
16
|
+
word.split('').each do |letter|
|
17
|
+
place_letter(letter)
|
18
|
+
update_coordinates
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?
|
23
|
+
valid_coordinates? && valid_letters?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def last_x
|
29
|
+
return @last_x if defined? @last_x
|
30
|
+
|
31
|
+
@last_x = coordinate.x
|
32
|
+
(word.length - 1).times do
|
33
|
+
@last_x += direction.x
|
34
|
+
end
|
35
|
+
|
36
|
+
@last_x
|
37
|
+
end
|
38
|
+
|
39
|
+
def last_y
|
40
|
+
return @last_y if defined? @last_y
|
41
|
+
|
42
|
+
@last_y = coordinate.y
|
43
|
+
(word.length - 1).times do
|
44
|
+
@last_y += direction.y
|
45
|
+
end
|
46
|
+
|
47
|
+
@last_y
|
48
|
+
end
|
49
|
+
|
50
|
+
def last_z
|
51
|
+
return @last_z if defined? @last_z
|
52
|
+
|
53
|
+
@last_z = coordinate.z
|
54
|
+
(word.length - 1).times do
|
55
|
+
@last_z += direction.z
|
56
|
+
end
|
57
|
+
|
58
|
+
@last_z
|
59
|
+
end
|
60
|
+
|
61
|
+
def valid_coordinates?
|
62
|
+
positive_last_coordinates? && last_coordinates_in_plane?
|
63
|
+
end
|
64
|
+
|
65
|
+
def valid_letters?
|
66
|
+
bad_overlap, intersection_points = false, 0
|
67
|
+
|
68
|
+
letters.count.times do |position|
|
69
|
+
next if letter_at(position).blank?
|
70
|
+
|
71
|
+
if letters[position] == letter_at(position)
|
72
|
+
intersection_points += 1
|
73
|
+
else
|
74
|
+
bad_overlap = true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
!bad_overlap && intersection_points < 2
|
79
|
+
end
|
80
|
+
|
81
|
+
def letters
|
82
|
+
@letters ||= word.split('')
|
83
|
+
end
|
84
|
+
|
85
|
+
def x_at(position)
|
86
|
+
coordinate.x + position * direction.x
|
87
|
+
end
|
88
|
+
|
89
|
+
def y_at(position)
|
90
|
+
coordinate.y + position * direction.y
|
91
|
+
end
|
92
|
+
|
93
|
+
def z_at(position)
|
94
|
+
coordinate.z + position * direction.z
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module WordSearch
|
2
|
+
class Solver
|
3
|
+
attr_accessor :script, :word_bank, :plane
|
4
|
+
|
5
|
+
def initialize(script, word_list_file, plane_file)
|
6
|
+
@script = script
|
7
|
+
@word_bank = WordBank.new(word_list_file)
|
8
|
+
@plane = TwoDimensional::Plane.make_from_file(plane_file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
load(script, true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module WordSearch
|
2
|
+
module ThreeDimensional
|
3
|
+
class Direction
|
4
|
+
include Ruby::Enum
|
5
|
+
|
6
|
+
# Standard cardinal directions
|
7
|
+
define :N, [0, 1, 0]
|
8
|
+
define :NE, [1, 1, 0]
|
9
|
+
define :E, [1, 0, 0]
|
10
|
+
define :SE, [1, -1, 0]
|
11
|
+
define :S, [0, -1, 0]
|
12
|
+
define :SW, [-1, -1, 0]
|
13
|
+
define :W, [-1, 0, 0]
|
14
|
+
define :NW, [-1, 1, 0]
|
15
|
+
|
16
|
+
# cardinal directions with a positive z index (forward)
|
17
|
+
define :NF, [0, 1, 1]
|
18
|
+
define :NEF, [1, 1, 1]
|
19
|
+
define :EF, [1, 0, 1]
|
20
|
+
define :SEF, [1, -1, 1]
|
21
|
+
define :SF, [0, -1, 1]
|
22
|
+
define :SWF, [-1, -1, 1]
|
23
|
+
define :WF, [-1, 0, 1]
|
24
|
+
define :NWF, [-1, 1, 1]
|
25
|
+
|
26
|
+
# cardinal directions with a negative z index (back)
|
27
|
+
define :NB, [0, 1, -1]
|
28
|
+
define :NEB, [1, 1, -1]
|
29
|
+
define :EB, [1, 0, -1]
|
30
|
+
define :SEB, [1, -1, -1]
|
31
|
+
define :SB, [0, -1, -1]
|
32
|
+
define :SWB, [-1, -1, -1]
|
33
|
+
define :WB, [-1, 0, -1]
|
34
|
+
define :NWB, [-1, 1, -1]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module WordSearch
|
2
|
+
module ThreeDimensional
|
3
|
+
class Generator < Generator::Base
|
4
|
+
def directions
|
5
|
+
Direction.values.shuffle.map do |direction|
|
6
|
+
Point.new(direction.first, direction.second, direction.third)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
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
|
+
def random_point
|
31
|
+
Point.new(random(plane.x), random(plane.y), random(plane.z))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module WordSearch
|
2
|
+
module ThreeDimensional
|
3
|
+
class Plane < Plane::Base
|
4
|
+
attr_accessor :z
|
5
|
+
validates :z, numericality: { greater_than_or_equal_to: 1 }
|
6
|
+
|
7
|
+
def initialize(x, y, z)
|
8
|
+
@x, @y, @z = x, y, z
|
9
|
+
|
10
|
+
initialize_plane do |x_point, y_point|
|
11
|
+
self[x_point][y_point] = {}
|
12
|
+
z.times do |z_point|
|
13
|
+
self[x_point][y_point][z_point] =
|
14
|
+
Point.new(x_point, y_point, z_point)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
(0..(z - 1)).map do |z_slice|
|
21
|
+
values.map do |row|
|
22
|
+
row.values.map { |ys| ys[z_slice] }.map(&:to_s)
|
23
|
+
end.transpose.reverse.map(&:join).join("\n")
|
24
|
+
end.join("\n\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
def two_dimensional?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def three_dimensional?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def total_points
|
36
|
+
x * y * z
|
37
|
+
end
|
38
|
+
|
39
|
+
def max
|
40
|
+
[x, y, z].max
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_letters
|
44
|
+
super do |x_point, y_point|
|
45
|
+
z.times do |z_point|
|
46
|
+
self[x_point][y_point][z_point].letter ||= random_letter
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def make_from_file(file)
|
53
|
+
string = File.read(file).split("\n\n").map(&:split)
|
54
|
+
|
55
|
+
return false unless valid_file?(string)
|
56
|
+
|
57
|
+
make_word_search(string)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def valid_file?(string)
|
63
|
+
string.collect(&:length).uniq.count == 1 &&
|
64
|
+
string.flat_map { |row| row.map(&:length) }.uniq.count == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_word_search(string)
|
68
|
+
plane = empty_3d_plane_from_string(string)
|
69
|
+
|
70
|
+
string.each_with_index do |slice, z|
|
71
|
+
slice.reverse.each_with_index do |row, y|
|
72
|
+
row.split('').each_with_index do |letter, x|
|
73
|
+
plane[x][y][z] = Point.new(x, y, z, letter)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
plane
|
79
|
+
end
|
80
|
+
|
81
|
+
def empty_3d_plane_from_string(string)
|
82
|
+
x = string.flat_map { |row| row.map(&:length) }.uniq.first
|
83
|
+
y = string.collect(&:length).uniq.first
|
84
|
+
new(x, y, string.count)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module WordSearch
|
2
|
+
module ThreeDimensional
|
3
|
+
class PositionWord < PositionWord::Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def place_letter(letter)
|
7
|
+
plane[coordinate.x][coordinate.y][coordinate.z].letter = letter
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_coordinates
|
11
|
+
coordinate.x += direction.x
|
12
|
+
coordinate.y += direction.y
|
13
|
+
coordinate.z += direction.z
|
14
|
+
end
|
15
|
+
|
16
|
+
def positive_last_coordinates?
|
17
|
+
last_x >= 0 && last_y >= 0 && last_z >= 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def last_coordinates_in_plane?
|
21
|
+
last_x < plane.x && last_y < plane.y && last_z < plane.z
|
22
|
+
end
|
23
|
+
|
24
|
+
def letter_at(position)
|
25
|
+
plane.dig(x_at(position), y_at(position), z_at(position)).letter
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module WordSearch
|
2
|
+
module TwoDimensional
|
3
|
+
class Direction
|
4
|
+
include Ruby::Enum
|
5
|
+
|
6
|
+
# Standard cardinal directions
|
7
|
+
define :N, [0, 1]
|
8
|
+
define :NE, [1, 1]
|
9
|
+
define :E, [1, 0]
|
10
|
+
define :SE, [1, -1]
|
11
|
+
define :S, [0, -1]
|
12
|
+
define :SW, [-1, -1]
|
13
|
+
define :W, [-1, 0]
|
14
|
+
define :NW, [-1, 1]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module WordSearch
|
2
|
+
module TwoDimensional
|
3
|
+
class Generator < Generator::Base
|
4
|
+
def directions
|
5
|
+
Direction.values.shuffle.map do |direction|
|
6
|
+
Point.new(direction.first, direction.second)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
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
|
+
def random_point
|
31
|
+
Point.new(random(plane.x), random(plane.y))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|