-
Notifications
You must be signed in to change notification settings - Fork 5
/
ochimono.rb
223 lines (182 loc) · 4.86 KB
/
ochimono.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
require 'io/console'
class Drop
attr_accessor :x, :y, :emoji
SUSHI = "\u{1F363}"
BEER = "\u{1F37A}"
PIZZA = "\u{1F355}"
ONIGIRI = "\u{1F359}"
EMOJIS = [SUSHI, BEER, PIZZA, ONIGIRI]
def initialize(x)
@x = x
@y = 0
@emoji = EMOJIS.sample
end
end
class Ochimono
ROWS = 12
COLS = 6
Y_VELOCITY = 0.1
def initialize
@drops = [Drop.new(2), Drop.new(3)]
@fixed_drops = []
@commands = []
@y_velocity = Y_VELOCITY
@score = 0
end
def clear_screen
puts "\e[H\e[2J"
end
def draw_boarders
print "\e[0;0H#{'*' * ((COLS + 1)* 2)}\e[0;0H"
print "\e[#{ROWS + 3};0H#{'*' * ((COLS + 1) * 2)}\e[0;0H"
(ROWS + 3).times do |r|
print "\e[#{r};0H*\e[0;0H"
print "\e[#{r};#{(COLS + 1) * 2}H*\e[0;0H"
end
end
def draw_score
print "\e[0;#{(COLS + 1) * 2 + 2}Hscore:#{@score}\e[0;0H"
end
def can_fall?(drop)
@fixed_drops.each do |fixed_drop|
next if drop == fixed_drop
return false if drop.x == fixed_drop.x && (drop.y + 1 == fixed_drop.y || drop.y.ceil == fixed_drop.y)
end
drop.y < ROWS
end
def fall_drops
if landing?
@fixed_drops.concat(@drops)
@drops = []
@y_velocity = Y_VELOCITY
end
(@drops + @fixed_drops).each do |drop|
drop.y += @y_velocity if can_fall?(drop)
drop.y = drop.y.floor unless can_fall?(drop)
end
end
def remove_connected_drops
drops_to_remove = []
@fixed_drops.each do |drop|
connected_drops = find_connected_drops(drop, [])
if connected_drops.size >= 4
drops_to_remove += connected_drops
end
end
drops_to_remove.each do |drop|
@fixed_drops.delete(drop)
end
end
def find_connected_drops(origin_drop, connected_drops)
connected_drops << origin_drop
x = origin_drop.x
y = origin_drop.y
[[0, -1], [1, 0], [0, 1], [-1, 0]].each do |dx, dy|
if next_drop = @fixed_drops.find { |drop| drop.x == x + dx && drop.y == y + dy && drop.emoji == origin_drop.emoji }
next if connected_drops.include?(next_drop)
connected_drops = find_connected_drops(next_drop, connected_drops)
end
end
connected_drops
end
def landing?
@drops.any? { |drop| !can_fall?(drop) }
end
def fixed?
@fixed_drops.none? { |drop| can_fall?(drop) }
end
def valid_position?(x,y)
x >= 0 && x < COLS && y >= 0 && y < ROWS
end
def rotate_drops(direction)
return if landing? || @drops.empty?
center = @drops[0]
rotating = @drops[1]
dx = center.x - rotating.x
dy = center.y - rotating.y
relative_positions = [[0, -1], [1, 0], [0, 1], [-1, 0]]
if relative_position_index = relative_positions.index { |pos| (pos[0] - dx).abs < 0.1 && (pos[1] - dy).abs < 0.1 }
index_offset = direction == :left ? -1 : 1
rotated_position = relative_positions[(relative_position_index + index_offset) % relative_positions.size]
new_rotating_x = center.x + rotated_position[0]
new_rotating_y = center.y + rotated_position[1]
obstacle = @fixed_drops.find { |fixed_drop| fixed_drop.x == new_rotating_x && fixed_drop.y == new_rotating_y.ceil }
return if obstacle || !valid_position?(new_rotating_x, new_rotating_y)
rotating.x = new_rotating_x
rotating.y = new_rotating_y
end
end
def acceralate_drops
@y_velocity = Y_VELOCITY * 10
end
def get_command
@commands.shift
end
def main_loop
loop do
clear_screen
while command = get_command
case command
when :left
left_drop = @drops.sort_by(&:x)[0]
if left_drop && left_drop.x - 1 >= 0
@drops.each { |drop| drop.x -= 1}
end
when :right
right_drop = @drops.sort_by(&:x).reverse[0]
if right_drop && right_drop.x + 1 < COLS
@drops.each { |drop| drop.x += 1}
end
when :rotate_left
rotate_drops(:left)
when :rotate_right
rotate_drops(:right)
when :down
acceralate_drops
end
end
fall_drops
remove_connected_drops if fixed?
if @drops.empty? && fixed?
@drops = [Drop.new(2), Drop.new(3)]
end
draw_boarders
draw_score
(@drops + @fixed_drops).each do |drop|
x = drop.x
y = drop.y.floor
print "\e[#{y + 2};#{(x + 1)* 2}H#{drop.emoji} \e[0;0H"
end
sleep 0.05
end
end
def start
Thread.new do
begin
main_loop
rescue => e
puts e
puts e.backtrace
exit
end
end
loop do
command = STDIN.getch.chr
case command
when 'l'
@commands << :right
when 'h'
@commands << :left
when 'j'
@commands << :rotate_left
when 'k'
@commands << :rotate_right
when ' '
@commands << :down
when 'q'
exit
end
end
end
end
Ochimono.new.start