Class: Tetris::Model::Tetromino

Inherits:
Object
  • Object
show all
Defined in:
lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb

Constant Summary collapse

ORIENTATIONS =
[:north, :east, :south, :west]
LETTER_COLORS =
{
  I: :cyan,
  J: :blue,
  L: :dark_yellow,
  O: :yellow,
  S: :green,
  T: :magenta,
  Z: :red,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(game) ⇒ Tetromino

Returns a new instance of Tetromino.



45
46
47
48
49
50
51
52
53
54
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 45

def initialize(game)
  @game = game
  @letter = LETTER_COLORS.keys.sample
  @orientation = :north
  @blocks = default_blocks
  @preview = true
  new_row = 0
  new_column = ((Model::Game::PREVIEW_PLAYFIELD_WIDTH - width)/2).to_i
  update_playfield(new_row, new_column)
end

Instance Attribute Details

#blocksObject

Returns the value of attribute blocks.



43
44
45
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 43

def blocks
  @blocks
end

#columnObject

Returns the value of attribute column.



43
44
45
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 43

def column
  @column
end

#gameObject (readonly)

Returns the value of attribute game.



41
42
43
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 41

def game
  @game
end

#letterObject (readonly)

Returns the value of attribute letter.



41
42
43
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 41

def letter
  @letter
end

#orientationObject

Returns the value of attribute orientation.



43
44
45
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 43

def orientation
  @orientation
end

#previewObject (readonly) Also known as: preview?

Returns the value of attribute preview.



41
42
43
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 41

def preview
  @preview
end

#rowObject

Returns the value of attribute row.



43
44
45
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 43

def row
  @row
end

Instance Method Details

#add_to_playfieldObject



78
79
80
81
82
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 78

def add_to_playfield
  update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
    playfield[playfield_row][playfield_column].color = blocks[row_index][column_index].color if playfield_row >= 0 && playfield[playfield_row][playfield_column]&.clear? && !blocks[row_index][column_index].clear? && playfield[playfield_row][playfield_column].color != blocks[row_index][column_index].color
  end
end

#bottom_most_block_for_column(column) ⇒ Object



123
124
125
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 123

def bottom_most_block_for_column(column)
  bottom_most_blocks.detect {|bottom_most_block| (@column + bottom_most_block[:column_index]) == column}
end

#bottom_most_blocksObject

Returns bottom-most blocks of a tetromino, which could be from multiple rows depending on shape (e.g. T)



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 108

def bottom_most_blocks
  width.times.map do |column_index|
    row_blocks_with_row_index = @blocks.each_with_index.to_a.reverse.detect do |row_blocks, row_index|
      !row_blocks[column_index].clear?
    end
    bottom_most_block = row_blocks_with_row_index[0][column_index]
    bottom_most_block_row = row_blocks_with_row_index[1]
    {
      block: bottom_most_block,
      row_index: bottom_most_block_row,
      column_index: column_index
    }
  end
end

#colorObject



300
301
302
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 300

def color
  LETTER_COLORS[@letter]
end

#default_blocksObject



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 261

def default_blocks
  case @letter
  when :I
    [
      [block, block, block, block]
    ]
  when :J
    [
      [block, block, block],
      [empty, empty, block],
    ]
  when :L
    [
      [block, block, block],
      [block, empty, empty],
    ]
  when :O
    [
      [block, block],
      [block, block],
    ]
  when :S
    [
      [empty, block, block],
      [block, block, empty],
    ]
  when :T
    [
      [block, block, block],
      [empty, block, empty],
    ]
  when :Z
    [
      [block, block, empty],
      [empty, block, block],
    ]
  end
end

#down!(instant: false) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 185

def down!(instant: false)
  launch! if preview?
  unless stopped?
    block_count = 1
    if instant
      remaining_height, bottom_touching_block = remaining_height_and_bottom_touching_block
      block_count = remaining_height - @row
    end
    new_row = @row + block_count
    update_playfield(new_row, @column)
  end
end

#heightObject



181
182
183
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 181

def height
  @blocks.size
end

#hypothetical_tetrominoObject



242
243
244
245
246
247
248
249
250
251
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 242

def hypothetical_tetromino
  clone.tap do |hypo_clone|
    remove_from_playfield
    hypo_clone.blocks = @blocks.map do |row_blocks|
      row_blocks.map do |column_block|
        column_block.clone
      end
    end
  end
end

#include_block?(block) ⇒ Boolean

Returns:

  • (Boolean)


304
305
306
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 304

def include_block?(block)
  @blocks.flatten.include?(block)
end

#launch!Object



60
61
62
63
64
65
66
67
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 60

def launch!
  remove_from_playfield
  @preview = false
  new_row = 1 - height
  new_column = ((game.playfield_width - width)/2).to_i
  update_playfield(new_row, new_column)
  game.tetrominoes << self
end

#left!Object



198
199
200
201
202
203
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 198

def left!
  unless left_blocked?
    new_column = @column - 1
    update_playfield(@row, new_column)
  end
end

#left_blocked?Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 152

def left_blocked?
  (@column == 0) ||
    left_most_blocks.any? { |left_most_block|
      (@row + left_most_block[:row_index]) >= 0 &&
        playfield[@row + left_most_block[:row_index]][@column + left_most_block[:column_index] - 1].occupied?
    }
end

#left_most_blocksObject

Returns right-most blocks of a tetromino, which could be from multiple columns depending on shape (e.g. T)



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 161

def left_most_blocks
  @blocks.each_with_index.map do |row_blocks, row_index|
    column_block_with_column_index = row_blocks.each_with_index.to_a.detect do |column_block, column_index|
      !column_block.clear?
    end
    if column_block_with_column_index
      left_most_block = column_block_with_column_index[0]
      {
        block: left_most_block,
        row_index: row_index,
        column_index: column_block_with_column_index[1]
      }
    end
  end.compact
end

#playfieldObject



56
57
58
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 56

def playfield
  @preview ? game.preview_playfield : game.playfield
end

#remaining_height_and_bottom_touching_blockObject



253
254
255
256
257
258
259
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 253

def remaining_height_and_bottom_touching_block
  playfield_remaining_heights = game.playfield_remaining_heights(self)
  bottom_most_blocks.map do |bottom_most_block|
    playfield_column = @column + bottom_most_block[:column_index]
    [playfield_remaining_heights[playfield_column] - (bottom_most_block[:row_index] + 1), bottom_most_block]
  end.min_by(&:first)
end

#remove_from_playfieldObject



84
85
86
87
88
89
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 84

def remove_from_playfield
  return if @row.nil? || @column.nil?
  update_playfield_block do |playfield_row, playfield_column, row_index, column_index|
    playfield[playfield_row][playfield_column].clear if playfield_row >= 0 && !blocks[row_index][column_index].clear? && playfield[playfield_row][playfield_column]&.color == color
  end
end

#right!Object



205
206
207
208
209
210
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 205

def right!
  unless right_blocked?
    new_column = @column + 1
    update_playfield(@row, new_column)
  end
end

#right_blocked?Boolean

Returns:

  • (Boolean)


127
128
129
130
131
132
133
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 127

def right_blocked?
  (@column == game.playfield_width - width) ||
    right_most_blocks.any? { |right_most_block|
      (@row + right_most_block[:row_index]) >= 0 &&
        playfield[@row + right_most_block[:row_index]][@column + right_most_block[:column_index] + 1].occupied?
    }
end

#right_most_blocksObject

Returns right-most blocks of a tetromino, which could be from multiple columns depending on shape (e.g. T)



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 136

def right_most_blocks
  @blocks.each_with_index.map do |row_blocks, row_index|
    column_block_with_column_index = row_blocks.each_with_index.to_a.reverse.detect do |column_block, column_index|
      !column_block.clear?
    end
    if column_block_with_column_index
      right_most_block = column_block_with_column_index[0]
      {
        block: right_most_block,
        row_index: row_index,
        column_index: column_block_with_column_index[1]
      }
    end
  end.compact
end

#rotate!(direction) ⇒ Object

Rotate in specified direcation, which can be :right (clockwise) or :left (counterclockwise)



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 213

def rotate!(direction)
  return if stopped?
  can_rotate = nil
  new_blocks = nil
  game.hypothetical do
    hypothetical_rotated_tetromino = hypothetical_tetromino
    new_blocks = hypothetical_rotated_tetromino.rotate_blocks(direction)
    can_rotate = !hypothetical_rotated_tetromino.stopped? && !hypothetical_rotated_tetromino.right_blocked? && !hypothetical_rotated_tetromino.left_blocked?
  end
  if can_rotate
    remove_from_playfield
    self.orientation = ORIENTATIONS[ORIENTATIONS.rotate(direction == :right ? -1 : 1).index(@orientation)]
    self.blocks = new_blocks
    update_playfield(@row, @column)
  end
rescue => e
  puts e.full_message
end

#rotate_blocks(direction) ⇒ Object



232
233
234
235
236
237
238
239
240
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 232

def rotate_blocks(direction)
  new_blocks = Matrix[*@blocks].transpose.to_a
  if direction == :right
    new_blocks = new_blocks.map(&:reverse)
  else
    new_blocks = new_blocks.reverse
  end
  Matrix[*new_blocks].to_a
end

#stopped?Boolean

Returns:

  • (Boolean)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 91

def stopped?
  return true if @stopped || @preview
  playfield_remaining_heights = game.playfield_remaining_heights(self)
  result = bottom_most_blocks.any? do |bottom_most_block|
    playfield_column = @column + bottom_most_block[:column_index]
    playfield_remaining_heights[playfield_column] &&
      @row + bottom_most_block[:row_index] >= playfield_remaining_heights[playfield_column] - 1
  end
  if result && !game.hypothetical?
    @stopped = result
    game.consider_eliminating_lines
    @game.consider_adding_tetromino
  end
  result
end

#update_playfield(new_row = nil, new_column = nil) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 69

def update_playfield(new_row = nil, new_column = nil)
  remove_from_playfield
  if !new_row.nil? && !new_column.nil?
    @row = new_row
    @column = new_column
    add_to_playfield
  end
end

#widthObject



177
178
179
# File 'lib/glimmer-dsl-opal/samples/elaborate/tetris/model/tetromino.rb', line 177

def width
  @blocks[0].size
end