Class: GlimmerTetris::Model::Game

Inherits:
Object
  • Object
show all
Defined in:
app/glimmer_tetris/model/game.rb

Constant Summary collapse

PLAYFIELD_WIDTH =
10
PLAYFIELD_HEIGHT =
20
PREVIEW_PLAYFIELD_WIDTH =
4
PREVIEW_PLAYFIELD_HEIGHT =
2
SCORE_MULTIPLIER =
{1 => 40, 2 => 100, 3 => 300, 4 => 1200}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT) ⇒ Game

Returns a new instance of Game.



47
48
49
50
51
52
53
54
55
# File 'app/glimmer_tetris/model/game.rb', line 47

def initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT)
  @playfield_width = playfield_width
  @playfield_height = playfield_height
  @high_scores = []
  @show_high_scores = false
  @beeping = true
  @up_arrow_action = :rotate_left
  load_high_scores!
end

Instance Attribute Details

#added_high_scoreObject Also known as: added_high_score?

Returns the value of attribute added_high_score.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def added_high_score
  @added_high_score
end

#beepingObject Also known as: beeping?

Returns the value of attribute beeping.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def beeping
  @beeping
end

#game_overObject Also known as: game_over?

Returns the value of attribute game_over.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def game_over
  @game_over
end

#high_scoresObject

Returns the value of attribute high_scores.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def high_scores
  @high_scores
end

#levelObject

Returns the value of attribute level.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def level
  @level
end

#linesObject

Returns the value of attribute lines.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def lines
  @lines
end

#pausedObject Also known as: paused?

Returns the value of attribute paused.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def paused
  @paused
end

#playfield_heightObject (readonly)

Returns the value of attribute playfield_height.



40
41
42
# File 'app/glimmer_tetris/model/game.rb', line 40

def playfield_height
  @playfield_height
end

#playfield_widthObject (readonly)

Returns the value of attribute playfield_width.



40
41
42
# File 'app/glimmer_tetris/model/game.rb', line 40

def playfield_width
  @playfield_width
end

#preview_tetrominoObject

Returns the value of attribute preview_tetromino.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def preview_tetromino
  @preview_tetromino
end

#scoreObject

Returns the value of attribute score.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def score
  @score
end

#show_high_scoresObject

Returns the value of attribute show_high_scores.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def show_high_scores
  @show_high_scores
end

#up_arrow_actionObject

Returns the value of attribute up_arrow_action.



41
42
43
# File 'app/glimmer_tetris/model/game.rb', line 41

def up_arrow_action
  @up_arrow_action
end

Instance Method Details

#add_high_score!Object



90
91
92
93
# File 'app/glimmer_tetris/model/game.rb', line 90

def add_high_score!
  self.added_high_score = true
  high_scores.prepend(PastGame.new("Player #{high_scores.count + 1}", score, lines, level))
end

#beepObject



204
205
206
# File 'app/glimmer_tetris/model/game.rb', line 204

def beep
  @beeper&.call if beeping
end

#calculate_score!(eliminated_lines) ⇒ Object



191
192
193
194
# File 'app/glimmer_tetris/model/game.rb', line 191

def calculate_score!(eliminated_lines)
  new_score = SCORE_MULTIPLIER[eliminated_lines] * (level + 1)
  self.score += new_score
end

#clear_high_scores!Object



86
87
88
# File 'app/glimmer_tetris/model/game.rb', line 86

def clear_high_scores!
  high_scores.clear
end

#configure_beeper(&beeper) ⇒ Object



57
58
59
# File 'app/glimmer_tetris/model/game.rb', line 57

def configure_beeper(&beeper)
  @beeper = beeper
end

#consider_adding_tetrominoObject



252
253
254
255
256
257
# File 'app/glimmer_tetris/model/game.rb', line 252

def consider_adding_tetromino
  if tetrominoes.empty? || current_tetromino.stopped?
    preview_tetromino.launch!
    preview_next_tetromino!
  end
end

#consider_eliminating_linesObject



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'app/glimmer_tetris/model/game.rb', line 259

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_tetrominoObject



142
143
144
# File 'app/glimmer_tetris/model/game.rb', line 142

def current_tetromino
  tetrominoes.last
end

#delayObject



200
201
202
# File 'app/glimmer_tetris/model/game.rb', line 200

def delay
  [1.1 - (level.to_i * 0.1), 0.001].max
end

#down!(instant: false) ⇒ Object



121
122
123
124
125
# File 'app/glimmer_tetris/model/game.rb', line 121

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?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'app/glimmer_tetris/model/game.rb', line 61

def game_in_progress?
  !game_over? && !paused?
end

#game_over!Object



80
81
82
83
84
# File 'app/glimmer_tetris/model/game.rb', line 80

def game_over!
  add_high_score!
  beep
  self.game_over = true
end

#hypothetical(&block) ⇒ Object

Executes a hypothetical scenario without truly changing playfield permanently



160
161
162
163
164
# File 'app/glimmer_tetris/model/game.rb', line 160

def hypothetical(&block)
  @playfield = hypothetical_playfield
  block.call
  @playfield = @original_playfield
end

#hypothetical?Boolean

Returns whether currently executing a hypothetical scenario

Returns:

  • (Boolean)


167
168
169
# File 'app/glimmer_tetris/model/game.rb', line 167

def hypothetical?
  @playfield != @original_playfield
end

#hypothetical_playfieldObject



171
172
173
174
175
176
177
# File 'app/glimmer_tetris/model/game.rb', line 171

def hypothetical_playfield
  @playfield_height.times.map { |row|
    @playfield_width.times.map { |column|
      playfield[row][column].clone
    }
  }
end

#instant_down_on_upObject



212
213
214
# File 'app/glimmer_tetris/model/game.rb', line 212

def instant_down_on_up
  self.up_arrow_action == :instant_down
end

#instant_down_on_up=(value) ⇒ Object



208
209
210
# File 'app/glimmer_tetris/model/game.rb', line 208

def instant_down_on_up=(value)
  self.up_arrow_action = :instant_down if value
end

#left!Object



132
133
134
135
# File 'app/glimmer_tetris/model/game.rb', line 132

def left!
  return unless game_in_progress?
  current_tetromino.left!
end

#level_up!Object



196
197
198
# File 'app/glimmer_tetris/model/game.rb', line 196

def level_up!
  self.level += 1 if lines >= self.level*10
end

#load_high_scores!Object



104
105
106
107
108
109
110
111
# File 'app/glimmer_tetris/model/game.rb', line 104

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

#playfieldObject

Returns blocks in the playfield



151
152
153
154
155
156
157
# File 'app/glimmer_tetris/model/game.rb', line 151

def playfield
  @playfield ||= @original_playfield = @playfield_height.times.map {
    @playfield_width.times.map {
      Block.new
    }
  }
end

#playfield_remaining_heights(tetromino = nil) ⇒ Object



275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'app/glimmer_tetris/model/game.rb', line 275

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!Object



187
188
189
# File 'app/glimmer_tetris/model/game.rb', line 187

def preview_next_tetromino!
  self.preview_tetromino = Tetromino.new(self)
end

#preview_playfieldObject



179
180
181
182
183
184
185
# File 'app/glimmer_tetris/model/game.rb', line 179

def preview_playfield
  @preview_playfield ||= PREVIEW_PLAYFIELD_HEIGHT.times.map {|row|
    PREVIEW_PLAYFIELD_WIDTH.times.map {|column|
      Block.new
    }
  }
end

#reset_playfieldObject



236
237
238
239
240
241
242
# File 'app/glimmer_tetris/model/game.rb', line 236

def reset_playfield
  playfield.each do |row|
    row.each do |block|
      block.clear
    end
  end
end

#reset_preview_playfieldObject



244
245
246
247
248
249
250
# File 'app/glimmer_tetris/model/game.rb', line 244

def reset_preview_playfield
  preview_playfield.each do |row|
    row.each do |block|
      block.clear
    end
  end
end

#reset_tetrominoesObject



232
233
234
# File 'app/glimmer_tetris/model/game.rb', line 232

def reset_tetrominoes
  @tetrominoes = []
end

#right!Object



127
128
129
130
# File 'app/glimmer_tetris/model/game.rb', line 127

def right!
  return unless game_in_progress?
  current_tetromino.right!
end

#rotate!(direction) ⇒ Object



137
138
139
140
# File 'app/glimmer_tetris/model/game.rb', line 137

def rotate!(direction)
  return unless game_in_progress?
  current_tetromino.rotate!(direction)
end

#rotate_left_on_upObject



228
229
230
# File 'app/glimmer_tetris/model/game.rb', line 228

def rotate_left_on_up
  self.up_arrow_action == :rotate_left
end

#rotate_left_on_up=(value) ⇒ Object



224
225
226
# File 'app/glimmer_tetris/model/game.rb', line 224

def rotate_left_on_up=(value)
  self.up_arrow_action = :rotate_left if value
end

#rotate_right_on_upObject



220
221
222
# File 'app/glimmer_tetris/model/game.rb', line 220

def rotate_right_on_up
  self.up_arrow_action == :rotate_right
end

#rotate_right_on_up=(value) ⇒ Object



216
217
218
# File 'app/glimmer_tetris/model/game.rb', line 216

def rotate_right_on_up=(value)
  self.up_arrow_action = :rotate_right if value
end

#save_high_scores!Object



95
96
97
98
99
100
101
102
# File 'app/glimmer_tetris/model/game.rb', line 95

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!Object Also known as: restart!



65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'app/glimmer_tetris/model/game.rb', line 65

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

#tetris_dirObject



113
114
115
# File 'app/glimmer_tetris/model/game.rb', line 113

def tetris_dir
  @tetris_dir ||= File.join(File.expand_path('~'), '.glimmer-tetris')
end

#tetris_high_score_fileObject



117
118
119
# File 'app/glimmer_tetris/model/game.rb', line 117

def tetris_high_score_file
  File.join(tetris_dir, "high_scores.txt")
end

#tetrominoesObject



146
147
148
# File 'app/glimmer_tetris/model/game.rb', line 146

def tetrominoes
  @tetrominoes ||= reset_tetrominoes
end