class Minehunter::Game

Responsible for playing mine hunting game

@api public

Constants

EXIT_KEYS

The keys to exit game

@api private

Attributes

cursor[R]

The terminal cursor clearing and positioning

@api public

Public Class Methods

new(input: $stdin, output: $stdout, env: {}, width: nil, height: nil, mines_limit: nil, screen_width: nil, screen_height: nil, decorator: DEFAULT_DECORATOR, randomiser: DEFAULT_RANDOMISER) click to toggle source

Create a Game instance

@param [IO] input

the input stream, defaults to stdin

@param [IO] output

the output stream, defaults to stdout

@param [Hash] env

the environment variables

@param [Integer] width

the number of columns

@param [Integer] height

the number of rows

@param [Integer] mines_limit

the total number of mines

@param [Integer] screen_width

the terminal screen width

@param [Integer] screen_height

the terminal screen height

@param [Pastel] decorator

the decorator for styling

@param [Proc] randomiser

the random number generator

@api public

# File lib/minehunter/game.rb, line 49
def initialize(input: $stdin, output: $stdout, env: {},
               width: nil, height: nil, mines_limit: nil,
               screen_width: nil, screen_height: nil,
               decorator: DEFAULT_DECORATOR, randomiser: DEFAULT_RANDOMISER)
  @output = output
  @width = width
  @top = (screen_height - height - 4) / 2
  @left = (screen_width - width - 4) / 2
  @pos_x = (width - 1) / 2
  @pos_y = (height - 1) / 2
  @decorator = decorator
  @randomiser = randomiser
  @box = TTY::Box
  @cursor = TTY::Cursor
  @reader = TTY::Reader.new(input: input, output: output, env: env,
                            interrupt: :exit)
  @grid = Grid.new(width: width, height: height, mines_limit: mines_limit)
  @intro = Intro
  @intro_top = (screen_height - @intro.height - 2) / 2
  @intro_left = (screen_width - @intro.width - 4) / 2

  reset
end

Public Instance Methods

finished?() click to toggle source

Check whether or not the game is finished

@return [Boolean]

@api public

# File lib/minehunter/game.rb, line 90
def finished?
  @lost || @grid.cleared?
end
flag() click to toggle source

Place a flag

@api private

# File lib/minehunter/game.rb, line 205
def flag
  return if finished?

  @grid.flag(@curr_x, @curr_y) unless finished?
end
keyctrl_x(*) click to toggle source

Quit game

@api private

# File lib/minehunter/game.rb, line 214
def keyctrl_x(*)
  @output.print cursor.clear_screen
  @output.print cursor.move_to(0, 0)
  @stop = true
end
Also aliased as: keyescape
keydown(*) click to toggle source

Move cursor down

@api private

# File lib/minehunter/game.rb, line 248
def keydown(*)
  return if finished?

  @curr_y = @grid.move_down(@curr_y)
end
keyenter(*)
Alias for: keyspace
keyescape(*)
Alias for: keyctrl_x
keyleft(*) click to toggle source

Move cursor left

@api private

# File lib/minehunter/game.rb, line 257
def keyleft(*)
  return if finished?

  @curr_x = @grid.move_left(@curr_x)
end
keypress(event) click to toggle source

Control game movement and actions

@param [TTY::Reader::KeyEvent] event

the keypress event

@api private

# File lib/minehunter/game.rb, line 190
def keypress(event)
  case event.value.to_sym
  when :h, :a then keyleft
  when :l, :d then keyright
  when :j, :s then keydown
  when :k, :w then keyup
  when :f, :g then flag
  when :r then reset
  when :q then keyctrl_x
  end
end
keyreturn(*)
Alias for: keyspace
keyright(*) click to toggle source

Move cursor right

@api private

# File lib/minehunter/game.rb, line 266
def keyright(*)
  return if finished?

  @curr_x = @grid.move_right(@curr_x)
end
keyspace(*) click to toggle source

Uncover a field

@api private

# File lib/minehunter/game.rb, line 224
def keyspace(*)
  return if @grid.flag?(@curr_x, @curr_y)

  if @first_uncover
    @grid.fill_with_mines(@curr_x, @curr_y, randomiser: @randomiser)
    @first_uncover = false
  end
  @lost = @grid.uncover(@curr_x, @curr_y)
end
Also aliased as: keyenter, keyreturn
keyup(*) click to toggle source

Move cursor up

@api private

# File lib/minehunter/game.rb, line 239
def keyup(*)
  return if finished?

  @curr_y = @grid.move_up(@curr_y)
end
render_grid() click to toggle source

Render grid with current position marker

@api private

# File lib/minehunter/game.rb, line 180
def render_grid
  @grid.render(@curr_x, @curr_y, decorator: @decorator)
end
render_grid_box() click to toggle source

Render box with grid

@return [String]

@api private

# File lib/minehunter/game.rb, line 149
def render_grid_box
  @box.frame(
    render_grid,
    top: @top + 2,
    left: @left,
    padding: [0, 1],
    border: {
      top_left: :divider_right,
      top_right: :divider_left
    }
  )
end
render_intro_box() click to toggle source

Render box with intro

@return [String]

@api private

# File lib/minehunter/game.rb, line 118
def render_intro_box
  @box.frame(
    @intro.render,
    top: @intro_top,
    left: @intro_left,
    padding: [0, 1]
  )
end
render_status_box() click to toggle source

Render box with status message

@return [String]

@api private

# File lib/minehunter/game.rb, line 132
def render_status_box
  @box.frame(
    status,
    top: @top,
    left: @left,
    width: @width + 4,
    padding: [0, 1],
    border: {bottom: false},
    align: :center
  )
end
reset() click to toggle source

Reset game

@api public

# File lib/minehunter/game.rb, line 76
def reset
  @curr_x = @pos_x
  @curr_y = @pos_y
  @first_uncover = true
  @lost = false
  @stop = false
  @grid.reset
end
run() click to toggle source

Start the game

@api public

# File lib/minehunter/game.rb, line 97
def run
  @output.print cursor.hide + cursor.clear_screen + render_intro_box
  pressed_key = @reader.read_keypress
  keyctrl_x if EXIT_KEYS.include?(pressed_key)

  @output.print cursor.clear_screen
  @reader.subscribe(self)

  until @stop
    @output.print render_status_box + render_grid_box
    @reader.read_keypress
  end
ensure
  @output.print cursor.show
end
status() click to toggle source

Status message

@return [String]

@api public

# File lib/minehunter/game.rb, line 167
def status
  if @lost
    "GAME OVER"
  elsif @grid.cleared?
    "YOU WIN"
  else
    "Flags #{@grid.flags_remaining}"
  end
end