grid_struct 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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