Tetris 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.
- checksums.yaml +7 -0
- data/bin/tetris +5 -0
- data/lib/block.rb +45 -0
- data/lib/shape.rb +154 -0
- data/lib/shapeI.rb +38 -0
- data/lib/shapeJ.rb +20 -0
- data/lib/shapeL.rb +37 -0
- data/lib/shapeO.rb +35 -0
- data/lib/shapeS.rb +37 -0
- data/lib/shapeT.rb +37 -0
- data/lib/shapeZ.rb +14 -0
- data/lib/tetris.rb +162 -0
- data/media/Tetris.ogg +0 -0
- data/media/block.png +0 -0
- data/test/spec_helper.rb +7 -0
- data/test/test_tetris.rb +60 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7ce32b8877ef2c2a93a80eda998759003fb2c117
|
4
|
+
data.tar.gz: cddfe6415bbd8217f23d7442f35c4e76b76fc51f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 398beb852d5c477e900c663d9dc598598baf2afab355f40e3769bce2a541042496c46879cb2f65899bfb661da4ec6245d3d827b7369d48008739eabee3b46383
|
7
|
+
data.tar.gz: 771a5cca7efea5491c3a7dcc1d74a4972d44ad4a049b14d5c0e08ffad3fbf6fb31f5d04f4e35c6695d99327871c5c5bea36595366cfff27382fc2361108a8db0
|
data/bin/tetris
ADDED
data/lib/block.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'gosu'
|
3
|
+
|
4
|
+
# One block - each shape is formed by multiple blocks
|
5
|
+
class Block
|
6
|
+
attr_accessor :x, :y, :color
|
7
|
+
|
8
|
+
@@image = nil
|
9
|
+
@@width = 32
|
10
|
+
@@height = 32
|
11
|
+
|
12
|
+
def self.width
|
13
|
+
@@width
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.height
|
17
|
+
@@height
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(game, x = 0, y = 0)
|
21
|
+
@@image = Gosu::Image.new(game, '../media/block.png', false) if @@image.nil?
|
22
|
+
@@width = @@image.width unless @@image.nil?
|
23
|
+
@@height = @@image.height unless @@image.nil?
|
24
|
+
|
25
|
+
@x = x
|
26
|
+
@y = y
|
27
|
+
@game = game
|
28
|
+
|
29
|
+
# AA RR GG BB
|
30
|
+
@color = 0xFFFF0000
|
31
|
+
end
|
32
|
+
|
33
|
+
def draw
|
34
|
+
# x, y, z, factor_z, factor_y, color, mode (= :default)
|
35
|
+
@@image.draw(@x, @y, 0, 1, 1, @color)
|
36
|
+
end
|
37
|
+
|
38
|
+
def collides_with_blocks?
|
39
|
+
@game.blocks.each do |block|
|
40
|
+
return true if block.x == @x && block.y == @y
|
41
|
+
end
|
42
|
+
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
data/lib/shape.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'gosu'
|
2
|
+
|
3
|
+
# General Shape class
|
4
|
+
class Shape
|
5
|
+
attr_accessor :rotation, :x, :y
|
6
|
+
attr_reader :blocks
|
7
|
+
|
8
|
+
def initialize(game)
|
9
|
+
@game = game
|
10
|
+
@x = @y = 0
|
11
|
+
@blocks = []
|
12
|
+
|
13
|
+
# is the shape currently falling?
|
14
|
+
@falling = true
|
15
|
+
|
16
|
+
# by which block to rotate
|
17
|
+
@rotation_block = @blocks[1]
|
18
|
+
|
19
|
+
# how many rotation states are there
|
20
|
+
@rotations = 1
|
21
|
+
|
22
|
+
# rotation state
|
23
|
+
@rotation = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def update
|
27
|
+
return unless @falling
|
28
|
+
|
29
|
+
old_x = @x
|
30
|
+
old_y = @y
|
31
|
+
|
32
|
+
# first handle x change (handle keyboard - movement -
|
33
|
+
# and check for collision)
|
34
|
+
if update_move_x
|
35
|
+
if @game.button_down?(Gosu::KbLeft)
|
36
|
+
@x -= 32
|
37
|
+
elsif @game.button_down?(Gosu::KbRight)
|
38
|
+
@x += 32
|
39
|
+
end
|
40
|
+
|
41
|
+
@x = old_x if collides?
|
42
|
+
end
|
43
|
+
|
44
|
+
# then handle y change, so we can set @falling to false
|
45
|
+
# game speed check
|
46
|
+
|
47
|
+
if update_move_y
|
48
|
+
@y += 32
|
49
|
+
|
50
|
+
if collides?
|
51
|
+
@y = old_y
|
52
|
+
@falling = false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Rotates the shape
|
58
|
+
def rotate
|
59
|
+
return if @rotation_block.nil?
|
60
|
+
|
61
|
+
(1..@rotation % @rotations).each do |_i|
|
62
|
+
@blocks.each do |block|
|
63
|
+
old_x = block.x
|
64
|
+
old_y = block.y
|
65
|
+
block.x = @rotation_block.x + (@rotation_block.y - old_y)
|
66
|
+
block.y = @rotation_block.y - (@rotation_block.x - old_x)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def shape_to_array
|
72
|
+
blocks_array = []
|
73
|
+
get_blocks.each do |block|
|
74
|
+
blocks_array << [block.x, block.y]
|
75
|
+
end
|
76
|
+
blocks_array
|
77
|
+
end
|
78
|
+
|
79
|
+
# updates move counter
|
80
|
+
def update_move_x
|
81
|
+
if @game.elapsed_seconds > @game.last_move_x + 0.05
|
82
|
+
@game.last_move_x = @game.elapsed_seconds
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def update_move_y
|
87
|
+
if @game.button_down?(Gosu::KbDown)
|
88
|
+
@game.game_speed *= 0.2
|
89
|
+
else
|
90
|
+
@game.game_speed = 1
|
91
|
+
end
|
92
|
+
|
93
|
+
if @game.elapsed_seconds > @game.last_move_y + @game.game_speed
|
94
|
+
@game.last_move_y = @game.elapsed_seconds
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def collides?
|
99
|
+
if collides_with_walls? || collides_with_blocks?
|
100
|
+
return true
|
101
|
+
end
|
102
|
+
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
def collides_with_walls?
|
107
|
+
max_y = maximum_y_block
|
108
|
+
min_x = minimum_x_block
|
109
|
+
max_x = maximum_x_block
|
110
|
+
|
111
|
+
min_x.x < 0 || max_x.x >= @game.screen_width || max_y.y + Block.height > @game.screen_height
|
112
|
+
end
|
113
|
+
|
114
|
+
def collides_with_blocks?
|
115
|
+
get_blocks.each do |block|
|
116
|
+
return true if block.collides_with_blocks?
|
117
|
+
end
|
118
|
+
|
119
|
+
false
|
120
|
+
end
|
121
|
+
|
122
|
+
# note that maximum y means the lowest block!
|
123
|
+
def maximum_y_block
|
124
|
+
get_blocks.max_by(&:y)
|
125
|
+
end
|
126
|
+
|
127
|
+
def minimum_x_block
|
128
|
+
get_blocks.min_by(&:x)
|
129
|
+
end
|
130
|
+
|
131
|
+
def maximum_x_block
|
132
|
+
get_blocks.max_by(&:x)
|
133
|
+
end
|
134
|
+
|
135
|
+
# flips the shape by y axis
|
136
|
+
def translate_by_y
|
137
|
+
min = @blocks.min_by(&:x)
|
138
|
+
max = @blocks.max_by(&:x)
|
139
|
+
center = (min.x + max.x) / 2.0
|
140
|
+
@blocks.each do |block|
|
141
|
+
block.x = 2 * center - block.x - Block.width
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def draw
|
146
|
+
get_blocks.each(&:draw)
|
147
|
+
end
|
148
|
+
|
149
|
+
def falling?
|
150
|
+
@falling
|
151
|
+
end
|
152
|
+
|
153
|
+
attr_writer :falling
|
154
|
+
end
|
data/lib/shapeI.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'shape.rb'
|
2
|
+
require 'block.rb'
|
3
|
+
|
4
|
+
# [ ]
|
5
|
+
# [ ]
|
6
|
+
# [ ]
|
7
|
+
# [ ]
|
8
|
+
class ShapeI < Shape
|
9
|
+
def initialize(game)
|
10
|
+
super(game)
|
11
|
+
|
12
|
+
@blocks[0] = Block.new(game)
|
13
|
+
@blocks[1] = Block.new(game)
|
14
|
+
@blocks[2] = Block.new(game)
|
15
|
+
@blocks[3] = Block.new(game)
|
16
|
+
|
17
|
+
@rotation_block = @blocks[1]
|
18
|
+
@rotations = 2
|
19
|
+
|
20
|
+
@blocks.each do |block|
|
21
|
+
block.color = 0xFFFF0000
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_blocks
|
26
|
+
@blocks[0].x = @x
|
27
|
+
@blocks[1].x = @x
|
28
|
+
@blocks[2].x = @x
|
29
|
+
@blocks[3].x = @x
|
30
|
+
|
31
|
+
@blocks[0].y = @y
|
32
|
+
@blocks[1].y = @blocks[0].y + Block.height
|
33
|
+
@blocks[2].y = @blocks[1].y + Block.height
|
34
|
+
@blocks[3].y = @blocks[2].y + Block.height
|
35
|
+
rotate
|
36
|
+
@blocks
|
37
|
+
end
|
38
|
+
end
|
data/lib/shapeJ.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'shapeL.rb'
|
2
|
+
require 'block.rb'
|
3
|
+
|
4
|
+
# [ ]
|
5
|
+
# [ ]
|
6
|
+
# [ ][ ]
|
7
|
+
class ShapeJ < ShapeL
|
8
|
+
def get_blocks
|
9
|
+
old_rotation = @rotation
|
10
|
+
@rotation = 0
|
11
|
+
|
12
|
+
super
|
13
|
+
translate_by_y
|
14
|
+
|
15
|
+
@rotation = old_rotation
|
16
|
+
rotate
|
17
|
+
|
18
|
+
@blocks
|
19
|
+
end
|
20
|
+
end
|
data/lib/shapeL.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'shape.rb'
|
2
|
+
require 'block.rb'
|
3
|
+
|
4
|
+
# [ ]
|
5
|
+
# [ ]
|
6
|
+
# [ ][ ]
|
7
|
+
class ShapeL < Shape
|
8
|
+
def initialize(game)
|
9
|
+
super(game)
|
10
|
+
|
11
|
+
@blocks[0] = Block.new(game)
|
12
|
+
@blocks[1] = Block.new(game)
|
13
|
+
@blocks[2] = Block.new(game)
|
14
|
+
@blocks[3] = Block.new(game)
|
15
|
+
|
16
|
+
@rotation_block = @blocks[1]
|
17
|
+
@rotations = 4
|
18
|
+
|
19
|
+
@blocks.each do |block|
|
20
|
+
block.color = 0xFF0000FF
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_blocks
|
25
|
+
@blocks[0].x = @x
|
26
|
+
@blocks[1].x = @x
|
27
|
+
@blocks[2].x = @x
|
28
|
+
@blocks[3].x = @x + Block.width
|
29
|
+
|
30
|
+
@blocks[0].y = @y
|
31
|
+
@blocks[1].y = @blocks[0].y + Block.height
|
32
|
+
@blocks[2].y = @blocks[1].y + Block.height
|
33
|
+
@blocks[3].y = @blocks[2].y
|
34
|
+
rotate
|
35
|
+
@blocks
|
36
|
+
end
|
37
|
+
end
|
data/lib/shapeO.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'shape.rb'
|
2
|
+
require 'block.rb'
|
3
|
+
|
4
|
+
# [ ][ ]
|
5
|
+
# [ ][ ]
|
6
|
+
class ShapeO < Shape
|
7
|
+
def initialize(game)
|
8
|
+
super(game)
|
9
|
+
|
10
|
+
@blocks[0] = Block.new(game)
|
11
|
+
@blocks[1] = Block.new(game)
|
12
|
+
@blocks[2] = Block.new(game)
|
13
|
+
@blocks[3] = Block.new(game)
|
14
|
+
|
15
|
+
@rotations = 1
|
16
|
+
|
17
|
+
@blocks.each do |block|
|
18
|
+
block.color = 0xFF00FF00
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_blocks
|
23
|
+
@blocks[0].x = @x
|
24
|
+
@blocks[1].x = @x + Block.width
|
25
|
+
@blocks[2].x = @x
|
26
|
+
@blocks[3].x = @x + Block.width
|
27
|
+
|
28
|
+
@blocks[0].y = @y
|
29
|
+
@blocks[1].y = @y
|
30
|
+
@blocks[2].y = @blocks[0].y + Block.height
|
31
|
+
@blocks[3].y = @blocks[1].y + Block.height
|
32
|
+
|
33
|
+
@blocks
|
34
|
+
end
|
35
|
+
end
|
data/lib/shapeS.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'shape.rb'
|
2
|
+
require 'block.rb'
|
3
|
+
|
4
|
+
# [ ]
|
5
|
+
# [ ][ ]
|
6
|
+
# [ ]
|
7
|
+
class ShapeS < Shape
|
8
|
+
def initialize(game)
|
9
|
+
super(game)
|
10
|
+
|
11
|
+
@blocks[0] = Block.new(game)
|
12
|
+
@blocks[1] = Block.new(game)
|
13
|
+
@blocks[2] = Block.new(game)
|
14
|
+
@blocks[3] = Block.new(game)
|
15
|
+
|
16
|
+
@rotation_block = @blocks[1]
|
17
|
+
@rotations = 2
|
18
|
+
|
19
|
+
@blocks.each do |block|
|
20
|
+
block.color = 0xFFFFFF00
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_blocks
|
25
|
+
@blocks[0].x = @x
|
26
|
+
@blocks[1].x = @x
|
27
|
+
@blocks[2].x = @x + Block.width
|
28
|
+
@blocks[3].x = @x + Block.width
|
29
|
+
|
30
|
+
@blocks[0].y = @y
|
31
|
+
@blocks[1].y = @blocks[0].y + Block.height
|
32
|
+
@blocks[2].y = @blocks[0].y + Block.height
|
33
|
+
@blocks[3].y = @blocks[2].y + Block.height
|
34
|
+
rotate
|
35
|
+
@blocks
|
36
|
+
end
|
37
|
+
end
|
data/lib/shapeT.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'shape.rb'
|
2
|
+
require 'block.rb'
|
3
|
+
|
4
|
+
# [ ][ ][ ]
|
5
|
+
# [ ]
|
6
|
+
# [ ]
|
7
|
+
class ShapeT < Shape
|
8
|
+
def initialize(game)
|
9
|
+
super(game)
|
10
|
+
|
11
|
+
@blocks[0] = Block.new(game)
|
12
|
+
@blocks[1] = Block.new(game)
|
13
|
+
@blocks[2] = Block.new(game)
|
14
|
+
@blocks[3] = Block.new(game)
|
15
|
+
|
16
|
+
@rotation_block = @blocks[1]
|
17
|
+
@rotations = 4
|
18
|
+
|
19
|
+
@blocks.each do |block|
|
20
|
+
block.color = 0xFF00FFFF
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_blocks
|
25
|
+
@blocks[0].x = @x
|
26
|
+
@blocks[1].x = @x + Block.width
|
27
|
+
@blocks[2].x = @blocks[1].x + Block.width
|
28
|
+
@blocks[3].x = @x + Block.width
|
29
|
+
|
30
|
+
@blocks[0].y = @y
|
31
|
+
@blocks[1].y = @y
|
32
|
+
@blocks[2].y = @y
|
33
|
+
@blocks[3].y = @y + Block.height
|
34
|
+
rotate
|
35
|
+
@blocks
|
36
|
+
end
|
37
|
+
end
|
data/lib/shapeZ.rb
ADDED
data/lib/tetris.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'gosu'
|
3
|
+
require 'block.rb'
|
4
|
+
require 'shapeL.rb'
|
5
|
+
require 'shapeJ.rb'
|
6
|
+
require 'shapeI.rb'
|
7
|
+
require 'shapeO.rb'
|
8
|
+
require 'shapeT.rb'
|
9
|
+
require 'shapeZ.rb'
|
10
|
+
require 'shapeS.rb'
|
11
|
+
|
12
|
+
# Main class for Tetris.
|
13
|
+
class Game < Gosu::Window
|
14
|
+
attr_accessor :blocks, :current_shape, :score, :fall_speed, :game_state
|
15
|
+
attr_accessor :game_speed, :screen_width, :screen_height, :elapsed_seconds
|
16
|
+
attr_accessor :last_move_x, :last_move_y
|
17
|
+
|
18
|
+
attr_reader :shape
|
19
|
+
|
20
|
+
STATE_PLAYING = 1
|
21
|
+
STATE_END = 2
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@screen_width = 320
|
25
|
+
@screen_height = 640
|
26
|
+
|
27
|
+
super(@screen_width, @screen_height, false)
|
28
|
+
@score = 0
|
29
|
+
@blocks = []
|
30
|
+
@game_state = STATE_END
|
31
|
+
@game_speed = 1 # the lower, the faster
|
32
|
+
|
33
|
+
@game_over_text = Gosu::Image.from_text(self, 'Game Over', 'Arial', 40)
|
34
|
+
@press_space_text = Gosu::Image.from_text(self, 'Press SPACE to start a new game', 'Arial', 15)
|
35
|
+
@music = Gosu::Song.new('../media/Tetris.ogg')
|
36
|
+
|
37
|
+
self.caption = "Tetris! (#{@score} Points)"
|
38
|
+
@shape = nil
|
39
|
+
@elapsed_seconds = 0
|
40
|
+
@last_move_y = 0
|
41
|
+
@last_move_x = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def draw
|
45
|
+
@blocks.each(&:draw)
|
46
|
+
@shape.draw unless @shape.nil?
|
47
|
+
return if @game_state != STATE_END
|
48
|
+
|
49
|
+
@game_over_text.draw(@screen_width / 2 - 75, @screen_height / 2 - 40, 0)
|
50
|
+
@press_space_text.draw(@screen_width / 2 - 85, @screen_height / 2, 0)
|
51
|
+
end
|
52
|
+
|
53
|
+
# this is a callback for key up events or equivalent (there are
|
54
|
+
# constants for gamepad buttons and mouse clicks)
|
55
|
+
def button_up(key)
|
56
|
+
close if key == Gosu::KbEscape
|
57
|
+
end
|
58
|
+
|
59
|
+
# better responsiveness than button_up (rotates the shape immediately)
|
60
|
+
def button_down(key)
|
61
|
+
if @game_state == STATE_END
|
62
|
+
|
63
|
+
# (re)start the game
|
64
|
+
if key == Gosu::KbSpace
|
65
|
+
@blocks = []
|
66
|
+
@score = 0
|
67
|
+
@shape = nil
|
68
|
+
self.caption = "Tetris! (#{@score} Points)"
|
69
|
+
@game_state = STATE_PLAYING
|
70
|
+
@music.stop
|
71
|
+
|
72
|
+
# true indicates whether it should loop
|
73
|
+
@music.play(true)
|
74
|
+
end
|
75
|
+
elsif @game_state == STATE_PLAYING
|
76
|
+
if key == Gosu::KbSpace && !@shape.nil?
|
77
|
+
@shape.rotation += 1
|
78
|
+
@shape.rotation -= 1 if @shape.collides?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# deletes complete lines
|
84
|
+
# returns the number of deleted lines
|
85
|
+
def delete_lines
|
86
|
+
lines_to_delete = []
|
87
|
+
|
88
|
+
@shape.get_blocks.each do |block|
|
89
|
+
sum = @blocks.count { |x| x.y == block.y }
|
90
|
+
if sum == @screen_width / Block.width
|
91
|
+
lines_to_delete << block.y
|
92
|
+
@blocks.delete_if { |x| x.y == block.y }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
update_score(lines_to_delete)
|
97
|
+
move_blocks_down(lines_to_delete)
|
98
|
+
|
99
|
+
lines_to_delete
|
100
|
+
end
|
101
|
+
|
102
|
+
# updates score
|
103
|
+
def update_score(lines)
|
104
|
+
return if lines.empty?
|
105
|
+
|
106
|
+
@score += 2**(lines.size - 1)
|
107
|
+
self.caption = "Tetris! (#{@score} Points)"
|
108
|
+
end
|
109
|
+
|
110
|
+
# moves appropriate blocks down (typically after lines removal)
|
111
|
+
def move_blocks_down(lines)
|
112
|
+
return if lines.empty?
|
113
|
+
|
114
|
+
@blocks.each do |block|
|
115
|
+
block.y += lines.size * Block.height if block.y < lines.min
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def update
|
120
|
+
update_delta
|
121
|
+
|
122
|
+
return if @game_state != STATE_PLAYING
|
123
|
+
|
124
|
+
# do we have a falling shape?
|
125
|
+
if !@shape.nil? && @shape.falling?
|
126
|
+
@shape.update
|
127
|
+
else
|
128
|
+
spawn_shape
|
129
|
+
@game_state = STATE_END if @shape.collides?
|
130
|
+
end
|
131
|
+
# with a delta we need to express the speed of our entities in
|
132
|
+
# terms of pixels/second
|
133
|
+
end
|
134
|
+
|
135
|
+
def update_delta
|
136
|
+
# Gosu::millisecodns returns the time since the window was created
|
137
|
+
# Divide by 1000 since we want to work in seconds
|
138
|
+
@current_time = Gosu.milliseconds / 1000.0
|
139
|
+
# clamping here is important to avoid strange behaviors
|
140
|
+
@delta = [@current_time - @elapsed_seconds, 0.25].min
|
141
|
+
@elapsed_seconds = @current_time
|
142
|
+
end
|
143
|
+
|
144
|
+
# Spawns a new shape
|
145
|
+
def spawn_shape
|
146
|
+
# Add the previous shape to the block list
|
147
|
+
unless @shape.nil?
|
148
|
+
@shape.get_blocks.each do |block|
|
149
|
+
@blocks << block
|
150
|
+
end
|
151
|
+
|
152
|
+
# erase completed lines
|
153
|
+
delete_lines
|
154
|
+
end
|
155
|
+
|
156
|
+
shapes = [ShapeJ.new(self), ShapeL.new(self), ShapeI.new(self), ShapeO.new(self), ShapeT.new(self), ShapeZ.new(self), ShapeS.new(self)]
|
157
|
+
@shape = shapes.sample
|
158
|
+
|
159
|
+
@shape.x = Block.width * 4
|
160
|
+
@shape.get_blocks
|
161
|
+
end
|
162
|
+
end
|
data/media/Tetris.ogg
ADDED
Binary file
|
data/media/block.png
ADDED
Binary file
|
data/test/spec_helper.rb
ADDED
data/test/test_tetris.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/tetris'
|
3
|
+
require 'gosu'
|
4
|
+
|
5
|
+
describe Game do
|
6
|
+
before(:each) do
|
7
|
+
@game = Game.new
|
8
|
+
@game.button_down(Gosu::KbSpace) # start the game
|
9
|
+
@game.update
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'Initial score is 0' do
|
13
|
+
it { expect(@game.score).to eq 0 }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'There are no blocks at the beginning' do
|
17
|
+
it { expect(@game.blocks).to be_empty }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'A shape has spawned after the game has been started' do
|
21
|
+
it 'spawns a shape' do
|
22
|
+
expect(@game.shape).to_not be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'No lines should be deleted at the beginning of the game' do
|
27
|
+
it 'Doesn\'t delete any lines' do
|
28
|
+
expect(@game.delete_lines.size).to eq 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'The game should delete complete lines' do
|
33
|
+
before(:each) do
|
34
|
+
@game.shape.y = @game.screen_height
|
35
|
+
|
36
|
+
(0..(@game.screen_width - Block.width)).step(Block.width) do |i|
|
37
|
+
@game.blocks << Block.new(@game, i, @game.screen_height)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'Correctly deletes lines' do
|
42
|
+
expect(@game.delete_lines.size).to eq 1
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'Correctly increases score' do
|
46
|
+
expect{@game.delete_lines}.to change{@game.score}.from(0).to(1)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'Player should be able to lose' do
|
51
|
+
it 'Player loses' do
|
52
|
+
(0..(@game.screen_width - Block.width)).step(Block.width) do |i|
|
53
|
+
@game.blocks << Block.new(@game, Block.width * 4, i)
|
54
|
+
end
|
55
|
+
|
56
|
+
@game.shape.falling = false
|
57
|
+
expect{@game.update}.to change{@game.game_state}.from(1).to(2)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Tetris
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Filip Vondrasek
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple Tetris game
|
14
|
+
email: filip@vondrasek.net
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- bin/tetris
|
20
|
+
- lib/block.rb
|
21
|
+
- lib/shape.rb
|
22
|
+
- lib/shapeI.rb
|
23
|
+
- lib/shapeJ.rb
|
24
|
+
- lib/shapeL.rb
|
25
|
+
- lib/shapeO.rb
|
26
|
+
- lib/shapeS.rb
|
27
|
+
- lib/shapeT.rb
|
28
|
+
- lib/shapeZ.rb
|
29
|
+
- lib/tetris.rb
|
30
|
+
- media/Tetris.ogg
|
31
|
+
- media/block.png
|
32
|
+
- test/spec_helper.rb
|
33
|
+
- test/test_tetris.rb
|
34
|
+
homepage: ''
|
35
|
+
licenses:
|
36
|
+
- WTFPL
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 2.4.5
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: A simple Tetris game
|
58
|
+
test_files: []
|