-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtetromino.rb
159 lines (144 loc) · 4.98 KB
/
tetromino.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
require "matrix" # matrix is installed when you install ruby, no need to use gem. docs: https://ruby-doc.org/stdlib-2.5.1/libdoc/matrix/rdoc/Matrix.html
class Tetromino # A tetromino is a tetris piece
attr_reader :piece_data, :width, :height, :pos, :gameboard, :soft_dead, :hard_dead, :fall_rate
attr_accessor :moved
def initialize(gameboard, piece_data, pos, gameboard_height, gameboard_width, scoreboard)
raise ArgumentError unless piece_data.is_a? Matrix
@scoreboard = scoreboard
@gameboard_height = gameboard_height
@gameboard_width = gameboard_width
@gameboard = gameboard
@pos = pos
@piece_data = piece_data
@width = piece_data.row(0).to_a.length
@height = piece_data.column(0).to_a.length
@moved = false
@soft_dead = false
@hard_dead = false
@fall_rate = 30 # will fall every @fall_rate/60 seconds
@accelerated = false
end
def eval_dead(collided, tmp=false)
if collided
if soft_dead
@hard_dead = true
else
@soft_dead = true
end
elsif soft_dead
@soft_dead = false
end
end
def put_tetromino(_gameboard=gameboard, _pos=pos, _width=width, _height=height, _piece_data=piece_data, clear=false)
new_gameboard = Gameboard[*_gameboard] # changing new_gameboard changes _gameboard too, which we don't want.
(0..._width).each do |i|
(0..._height).each do |j|
if _piece_data[j, i] != 0
if clear
if new_gameboard == Gameboard.empty(0, 0)
new_gameboard = Gameboard.zero(20, 10)
end
new_gameboard[_pos[0]+j, _pos[1]+i] = 0
else
new_gameboard[_pos[0]+j, _pos[1]+i] = _piece_data[j, i]
end
end
end
end
new_gameboard
end
def collision_detect(shadow_gameboard, shadow_pos, shadow_size, shadow_piece_data)
shadowGameboardWithoutTetromino = put_tetromino(shadow_gameboard, shadow_pos, shadow_size[1], shadow_size[0], shadow_piece_data, clear=true)
shadow_slice = shadowGameboardWithoutTetromino.minor((shadow_pos[0]...shadow_pos[0]+shadow_size[0]), (shadow_pos[1]...shadow_pos[1]+shadow_size[1]))
gameboardWithoutTetromino = put_tetromino(gameboard, pos, width, height, piece_data, clear=true)
real_slice = gameboardWithoutTetromino.minor((shadow_pos[0]...shadow_pos[0]+shadow_size[0]), (shadow_pos[1]...shadow_pos[1]+shadow_size[1]))
shadow_slice != real_slice
end
def update(pos_index, inc, allow_die=true)
gameboardWithoutTetromino = put_tetromino(gameboard, pos, width, height, piece_data, clear=true)
shadow_pos = pos[0...pos.length]
shadow_pos[pos_index] += inc
shadow_gameboard = put_tetromino(gameboardWithoutTetromino, shadow_pos, width, height, piece_data)
collided = collision_detect(shadow_gameboard, shadow_pos, [height, width], piece_data)
if allow_die
eval_dead(collided)
end
if (!soft_dead || pos_index == 1) && !collided
@pos = shadow_pos
@gameboard = shadow_gameboard
end
end
def fall
if pos[0] + height < @gameboard_height
update(0, 1)
else
eval_dead(true, true)
end
end
def reset_fall_rate
if @accelerated
@fall_rate *= 8
end
@accelerated = false
end
def update_fall_rate
if @fall_rate != 1
@fall_rate = 32 - @scoreboard.level
end
end
def hard_drop
while !hard_dead
fall
end
end
def move(dir)
case dir
when "left"
if pos[1] > 0
update(1, -1, allow_die=false)
return true
end
when "right"
if pos[1] < @gameboard_width - width
update(1, 1, allow_die=false)
return true
end
when "up"
return rotate
when "down"
if !@accelerated && !hard_dead # needs && !hard_dead so no extra score is added in the interval between tetromino dying and new tetromino spawning
@scoreboard.score_acceleration
@fall_rate /= 8
@accelerated = true
end
return @accelerated
when "space"
start_y = pos[0]
hard_drop
end_y = pos[0]
@scoreboard.score_hard_drop(end_y - start_y)
return true
end
false
end
def rotate()
gameboardWithoutTetromino = put_tetromino(gameboard, pos, width, height, piece_data, clear=true)
shadowPieceData = Matrix[*(0...width).map {|i| piece_data.transpose.row(i).to_a.reverse}] # Matrix.transpose almost rotates, but we need to reverse each row. Asterix to prevent everything to be nested in one []
shadow_width = shadowPieceData.row(0).to_a.length
shadow_height = shadowPieceData.column(0).to_a.length
begin
shadow_gameboard = put_tetromino(gameboardWithoutTetromino, pos, shadow_width, shadow_height, shadowPieceData)
if collision_detect(shadow_gameboard, pos, [shadow_height, shadow_width], shadowPieceData)
return false
else
@width = shadow_width
@height = shadow_height
@piece_data = shadowPieceData
@gameboard = shadow_gameboard
end
rescue
return false
end
true
end
end