class Minesweeprb::Game

Constants

LOSE
SPRITES
WIN

Attributes

active_square[RW]
flagged_squares[R]
height[R]
marked_squares[R]
mines[R]
revealed_squares[R]
start_time[R]
width[R]

Public Class Methods

new(label:, width:, height:, mines:) click to toggle source
# File lib/minesweeprb/game.rb, line 28
def initialize(label:, width:, height:, mines:)
  @width = width
  @height = height
  @mines = mines
  restart
end

Public Instance Methods

active_square=(pos) click to toggle source
# File lib/minesweeprb/game.rb, line 79
def active_square=(pos)
  x, y = pos
  x = x < 0 ? width - 1 : x
  x = x > width - 1 ? 0 : x
  y = y < 0 ? height - 1 : y
  y = y > height - 1 ? 0 : y

  @active_square = [x,y]
end
center() click to toggle source
# File lib/minesweeprb/game.rb, line 50
def center
  [(width / 2).floor, (height / 2).floor]
end
cycle_flag() click to toggle source
# File lib/minesweeprb/game.rb, line 105
def cycle_flag
  return if over? || revealed_squares.empty? || revealed_squares.include?(active_square)

  if flagged_squares.include?(active_square)
    @flagged_squares -= [active_square]
    @marked_squares += [active_square]
  elsif marked_squares.include?(active_square)
    @marked_squares -= [active_square]
  elsif flagged_squares.length < mines
    @flagged_squares += [active_square]
  end
end
end_time() click to toggle source
# File lib/minesweeprb/game.rb, line 54
def end_time
  @end_time || now
end
face() click to toggle source
# File lib/minesweeprb/game.rb, line 89
def face
  if won?
    SPRITES[:win_face]
  elsif lost?
    SPRITES[:lose_face]
  else
    SPRITES[:play_face]
  end
end
game_over_message() click to toggle source
# File lib/minesweeprb/game.rb, line 162
def game_over_message
  won? ? WIN : LOSE
end
header() click to toggle source
# File lib/minesweeprb/game.rb, line 99
def header
  "#{SPRITES[:mine]} #{remaining_mines.to_s.rjust(3, '0')}" \
  "  #{face}  " \
  "#{SPRITES[:clock]} #{time.round.to_s.rjust(3, '0')}"
end
lost?() click to toggle source
# File lib/minesweeprb/game.rb, line 154
def lost?
  (revealed_squares & @mined_squares).any?
end
move(direction) click to toggle source
# File lib/minesweeprb/game.rb, line 64
def move(direction)
  return if over?

  x, y = active_square

  case direction
  when :up then y -= 1
  when :down then y += 1
  when :left then x -= 1
  when :right then x += 1
  end

  self.active_square = [x,y]
end
over?() click to toggle source
# File lib/minesweeprb/game.rb, line 158
def over?
  won? || lost?
end
play_grid() click to toggle source
# File lib/minesweeprb/game.rb, line 126
def play_grid
  height.times.map do |y|
    width.times.map do |x|
      square = [x,y]

      if @mined_squares.include?(square) && (revealed_squares.include?(square) || over?)
        SPRITES[:mine]
      elsif revealed_squares.include?(square) || over?
        SPRITES[:clues][@grid[y][x]]
      elsif flagged_squares.include?(square)
        SPRITES[:flag]
      elsif marked_squares.include?(square)
        SPRITES[:mark]
      else
        SPRITES[:square]
      end
    end
  end
end
remaining_mines() click to toggle source
# File lib/minesweeprb/game.rb, line 46
def remaining_mines
  mines - flagged_squares.length
end
restart() click to toggle source
# File lib/minesweeprb/game.rb, line 35
def restart
  @active_square = center
  @flagged_squares = []
  @marked_squares = []
  @mined_squares = []
  @revealed_squares = []
  @grid = Array.new(height) { Array.new(width) }
  @start_time = nil
  @end_time = nil
end
reveal_active_square() click to toggle source
# File lib/minesweeprb/game.rb, line 118
def reveal_active_square
  return if over? || flagged_squares.include?(active_square)

  x, y = active_square
  reveal_square(x, y)
  @end_time = now if over?
end
started?() click to toggle source
# File lib/minesweeprb/game.rb, line 146
def started?
  !over? && revealed_squares.count > 0
end
time() click to toggle source
# File lib/minesweeprb/game.rb, line 58
def time
  return 0 unless start_time

  end_time - start_time
end
won?() click to toggle source
# File lib/minesweeprb/game.rb, line 150
def won?
  !lost? && revealed_squares.count == width * height - mines
end

Private Instance Methods

lose!() click to toggle source
# File lib/minesweeprb/game.rb, line 218
def lose!
  @revealed_squares |= @mined_squares
end
neighbors(x,y) click to toggle source
# File lib/minesweeprb/game.rb, line 222
def neighbors(x,y)
  [
    # top
    [x - 1, y - 1],
    [x - 0, y - 1],
    [x + 1, y - 1],

    # sides
    [x - 1, y - 0],
    [x + 1, y - 0],

    # bottom
    [x - 1, y + 1],
    [x - 0, y + 1],
    [x + 1, y + 1],
  ].select do |x,y|
    x.between?(0, width-1) && y.between?(0, height-1)
  end
end
now() click to toggle source
# File lib/minesweeprb/game.rb, line 168
def now
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
place_clues() click to toggle source
# File lib/minesweeprb/game.rb, line 192
def place_clues
  width.times do |x|
    height.times do |y|
      @grid[y][x] = square_value(x,y)
    end
  end
end
place_mines() click to toggle source
# File lib/minesweeprb/game.rb, line 178
def place_mines
  mines.times do
    pos = random_square
    pos = random_square while pos == active_square || @mined_squares.include?(pos)
    @mined_squares << pos
  end
end
random_square() click to toggle source
# File lib/minesweeprb/game.rb, line 186
def random_square
  x = (1..width).to_a.sample - 1
  y = (1..height).to_a.sample - 1
  [x, y]
end
reveal_square(x,y) click to toggle source
# File lib/minesweeprb/game.rb, line 206
def reveal_square(x,y)
  square = [x,y]
  return if over? || flagged_squares.include?(active_square)
  start_game if revealed_squares.empty?
  return if revealed_squares.include?(square)
  return lose! if @mined_squares.include?(square)

  @revealed_squares << [x,y]
  value = @grid[y][x]
  neighbors(x,y).each { |x,y| reveal_square(x,y) } if value == 0
end
square_value(x,y) click to toggle source
# File lib/minesweeprb/game.rb, line 200
def square_value(x,y)
  return if @mined_squares.include?([x,y])

  (neighbors(x,y) & @mined_squares).length
end
start_game() click to toggle source
# File lib/minesweeprb/game.rb, line 172
def start_game
  place_mines
  place_clues
  @start_time = now
end