Class: GamesParadise::Minesweeper::Terminal::Grid

Inherits:
Object
  • Object
show all
Defined in:
lib/games_paradise/minesweeper/terminal/grid.rb

Overview

GamesParadise::Minesweeper::Terminal::Grid

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width: nil, height: nil, mines_limit: nil) ⇒ Grid

#

initialize

Create a Grid instance.

#

Parameters:

  • width (Integer) (defaults to: nil)

    the number of columns

  • height (Integer) (defaults to: nil)

    the number of rows

  • mines_limit (Integer) (defaults to: nil)

    the total number of mines



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 49

def initialize(
    width: nil, height: nil, mines_limit: nil
  )
  if mines_limit >= width * height
    raise Error, 'cannot have more mines than available fields'
  end
  @width = width
  @height = height
  @mines_limit = mines_limit
  reset
end

Instance Attribute Details

#flags_remainingInteger (readonly)

#

Track the number of flags remaining

#

Returns:

  • (Integer)


28
29
30
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 28

def flags_remaining
  @flags_remaining
end

#unmined_fields_remainingInteger (readonly)

#

Track the number of unmined fields remaining

#

Returns:

  • (Integer)


35
36
37
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 35

def unmined_fields_remaining
  @unmined_fields_remaining
end

Instance Method Details

#at(x, y) ⇒ Integer

#

at

Find field index at a given position.

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Integer)


155
156
157
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 155

def at(x, y)
  y * @width + x
end

#cleared?Boolean

#

cleared?

Check whether or not the grid is cleared.

#

Returns:

  • (Boolean)


82
83
84
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 82

def cleared?
  @unmined_fields_remaining.zero?
end

#count_flags_next_to(x, y) ⇒ Integer

#

count_flags_next_to

Total number of flags next to a given position

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Integer)


317
318
319
320
321
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 317

def count_flags_next_to(x, y)
  fields_next_to(x, y).reduce(0) do |acc, cords|
    acc + (field_at(*cords).flag? ? 1 : 0)
  end
end

#count_mines_next_to(x, y) ⇒ Integer

#

Total number of mines next to a given position

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Integer)


298
299
300
301
302
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 298

def count_mines_next_to(x, y)
  fields_next_to(x, y).reduce(0) { |acc, cords|
    acc + (field_at(*cords).mine? ? 1 : 0)
  }
end

#field_at(x, y) ⇒ Object

#

field_at

Find a field at a given position

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate



169
170
171
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 169

def field_at(x, y)
  @fields[at(x, y)]
end

#fields_next_to(x, y) ⇒ Enumerator

#

Enumerate fields next to a given position

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Enumerator)

    the coordinates for nearby fields



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 264

def fields_next_to(x, y)
  return to_enum(:fields_next_to, x, y) unless block_given?
  -1.upto(1) do |offset_x|
    -1.upto(1) do |offset_y|
      close_x = x + offset_x
      close_y = y + offset_y
      next if close_x == x && close_y == y
      next unless within?(close_x, close_y)
      yield(close_x, close_y)
    end
  end
end

#fill_with_mines(x, y, randomiser: ::GamesParadise::Minesweeper::Terminal::DEFAULT_RANDOMISER) ⇒ Object

#

fill_with_mines

Fill grid with mines skipping the current position and nearby fields

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

  • randomiser (Proc) (defaults to: ::GamesParadise::Minesweeper::Terminal::DEFAULT_RANDOMISER)

    the mine position randomiser



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 233

def fill_with_mines(
    x,
    y,
    randomiser: ::GamesParadise::Minesweeper::Terminal::DEFAULT_RANDOMISER
  )
  limit = @mines_limit
  while limit > 0
    mine_x = randomiser[@width]
    mine_y = randomiser[@height]
    next if mine_x == x && mine_y == y
    next if fields_next_to(x, y).include?([mine_x, mine_y])
    field = field_at(mine_x, mine_y)
    next if field.mine?
    field.mine!
    limit -= 1
  end
end

#flag(x, y) ⇒ Object

#

flag

Add or remove a flag at a given position

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate



197
198
199
200
201
202
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 197

def flag(x, y)
  field = field_at(x, y)
  return unless field.cover?
  @flags_remaining += field.flag? ? 1 : -1
  field.flag
end

#flag?(x, y) ⇒ Boolean

#

flag?

Check whether or not there is a flag at a given position.

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Boolean)


217
218
219
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 217

def flag?(x, y)
  field_at(x, y).flag?
end

#mine(x, y) ⇒ Object

#

mine

Set a mine at a given position.

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate



183
184
185
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 183

def mine(x, y)
  field_at(x, y).mine!
end

#minesArray<Field>

#

mines

All fields with mines.

#

Returns:



93
94
95
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 93

def mines
  @fields.select(&:mine?)
end

#move_down(y) ⇒ Integer

#

move_down

Move down on the grid

#

Returns:

  • (Integer)


116
117
118
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 116

def move_down(y)
  y == @height - 1 ? 0 : y + 1
end

#move_left(x) ⇒ Integer

#

move_left

Move left on the grid

#

Returns:

  • (Integer)


127
128
129
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 127

def move_left(x)
  x.zero? ? @width - 1 : x - 1
end

#move_right(x) ⇒ Integer

#

move_right

Move right on the grid

#

Returns:

  • (Integer)


139
140
141
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 139

def move_right(x)
  x == @width - 1 ? 0 : x + 1
end

#move_up(y) ⇒ Integer

#

move_up

Move up on the grid

#

Returns:

  • (Integer)


104
105
106
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 104

def move_up(y)
  y.zero? ? @height - 1 : y - 1
end

#render(x, y, decorator: DEFAULT_DECORATOR) ⇒ String

render

Render grid

Returns:

  • (String)


405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 405

def render(x, y, decorator: DEFAULT_DECORATOR)
  out = []
  @height.times do |field_y|
    @width.times do |field_x|
      field = field_at(field_x, field_y)
      rendered_field = field.render(decorator: decorator)
      if field_x == x && field_y == y && decorator
        bg_color = field.mine? && !field.cover? ? :on_red : :on_green
        rendered_field = decorator[rendered_field, bg_color]
      end
      out << rendered_field
    end
    out << "\n"
  end
  out.join
end

#resetObject

#

reset

Reset all fields to defaults

#


66
67
68
69
70
71
72
73
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 66

def reset
  @fields = [] # This must come early.
  (@width * @height).times { |i|
    @fields[i] = ::GamesParadise::Minesweeper::Terminal::Field.new
  }
  @unmined_fields_remaining = @width * @height - @mines_limit
  @flags_remaining = @mines_limit
end

#uncover(x, y) ⇒ Boolean

#

uncover

Uncover fields surrounding the position

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Boolean)

    whether or not uncovered a mine



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 336

def uncover(x, y)
  field = field_at(x, y)
  if field.mine?
    field.uncover
    uncover_mines
    return true
  end
  return uncover_around(x, y) unless field.cover?
  mine_count = count_mines_next_to(x, y)
  field.mine_count = mine_count
  flag(x, y) if field.flag?
  field.uncover
  @unmined_fields_remaining -= 1
  if mine_count.zero?
    fields_next_to(x, y) do |close_x, close_y|
      close_field = field_at(close_x, close_y)
      next if !close_field.cover? || close_field.mine?
      uncover(close_x, close_y)
    end
  end
  false
end

#uncover_around(x, y) ⇒ Boolean

#

Uncover fields around numbered field matching flags count

#

Parameters:

  • x (Integer)

    the x coordinate

  • y (Integer)

    the y coordinate

Returns:

  • (Boolean)

    whether or not uncovered a mine



371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 371

def uncover_around(x, y)
  field = field_at(x, y)
  uncovered_mine = false
  if count_flags_next_to(x, y) != field.mine_count
    return uncovered_mine
  end
  fields_next_to(x, y) do |close_x, close_y|
    close_field = field_at(close_x, close_y)
    next if !close_field.cover? || close_field.flag?
    uncover(close_x, close_y)
    uncovered_mine = true if close_field.mine?
  end
  uncovered_mine
end

#uncover_minesObject

#

Uncover all mines without a flag

#


389
390
391
392
393
394
395
396
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 389

def uncover_mines
  @fields.each { |field|
    if field.mine? && !field.flag? || field.flag? && !field.mine?
      field.wrong if field.flag?
      field.uncover
    end
  }
end

#within?(x, y) ⇒ Boolean

#

within?

Check whether coordinates are within the grid

return [Boolean]

#

Returns:

  • (Boolean)


284
285
286
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 284

def within?(x, y)
  x >= 0 && x < @width && y >= 0 && y < @height
end