class Tetris::Model::Game

Constants

PLAYFIELD_HEIGHT
PLAYFIELD_WIDTH
PREVIEW_PLAYFIELD_HEIGHT
PREVIEW_PLAYFIELD_WIDTH
SCORE_MULTIPLIER
SPEEDS
SPEED_INITIAL_DELAYS
UP_ARROW_ACTIONS

Attributes

added_high_score[RW]
added_high_score?[RW]
beeping[RW]
beeping?[RW]
game_over[RW]
game_over?[RW]
high_scores[RW]
initial_delay[RW]
level[RW]
lines[RW]
paused[RW]
paused?[RW]
playfield_height[R]
playfield_width[R]
preview_tetromino[RW]
score[RW]
show_high_scores[RW]
show_preview_tetromino[RW]
show_preview_tetromino?[RW]
up_arrow_action[RW]

Public Class Methods

new(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 51
def initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT)
  @initial_delay = SPEED_INITIAL_DELAYS[:snail]
  @playfield_width = playfield_width
  @playfield_height = playfield_height
  @high_scores = []
  @show_high_scores = false
  @beeping = true
  @up_arrow_action = :rotate_left
  @show_preview_tetromino = true
  load_high_scores!
end

Public Instance Methods

add_high_score!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 97
def add_high_score!
  self.added_high_score = true
  high_scores.prepend(PastGame.new("Player #{high_scores.count + 1}", score, lines, level))
  save_high_scores!
end
beep() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 212
def beep
  @beeper&.call if beeping
end
calculate_score!(eliminated_lines) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 199
def calculate_score!(eliminated_lines)
  new_score = SCORE_MULTIPLIER[eliminated_lines] * (level + 1)
  self.score += new_score
end
clear_high_scores!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 92
def clear_high_scores!
  high_scores.clear
  save_high_scores!
end
configure_beeper(&beeper) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 63
def configure_beeper(&beeper)
  @beeper = beeper
end
consider_adding_tetromino() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 260
def consider_adding_tetromino
  if tetrominoes.empty? || current_tetromino.stopped?
    preview_tetromino.launch!
    preview_next_tetromino!
  end
end
consider_eliminating_lines() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 267
def consider_eliminating_lines
  eliminated_lines = 0
  playfield.each_with_index do |row, playfield_row|
    if row.all? {|block| !block.clear?}
      eliminated_lines += 1
      shift_blocks_down_above_row(playfield_row)
    end
  end
  if eliminated_lines > 0
    beep
    self.lines += eliminated_lines
    level_up!
    calculate_score!(eliminated_lines)
  end
end
current_tetromino() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 150
def current_tetromino
  tetrominoes.last
end
delay() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 208
def delay
  [@initial_delay - (level.to_i * 0.1), 0.001].max
end
down!(instant: false) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 129
def down!(instant: false)
  return unless game_in_progress?
  current_tetromino.down!(instant: instant)
  game_over! if current_tetromino.row <= 0 && current_tetromino.stopped?
end
game_in_progress?() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 67
def game_in_progress?
  !game_over? && !paused?
end
game_over!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 86
def game_over!
  add_high_score!
  beep
  self.game_over = true
end
hypothetical(&block) click to toggle source

Executes a hypothetical scenario without truly changing playfield permanently

# File samples/elaborate/tetris/model/game.rb, line 168
def hypothetical(&block)
  @playfield = hypothetical_playfield
  block.call
  @playfield = @original_playfield
end
hypothetical?() click to toggle source

Returns whether currently executing a hypothetical scenario

# File samples/elaborate/tetris/model/game.rb, line 175
def hypothetical?
  @playfield != @original_playfield
end
hypothetical_playfield() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 179
def hypothetical_playfield
  @playfield_height.times.map { |row|
    @playfield_width.times.map { |column|
      playfield[row][column].clone
    }
  }
end
left!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 140
def left!
  return unless game_in_progress?
  current_tetromino.left!
end
level_up!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 204
def level_up!
  self.level += 1 if lines >= self.level*10
end
load_high_scores!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 112
def load_high_scores!
  if File.exist?(tetris_high_score_file)
    self.high_scores = File.read(tetris_high_score_file).split("\n").map {|line| PastGame.new(*line.split("\t")) }
  end
rescue => e
  # Fail safely by keeping high scores in memory if unable to access disk
  Glimmer::Config.logger.error {"Failed to load high scores from: #{tetris_high_score_file}\n#{e.full_message}"}
end
playfield() click to toggle source

Returns blocks in the playfield

# File samples/elaborate/tetris/model/game.rb, line 159
def playfield
  @playfield ||= @original_playfield = @playfield_height.times.map do
    @playfield_width.times.map do
      Block.new
    end
  end
end
playfield_remaining_heights(tetromino = nil) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 283
def playfield_remaining_heights(tetromino = nil)
  @playfield_width.times.map do |playfield_column|
    bottom_most_block = tetromino.bottom_most_block_for_column(playfield_column)
    (playfield.each_with_index.detect do |row, playfield_row|
      !row[playfield_column].clear? &&
      (
        tetromino.nil? ||
        bottom_most_block.nil? ||
        (playfield_row > tetromino.row + bottom_most_block[:row_index])
      )
    end || [nil, @playfield_height])[1]
  end.to_a
end
preview_next_tetromino!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 195
def preview_next_tetromino!
  self.preview_tetromino = Tetromino.new(self)
end
preview_playfield() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 187
def preview_playfield
  @preview_playfield ||= PREVIEW_PLAYFIELD_HEIGHT.times.map {|row|
    PREVIEW_PLAYFIELD_WIDTH.times.map {|column|
      Block.new
    }
  }
end
reset_playfield() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 244
def reset_playfield
  playfield.each do |row|
    row.each do |block|
      block.clear
    end
  end
end
reset_preview_playfield() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 252
def reset_preview_playfield
  preview_playfield.each do |row|
    row.each do |block|
      block.clear
    end
  end
end
reset_tetrominoes() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 240
def reset_tetrominoes
  @tetrominoes = []
end
restart!()
Alias for: start!
right!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 135
def right!
  return unless game_in_progress?
  current_tetromino.right!
end
rotate!(direction) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 145
def rotate!(direction)
  return unless game_in_progress?
  current_tetromino.rotate!(direction)
end
save_high_scores!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 103
def save_high_scores!
  high_score_file_content = @high_scores.map {|past_game| past_game.to_a.join("\t") }.join("\n")
  FileUtils.mkdir_p(tetris_dir)
  File.write(tetris_high_score_file, high_score_file_content)
rescue => e
  # Fail safely by keeping high scores in memory if unable to access disk
  Glimmer::Config.logger.error {"Failed to save high scores in: #{tetris_high_score_file}\n#{e.full_message}"}
end
start!() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 71
def start!
  self.show_high_scores = false
  self.paused = false
  self.level = 1
  self.score = 0
  self.lines = 0
  reset_playfield
  reset_preview_playfield
  reset_tetrominoes
  preview_next_tetromino!
  consider_adding_tetromino
  self.game_over = false
end
Also aliased as: restart!
tetris_dir() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 121
def tetris_dir
  @tetris_dir ||= File.join(Dir.home, '.glimmer-tetris')
end
tetris_high_score_file() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 125
def tetris_high_score_file
  File.join(tetris_dir, "high_scores.txt")
end
tetrominoes() click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 154
def tetrominoes
  @tetrominoes ||= reset_tetrominoes
end

Private Instance Methods

shift_blocks_down_above_row(row) click to toggle source
# File samples/elaborate/tetris/model/game.rb, line 299
def shift_blocks_down_above_row(row)
  row.downto(0) do |playfield_row|
     playfield[playfield_row].each_with_index do |block, playfield_column|
       previous_row = playfield[playfield_row - 1]
       previous_block = previous_row[playfield_column]
       block.color = previous_block.color unless block.color == previous_block.color
     end
  end
  playfield[0].each(&:clear)
end