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.
@@ -0,0 +1,377 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Glass do
4
+
5
+ before do
6
+ @window = DummyWindow.new
7
+ @window.glass = @glass = Glass.new(@window, 1, 2)
8
+ @window.status = @status = Status.new(@window, 20, 2)
9
+ end
10
+
11
+ describe "initialization" do
12
+ it "should use correct blocks color" do
13
+ @glass.instance_variable_get('@block').color.should == Glass::COLOR
14
+ end
15
+
16
+ it "should assign the initial position" do
17
+ @glass.pos_x.should == 1
18
+ @glass.pos_y.should == 2
19
+ end
20
+
21
+ it "should render the glass walls correctly" do
22
+ @glass.should render_blocks(%Q{
23
+ |x. . . . . . . . . . . . .x|
24
+ |x. . . . . . . . . . . . .x|
25
+ |x. . . . . . . . . . . . .x|
26
+ |x. . . . . . . . . . . . .x|
27
+ |x. . . . . . . . . . . . .x|
28
+ |x. . . . . . . . . . . . .x|
29
+ |x. . . . . . . . . . . . .x|
30
+ |x. . . . . . . . . . . . .x|
31
+ |x. . . . . . . . . . . . .x|
32
+ |x. . . . . . . . . . . . .x|
33
+ |x. . . . . . . . . . . . .x|
34
+ |x. . . . . . . . . . . . .x|
35
+ |x. . . . . . . . . . . . .x|
36
+ |x. . . . . . . . . . . . .x|
37
+ |x. . . . . . . . . . . . .x|
38
+ |x. . . . . . . . . . . . .x|
39
+ |x. . . . . . . . . . . . .x|
40
+ |x. . . . . . . . . . . . .x|
41
+ |x. . . . . . . . . . . . .x|
42
+ |x. . . . . . . . . . . . .x|
43
+ |x. . . . . . . . . . . . .x|
44
+ |x. . . . . . . . . . . . .x|
45
+ |x. . . . . . . . . . . . .x|
46
+ |x. . . . . . . . . . . . .x|
47
+ |x.x.x.x.x.x.x.x.x.x.x.x.x.x|
48
+ })
49
+ end
50
+
51
+ it "should have an empty matrix" do
52
+ @glass.should have_matrix(%Q{
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
+ end
79
+ end
80
+
81
+ describe "#spaces_below" do
82
+ before do
83
+ @glass = Glass.new(@window, 1, 2)
84
+ @figure = Figure.new(@window, :l_crutch)
85
+ @figure.pos_x = 8
86
+ @figure.pos_y = 2
87
+ end
88
+
89
+ it "should calculate the distance correctly when the figure is vertical" do
90
+ @figure.rotate_left if @figure.size_x > @figure.size_y
91
+ @glass.spaces_below(@figure).should == Glass::HEIGHT - @figure.size_y
92
+ end
93
+
94
+ it "should calculate the distance correctly when the figure is vertical" do
95
+ @figure.rotate_left if @figure.size_y > @figure.size_y
96
+ @glass.spaces_below(@figure).should == Glass::HEIGHT - @figure.size_y
97
+ end
98
+
99
+ it "should calculate the distance correctly when a figure is hanging in the middle" do
100
+ @offset = Glass::HEIGHT / 2
101
+ @figure.pos_y = @glass.pos_y + @offset
102
+
103
+ @glass.spaces_below(@figure).should == Glass::HEIGHT - @figure.size_y - @offset
104
+ end
105
+
106
+ describe "with some blocks in the glass" do
107
+ before do
108
+ @stack_height = 3
109
+
110
+ @stack_height.times do |y|
111
+ 3.times do |x|
112
+ @glass.matrix[Glass::HEIGHT - 1 - y][x] = Color::WHITE
113
+ end
114
+ end
115
+ end
116
+
117
+ it "should subtract the blocks height when the figure is above them" do
118
+ @figure.pos_x = @glass.pos_x + 1
119
+ @glass.spaces_below(@figure).should == Glass::HEIGHT - @figure.size_y - @stack_height
120
+ end
121
+
122
+ it "should not calculate the blocks height when the figure is not above them" do
123
+ @figure.pos_x = @glass.pos_x + 6
124
+ @glass.spaces_below(@figure).should == Glass::HEIGHT - @figure.size_y
125
+ end
126
+
127
+ it "should calculate the distance correctly when the figure is in the middle" do
128
+ @figure.pos_x = @glass.pos_x + 1
129
+ @figure.pos_y = 10
130
+
131
+ @glass.spaces_below(@figure).should == Glass::HEIGHT -
132
+ @figure.size_y - @stack_height - @figure.pos_y + @glass.pos_y
133
+ end
134
+ end
135
+ end
136
+
137
+ describe "#glue_in" do
138
+ before do
139
+ @figure = Figure.new(@window, :stairs)
140
+ end
141
+
142
+ it "should glue the figure in when it's on top" do
143
+ @figure.move_to(@glass.pos_x + 3, @glass.pos_y)
144
+ @glass.glue_in(@figure)
145
+ @glass.should have_matrix(%Q{
146
+ | . . . . . . . . . . . |
147
+ | . . . . . . . . . . . |
148
+ | . . . . . . . . . . . |
149
+ | . . . . . . . . . . . |
150
+ | . . . . . . . . . . . |
151
+ | . . . . . . . . . . . |
152
+ | . . . . . . . . . . . |
153
+ | . . . . . . . . . . . |
154
+ | . . . . . . . . . . . |
155
+ | . . . . . . . . . . . |
156
+ | . . . . . . . . . . . |
157
+ | . . . . . . . . . . . |
158
+ | . . . . . . . . . . . |
159
+ | . . . . . . . . . . . |
160
+ | . . . . . . . . . . . |
161
+ | . . . . . . . . . . . |
162
+ | . . . . . . . . . . . |
163
+ | . . . . . . . . . . . |
164
+ | . . . . . . . . . . . |
165
+ | . . . . . . . . . . . |
166
+ | . . . . . . . . . . . |
167
+ | . .x. . . . . . . . . |
168
+ | . .x.x. . . . . . . . |
169
+ | . . .x.x. . . . . . . |
170
+ })
171
+ end
172
+
173
+ it "should call the status for scores" do
174
+ @figure.move_to(@glass.pos_x + 3, @glass.pos_y)
175
+
176
+ @status.should_receive(:count_drop).with(@figure)
177
+ @status.should_receive(:count_kill).with([])
178
+
179
+ @glass.glue_in(@figure)
180
+ end
181
+
182
+ describe "with some blocks already in the glass" do
183
+ before do
184
+ figure = Figure.new(@window, :l_crutch)
185
+ figure.move_to(@glass.pos_x + 2, @glass.pos_y)
186
+ @glass.glue_in(figure)
187
+ end
188
+
189
+ it "should glue figures next to each other" do
190
+ @figure.move_to(@glass.pos_x + 7, @glass.pos_y + 6)
191
+ @glass.glue_in(@figure)
192
+
193
+ @glass.should have_matrix(%Q{
194
+ | . . . . . . . . . . . |
195
+ | . . . . . . . . . . . |
196
+ | . . . . . . . . . . . |
197
+ | . . . . . . . . . . . |
198
+ | . . . . . . . . . . . |
199
+ | . . . . . . . . . . . |
200
+ | . . . . . . . . . . . |
201
+ | . . . . . . . . . . . |
202
+ | . . . . . . . . . . . |
203
+ | . . . . . . . . . . . |
204
+ | . . . . . . . . . . . |
205
+ | . . . . . . . . . . . |
206
+ | . . . . . . . . . . . |
207
+ | . . . . . . . . . . . |
208
+ | . . . . . . . . . . . |
209
+ | . . . . . . . . . . . |
210
+ | . . . . . . . . . . . |
211
+ | . . . . . . . . . . . |
212
+ | . . . . . . . . . . . |
213
+ | . . . . . . . . . . . |
214
+ | .x. . . . . . . . . . |
215
+ | .x.x. . . .x. . . . . |
216
+ | .x. . . . .x.x. . . . |
217
+ | .x. . . . . .x.x. . . |
218
+ })
219
+ end
220
+
221
+ it "should glue figures on top of each other" do
222
+ @figure.move_to(@glass.pos_x + 3, @glass.pos_y)
223
+ @glass.glue_in(@figure)
224
+
225
+ @figure.move_to(@glass.pos_x + 2, @glass.pos_y)
226
+ @glass.glue_in(@figure)
227
+
228
+ @glass.should have_matrix(%Q{
229
+ | . . . . . . . . . . . |
230
+ | . . . . . . . . . . . |
231
+ | . . . . . . . . . . . |
232
+ | . . . . . . . . . . . |
233
+ | . . . . . . . . . . . |
234
+ | . . . . . . . . . . . |
235
+ | . . . . . . . . . . . |
236
+ | . . . . . . . . . . . |
237
+ | . . . . . . . . . . . |
238
+ | . . . . . . . . . . . |
239
+ | . . . . . . . . . . . |
240
+ | . . . . . . . . . . . |
241
+ | . . . . . . . . . . . |
242
+ | . . . . . . . . . . . |
243
+ | . . . . . . . . . . . |
244
+ | . . . . . . . . . . . |
245
+ | .x. . . . . . . . . . |
246
+ | .x.x. . . . . . . . . |
247
+ | . .x.x. . . . . . . . |
248
+ | . .x. . . . . . . . . |
249
+ | .x.x.x. . . . . . . . |
250
+ | .x.x.x.x. . . . . . . |
251
+ | .x. . . . . . . . . . |
252
+ | .x. . . . . . . . . . |
253
+ })
254
+ end
255
+ end
256
+ end
257
+
258
+ describe "#has_space_for?" do
259
+ before do
260
+ @figure = Figure.new(@window, :cross)
261
+ end
262
+
263
+ describe "in an empty glass" do
264
+ it "should say 'yes' when the figure is in the middle" do
265
+ @glass.should have_space_for(
266
+ @figure.matrix, @glass.pos_x + 4, @glass.pos_y + 4
267
+ )
268
+ end
269
+
270
+ it "should say 'nope' when the figure is out of the left border" do
271
+ @glass.should_not have_space_for(
272
+ @figure.matrix, @glass.pos_x, @glass.pos_y
273
+ )
274
+ end
275
+
276
+ it "should say 'nope' if a figure is out of the right border" do
277
+ @glass.should_not have_space_for(
278
+ @figure.matrix, @glass.pos_x + Glass::WIDTH + 2 - @figure.size_x, @glass.pos_y
279
+ )
280
+ end
281
+
282
+ it "should say 'nope' if a figure is below the bottom line" do
283
+ @glass.should_not have_space_for(
284
+ @figure.matrix, @glass.pos_x + 4, @glass.pos_y + Glass::HEIGHT - @figure.size_y + 1
285
+ )
286
+ end
287
+
288
+ it "should say 'yup' for a figure next to the left border" do
289
+ @glass.should have_space_for(
290
+ @figure.matrix, @glass.pos_x + 1, @glass.pos_y
291
+ )
292
+ end
293
+
294
+ it "should say 'yup' for a figure next to the right border" do
295
+ @glass.should have_space_for(
296
+ @figure.matrix, @glass.pos_x + Glass::WIDTH + 1 - @figure.size_x, @glass.pos_y
297
+ )
298
+ end
299
+
300
+ it "should say 'yup' for a figure next to the bottom border" do
301
+ @glass.should have_space_for(
302
+ @figure.matrix, @glass.pos_x + 4, @glass.pos_y + Glass::HEIGHT - @figure.size_y
303
+ )
304
+ end
305
+ end
306
+
307
+ describe "with some blocks in the glass" do
308
+ before do
309
+ 10.times do |i|
310
+ @glass.matrix[i][i] = Color::GRAY
311
+ end
312
+ end
313
+
314
+ it "should say 'yup' for figures that are away from the blocks" do
315
+ @glass.should have_space_for(
316
+ @figure.matrix, @glass.pos_x + 8, @glass.pos_y
317
+ )
318
+ end
319
+
320
+ it "should say 'yup' for figures that fit the landscape" do
321
+ @glass.should have_space_for(
322
+ @figure.matrix, @glass.pos_x + 3, @glass.pos_y
323
+ )
324
+ end
325
+
326
+ it "should say 'nope' for figures that intersect with the landscape" do
327
+ @glass.should_not have_space_for(
328
+ @figure.matrix, @glass.pos_x + 1, @glass.pos_y
329
+ )
330
+ end
331
+ end
332
+ end
333
+
334
+ describe "#remove_full_lines" do
335
+
336
+ before do
337
+ 10.times do |y|
338
+ @glass.matrix[y].each_with_index do |cell, x|
339
+ @glass.matrix[Glass::HEIGHT - y - 1][x] =
340
+ y % 2 == 0 || x % 2 == 0 ? 0xFFFFFFFF : nil
341
+ end
342
+ end
343
+
344
+ @glass.remove_full_lines
345
+ end
346
+
347
+ it "should have matrix like that" do
348
+ @glass.should have_matrix(%Q{
349
+ | . . . . . . . . . . . |
350
+ | . . . . . . . . . . . |
351
+ | . . . . . . . . . . . |
352
+ | . . . . . . . . . . . |
353
+ | . . . . . . . . . . . |
354
+ | . . . . . . . . . . . |
355
+ | . . . . . . . . . . . |
356
+ | . . . . . . . . . . . |
357
+ | . . . . . . . . . . . |
358
+ | . . . . . . . . . . . |
359
+ | . . . . . . . . . . . |
360
+ | . . . . . . . . . . . |
361
+ | . . . . . . . . . . . |
362
+ | . . . . . . . . . . . |
363
+ | . . . . . . . . . . . |
364
+ | . . . . . . . . . . . |
365
+ | . . . . . . . . . . . |
366
+ | . . . . . . . . . . . |
367
+ | . . . . . . . . . . . |
368
+ |x. .x. .x. .x. .x. .x. |
369
+ |x. .x. .x. .x. .x. .x. |
370
+ |x. .x. .x. .x. .x. .x. |
371
+ |x. .x. .x. .x. .x. .x. |
372
+ |x. .x. .x. .x. .x. .x. |
373
+ })
374
+ end
375
+ end
376
+
377
+ end
@@ -0,0 +1,194 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Status do
4
+ before :all do
5
+ @window = DummyWindow.new
6
+ @window.glass = Glass.new(@window, 1, 1)
7
+
8
+ @head_font_args = [@window, Status::HEAD_FONT[0], Status::HEAD_FONT[1]]
9
+ @text_font_args = [@window, Status::TEXT_FONT[0], Status::TEXT_FONT[1]]
10
+
11
+ @head_font = Font.new(*@head_font_args)
12
+ @text_font = Font.new(*@text_font_args)
13
+
14
+ @status = Status.new(@window, 10, 20)
15
+ end
16
+
17
+ describe "initialization" do
18
+ it "should instantiate correct fonts" do
19
+ Font.should_receive(:new).with(*@head_font_args).and_return(@head_font)
20
+ Font.should_receive(:new).with(*@text_font_args).and_return(@text_font)
21
+
22
+ @status = Status.new(@window, 10, 20)
23
+ end
24
+
25
+ it "should set correct positions" do
26
+ @status.pos_x.should == 10
27
+ @status.pos_y.should == 20
28
+ end
29
+
30
+ it "should set first level" do
31
+ @status.level.should == 1
32
+ end
33
+
34
+ it "should set zero lines" do
35
+ @status.lines.should == 0
36
+ end
37
+
38
+ it "should set zero figures" do
39
+ @status.figures.should == 0
40
+ end
41
+
42
+ it "should set zero score" do
43
+ @status.score.should == 0
44
+ end
45
+ end
46
+
47
+ describe '#reset!' do
48
+ before :all do
49
+ @status.level = 8
50
+ @status.score = 9999
51
+ @status.lines = 234
52
+ @status.figures = 1234
53
+
54
+ @status.reset!
55
+ end
56
+
57
+ it "should set the first level" do
58
+ @status.level.should == 1
59
+ end
60
+
61
+ it "should set zero lines" do
62
+ @status.lines.should == 0
63
+ end
64
+
65
+ it "should set zero figures" do
66
+ @status.figures.should == 0
67
+ end
68
+
69
+ it "should set zero score" do
70
+ @status.score.should == 0
71
+ end
72
+ end
73
+
74
+ describe "#draw" do
75
+ it "should draw the things" do
76
+ Font.should_receive(:new).with(*@head_font_args).and_return(@head_font)
77
+ Font.should_receive(:new).with(*@text_font_args).and_return(@text_font)
78
+
79
+ @figure = Figure.new(@window)
80
+ @status = Status.new(@window, 10, 20)
81
+ @status.figure = @figure
82
+
83
+ @figure.should_receive(:pos_x=).with(10)
84
+ @figure.should_receive(:pos_y=).with(22)
85
+ @figure.should_receive(:draw)
86
+
87
+ @head_font.should_receive(:draw).with("Next:", 200, 395, 0, 1.0, 1.0, Status::HEAD_FONT[2])
88
+ @head_font.should_receive(:draw).with("Score:", 200, 575, 0, 1.0, 1.0, Status::HEAD_FONT[2])
89
+ @head_font.should_receive(:draw).with("Winnars:", 200, 755, 0, 1.0, 1.0, Status::HEAD_FONT[2])
90
+
91
+ @status.draw
92
+ end
93
+ end
94
+
95
+ describe "scoring" do
96
+ before do
97
+ @figure = Figure.new(@window)
98
+
99
+ @status.score = 0
100
+ @status.level = 1
101
+ @status.lines = 0
102
+ @status.figures = 0
103
+ end
104
+
105
+ it "should calculate correctly a figure drop score" do
106
+ @figure.distance = 8
107
+
108
+ @status.count_drop @figure
109
+
110
+ @status.score.should == 8
111
+ @status.figures.should == 1
112
+ end
113
+
114
+ it "should multiply the drop score by the current level" do
115
+ @figure.distance = 9
116
+ @status.level = 3
117
+
118
+ @status.count_drop @figure
119
+
120
+ @status.score.should == 27
121
+ @status.figures.should == 1
122
+ end
123
+
124
+ it "should score lines kill according to the scoring system" do
125
+ Status::SCORING_SYSTEM.each do |lines_num, score|
126
+ @status.score = 0
127
+ @status.lines = 0
128
+
129
+ @status.count_kill Array.new(lines_num)
130
+
131
+ @status.score.should == score
132
+ @status.lines.should == lines_num
133
+ end
134
+ end
135
+
136
+ it "should multiply the lines kill score by the current level" do
137
+ Status::SCORING_SYSTEM.each do |lines_num, score|
138
+ @status.score = 0
139
+ @status.lines = 0
140
+ @status.level = 4
141
+
142
+ @status.count_kill Array.new(lines_num)
143
+
144
+ @status.score.should == score * 4
145
+ @status.lines.should == lines_num
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "levelups" do
151
+ before do
152
+ @status.reset!
153
+ end
154
+
155
+ it "should not levelup until the user scores a minimum amount of lines" do
156
+ (Status::LEVELUP - 1).times do
157
+ @status.count_kill [1]
158
+ end
159
+
160
+ @status.level.should == 1
161
+ end
162
+
163
+ it "should levelup right after the user scores the needed amount of lines" do
164
+ Status::LEVELUP.times do
165
+ @status.count_kill [1]
166
+ end
167
+
168
+ @status.level.should == 2
169
+ end
170
+
171
+ it "should keep excessive lines count after each levelup" do
172
+ (Status::LEVELUP - 1).times do
173
+ @status.count_kill [1]
174
+ end
175
+
176
+ @status.level.should == 1
177
+
178
+ @status.count_kill [1,2,3]
179
+
180
+ @status.level.should == 2
181
+
182
+ (Status::LEVELUP - 3).times do
183
+ @status.count_kill [1]
184
+ end
185
+
186
+ @status.level.should == 2
187
+
188
+ @status.count_kill [1]
189
+
190
+ @status.level.should == 3
191
+ end
192
+ end
193
+
194
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,103 @@
1
+ require 'rspec'
2
+ require File.join(File.dirname(__FILE__), '..', 'pentix.rb')
3
+
4
+ # a dummy window class to test the modules separately from the main game
5
+ class DummyWindow < Gosu::Window
6
+ attr_accessor :glass, :status
7
+
8
+ # converting `.new` into a singleton to avoid memory leaks in Gosu
9
+ def self.new
10
+ @@win ||= super
11
+ end
12
+
13
+ def initialize
14
+ super(100, 100, false)
15
+ end
16
+
17
+ end
18
+
19
+ #
20
+ # Some pretty formatted matrixes handling helpers
21
+ #
22
+ module MatrixHelper
23
+ def str_to_matrix(str)
24
+ str.scan(/\s*\|(.+?)\|/).map do |line|
25
+ line[0].split('.').map{ |c| c != ' ' }
26
+ end
27
+ end
28
+
29
+ def matrix_to_str(matrix)
30
+ matrix.map do |row|
31
+ "|#{row.map{ |c| c ? 'x' : ' '}.join('.')}|"
32
+ end.join("\n ")
33
+ end
34
+
35
+ def draw_matrix(figure)
36
+ block = actual.instance_variable_get('@block')
37
+ matrix = figure.instance_variable_get('@matrix')
38
+ result = []
39
+ max_x = 0
40
+
41
+ block.class.instance_eval do
42
+ define_method :draw do |x, y|
43
+ x = x - figure.pos_x
44
+ y = y - figure.pos_y
45
+
46
+ result[y] ||= []
47
+ result[y][x] = true
48
+ max_x = x if max_x < x
49
+ end
50
+ end
51
+
52
+ figure.draw
53
+
54
+ # making the size of the rows even
55
+ result.map do |row|
56
+ (row || []).tap do |row|
57
+ (0..max_x).each do |x|
58
+ row[x] = false unless row[x]
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # making a set of boolean values out of the matrix
65
+ def boolify(matrix)
66
+ matrix.map do |row|
67
+ row.map{ |cell| !!cell}
68
+ end
69
+ end
70
+ end
71
+
72
+ #
73
+ # Figures rendering matcher
74
+ #
75
+ RSpec::Matchers.define :render_blocks do |expected|
76
+ extend MatrixHelper
77
+
78
+ match do |actual|
79
+ boolify(draw_matrix(actual)) == boolify(str_to_matrix(expected))
80
+ end
81
+
82
+ failure_message_for_should do |actual|
83
+ "expected: #{matrix_to_str(str_to_matrix(expected))}\n\n" \
84
+ " got: #{matrix_to_str(draw_matrix(actual))}"
85
+ end
86
+ end
87
+
88
+
89
+ #
90
+ # a readable matrix matcher
91
+ #
92
+ RSpec::Matchers.define :have_matrix do |expected|
93
+ extend MatrixHelper
94
+
95
+ match do |actual|
96
+ boolify(actual.matrix) == boolify(str_to_matrix(expected))
97
+ end
98
+
99
+ failure_message_for_should do |actual|
100
+ "expected: #{matrix_to_str(str_to_matrix(expected))}\n\n" \
101
+ " got: #{matrix_to_str(actual.matrix)}"
102
+ end
103
+ end