grid_struct 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8123cc6cecee6f31395c03f4f6bb71d097ac4fd1
4
+ data.tar.gz: 8a620e7b2af1da9daa5bdb595c388c535a14c16d
5
+ SHA512:
6
+ metadata.gz: 64a7448a8fb6b4273d5dcf61d7625f523c5db7b9b97cc26cf66b8580a4df4c2cc2d7a28f4e785c82edd811ed305106be57c2768b1da0a909925dd600dfdae75a
7
+ data.tar.gz: 286fe7c82704872ecc924f2c414f5b0089b607623e4c2fecb79ee200ac547f3f7f7dfd3cde17e5c5755a9f5bd541f66f6df665f12d0b9288cc7fa7b34f811141
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ *.swo
5
+ .bundle
6
+ .config
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grid_struct.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,17 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ # Note: The cmd option is now required due to the increasing number of ways
5
+ # rspec may be run, below are examples of the most common uses.
6
+ # * bundler: 'bundle exec rspec'
7
+ # * bundler binstubs: 'bin/rspec'
8
+ # * spring: 'bin/rsspec' (This will use spring if running and you have
9
+ # installed the spring binstubs per the docs)
10
+ # * zeus: 'zeus rspec' (requires the server to be started separetly)
11
+ # * 'just' rspec: 'rspec'
12
+ guard :rspec, cmd: 'bundle exec rspec' do
13
+ watch(%r{^spec/.+_spec\.rb$})
14
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
15
+ watch('spec/spec_helper.rb') { "spec" }
16
+ end
17
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Samuel Molinari
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # GridStruct
2
+
3
+ Manipulate grid like structure in Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'grid_struct'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install grid_struct
18
+
19
+ ## Usage
20
+
21
+ ### Create a GridStruct
22
+
23
+ In order to create a grid, you have to pass a 2 arguments:
24
+
25
+ - ``rows``: the number of rows your grid has (the height of the grid)
26
+ - ``columns``: the number columns your grid has (the width of the grid)
27
+
28
+ ```ruby
29
+ rows = 9
30
+ columns = 5
31
+
32
+ # Create a grid of size 5x9
33
+ grid = GridStruct.new(rows, columns)
34
+
35
+ grid.size # => 45
36
+ grid.columns # => 5
37
+ grid.rows # => 9
38
+ ```
39
+
40
+ It is possible to initialize the array with pre-set values.
41
+ The 3rd argument must be an array.
42
+
43
+ If we want to initialize the following grid:
44
+
45
+ ```
46
+ +---+---+---+
47
+ | X | O | O |
48
+ +---+---+---+
49
+ | X | x | O |
50
+ +---+---+---+
51
+ | O | X | O |
52
+ +---+---+---+
53
+
54
+ ```
55
+
56
+ Use the following array structure:
57
+
58
+ ```ruby
59
+ # +-----------+-----------+-----------+
60
+ # | ROW 0 | ROW 1 | ROW 2 |
61
+ # +---------+-----------+-----------+-----------+
62
+ # | COLUMNS | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 |
63
+ grid_data = ['X','O','O','X','X','O','O','X','O']
64
+
65
+ tic_tac_toe = GridStruct.new(3, 3, grid_data)
66
+ ```
67
+
68
+ ``GridStruct`` actually store your values extactly the same way, in a 1-dimentional array.
69
+
70
+ ### Basics
71
+
72
+ Now you know how to create grids, it's time to learn how to use our new data structure.
73
+
74
+ #### Read the data store
75
+
76
+ As mentionned above, your data are stored in a 1-dimentional array.
77
+
78
+ ```ruby
79
+ tic_tac_toe.store # => ["X","O","O","X","X","O","O","X","O"]
80
+
81
+ GridStruct.new(9,9).store # => []
82
+ ```
83
+
84
+ As you can see, the ``store`` always starts as an empty array unless you decide to pre-fill the grid.
85
+
86
+ #### Set value
87
+
88
+ To set a value, pass the row and column you want to fill, and a block that will return the value
89
+
90
+ ```
91
+ GridStruct#set(row, column) { value }
92
+ ```
93
+
94
+ ```ruby
95
+ sudoku_grid = GridStruct.new(9,9)
96
+
97
+ sudoku_grid.set(0,0) { 'Hello World' }
98
+ sudoku_grid.store # => ["Hello World"]
99
+
100
+ sudoku_grid.set(1,0) { 'Row: 1, Col: 0' }
101
+ sudoku_grid.store # => ["Hello World",nil,nil,nil,nil,nil,nil,nil,"Row: 1, Col: 0"]
102
+ ```
103
+
104
+ #### Get value
105
+
106
+ To get a value at a specific coordinate, use th ``get`` method.
107
+
108
+ ```
109
+ GridStruct#get(row, column) # => value
110
+ ```
111
+
112
+ ```ruby
113
+ sudoku_grid.get(1,0) # => "Row: 1, Col: 0"
114
+ ```
115
+
116
+ ### Iterate
117
+
118
+ You can iterate through the grid using the each method
119
+
120
+ ```
121
+ GridStruct#each { |value, row, column| # Do something }
122
+ ```
123
+
124
+ ### Mass update
125
+
126
+ #### Grid
127
+
128
+ Incase you need to update each element within the grid, use the ``map!`` method
129
+
130
+ ```
131
+ GridStruct#map! { |value, row, column| # Return new value }
132
+ ```
133
+
134
+ ```ruby
135
+ grid = GridStruct.new(3,3)
136
+ grid.map! { |value, row, column| (row * grid.columns) + column }
137
+
138
+ grid.store # => [0,1,2,3,4,5,6,7,8]
139
+ ```
140
+
141
+ #### Row
142
+
143
+ You can update a specific row if needed, for example, if we want to update the middle row
144
+
145
+ ```
146
+ GridStruct#map_row! { |value, column| # Return new value }
147
+ ```
148
+
149
+ ```
150
+ +---+---+---+
151
+ | 0 | 1 | 2 |
152
+ +---+---+---+
153
+ Update this row → | 3 | 4 | 5 |
154
+ +---+---+---+
155
+ | 6 | 7 | 8 |
156
+ +---+---+---+
157
+ ```
158
+
159
+ ```ruby
160
+ grid.map_row!(1) { |value| value * 10 }
161
+
162
+ grid.store # => [0,1,2,30,40,50,6,7,8]
163
+ ```
164
+
165
+
166
+ #### Column
167
+
168
+ You can update a specific row if needed, for example, if we want to update the middle row
169
+
170
+ ```
171
+ GridStruct#map_column! { |value, row| # Return new value }
172
+ ```
173
+
174
+ ```
175
+ Update this columns
176
+
177
+ +---+---+---+
178
+ | 0 | 1 | 2 |
179
+ +---+---+---+
180
+ | 3 | 4 | 5 |
181
+ +---+---+---+
182
+ | 6 | 7 | 8 |
183
+ +---+---+---+
184
+ ```
185
+
186
+ ```ruby
187
+ grid.map_column!(2) { |value| value * 10 }
188
+
189
+ grid.store # => [0,1,20,3,4,50,6,7,80]
190
+ ```
191
+
192
+ ### Selectors
193
+
194
+ Selector gives you access to line of values within the grid, and allows you to
195
+ only act on that line. Each selector return a (or an array of) GridStruct::Selector.
196
+
197
+ #### Overview
198
+
199
+ A selector has the following instances:
200
+
201
+ - ``grid``: The grid it is selecting from
202
+ - ``indexes``: An array of indexes mapping to the selected values in the grid store
203
+
204
+ ```ruby
205
+ grid = GridStruct.new(3,3)
206
+
207
+ grid.map! do |value, row, column|
208
+ (row * grid.columns + column) * 10
209
+ end
210
+
211
+ grid.row(0) # => #<GridStruct::Selector:0x007fb3d11decf0 @grid=#<GridStruct:0x007fb3d15541f0 @columns=3, @rows=3, @store=[0, 10, 20, 30, 40, 50, 60, 70, 80]>, @indexes=[0, 1, 2]>
212
+ ```
213
+
214
+ You can retrieve and update values using ``[]``. It will map the action to the grid.
215
+
216
+ ```ruby
217
+ first_row = grid.row(0)
218
+
219
+ first_row.to_a # => [0,10,20]
220
+
221
+ first_row[0] # => 0
222
+ first_row[1] # => 10
223
+ first_row[2] # => 20
224
+
225
+ first_row[0] = -100
226
+
227
+ grid.to_a # => [-100,10,20,30,40,50,60,70,80]
228
+
229
+ ```
230
+
231
+ #### Row
232
+
233
+ To select a specific row, use the method ``row``
234
+
235
+ ```
236
+ GridStruct#row(row_number)
237
+ ```
238
+
239
+ ```ruby
240
+ rows = []
241
+
242
+ # Fetch selector for every rows
243
+ grid.rows.times.each do |row_index|
244
+ rows[row_index] = grid.row(row_index)
245
+ end
246
+ ```
247
+
248
+ #### Column
249
+
250
+ To select a specific column, use the method ``column``
251
+
252
+ ```
253
+ GridStruct#column(column_number)
254
+ ```
255
+
256
+ ```ruby
257
+ columns = []
258
+
259
+ # Fetch selector for every columns
260
+ grid.columns.times.each do |column_index|
261
+ columns[column_index] = grid.column(column_index)
262
+ end
263
+ ```
264
+
265
+ #### Diagonals
266
+
267
+ The diagonals retrieval works slightly differently from the two previous methods.
268
+ In order to retrieve diagonals, you must provide the coordinates of a cell in the grid. This will retrieve the diagonals that cross through that specific cell.
269
+ The returned array can be of size 0 (no diagonals found, for example, in a grid of size 1), 1 (when retrieving diagonals from the corners of the grid) or 2.
270
+
271
+ ```
272
+ GridStruct#diagonals(1,1) # => [#<GridStruct::Selector ...>, #<GridStruct::Selector ...>]
273
+ ```
274
+
275
+ ```ruby
276
+ diagonals = grid.diagonals(1,1)
277
+
278
+ diagonals.first.to_a # => [0,40,80]
279
+ diagonals.last.to_a # => [30,40,60]
280
+
281
+ corner_diagonal = grid.diagonals(0,0) # fetch diagonals from top left corner
282
+
283
+ corner_diagonal.size # => 1
284
+ corner_diagonal.first.to_a # => [0,40,80]
285
+ ```
286
+
287
+ #### Slice
288
+
289
+ Imagine a slice as a projection of a section of your grid.
290
+ Use the ``slice`` method to access a single slice.
291
+
292
+ ```
293
+ GridStruct#slice(slice_index, rows: slice_rows, columns: slice_columns) # => #<GridStruct::Selector ...>
294
+ ```
295
+
296
+ In the following example, we are manipulating a sudoku grid, and we want to access the middle 3x3 square
297
+
298
+ ```ruby
299
+ sudoku_grid = GridStruct.new(9,9)
300
+
301
+ sudoku_grid.map! do |v, r, c|
302
+ (r * sudoku_grid.columns) + c + 1
303
+ end
304
+
305
+ sudoku_grid.slice(4, rows: 3, columns: 3) # => #<GridStruct:0x007fb3d16e4e70 @columns=3, @rows=3, @store=[31, 32, 33, 40, 41, 42, 49, 50, 51]>
306
+ ```
307
+
308
+ Use the ``each_slice`` method to go through each slices
309
+
310
+ ```
311
+ GridStruct#each_slice(slice_rows,slice_columns) { |slice, slice_index| # Do something }
312
+ ```
313
+
314
+ ```ruby
315
+ sudoku_grid.each_slice(3,3) do |slice, index|
316
+ # Do something
317
+ end
318
+ ```
319
+
320
+ ## Contributing
321
+
322
+ 1. Fork it
323
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
324
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
325
+ 4. Push to the branch (`git push origin my-new-feature`)
326
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grid_struct/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "grid_struct"
8
+ spec.version = GridStruct::VERSION
9
+ spec.authors = ["Samuel Molinari"]
10
+ spec.email = ["samuel@molinari.me"]
11
+ spec.description = %q{Grid data structure}
12
+ spec.summary = %q{Handle a grid data structure}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "guard"
25
+ spec.add_development_dependency "guard-rspec"
26
+ end
@@ -0,0 +1,38 @@
1
+ class ::GridStruct::Selector
2
+
3
+ attr_reader :grid, :indexes, :rows, :columns
4
+
5
+ def initialize(grid,*indexes)
6
+ @grid = grid
7
+ @indexes = indexes.freeze
8
+ end
9
+
10
+ def dimensions(rows,columns)
11
+ @rows = rows
12
+ @columns = columns
13
+ return self
14
+ end
15
+
16
+ def [](i)
17
+ mapped_index = @indexes[i]
18
+ mapped_index.nil? ? nil : @grid.store[mapped_index]
19
+ end
20
+
21
+ def []=(i,value)
22
+ mapped_index = @indexes[i]
23
+ @grid.store[mapped_index] = value unless mapped_index.nil?
24
+ end
25
+
26
+ def to_a
27
+ @indexes.size.times.map do |i|
28
+ self[i]
29
+ end
30
+ end
31
+
32
+ def to_grid(rows = nil,columns = nil)
33
+ rows ||= @rows
34
+ columns ||= @columns
35
+ GridStruct.new(rows,columns,to_a)
36
+ end
37
+
38
+ end
@@ -0,0 +1,3 @@
1
+ class GridStruct
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,186 @@
1
+ require 'grid_struct/version'
2
+ require 'grid_struct/selector'
3
+
4
+ class GridStruct
5
+
6
+ attr_reader :store,
7
+ :rows,
8
+ :columns
9
+
10
+ def initialize(rows, columns, store = [])
11
+ @rows = rows
12
+ @columns = columns
13
+ @store = store || []
14
+ end
15
+
16
+ def set(row,column)
17
+ @store[get_index(row,column)] = yield
18
+ end
19
+
20
+ def map!
21
+ size.times.map do |index|
22
+ value = @store[index]
23
+ row_column = get_row_column_at(index)
24
+ @store[index] = yield(value, row_column[:row], row_column[:column])
25
+ end
26
+ return self
27
+ end
28
+
29
+ def map_row!(n)
30
+ @columns.times.each do |column|
31
+ index = get_index(n,column)
32
+ value = @store[index]
33
+ @store[index] = yield(value,column)
34
+ end
35
+ return self
36
+ end
37
+
38
+ def map_column!(n)
39
+ @rows.times.each do |row|
40
+ index = get_index(row,n)
41
+ value = @store[index]
42
+ @store[index] = yield(value,row)
43
+ end
44
+ return self
45
+ end
46
+
47
+ def each
48
+ @store.fill(nil,@store.size...size).each.with_index do |value, index|
49
+ row_column = get_row_column_at(index)
50
+ yield(value,row_column[:row],row_column[:column])
51
+ end
52
+ end
53
+
54
+ def each_slice(rows,columns)
55
+ sub_rows = (@rows / rows.to_f).ceil
56
+ sub_columns = (@columns / columns.to_f).ceil
57
+
58
+ sub_rows.times.each do |sub_row|
59
+ sub_columns.ceil.times.each do |sub_column|
60
+
61
+ index = sub_column + (sub_row * sub_columns)
62
+ yield(slice(index, rows: rows, columns: columns), index)
63
+
64
+ end
65
+ end
66
+ end
67
+
68
+ def get(row,column)
69
+ @store[get_index(row,column)]
70
+ end
71
+
72
+ ##
73
+ # TODO Improve
74
+ def slice(index,dimensions)
75
+ rows = dimensions[:rows]
76
+ columns = dimensions[:columns]
77
+ sub_columns = (@columns / columns.to_f).ceil
78
+
79
+ sub_row = index / sub_columns
80
+ sub_column = index - (sub_row * sub_columns)
81
+
82
+ indexes = []
83
+ rows.times.each do |r|
84
+ start_index = (r * @columns) +
85
+ (sub_row * rows * @columns) +
86
+ (sub_column * columns)
87
+ end_index = start_index + columns
88
+
89
+ start_row = get_row_column_at(start_index)[:row]
90
+ end_row = get_row_column_at(end_index)[:row]
91
+
92
+ if start_row != end_row
93
+ extras = [0,(end_index - @columns)].max % @columns
94
+ end_index -= extras
95
+ end
96
+
97
+ final_row_indexes = (start_index...end_index).to_a
98
+ final_row_indexes.fill(nil,final_row_indexes.size...columns)
99
+ indexes += final_row_indexes
100
+ end
101
+
102
+ return GridStruct::Selector.new(self, *indexes).dimensions(rows,columns)
103
+ end
104
+
105
+ def row(n)
106
+ start_index = (n * @columns)
107
+ end_index = start_index + @columns
108
+ GridStruct::Selector.new(self,*(start_index...end_index))
109
+ end
110
+
111
+ def column(n)
112
+ GridStruct::Selector.new(self,*@rows.times.map do |row|
113
+ get_index(row,n)
114
+ end)
115
+ end
116
+
117
+ def diagonals(row,column)
118
+ selectors = []
119
+
120
+ first = diagonal_builder({row: row, column: column})
121
+ second = diagonal_builder({row: row, column: column}, 1, -1)
122
+
123
+ selectors.push(GridStruct::Selector.new(self, *first)) if first.size > 1
124
+ selectors.push(GridStruct::Selector.new(self, *second)) if second.size > 1
125
+
126
+ return selectors
127
+ end
128
+
129
+ def size
130
+ @rows * @columns
131
+ end
132
+
133
+ def to_a
134
+ store_size = @store.size
135
+ return @store.dup.fill(nil,store_size...size)
136
+ end
137
+
138
+ def ==(other)
139
+ @store == other.instance_variable_get(:@store)
140
+ end
141
+
142
+ protected
143
+
144
+ def get_index(row,column)
145
+ (row * @columns) + column
146
+ end
147
+
148
+ def get_row_column_at(index)
149
+ row = index / @columns
150
+ column = index - (row * @columns)
151
+ return { row: row, column: column }
152
+ end
153
+
154
+ def diagonal_builder(coordinates, row_direction = 1, column_direction = 1, diagonal_indexes = [], action = :push)
155
+ row = coordinates[:row]
156
+ column = coordinates[:column]
157
+ index = get_index(row,column)
158
+
159
+ if !diagonal_indexes.include?(index) &&
160
+ row >= 0 &&
161
+ column >= 0 &&
162
+ row < @rows &&
163
+ column < @columns
164
+
165
+ diagonal_indexes.method(action).call(get_index(row,column))
166
+
167
+ diagonal_builder({ row: row - (row_direction * 1),
168
+ column: column - (column_direction * 1) },
169
+ row_direction,
170
+ column_direction,
171
+ diagonal_indexes,
172
+ :unshift)
173
+
174
+ diagonal_builder({ row: row + (row_direction * 1),
175
+ column: column + (column_direction * 1) },
176
+ row_direction,
177
+ column_direction,
178
+ diagonal_indexes,
179
+ :push)
180
+
181
+ end
182
+
183
+ return diagonal_indexes
184
+ end
185
+
186
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::GridStruct::Selector do
4
+
5
+ let(:grid) { GridStruct.new(9,9) }
6
+ let(:indexes) { [0,3,34,57] }
7
+ subject(:selector) { described_class.new(grid,*indexes) }
8
+
9
+ before(:each) do
10
+ grid.map! do |value,row,column|
11
+ ((row * grid.columns) + column) * 2
12
+ end
13
+ end
14
+
15
+ it { is_expected.to have_attributes(grid: grid,
16
+ indexes: indexes) }
17
+
18
+ describe '#[]' do
19
+ it 'retrieve the x element in the current selection' do
20
+ expect(selector[0]).to eq 0
21
+ expect(selector[1]).to eq 6
22
+ expect(selector[2]).to eq 68
23
+ expect(selector[3]).to eq 114
24
+ end
25
+
26
+ context 'when mapped index is nil' do
27
+ let(:broken_selector) { described_class.new(grid, [0,nil,nil,nil]) }
28
+
29
+ it 'returns nil' do
30
+ expect(broken_selector[1]).to be_nil
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#[]=' do
36
+ it 'sets the value in the grid through the selector' do
37
+ selector[0] = -10
38
+ selector[1] = -3
39
+ selector[2] = 'hello world'
40
+ expect(grid.store[0]).to eq -10
41
+ expect(grid.store[3]).to eq -3
42
+ expect(grid.store[34]).to eq 'hello world'
43
+ end
44
+
45
+ context 'when mapped index is nil' do
46
+ let(:broken_selector) { described_class.new(grid, [0,nil,nil,nil]) }
47
+
48
+ it 'ignores action' do
49
+ store = grid.store
50
+ broken_selector[1] = 100
51
+ expect(grid.store).to eq store
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#dimensions' do
57
+ it 'sets the virtual dimensions of the current selection' do
58
+ selector.dimensions(2,3)
59
+ expect(selector.rows).to eq 2
60
+ expect(selector.columns).to eq 3
61
+ end
62
+ end
63
+
64
+ describe '#to_a' do
65
+ it 'fetches and returns selector values into an array' do
66
+ expect(selector.to_a).to eq [0,6,68,114]
67
+ end
68
+ it 'does not modify the grid when manipulating retuning array' do
69
+ array = selector.to_a
70
+ array[0] = -100
71
+ expect(grid.store[0]).not_to eq -100
72
+ end
73
+ end
74
+
75
+ describe 'to_grid' do
76
+ it 'converts current selection into a new grid, with the given dimensions' do
77
+ expect(selector.to_grid(2,2)).to eq GridStruct.new(2,2,[0,6,68,114])
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,296 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::GridStruct do
4
+
5
+ let(:sudoku_grid) { GridStruct.new(9,9) }
6
+ before(:each) do
7
+ # Fill grid as such
8
+ #
9
+ # |----|----|----|----|----|----|----|----|----|
10
+ # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
11
+ # |----|----|----|----|----|----|----|----|----|
12
+ # | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
13
+ # |----|----|----|----|----|----|----|----|----|
14
+ # | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
15
+ # |----|----|----|----|----|----|----|----|----|
16
+ # | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
17
+ # |----|----|----|----|----|----|----|----|----|
18
+ # | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
19
+ # |----|----|----|----|----|----|----|----|----|
20
+ # | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
21
+ # |----|----|----|----|----|----|----|----|----|
22
+ # | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
23
+ # |----|----|----|----|----|----|----|----|----|
24
+ # | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
25
+ # |----|----|----|----|----|----|----|----|----|
26
+ # | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
27
+ # |----|----|----|----|----|----|----|----|----|
28
+
29
+ sudoku_grid.map! do |value, row, column|
30
+ row * sudoku_grid.columns + column
31
+ end
32
+ end
33
+
34
+ let(:rows) { 2 }
35
+ let(:columns) { 3 }
36
+ subject(:grid) { GridStruct.new(rows, columns) }
37
+
38
+ it { is_expected.to have_attributes(rows: rows,
39
+ columns: columns) }
40
+
41
+ describe '#size' do
42
+ it 'is the multiplication of with and height' do
43
+ expect(grid.size).to be rows * columns
44
+ end
45
+ end
46
+
47
+ describe '#set' do
48
+ it 'sets the value at the given row and column' do
49
+ grid.set(0,0) { 1 }
50
+ store = grid.instance_variable_get(:@store)
51
+ expect(store).to eq [1]
52
+
53
+ grid.set(1,0) { 2 }
54
+ store = grid.instance_variable_get(:@store)
55
+ expect(store).to eq [1,nil,nil,2]
56
+ end
57
+ end
58
+
59
+ describe '#each' do
60
+ before(:each) do
61
+ grid.instance_variable_set(:@store, [0,1,2,3,4,5])
62
+ end
63
+
64
+ it 'iterates through each elements of the grid (left to right, top to bottom)' do
65
+ values = []
66
+ grid.each do |element,row,column|
67
+ values[(row * grid.columns) + column] = element
68
+ end
69
+ expect(values.size).to be grid.size
70
+ expect(values).to eq grid.to_a
71
+ end
72
+ end
73
+
74
+ describe '#map!' do
75
+ it 'replaces all the elements in the grid' do
76
+ grid.map! do |value, row, column|
77
+ (row + 1) * (column + 1)
78
+ end
79
+ store = grid.instance_variable_get(:@store)
80
+ expect(store).to eq [1,2,3,2,4,6]
81
+ end
82
+ end
83
+
84
+ describe '#map_row!' do
85
+ it 'replaces the elements in the targeted row' do
86
+ grid.map_row!(0) { |value, column| column }
87
+ store = grid.instance_variable_get(:@store)
88
+ expect(store).to eq [0,1,2]
89
+
90
+ grid.map_row!(1) { |value, column| column**2 }
91
+ store = grid.instance_variable_get(:@store)
92
+ expect(store).to eq [0,1,2,0,1,4]
93
+ end
94
+ end
95
+
96
+ describe '#map_column!' do
97
+ it 'replaces the elements in the targeted column' do
98
+ grid.map_column!(0) { |value, row| row }
99
+ store = grid.instance_variable_get(:@store)
100
+ expect(store).to eq [0,nil,nil,1]
101
+
102
+ grid.map_column!(1) { |value, row| row + 1 }
103
+ store = grid.instance_variable_get(:@store)
104
+ expect(store).to eq [0,1,nil,1,2]
105
+ end
106
+ end
107
+
108
+ describe '#each_slice' do
109
+ it 'iterates through sub-grids of the given rows and columns' do
110
+ sub_grids = []
111
+ sudoku_grid.each_slice(3,3) do |sub_grid,index|
112
+ sub_grids[index] = sub_grid
113
+ end
114
+ expect(sub_grids[0].to_a).to eq [0,1,2,9,10,11,18,19,20]
115
+ expect(sub_grids[1].to_a).to eq [3,4,5,12,13,14,21,22,23]
116
+ expect(sub_grids[2].to_a).to eq [6,7,8,15,16,17,24,25,26]
117
+ expect(sub_grids[3].to_a).to eq [27,28,29,36,37,38,45,46,47]
118
+ expect(sub_grids[4].to_a).to eq [30,31,32,39,40,41,48,49,50]
119
+ expect(sub_grids[5].to_a).to eq [33,34,35,42,43,44,51,52,53]
120
+ expect(sub_grids[6].to_a).to eq [54,55,56,63,64,65,72,73,74]
121
+ expect(sub_grids[7].to_a).to eq [57,58,59,66,67,68,75,76,77]
122
+ expect(sub_grids[8].to_a).to eq [60,61,62,69,70,71,78,79,80]
123
+ end
124
+
125
+ context 'when slice is equal to the grid' do
126
+ it 'iterates a single time with a copy of the current grid' do
127
+ selector = nil
128
+ sudoku_grid.each_slice(9,9) do |sub_grid|
129
+ selector = sub_grid
130
+ end
131
+ expect(selector.to_grid).to eq sudoku_grid
132
+ end
133
+ end
134
+
135
+ context 'when slice is bigger than the grid' do
136
+ it 'iterates a single time with content of current grid with extra edges' do
137
+ selector = nil
138
+ sudoku_grid.each_slice(10,10) do |sub_grid|
139
+ selector = sub_grid
140
+ end
141
+ sudoku_grid.rows.times.each do |row_index|
142
+ expect(selector.to_grid.row(row_index).to_a).to eq (sudoku_grid.row(row_index).to_a + [nil])
143
+ end
144
+ sudoku_grid.columns.times.each do |column_index|
145
+ expect(selector.to_grid.column(column_index).to_a).to eq (sudoku_grid.column(column_index).to_a + [nil])
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'when last slice bleeds out of the grid' do
151
+ it 'nullify the extras' do
152
+ sub_grids = []
153
+ sudoku_grid.each_slice(2,4) do |sub_grid,index|
154
+ sub_grids[index] = sub_grid
155
+ end
156
+ expect(sub_grids[2].to_a).to eq [8,nil,nil,nil,17,nil,nil,nil]
157
+ expect(sub_grids[5].to_a).to eq [26,nil,nil,nil,35,nil,nil,nil]
158
+ expect(sub_grids[8].to_a).to eq [44,nil,nil,nil,53,nil,nil,nil]
159
+ expect(sub_grids[11].to_a).to eq [62,nil,nil,nil,71,nil,nil,nil]
160
+ expect(sub_grids[12].to_a).to eq [72,73,74,75,nil,nil,nil,nil]
161
+ expect(sub_grids[13].to_a).to eq [76,77,78,79,nil,nil,nil,nil]
162
+ expect(sub_grids[14].to_a).to eq [80,nil,nil,nil,nil,nil,nil,nil]
163
+ end
164
+ end
165
+ end
166
+
167
+ describe '#get' do
168
+ context 'a non-set cell' do
169
+ it 'returns nil' do
170
+ expect(grid.get(0,0)).to be_nil
171
+ end
172
+ end
173
+ context 'an already set cell' do
174
+ before(:each) do
175
+ grid.instance_variable_set(:@store, [nil,8,nil,9,2,nil])
176
+ end
177
+
178
+ it 'returns the stored value at the given row and column' do
179
+ expect(grid.get(0,0)).to be_nil
180
+ expect(grid.get(0,1)).to be 8
181
+ expect(grid.get(0,2)).to be_nil
182
+ expect(grid.get(1,0)).to be 9
183
+ expect(grid.get(1,1)).to be 2
184
+ expect(grid.get(1,2)).to be_nil
185
+ end
186
+ end
187
+ end
188
+
189
+ describe '#row' do
190
+ before(:each) do
191
+ grid.instance_variable_set(:@store, [nil,8,nil,9,2,nil])
192
+ end
193
+
194
+ it 'returns a selector' do
195
+ expect(grid.row(0)).to be_kind_of(GridStruct::Selector)
196
+ end
197
+
198
+ it 'retrieves a selector of the requested row' do
199
+ expect(grid.row(0).to_a).to eq [nil,8,nil]
200
+ expect(grid.row(1).to_a).to eq [9,2,nil]
201
+ end
202
+ end
203
+
204
+ describe '#column' do
205
+ before(:each) do
206
+ grid.instance_variable_set(:@store, [nil,8,nil,9,2,nil])
207
+ end
208
+
209
+ it 'returns a selector' do
210
+ expect(grid.column(0)).to be_kind_of(GridStruct::Selector)
211
+ end
212
+
213
+ it 'retrieves a selector of the requested column' do
214
+ expect(grid.column(0).to_a).to eq [nil,9]
215
+ expect(grid.column(1).to_a).to eq [8,2]
216
+ expect(grid.column(2).to_a).to eq [nil,nil]
217
+ end
218
+ end
219
+
220
+ describe '#diagonals' do
221
+ it 'returns an array' do
222
+ expect(grid.diagonals(0,0)).to be_kind_of Array
223
+ end
224
+
225
+ context 'when fetching diagonals not from a corner' do
226
+ it 'returns 2 selectors' do
227
+ diagonals = sudoku_grid.diagonals(4,4)
228
+ expect(diagonals.size).to be 2
229
+ expect(diagonals.first).to be_kind_of(GridStruct::Selector)
230
+ expect(diagonals.last).to be_kind_of(GridStruct::Selector)
231
+ end
232
+
233
+ it 'returns the diagonals crossing the given coordinates' do
234
+ diagonals = sudoku_grid.diagonals(4,4)
235
+ expect(diagonals.first.to_a).to eq [0,10,20,30,40,50,60,70,80]
236
+ expect(diagonals.last.to_a).to eq [8,16,24,32,40,48,56,64,72]
237
+ end
238
+
239
+ context 'the returned selectors' do
240
+ it 'approprietly modify its matching element' do
241
+ diagonals = sudoku_grid.diagonals(4,4)
242
+ diagonals.first[4] = -100
243
+ expect(sudoku_grid.get(4,4))
244
+ end
245
+ end
246
+ end
247
+
248
+ context 'when fetching diagonals from a corner' do
249
+ it 'only returns 1 diagonal selector' do
250
+ expect(sudoku_grid.diagonals(0,0).size).to be 1
251
+ expect(sudoku_grid.diagonals(0,sudoku_grid.columns - 1).size).to be 1
252
+ expect(sudoku_grid.diagonals(sudoku_grid.rows - 1,0).size).to be 1
253
+ expect(sudoku_grid.diagonals(sudoku_grid.rows - 1, sudoku_grid.columns - 1).size).to be 1
254
+ end
255
+ end
256
+ end
257
+
258
+ describe '#slice' do
259
+ it 'returns a selector' do
260
+ expect(sudoku_grid.slice(0, rows: 3, columns: 3)).to be_kind_of(GridStruct::Selector)
261
+ end
262
+
263
+ context 'the returned selector' do
264
+ it 'modifies its matching element' do
265
+ slice = sudoku_grid.slice(0, rows: 3, columns: 3)
266
+ slice[4] = -100
267
+ expect(sudoku_grid.get(1,1)).to eq -100
268
+ end
269
+
270
+ context 'when slice goes out of bound' do
271
+ it 'adds extras when converting to grid' do
272
+ slice = sudoku_grid.slice(2, rows: 2, columns: 4)
273
+ expect(slice.to_grid.to_a).to eq [8,nil,nil,nil,17,nil,nil,nil]
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ describe '#to_a' do
280
+ it 'returns an array matching size of the grid' do
281
+ expect(grid.to_a.size).to be grid.size
282
+ end
283
+
284
+ it 'returns content of grid store' do
285
+ grid.instance_variable_set(:@store, [0,1,2,3,4,5])
286
+ expect(grid.to_a).to eq [0,1,2,3,4,5]
287
+ end
288
+
289
+ it 'returns a copy' do
290
+ grid.instance_variable_set(:@store, [0,1,2,3,4,5])
291
+ array = grid.to_a
292
+ array[0] = 100
293
+ expect(grid.get(0,0)).to be 0
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,7 @@
1
+ require 'grid_struct'
2
+
3
+ RSpec.configure do |config|
4
+ config.mock_with :rspec do |mocks|
5
+ mocks.syntax = :should
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grid_struct
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Molinari
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Grid data structure
84
+ email:
85
+ - samuel@molinari.me
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - Gemfile
93
+ - Guardfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - grid_struct.gemspec
98
+ - lib/grid_struct.rb
99
+ - lib/grid_struct/selector.rb
100
+ - lib/grid_struct/version.rb
101
+ - spec/lib/grid_struct/selector_spec.rb
102
+ - spec/lib/grid_struct_spec.rb
103
+ - spec/spec_helper.rb
104
+ homepage: ''
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.0.6
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Handle a grid data structure
128
+ test_files:
129
+ - spec/lib/grid_struct/selector_spec.rb
130
+ - spec/lib/grid_struct_spec.rb
131
+ - spec/spec_helper.rb