Tetris 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|