btetris_kp 0.0.1

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,118 @@
1
+ require 'btetris_kp/menu'
2
+ require 'btetris_kp/core/board'
3
+ require 'btetris_kp/constants'
4
+
5
+ module BTetrisKp
6
+ # class representing game window state
7
+ class GameState
8
+ attr_reader :game_over, :paused
9
+ attr_accessor :rows_cleared
10
+
11
+ def initialize(window, x, y)
12
+ @window = window
13
+ @font = Gosu::Font.new(@window, Gosu.default_font_name, 80)
14
+ @board = Board.new(@window, x, y)
15
+ @paused, @game_over = false, false
16
+ # press time, set 0 when left, right or down key is pressed
17
+ # used for smoother controls in update method
18
+ @counter, @press_time, @rows_cleared = 0, 0, 0
19
+ initialize_sounds
20
+ end
21
+
22
+ # initializes ingame sounds
23
+ def initialize_sounds
24
+ @drop_snd = Gosu::Sample.new(@window, Const::PATH_SND_DROP)
25
+ @clear_snd = Gosu::Sample.new(@window, Const::PATH_SND_POP)
26
+ @rotate_snd = Gosu::Sample.new(@window, Const::PATH_SND_ROTATE)
27
+ end
28
+
29
+ # inserts garbage in board depending on count of cleared rows
30
+ def insert_garbage!(cnt)
31
+ @board.insert_garbage!(cnt)
32
+ end
33
+
34
+ # returns game board in a string (current piece included)
35
+ def get_board_s
36
+ @board.get_board_s
37
+ end
38
+
39
+ # switches pause boolean
40
+ def pause!
41
+ @paused = !@paused
42
+ end
43
+
44
+ # updates game
45
+ def update
46
+ unless @paused || @game_over
47
+ @press_time += 1
48
+ @counter += 1
49
+
50
+ if @press_time % Const::DROP_SPEED == 0
51
+ # check if KbDown is pressed, move piece down in faster interval
52
+ @board.piece_down! if @window.button_down?(Gosu::KbDown)
53
+ end
54
+ if @press_time % Const::TURN_SPEED == 0
55
+ # check if KbLeft is pressed, move piece left in faster interval
56
+ @board.piece_left! if @window.button_down?(Gosu::KbLeft)
57
+ # check if KbRight is pressed, move piece right in faster interval
58
+ @board.piece_right! if @window.button_down?(Gosu::KbRight)
59
+ end
60
+
61
+ # checks if it's time for next step in game
62
+ if @counter % Const::GAME_SPEED == 0
63
+ @rows_cleared = @board.next_step!
64
+ @clear_snd.play if @rows_cleared > 0
65
+ # checks if it's game over
66
+ @game_over = @board.game_over?
67
+ end
68
+ end
69
+ end
70
+
71
+ # draws board and texts if paused or game over
72
+ def draw
73
+ @board.draw
74
+ x = @window.width / 2
75
+ y = @window.height / 2 - 30
76
+ @font.draw(Const::PAUSE_CAPTION, x - 130, y, 0) if @paused
77
+ @font.draw(Const::GAME_OVER_CAPTION, x - 170, y, 0) if @game_over
78
+ end
79
+
80
+ # button handler
81
+ def button_down(id)
82
+ if @game_over || @paused
83
+ pause! if @paused && id == Gosu::KbP
84
+ if @game_over && id == Gosu::KbEscape
85
+ @window.state = MenuState.new(@window)
86
+ end
87
+ else
88
+ # handling specific events
89
+ case id
90
+ when Gosu::KbEscape
91
+ @window.state = MenuState.new(@window)
92
+ when Gosu::KbP
93
+ pause!
94
+ when Gosu::KbLeft
95
+ @board.piece_left!
96
+ @press_time = 0
97
+ when Gosu::KbRight
98
+ @board.piece_right!
99
+ @press_time = 0
100
+ when Gosu::KbDown
101
+ @board.piece_down!
102
+ @press_time = 0
103
+ when Gosu::KbUp
104
+ @rotate_snd.play if @board.piece_rotate!
105
+ when Gosu::KbSpace
106
+ @board.piece_drop!
107
+ @drop_snd.play
108
+ @counter = Const::GAME_SPEED - 10
109
+ end
110
+ end
111
+ end
112
+
113
+ # hides default system cursor
114
+ def needs_cursor?
115
+ true
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,44 @@
1
+ require 'btetris_kp/constants'
2
+
3
+ module BTetrisKp
4
+ # class representing menu item
5
+ class MenuItem
6
+ def initialize(window, text, id, callback, font, x, y)
7
+ @window = window
8
+ @text = text
9
+ @callback = callback
10
+ @font = font
11
+ @color = Const::MENU_ITEM_CLR
12
+ @x = x
13
+ @y = y + id * (@font.height + 5)
14
+ end
15
+
16
+ # updates menu item, changes item color depending on mouse_over?
17
+ def update
18
+ if mouse_over?
19
+ @color = Const::MENU_ITEM_MO_CLR
20
+ else
21
+ @color = Const::MENU_ITEM_CLR
22
+ end
23
+ end
24
+
25
+ # returns true if mouse is over menu item
26
+ def mouse_over?
27
+ mx = @window.mouse_x
28
+ my = @window.mouse_y
29
+ (mx >= @x && my >= @y) &&
30
+ (mx <= @x + @font.text_width(@text)) &&
31
+ (my <= @y + @font.height)
32
+ end
33
+
34
+ # returns true menu item is clicked (click + mouse_over)
35
+ def clicked
36
+ @callback.call if mouse_over?
37
+ end
38
+
39
+ # draws menuitem on window (gosu)
40
+ def draw
41
+ @font.draw(@text, @x, @y, 0, 1, 1, @color)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,66 @@
1
+ require 'gosu'
2
+ require 'btetris_kp/constants'
3
+
4
+ module BTetrisKp
5
+ # class representing input box for IP adress / port / text
6
+ class TextField < Gosu::TextInput
7
+ attr_reader :x, :y
8
+
9
+ def initialize(window, font, x, y, text, filter, width, maxlen)
10
+ super()
11
+ @window = window
12
+ @font = font
13
+ @x = x
14
+ @y = y
15
+ @width = width
16
+ @filter = filter
17
+ @maxlen = maxlen
18
+ self.text = text
19
+ end
20
+
21
+ # filters text of characters specified in regexp
22
+ def filter(text)
23
+ text.upcase.gsub(@filter, '')
24
+ end
25
+
26
+ # updates textfield, limits length of text to @maxlen
27
+ def update
28
+ self.text = text.slice(0...@maxlen) if text.size > @maxlen
29
+ end
30
+
31
+ # draws textfield on window (gosu)
32
+ def draw
33
+ @window.draw_line(@x - Const::BORDER_GAP, @y + @font.height, Const::CARET_CLR,
34
+ @x + @width + 2 * Const::BORDER_GAP, @y + @font.height, Const::CARET_CLR)
35
+ unless text.nil?
36
+ # draws the caret if textfield is selected
37
+ pos_x = @x + @font.text_width(text[0...caret_pos])
38
+ if @window.text_input == self
39
+ @window.draw_line(pos_x, @y, Const::CARET_CLR,
40
+ pos_x, @y + @font.height, Const::CARET_CLR, 0)
41
+ end
42
+
43
+ @font.draw(text, @x, @y, 0)
44
+ end
45
+ end
46
+
47
+ # returns true if mouse is over textfield
48
+ def mouse_over?(mouse_x, mouse_y)
49
+ mouse_x >= x - Const::BORDER_GAP &&
50
+ mouse_x <= x + @width + Const::BORDER_GAP &&
51
+ mouse_y >= y - Const::BORDER_GAP &&
52
+ mouse_y <= y + @font.height + Const::BORDER_GAP
53
+ end
54
+
55
+ # moves the caret to the position specified by mouse
56
+ def move_caret(mouse_x)
57
+ 1.upto(text.length) do |i|
58
+ if mouse_x < x + @font.text_width(text[0...i])
59
+ self.caret_pos = self.selection_start = i - 1
60
+ return
61
+ end
62
+ end
63
+ self.caret_pos = self.selection_start = text.length
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,83 @@
1
+ require 'btetris_kp/game'
2
+ require 'btetris_kp/core/board'
3
+ require 'btetris_kp/gui/menuitem'
4
+ require 'btetris_kp/netsetup'
5
+ require 'btetris_kp/netjoin'
6
+ require 'btetris_kp/constants'
7
+
8
+ module BTetrisKp
9
+ # class representing main menu window state
10
+ class MenuState
11
+ def initialize(window)
12
+ @window = window
13
+ @font = Gosu::Font.new(@window, Gosu.default_font_name, 30)
14
+ initialize_title_image
15
+ generate_back_board
16
+ generate_menu
17
+ end
18
+
19
+ # loads title image and calculates position, size variables
20
+ def initialize_title_image
21
+ @title_image = Gosu::Image.new(@window, Const::PATH_IMAGE_TITLE, false)
22
+ @img_size_factor = (@window.width - 50.0) / @title_image.width
23
+ @img_x = (@window.width - @title_image.width * @img_size_factor) / 2
24
+ @img_y = 20
25
+ end
26
+
27
+ # generates background board
28
+ def generate_back_board
29
+ @board = Board.new(@window, @window.width / 3, 40)
30
+ nice_board = Array.new(Const::PNR_VER) { Array.new(Const::PNR_HOR, 0) }
31
+ nice_board.each_with_index do |row, x|
32
+ if x > Const::PNR_VER / 2
33
+ row.each_with_index do |val, y|
34
+ nice_board[x][y] = rand(Const::TILE_COLORS_NR + 1)
35
+ end
36
+ end
37
+ end
38
+ @board.board = nice_board
39
+ end
40
+
41
+ # generates menu, menu items and callback procedures for menu items
42
+ def generate_menu
43
+ @items = []
44
+ @x = @window.width / 3 + 30
45
+ @y = @title_image.height
46
+ n_g = proc { @window.state = GameState.new(@window, @window.width / 3, 40) }
47
+ cr_n = proc { @window.state = NetSetupState.new(@window) }
48
+ j_n = proc { @window.state = NetJoinState.new(@window) }
49
+ exit = proc { @window.close }
50
+ @items << MenuItem.new(@window, Const::MENU_NEW, 0, n_g, @font, @x, @y)
51
+ @items << MenuItem.new(@window, Const::MENU_CREATE, 1, cr_n, @font, @x, @y)
52
+ @items << MenuItem.new(@window, Const::MENU_JOIN, 2, j_n, @font, @x, @y)
53
+ @items << MenuItem.new(@window, Const::MENU_QUIT, 3, exit, @font, @x, @y)
54
+ end
55
+
56
+ # updates menu
57
+ def update
58
+ @items.each { |i| i.update }
59
+ end
60
+
61
+ # draws menu
62
+ def draw
63
+ @board.draw
64
+ @items.each { |i| i.draw }
65
+ @title_image.draw(@img_x, @img_y, 1, @img_size_factor, @img_size_factor)
66
+ end
67
+
68
+ # button handler
69
+ def button_down(id)
70
+ case id
71
+ when Gosu::KbEscape
72
+ @window.close
73
+ when Gosu::MsLeft then
74
+ @items.each { |i| i.clicked }
75
+ end
76
+ end
77
+
78
+ # shows default system cursor
79
+ def needs_cursor?
80
+ true
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,152 @@
1
+ require 'btetris_kp/menu'
2
+ require 'btetris_kp/core/board'
3
+ require 'btetris_kp/game'
4
+ require 'btetris_kp/constants'
5
+
6
+ module BTetrisKp
7
+ # class representing net game window state
8
+ # used for playin game over network (no matter if you are client or server)
9
+ class NetGameState
10
+ def initialize(window, socket)
11
+ @window = window
12
+ @socket = socket
13
+ @font = Gosu::Font.new(@window, Gosu.default_font_name, 80)
14
+ @game = GameState.new(@window, 10, 40)
15
+ @o_board = Board.new(@window, 2 * @window.width / 3 - 10, 40)
16
+ @winner = 0
17
+ end
18
+
19
+ # sends message to socket
20
+ def send_msg(msg)
21
+ s = ''
22
+ case msg
23
+ when Const::MSG_PAUSE
24
+ s = "#{Const::MSG_PAUSE}"
25
+ when Const::MSG_GAME_OVER
26
+ s = "#{Const::MSG_GAME_OVER}"
27
+ when Const::MSG_BOARD
28
+ s = "#{Const::MSG_BOARD}:#{@game.get_board_s}"
29
+ when Const::MSG_GARBAGE
30
+ s = "#{Const::MSG_GARBAGE}:#{@game.rows_cleared}"
31
+ end
32
+ begin
33
+ @socket.sendmsg_nonblock(s)
34
+ rescue
35
+ # problem s pripojenim -- game over
36
+ @socket.close
37
+ @window.state = MenuState.new(@window)
38
+ end
39
+ end
40
+
41
+ # blocking recv for cnt of bytes
42
+ def recv_block(cnt)
43
+ block = nil
44
+ begin
45
+ block = @socket.recv(cnt, Socket::MSG_WAITALL)
46
+ rescue
47
+ # problem s pripojenim -- game over
48
+ @socket.close
49
+ @window.state = MenuState.new(@window)
50
+ end
51
+ block
52
+ end
53
+
54
+ # recieves and manages game updates
55
+ def recv_msg(id)
56
+ case id
57
+ when Const::MSG_PAUSE
58
+ @game.pause!
59
+ when Const::MSG_GAME_OVER
60
+ @winner = Const::GAME_WON
61
+ when Const::MSG_BOARD
62
+ recv_block(1)
63
+ board = recv_block(Const::PNR_HOR * Const::PNR_VER)
64
+ @o_board.from_s!(board) unless board.nil?
65
+ when Const::MSG_GARBAGE
66
+ recv_block(1)
67
+ garbage = recv_block(1).to_i
68
+ @game.insert_garbage!(garbage)
69
+ end
70
+ end
71
+
72
+ # checks if there is incomming message
73
+ # returns a string with message id, or GOT_NO_MESSAGE
74
+ def check_msg
75
+ begin
76
+ s = @socket.recv_nonblock(1)
77
+ return s
78
+ rescue Errno::EAGAIN
79
+ # zadna zprava, hrajeme dal
80
+ rescue
81
+ # problem s pripojenim -- game over
82
+ @socket.close
83
+ @window.state = MenuState.new(@window)
84
+ rescue
85
+ end
86
+ Const::GOT_NO_MESSAGE
87
+ end
88
+
89
+ # updates net game state
90
+ def update
91
+ if @winner == Const::GAME_ON
92
+ until (m = check_msg) == Const::GOT_NO_MESSAGE
93
+ recv_msg(m)
94
+ end
95
+ unless @game.paused
96
+ @game.update
97
+ send_msg(Const::MSG_BOARD)
98
+ if @game.game_over
99
+ send_msg(Const::MSG_GAME_OVER)
100
+ @winner = Const::GAME_LOST
101
+ end
102
+ if @game.rows_cleared > 0
103
+ send_msg(Const::MSG_GARBAGE)
104
+ @game.rows_cleared = 0
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ # draws net game window (contains normal game + opponents board)
111
+ def draw
112
+ if @winner == Const::GAME_ON
113
+ @game.draw
114
+ @o_board.draw
115
+ elsif @winner == Const::GAME_LOST
116
+ @font.draw(Const::GAME_LOST_CAPTION,
117
+ @window.width / 2 - 170, @window.height / 2 - 30, 0)
118
+ else
119
+ @font.draw(Const::GAME_WON_CAPTION,
120
+ @window.width / 2 - 170, @window.height / 2 - 30, 0)
121
+ end
122
+ end
123
+
124
+ # button handler
125
+ def button_down(id)
126
+ if @winner == Const::GAME_ON
127
+ case id
128
+ when Gosu::KbP
129
+ send_msg(Const::MSG_PAUSE)
130
+ @game.pause!
131
+ when Gosu::KbEscape
132
+ send_msg(Const::MSG_GAME_OVER)
133
+ sleep(0.2)
134
+ @socket.close
135
+ @window.state = MenuState.new(@window)
136
+ else
137
+ @game.button_down(id)
138
+ end
139
+ else
140
+ if id == Gosu::KbEscape
141
+ @socket.close
142
+ @window.state = MenuState.new(@window)
143
+ end
144
+ end
145
+ end
146
+
147
+ # hides default system cursor
148
+ def needs_cursor?
149
+ true
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,115 @@
1
+ require 'socket'
2
+ require 'btetris_kp/netgame'
3
+ require 'btetris_kp/gui/textfield'
4
+ require 'btetris_kp/constants'
5
+
6
+ module BTetrisKp
7
+ # class representing a window for joining a game over network (CLIENT SIDE)
8
+ class NetJoinState
9
+ def initialize(window)
10
+ @window = window
11
+ @font = Gosu::Font.new(@window, Gosu.default_font_name, 40)
12
+ @connecting = false
13
+ initialize_title_image
14
+ initialize_textfields
15
+ @socket = nil
16
+ end
17
+
18
+ # loads title image and calculates position, size variables
19
+ def initialize_title_image
20
+ @title_image = Gosu::Image.new(@window, Const::PATH_IMAGE_TITLE, false)
21
+ @img_size_factor = (@window.width - 50.0) / @title_image.width
22
+ @img_x = (@window.width - @title_image.width * @img_size_factor) / 2
23
+ @img_y = 20
24
+ end
25
+
26
+ # initializes text fields for reading ip and port
27
+ # initializes positions for texts and textfields
28
+ def initialize_textfields
29
+ @text_x = @window.width / 3 + 10
30
+ @text_y = @window.height / 3
31
+ @ip_x = @text_x - @font.text_width(Const::IP_CAPTION) - 15
32
+ @port_x = @text_x - @font.text_width(Const::PORT_CAPTION) - 15
33
+ @text_fields = []
34
+ @text_fields << TextField.new(@window, @font, @text_x,
35
+ @text_y, Const::DEF_IP, /[^0-9.]/,
36
+ @font.text_width('000.000.000.000'), 15)
37
+ @text_fields << TextField.new(@window, @font, @text_x,
38
+ @text_y + @font.height + 10,
39
+ Const::DEF_PORT, /[^0-9]/,
40
+ @font.text_width('00000'), 5)
41
+ end
42
+
43
+ # tries to connect to server
44
+ def try_connect
45
+ begin
46
+ @socket = TCPSocket.new(@text_fields[0].text, @text_fields[1].text)
47
+ @socket.sendmsg_nonblock(Const::MSG_WELCOME)
48
+ msg = @socket.recv(1)
49
+ unless msg == Const::MSG_WELCOME
50
+ @socket.close
51
+ @socket = nil
52
+ end
53
+ rescue
54
+ # error while connecting
55
+ end
56
+ end
57
+
58
+ # updates window state, tries to connect to server, starts game when ready
59
+ def update
60
+ @text_fields.each { |t| t.update }
61
+ if @connecting
62
+ if @socket.nil?
63
+ try_connect
64
+ else
65
+ # socket created and connected, start net game
66
+ # windows text_input needs to be set on nil
67
+ # otherwise gosu wont register some keys
68
+ @window.text_input = nil
69
+ @window.state = NetGameState.new(@window, @socket)
70
+ end
71
+ end
72
+ end
73
+
74
+ # draws net join window
75
+ def draw
76
+ @text_fields.each { |t| t.draw }
77
+ @font.draw(Const::IP_CAPTION, @ip_x,
78
+ @text_y, 0)
79
+ @font.draw(Const::PORT_CAPTION, @port_x,
80
+ @text_y + @font.height + 10, 0)
81
+ @font.draw(Const::CONNECTING, @text_x,
82
+ @text_y + 3 * (@font.height + 10), 0) if @connecting
83
+ @title_image.draw(@img_x, @img_y, 1, @img_size_factor, @img_size_factor)
84
+ end
85
+
86
+ # button handler
87
+ def button_down(id)
88
+ case id
89
+ when Gosu::KbEscape
90
+ if @connecting
91
+ @connecting = false
92
+ else
93
+ # windows text_input needs to be set on nil
94
+ # otherwise gosu wont register some keys
95
+ @window.text_input = nil
96
+ @window.state = MenuState.new(@window)
97
+ end
98
+ when Gosu::KbReturn
99
+ @connecting = true
100
+ when Gosu::MsLeft
101
+ @window.text_input = @text_fields.find do |tf|
102
+ tf.mouse_over?(@window.mouse_x, @window.mouse_y)
103
+ end
104
+ unless @window.text_input.nil?
105
+ @window.text_input.move_caret(@window.mouse_x)
106
+ end
107
+ end
108
+ end
109
+
110
+ # shows default system cursor
111
+ def needs_cursor?
112
+ true
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,79 @@
1
+ require 'socket'
2
+ require 'btetris_kp/menu'
3
+ require 'btetris_kp/netgame'
4
+ require 'btetris_kp/constants'
5
+
6
+ module BTetrisKp
7
+ # class representing a window for creating a game over network (SERVER SIDE)
8
+ class NetSetupState
9
+ def initialize(window)
10
+ @window = window
11
+ @font = Gosu::Font.new(@window, Gosu.default_font_name, 40)
12
+ initialize_title_image
13
+ setup_server
14
+ @connected = false
15
+ end
16
+
17
+ # setups a server with random port
18
+ def setup_server
19
+ @port = 0
20
+ begin
21
+ @port = rand(40_000)
22
+ @server = TCPServer.new(@port)
23
+ rescue
24
+ retry
25
+ end
26
+ end
27
+
28
+ # loads title image and calculates position, size variables
29
+ def initialize_title_image
30
+ @title_image = Gosu::Image.new(@window, Const::PATH_IMAGE_TITLE, false)
31
+ @img_size_factor = (@window.width - 50.0) / @title_image.width
32
+ @img_x = (@window.width - @title_image.width * @img_size_factor) / 2
33
+ @img_y = 20
34
+ end
35
+
36
+ # tries to accept client connection, sets @connected true when accepted
37
+ def try_accept
38
+ begin
39
+ # tries to accept client connection
40
+ @socket = @server.accept_nonblock
41
+ @socket.sendmsg_nonblock(Const::MSG_WELCOME)
42
+ # if welcome msg is recvd, start game, else close socket
43
+ msg = @socket.recv(1)
44
+ msg == Const::MSG_WELCOME ? @connected = true : @socket.close
45
+ rescue
46
+ # no one connected, try again next time
47
+ end
48
+ end
49
+
50
+ # updates window state, checks for incomming connections
51
+ # starts game when ready
52
+ def update
53
+ if @connected
54
+ @window.state = NetGameState.new(@window, @socket)
55
+ else
56
+ try_accept
57
+ end
58
+ end
59
+
60
+ # draws net setup window
61
+ def draw
62
+ @font.draw(Const::SERVER_WAIT, @window.width / 5,
63
+ @window.height / 2 - 60, 0)
64
+ @font.draw("#{Const::SERVER_PORT}#{@port}", @window.width / 3.5,
65
+ @window.height / 2, 0)
66
+ @title_image.draw(@img_x, @img_y, 1, @img_size_factor, @img_size_factor)
67
+ end
68
+
69
+ # button handler
70
+ def button_down(id)
71
+ @window.state = MenuState.new(@window) if id == Gosu::KbEscape
72
+ end
73
+
74
+ # shows default system cursor
75
+ def needs_cursor?
76
+ true
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module BtetrisKp
2
+ VERSION = '0.0.1'
3
+ end
data/lib/btetris_kp.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "btetris_kp/version"
2
+
3
+ module BTetrisKp
4
+
5
+ end
data/media/drop.ogg ADDED
Binary file
data/media/pop.ogg ADDED
Binary file
data/media/rotate.ogg ADDED
Binary file
data/media/title.png ADDED
Binary file
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'spec_helper'
4
+ require 'gosu'
5
+ require "btetris_kp/btetris"
6
+
7
+ module BTetrisKp
8
+ describe BTetrisWindow do
9
+
10
+ it 'can initialize BTetrisWindow' do
11
+ @bt = BTetrisWindow.new
12
+ @bt.should(be_an_instance_of BTetrisWindow)
13
+ end
14
+ end
15
+ end