pentix 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +29 -0
- data/bin/pentix +3 -0
- data/lib/block.rb +20 -0
- data/lib/controls.rb +24 -0
- data/lib/figure.rb +155 -0
- data/lib/finish.rb +85 -0
- data/lib/game.rb +104 -0
- data/lib/glass.rb +158 -0
- data/lib/records.rb +40 -0
- data/lib/status.rb +110 -0
- data/media/block.png +0 -0
- data/pentix.rb +22 -0
- data/spec/lib/block_spec.rb +32 -0
- data/spec/lib/controls_spec.rb +13 -0
- data/spec/lib/figure_spec.rb +318 -0
- data/spec/lib/game_spec.rb +4 -0
- data/spec/lib/glass_spec.rb +377 -0
- data/spec/lib/status_spec.rb +194 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +103 -0
- metadata +78 -0
data/lib/status.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
#
|
2
|
+
# This class handles the game status and things like
|
3
|
+
# the next figure, the score and so one
|
4
|
+
#
|
5
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
6
|
+
#
|
7
|
+
class Status
|
8
|
+
attr_accessor :figure, :pos_x, :pos_y, :level, :lines, :figures, :score
|
9
|
+
|
10
|
+
HEAD_FONT = ['Courier', Block::SIZE + 5, Color::GRAY].freeze
|
11
|
+
TEXT_FONT = ['Courier New', Block::SIZE, Color::GRAY].freeze
|
12
|
+
TEXT_WIDTH = 22 # chars
|
13
|
+
|
14
|
+
SCORING_SYSTEM = {
|
15
|
+
1 => 100,
|
16
|
+
2 => 300,
|
17
|
+
3 => 500,
|
18
|
+
4 => 800,
|
19
|
+
5 => 1200
|
20
|
+
}
|
21
|
+
|
22
|
+
LEVELUP = 10 # the number of lines to level up
|
23
|
+
|
24
|
+
def initialize(window, x, y)
|
25
|
+
@pos_x = x
|
26
|
+
@pos_y = y
|
27
|
+
|
28
|
+
@head_font = Font.new(window, HEAD_FONT[0], HEAD_FONT[1])
|
29
|
+
@text_font = Font.new(window, TEXT_FONT[0], TEXT_FONT[1])
|
30
|
+
|
31
|
+
reset!
|
32
|
+
end
|
33
|
+
|
34
|
+
def draw
|
35
|
+
draw_head "Next:", 0
|
36
|
+
@figure.pos_x = @pos_x
|
37
|
+
@figure.pos_y = @pos_y + 2
|
38
|
+
@figure.draw
|
39
|
+
|
40
|
+
draw_head "Score:", 9
|
41
|
+
draw_text "Level", @level, 10
|
42
|
+
draw_text "Score", @score, 11
|
43
|
+
draw_text "Lines", @lines, 12
|
44
|
+
draw_text "Figures", @figures, 13
|
45
|
+
|
46
|
+
draw_head "Winnars:", 18
|
47
|
+
@records.each_with_index do |entry, i|
|
48
|
+
draw_text entry[0], entry[1], 19 + i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def reset!
|
53
|
+
@level = 1
|
54
|
+
@lines = 0
|
55
|
+
@score = 0
|
56
|
+
@figures = 0
|
57
|
+
|
58
|
+
# the next levelup lines num
|
59
|
+
@levelup = LEVELUP
|
60
|
+
|
61
|
+
@records = Records.top(6)
|
62
|
+
end
|
63
|
+
|
64
|
+
def count_drop(figure)
|
65
|
+
if figure.distance > 0
|
66
|
+
@score += figure.distance * @level
|
67
|
+
@figures += 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def count_kill(lines)
|
72
|
+
if lines.size > 0
|
73
|
+
@score += SCORING_SYSTEM[lines.size] * @level
|
74
|
+
@lines += lines.size
|
75
|
+
|
76
|
+
if @lines >= @levelup
|
77
|
+
@level += 1
|
78
|
+
@levelup = @lines + LEVELUP - @lines % LEVELUP
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
#
|
86
|
+
# A shortcut to draw a header text
|
87
|
+
#
|
88
|
+
def draw_head(text, pos)
|
89
|
+
@head_font.draw(
|
90
|
+
text, @pos_x * Block::SIZE,
|
91
|
+
(@pos_y + pos) * Block::SIZE - 5, # -5 is to shift the too large font a bit
|
92
|
+
0, 1.0, 1.0, HEAD_FONT[2]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# A shortcut to draw a normal text
|
98
|
+
#
|
99
|
+
def draw_text(text, value, pos)
|
100
|
+
value = "..#{value}"
|
101
|
+
text = text.slice(0, TEXT_WIDTH - value.size)
|
102
|
+
|
103
|
+
@text_font.draw(
|
104
|
+
text.ljust(TEXT_WIDTH - value.size, '.') + value,
|
105
|
+
@pos_x * Block::SIZE, (@pos_y + pos) * Block::SIZE,
|
106
|
+
0, 1.0, 1.0, TEXT_FONT[2]
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/media/block.png
ADDED
Binary file
|
data/pentix.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# The main script
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
5
|
+
#
|
6
|
+
|
7
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
8
|
+
|
9
|
+
require 'gosu'
|
10
|
+
include Gosu
|
11
|
+
|
12
|
+
require 'block'
|
13
|
+
require 'figure'
|
14
|
+
require 'glass'
|
15
|
+
require 'status'
|
16
|
+
require 'controls'
|
17
|
+
require 'records'
|
18
|
+
require 'finish'
|
19
|
+
require 'game'
|
20
|
+
|
21
|
+
|
22
|
+
Game.new.show if $0 == __FILE__
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Block do
|
4
|
+
before :all do
|
5
|
+
@window = DummyWindow.new
|
6
|
+
@i_args = [@window, 'media/block.png', true]
|
7
|
+
@image = Image.new(*@i_args)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should instance only one image" do
|
11
|
+
Image.should_receive(:new).with(*@i_args).once.and_return(@image)
|
12
|
+
|
13
|
+
@block1 = Block.new(@window, Color::WHITE)
|
14
|
+
@block2 = Block.new(@window, Color::WHITE)
|
15
|
+
@block3 = Block.new(@window, Color::WHITE)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should allow access to the block color" do
|
19
|
+
Block.new(@window, Color::WHITE).color.should == Color::WHITE
|
20
|
+
Block.new(@window, Color::GREEN).color.should == Color::GREEN
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should draw the block in the given position with assigned color" do
|
24
|
+
@color = Color::BLUE
|
25
|
+
@pos_x = 10
|
26
|
+
@pos_y = 20
|
27
|
+
|
28
|
+
@image.should_receive(:draw).with(@pos_x * Block::SIZE, @pos_y * Block::SIZE, 0, 1.0, 1.0, @color)
|
29
|
+
|
30
|
+
Block.new(@window, @color).draw(@pos_x, @pos_y)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Controls do
|
4
|
+
before :all do
|
5
|
+
@controls = Controls.new($test_window)
|
6
|
+
end
|
7
|
+
|
8
|
+
Controls::BUTTONS.each do |command, button|
|
9
|
+
it "should recognize the '#{command}' command" do
|
10
|
+
@controls.command_for(button).should == command
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
class DummyWindow
|
4
|
+
def show_next_figure
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Figure do
|
9
|
+
before :all do
|
10
|
+
@window = DummyWindow.new
|
11
|
+
|
12
|
+
@window.glass = @glass = Glass.new(@window, 1, 2)
|
13
|
+
@window.status = @status = Status.new(@window, 20, 2)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "initialization" do
|
17
|
+
|
18
|
+
describe "by name" do
|
19
|
+
before do
|
20
|
+
@name = :stairs
|
21
|
+
@color = Figure::COLORS[Figure::FIGURES[@name].split('|').last.to_sym]
|
22
|
+
@figure = Figure.new(@window, @name)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should assign the figure name" do
|
26
|
+
@figure.name.should == @name
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should set the right color" do
|
30
|
+
@figure.color.should == @color
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should assign a correct matrix" do
|
34
|
+
@figure.should have_matrix(%Q{
|
35
|
+
|x. . |
|
36
|
+
|x.x. |
|
37
|
+
| .x.x|
|
38
|
+
})
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should preset initial position 0:0" do
|
42
|
+
@figure.pos_x.should == 0
|
43
|
+
@figure.pos_y.should == 0
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should preset the figure sizes" do
|
47
|
+
@figure.size_x.should == 3
|
48
|
+
@figure.size_y.should == 3
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should instance random figures if called without a name" do
|
53
|
+
[
|
54
|
+
Figure.new(@window),
|
55
|
+
Figure.new(@window),
|
56
|
+
Figure.new(@window),
|
57
|
+
Figure.new(@window),
|
58
|
+
Figure.new(@window)
|
59
|
+
].map(&:name).uniq.size.should > 1
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#draw" do
|
65
|
+
|
66
|
+
it "should correctly render the cross figure" do
|
67
|
+
Figure.new(@window, :cross).should render_blocks(%Q{
|
68
|
+
| .x. |
|
69
|
+
|x.x.x|
|
70
|
+
| .x. |
|
71
|
+
})
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should correctly render the stairs figure" do
|
75
|
+
Figure.new(@window, :stairs).should render_blocks(%Q{
|
76
|
+
|x. . |
|
77
|
+
|x.x. |
|
78
|
+
| .x.x|
|
79
|
+
})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "manipulations" do
|
84
|
+
before do
|
85
|
+
@pos_x = @glass.pos_x + 4
|
86
|
+
@pos_y = @glass.pos_y
|
87
|
+
|
88
|
+
@figure = Figure.new(@window, :l_crutch)
|
89
|
+
@figure.move_to(@pos_x, @pos_y)
|
90
|
+
|
91
|
+
@distance = 10
|
92
|
+
@glass.stub!(:spaces_below).and_return(@distance)
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#move_to" do
|
96
|
+
before do
|
97
|
+
@figure.move_to(10, 20)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should set the x-position" do
|
101
|
+
@figure.pos_x.should == 10
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should set the y-position" do
|
105
|
+
@figure.pos_y.should == 20
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should recalc the distance" do
|
109
|
+
@figure.distance.should == @distance
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#move_left" do
|
114
|
+
before do
|
115
|
+
@figure.move_left
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should decrease the x-position by 1" do
|
119
|
+
@figure.pos_x.should == @pos_x - 1
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should set the figure's distance" do
|
123
|
+
@figure.distance.should == @distance
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should not move the figure beyond the left border" do
|
127
|
+
@pos_x = @figure.pos_x = @glass.pos_x + 1
|
128
|
+
@figure.move_left
|
129
|
+
@figure.pos_x.should == @pos_x
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#move_right" do
|
134
|
+
before do
|
135
|
+
@figure.move_right
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should increase the x-position by 1" do
|
139
|
+
@figure.pos_x.should == @pos_x + 1
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should set the figure's distance" do
|
143
|
+
@figure.distance.should == @distance
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should not move the figure beyond the right border" do
|
147
|
+
@pos_x = @figure.pos_x = @glass.pos_x + Glass::WIDTH + 2 - @figure.size_x
|
148
|
+
@figure.move_right
|
149
|
+
@figure.pos_x.should == @pos_x
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#rotate_left" do
|
154
|
+
before do
|
155
|
+
@figure.turn_left
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should rotate the matrix contr-clockwise" do
|
159
|
+
@figure.should have_matrix(%Q{
|
160
|
+
| .x. . |
|
161
|
+
|x.x.x.x|
|
162
|
+
})
|
163
|
+
@figure.turn_left
|
164
|
+
@figure.should have_matrix(%Q{
|
165
|
+
| .x|
|
166
|
+
| .x|
|
167
|
+
|x.x|
|
168
|
+
| .x|
|
169
|
+
})
|
170
|
+
@figure.turn_left
|
171
|
+
@figure.should have_matrix(%Q{
|
172
|
+
|x.x.x.x|
|
173
|
+
| . .x. |
|
174
|
+
})
|
175
|
+
@figure.turn_left
|
176
|
+
@figure.should have_matrix(%Q{
|
177
|
+
|x. |
|
178
|
+
|x.x|
|
179
|
+
|x. |
|
180
|
+
|x. |
|
181
|
+
})
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should recalculate the matrix size" do
|
185
|
+
@figure.size_x.should == 4
|
186
|
+
@figure.size_y.should == 2
|
187
|
+
|
188
|
+
@figure.turn_left
|
189
|
+
@figure.size_x.should == 2
|
190
|
+
@figure.size_y.should == 4
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should adjust figure's position to make it look centered" do
|
194
|
+
@figure.pos_x.should == @pos_x - 1
|
195
|
+
@figure.turn_left #making it vertical again
|
196
|
+
@figure.pos_x.should == @pos_x
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should set the figure's distance" do
|
200
|
+
@figure.distance.should == @distance
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should not turn the figure if there is no space for that" do
|
204
|
+
figure = Figure.new(@window, :h_stick)
|
205
|
+
|
206
|
+
# setting it vertically
|
207
|
+
figure.move_to(@glass.pos_x + 1, @glass.pos_y)
|
208
|
+
figure.turn_left
|
209
|
+
figure.should have_matrix(%Q{
|
210
|
+
|x|
|
211
|
+
|x|
|
212
|
+
|x|
|
213
|
+
|x|
|
214
|
+
|x|
|
215
|
+
})
|
216
|
+
|
217
|
+
# moving it to the left border
|
218
|
+
figure.move_to(@glass.pos_x + 1, @glass.pos_y)
|
219
|
+
figure.turn_left
|
220
|
+
figure.should have_matrix(%Q{
|
221
|
+
|x|
|
222
|
+
|x|
|
223
|
+
|x|
|
224
|
+
|x|
|
225
|
+
|x|
|
226
|
+
})
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#rotate_right" do
|
231
|
+
before do
|
232
|
+
@figure.turn_right
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should rotate the matrix clockwise" do
|
236
|
+
@figure.should have_matrix(%Q{
|
237
|
+
|x.x.x.x|
|
238
|
+
| . .x. |
|
239
|
+
})
|
240
|
+
@figure.turn_right
|
241
|
+
@figure.should have_matrix(%Q{
|
242
|
+
| .x|
|
243
|
+
| .x|
|
244
|
+
|x.x|
|
245
|
+
| .x|
|
246
|
+
})
|
247
|
+
@figure.turn_right
|
248
|
+
@figure.should have_matrix(%Q{
|
249
|
+
| .x. . |
|
250
|
+
|x.x.x.x|
|
251
|
+
})
|
252
|
+
@figure.turn_right
|
253
|
+
@figure.should have_matrix(%Q{
|
254
|
+
|x. |
|
255
|
+
|x.x|
|
256
|
+
|x. |
|
257
|
+
|x. |
|
258
|
+
})
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should recalculate the matrix size" do
|
262
|
+
@figure.size_x.should == 4
|
263
|
+
@figure.size_y.should == 2
|
264
|
+
|
265
|
+
@figure.turn_right
|
266
|
+
@figure.size_x.should == 2
|
267
|
+
@figure.size_y.should == 4
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should adjust figure's position to make it look centered" do
|
271
|
+
@figure.pos_x.should == @pos_x - 1
|
272
|
+
@figure.turn_left #making it vertical again
|
273
|
+
@figure.pos_x.should == @pos_x
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should set the figure's distance" do
|
277
|
+
@figure.distance.should == @distance
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should not turn the figure if there is no space for that" do
|
281
|
+
figure = Figure.new(@window, :h_stick)
|
282
|
+
|
283
|
+
# setting it vertically
|
284
|
+
figure.move_to(@glass.pos_x + 1, @glass.pos_y)
|
285
|
+
figure.turn_right
|
286
|
+
|
287
|
+
# moving it to the left border
|
288
|
+
figure.move_to(@glass.pos_x + 1, @glass.pos_y)
|
289
|
+
figure.turn_right
|
290
|
+
|
291
|
+
figure.should have_matrix(%Q{
|
292
|
+
|x|
|
293
|
+
|x|
|
294
|
+
|x|
|
295
|
+
|x|
|
296
|
+
|x|
|
297
|
+
})
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe "#drop" do
|
303
|
+
before do
|
304
|
+
@figure = Figure.new(@window)
|
305
|
+
@figure.move_to(@glass.pos_x + 1, @glass.pos_y)
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should call the glass to #glue_in the figure" do
|
309
|
+
@glass.should_receive(:glue_in).with(@figure)
|
310
|
+
@figure.drop
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should call the @window to #show_next_figure" do
|
314
|
+
@window.should_receive(:show_next_figure)
|
315
|
+
@figure.drop
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|