Class: Grid
- Inherits:
-
Object
- Object
- Grid
- Defined in:
- lib/aoc_rb_helpers/grid.rb
Overview
Provides helper methods for manipulating end querying two-dimensional grids.
Class Method Summary collapse
-
.from_input(input) ⇒ Object
Returns a new Grid initialized with the given input.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Returns
true
if the other grid has the same content in the same orientation,false
otherwise. -
#all_rotations ⇒ Array<Grid>
Returns an array of Grid objects in all possible rotations, copied from
self
. -
#beyond_grid?(row, column) ⇒ Boolean
Returns
true
if the provided coordinates exceed the bounds of the grid;false
otherwise. -
#cell(row, column) ⇒ Object?
Returns the value stored at coordinates (row, column) within the grid.
-
#dup ⇒ Grid
Returns a new Grid as a copy of self.
-
#each_cell {|coords, value| ... } ⇒ Grid, Enumerator
Iterates over each cell in the grid.
-
#each_cell! {|value| ... } ⇒ Grid, Enumerator
(also: #format_cells)
Calls the block, if given, with each cell value; replaces the cell in the grid with the block’s return value:.
-
#each_subgrid(rows, columns) {|subgrid| ... } ⇒ Grid, Enumerator
Calls the given block with each subgrid from
self
with the size constraints provided; returnsself
. -
#includes_coords?(row, column) ⇒ Boolean
(also: #within_grid?)
Returns
true
if the provided coordinates exist within the bounds of the grid;false
otherwise. -
#initialize(grid) ⇒ Grid
constructor
Returns a new Grid initialized with the provided two-dimensional array.
-
#locate(value) ⇒ Array<Integer>?
Returns the first coordinates within the grid containing the given value.
-
#locate_all(value) ⇒ Array<Array<Integer>>
Returns an array of coordinates for any location within the grid containing the given value.
-
#matches_with_rotations?(other) ⇒ Boolean
Returns
true
if the other grid can be rotated into an orientation where it is equal toself
,false
otherwise. -
#neighbours(row, column, cardinal: true, ordinal: false) ⇒ Array<Array<Integer>>
For the given position indicated by the
row
andcolumn
provided, returns an array of coordinates which are direct neighbours. -
#rotate!(direction = :clockwise) ⇒ self
Updates
self
with a rotated grid and returnsself
. -
#set_cell(row, column, value) ⇒ Object?
Updates the cell at coordinates (row, column) with the object provided in
value
; returns the given object. -
#subgrids(rows, columns) ⇒ Array<Grid>
Returns an array containing all of the subgrids of the specified dimensions.
Constructor Details
Class Method Details
.from_input(input) ⇒ Object
Returns a new Grid initialized with the given input.
8 9 10 |
# File 'lib/aoc_rb_helpers/grid.rb', line 8 def self.from_input(input) self.new(input.lines(chomp: true).map(&:chars)) end |
Instance Method Details
#==(other) ⇒ Boolean
81 82 83 84 |
# File 'lib/aoc_rb_helpers/grid.rb', line 81 def ==(other) return false unless other.is_a?(self.class) @grid == other.instance_variable_get(:@grid) end |
#all_rotations ⇒ Array<Grid>
Returns an array of Grid objects in all possible rotations, copied from self
.
101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/aoc_rb_helpers/grid.rb', line 101 def all_rotations rotations = [] grid = self.dup 4.times do rotations << grid.dup grid.rotate! end rotations end |
#beyond_grid?(row, column) ⇒ Boolean
Returns true
if the provided coordinates exceed the bounds of the grid; false
otherwise.
25 26 27 |
# File 'lib/aoc_rb_helpers/grid.rb', line 25 def beyond_grid?(row, column) !includes_coords?(row, column) end |
#cell(row, column) ⇒ Object?
Returns the value stored at coordinates (row, column) within the grid.
Returns nil
if the provided coordinates do not exist within the grid.
Row and column numbers are zero-indexed.
52 53 54 55 |
# File 'lib/aoc_rb_helpers/grid.rb', line 52 def cell(row, column) return nil unless includes_coords?(row, column) @grid[row][column] end |
#dup ⇒ Grid
Returns a new Grid as a copy of self.
115 116 117 |
# File 'lib/aoc_rb_helpers/grid.rb', line 115 def dup self.class.new Marshal.load(Marshal.dump(@grid)) end |
#each_cell {|coords, value| ... } ⇒ Grid, Enumerator
Iterates over each cell in the grid.
When a block is given, passes the coordinates and value of each cell to the block; returns self
:
g = Grid.new([
["a", "b"],
["c", "d"]
])
g.each_cell { |coords, value| puts "#{coords.inspect} => #{value}" }
Output:
[0, 0] => a
[0, 1] => b
[1, 0] => c
[1, 1] => d
When no block is given, returns a new Enumerator:
g = Grid.new([
[:a, "b"],
[3, true]
])
e = g.each_cell
e # => #<Enumerator: #<Grid: @grid=[[\"a\", \"b\"], [\"c\", \"d\"]]>:each_cell>
g1 = e.each { |coords, value| puts "#{coords.inspect} => #{value.class}: #{value}" }
Output:
[0, 0] => Symbol: a
[0, 1] => String: b
[1, 0] => Integer: 3
[1, 1] => TrueClass: true
250 251 252 253 254 255 256 257 258 |
# File 'lib/aoc_rb_helpers/grid.rb', line 250 def each_cell return to_enum(__callee__) unless block_given? @grid.each_with_index do |row, r_index| row.each_with_index do |cell, c_index| yield [[r_index, c_index], cell] end end self end |
#each_cell! {|value| ... } ⇒ Grid, Enumerator Also known as: format_cells
Calls the block, if given, with each cell value; replaces the cell in the grid with the block’s return value:
Returns a new Enumerator if no block given
267 268 269 270 271 272 273 274 275 |
# File 'lib/aoc_rb_helpers/grid.rb', line 267 def each_cell! return to_enum(__callee__) unless block_given? @grid.each_with_index do |row, r_index| row.each_with_index do |cell, c_index| @grid[r_index][c_index] = yield cell end end self end |
#each_subgrid(rows, columns) {|subgrid| ... } ⇒ Grid, Enumerator
Calls the given block with each subgrid from self
with the size constraints provided; returns self
.
Returns an enumerator if no block is given
139 140 141 142 143 144 145 146 147 148 |
# File 'lib/aoc_rb_helpers/grid.rb', line 139 def each_subgrid(rows, columns) return to_enum(__callee__, rows, columns) unless block_given? @grid.each_cons(rows) do |rows| rows[0].each_cons(columns).with_index do |_, col_index| yield Grid.new(rows.map { |row| row[col_index, columns] }) end end self end |
#includes_coords?(row, column) ⇒ Boolean Also known as: within_grid?
Returns true
if the provided coordinates exist within the bounds of the grid; false
otherwise.
35 36 37 |
# File 'lib/aoc_rb_helpers/grid.rb', line 35 def includes_coords?(row, column) row >= 0 && column >= 0 && row < @grid.length && column < @grid.first.length end |
#locate(value) ⇒ Array<Integer>?
Returns the first coordinates within the grid containing the given value. Returns nil
if not found.
If given an array of values, the first coordinate matching any of the given values will be returned.
Searches the grid from top left (+[0, 0]+) to bottom right, by scanning each row.
174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/aoc_rb_helpers/grid.rb', line 174 def locate(value) result = nil if value.is_a? Array value.each do |e| result = locate(e) break unless result.nil? end else result = locate_value value end result end |
#locate_all(value) ⇒ Array<Array<Integer>>
Returns an array of coordinates for any location within the grid containing the given value.
If given an array of values, the coordinates of any cell matching any of the given values will be returned.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/aoc_rb_helpers/grid.rb', line 195 def locate_all(value) locations = [] if value.is_a? Array @grid.each_with_index.select { |row, _r_index| value.any? { |el| row.include?(el) } }.each do |row, r_index| row.each_with_index do |cell, c_index| locations << [r_index, c_index] if value.include?(cell) end end else @grid.each_with_index.select { |row, _r_index| row.include?(value) }.each do |row, r_index| row.each_with_index do |cell, c_index| locations << [r_index, c_index] if cell == value end end end locations end |
#matches_with_rotations?(other) ⇒ Boolean
Returns true
if the other grid can be rotated into an orientation where it is equal to self
, false
otherwise.
grid = Grid.new([1, 2], [3, 4])
grid == Grid.new([1, 2], [3, 4]) # => true
grid == Grid.new([3, 1], [4, 2]) # => true
grid == Grid.new([1, 2], [4, 3]) # => false
grid == "non-grid object" # => false
95 96 97 |
# File 'lib/aoc_rb_helpers/grid.rb', line 95 def matches_with_rotations?(other) other.all_rotations.any? { |rotated| self == rotated } end |
#neighbours(row, column, cardinal: true, ordinal: false) ⇒ Array<Array<Integer>>
For the given position indicated by the row
and column
provided, returns an array of coordinates which are direct neighbours. The returned coordinates are in clockwise order starting directly above the given cell:
g = Grid.new([
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]
])
g.neighbours(1, 1) # => [[0, 1], [1, 2], [2, 1], [1, 0]]
If the keyword argument allow_diagonal: true is provided, diagonally accessible neighbours will also be included:
g = Grid.new([
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]
])
g.neighbours(1, 1) # => [[0, 1], [0, 2], [1, 2], [2, 2], [2, 1], [2, 0], [1, 0], [0, 0]]
If provided a block, each neighbour’s cell value is yielded to the block, and only those neighbours for which the block returns a truthy value will be returned in the results:
g = Grid.new([
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 11]
])
g.neighbours(1, 2) { |cell| cell.even? } # => [[0, 2], [2, 2]]
g.neighbours(1, 2, allow_diagonal: true) { |cell| cell <= 5 } # => [[0, 2], [0, 3], [1, 1], [0, 1]]
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/aoc_rb_helpers/grid.rb', line 313 def neighbours(row, column, cardinal: true, ordinal: false) possible_neighbours = [] possible_neighbours << [row - 1, column] if cardinal possible_neighbours << [row - 1, column + 1] if ordinal possible_neighbours << [row, column + 1] if cardinal possible_neighbours << [row + 1, column + 1] if ordinal possible_neighbours << [row + 1, column] if cardinal possible_neighbours << [row + 1, column - 1] if ordinal possible_neighbours << [row, column - 1] if cardinal possible_neighbours << [row - 1, column - 1] if ordinal valid_neighbours = possible_neighbours.select { |r, c| includes_coords?(r, c) } if block_given? valid_neighbours.select { |r, c| yield cell(r, c) } else valid_neighbours end end |
#rotate!(direction = :clockwise) ⇒ self
Updates self
with a rotated grid and returns self
.
Will rotate in a clockwise direction by default. Will rotate in an anticlockwise direction if passed a param which is not :clockwise
.
126 127 128 129 |
# File 'lib/aoc_rb_helpers/grid.rb', line 126 def rotate!(direction = :clockwise) @grid = direction == :clockwise ? @grid.transpose.map(&:reverse) : @grid.map(&:reverse).transpose self end |
#set_cell(row, column, value) ⇒ Object?
Updates the cell at coordinates (row, column) with the object provided in value
; returns the given object.
Returns nil
if the provided coordinates do not exist within the grid.
67 68 69 70 |
# File 'lib/aoc_rb_helpers/grid.rb', line 67 def set_cell(row, column, value) return nil unless includes_coords?(row, column) @grid[row][column] = value end |
#subgrids(rows, columns) ⇒ Array<Grid>
Returns an array containing all of the subgrids of the specified dimensions.
157 158 159 160 161 |
# File 'lib/aoc_rb_helpers/grid.rb', line 157 def subgrids(rows, columns) raise ArgumentError unless rows.is_a?(Integer) && rows > 0 && rows <= @grid.length raise ArgumentError unless columns.is_a?(Integer) && columns > 0 && columns <= @grid.first.length each_subgrid(rows, columns).to_a end |