Class: GamesParadise::Minesweeper::Terminal::Grid
- Inherits:
-
Object
- Object
- GamesParadise::Minesweeper::Terminal::Grid
- Defined in:
- lib/games_paradise/minesweeper/terminal/grid.rb
Overview
GamesParadise::Minesweeper::Terminal::Grid
Instance Attribute Summary collapse
-
#flags_remaining ⇒ Integer
readonly
# Track the number of flags remaining.
-
#unmined_fields_remaining ⇒ Integer
readonly
# Track the number of unmined fields remaining.
Instance Method Summary collapse
-
#at(x, y) ⇒ Integer
# === at.
-
#cleared? ⇒ Boolean
# === cleared?.
-
#count_flags_next_to(x, y) ⇒ Integer
# === count_flags_next_to.
-
#count_mines_next_to(x, y) ⇒ Integer
# Total number of mines next to a given position.
-
#field_at(x, y) ⇒ Object
# === field_at.
-
#fields_next_to(x, y) ⇒ Enumerator
# Enumerate fields next to a given position.
-
#fill_with_mines(x, y, randomiser: ::GamesParadise::Minesweeper::Terminal::DEFAULT_RANDOMISER) ⇒ Object
# === fill_with_mines.
-
#flag(x, y) ⇒ Object
# === flag.
-
#flag?(x, y) ⇒ Boolean
# === flag?.
-
#initialize(width: nil, height: nil, mines_limit: nil) ⇒ Grid
constructor
# === initialize.
-
#mine(x, y) ⇒ Object
# === mine.
-
#mines ⇒ Array<Field>
# === mines.
-
#move_down(y) ⇒ Integer
# === move_down.
-
#move_left(x) ⇒ Integer
# === move_left.
-
#move_right(x) ⇒ Integer
# === move_right.
-
#move_up(y) ⇒ Integer
# === move_up.
-
#render(x, y, decorator: DEFAULT_DECORATOR) ⇒ String
render.
-
#reset ⇒ Object
# === reset.
-
#uncover(x, y) ⇒ Boolean
# === uncover.
-
#uncover_around(x, y) ⇒ Boolean
# Uncover fields around numbered field matching flags count.
-
#uncover_mines ⇒ Object
# Uncover all mines without a flag ========================================================================= #.
-
#within?(x, y) ⇒ Boolean
# === within?.
Constructor Details
#initialize(width: nil, height: nil, mines_limit: nil) ⇒ Grid
#
initialize
Create a Grid instance.
#
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_remaining ⇒ Integer (readonly)
#
Track the number of flags remaining
#
28 29 30 |
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 28 def flags_remaining @flags_remaining end |
#unmined_fields_remaining ⇒ Integer (readonly)
#
Track the number of unmined fields remaining
#
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.
#
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.
#
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
#
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
#
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
#
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
#
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
#
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
#
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.
#
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.
#
183 184 185 |
# File 'lib/games_paradise/minesweeper/terminal/grid.rb', line 183 def mine(x, y) field_at(x, y).mine! end |
#mines ⇒ Array<Field>
#
mines
All fields with mines.
#
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
#
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
#
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
#
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
#
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
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 |
#reset ⇒ Object
#
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
#
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
#
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_mines ⇒ Object
#
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]
#
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 |