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/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Pentix In Ruby
|
2
|
+
|
3
|
+
Pentix is a morally superior Siberian version of the good old tetris game.
|
4
|
+
It uses `5x5` matrix and a collection of 29 advanced figures.
|
5
|
+
|
6
|
+
And yes, they won't allow you to operate a battle bear until you reach at
|
7
|
+
least level 15 in this game :)
|
8
|
+
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
You'll need `Ruby` and `gem install gosu`, after that just run
|
13
|
+
the `pentix.rb` file in the console
|
14
|
+
|
15
|
+
|
16
|
+
## The Purpose
|
17
|
+
|
18
|
+
This project is made solely for educational and self-training purposes.
|
19
|
+
Also, as an example for the [gosu](http://code.google.com/p/gosu) gem.
|
20
|
+
|
21
|
+
|
22
|
+
## License & Copyright
|
23
|
+
|
24
|
+
All the source code and media files in this project are released
|
25
|
+
under the terms of the
|
26
|
+
[Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License](http://creativecommons.org/licenses/by-nc-sa/3.0/)
|
27
|
+
|
28
|
+
Copyright (C) 2011 Nikolay Nemshilov
|
29
|
+
|
data/bin/pentix
ADDED
data/lib/block.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# A simple block to be used in the figures and the glass
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
5
|
+
#
|
6
|
+
class Block
|
7
|
+
attr_accessor :color, :size
|
8
|
+
|
9
|
+
SIZE = 20 # block size in pixels
|
10
|
+
FILE = File.join(File.dirname(__FILE__), '..', 'media', 'block.png')
|
11
|
+
|
12
|
+
def initialize(window, color)
|
13
|
+
@@img ||= Image.new(window, FILE, true)
|
14
|
+
@color = color
|
15
|
+
end
|
16
|
+
|
17
|
+
def draw(x, y)
|
18
|
+
@@img.draw(x * SIZE, y * SIZE, 0, 1.0, 1.0, @color)
|
19
|
+
end
|
20
|
+
end
|
data/lib/controls.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# And here we handle the controls
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
5
|
+
#
|
6
|
+
class Controls
|
7
|
+
|
8
|
+
BUTTONS = {
|
9
|
+
:quit => Button::KbQ,
|
10
|
+
:reset => Button::KbEscape,
|
11
|
+
:drop => Button::KbSpace,
|
12
|
+
:left => Button::KbLeft,
|
13
|
+
:right => Button::KbRight,
|
14
|
+
:turn_left => Button::KbUp,
|
15
|
+
:turn_right => Button::KbDown
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def command_for(button)
|
19
|
+
BUTTONS.each do |key, value|
|
20
|
+
return key if button === value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/figure.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
#
|
2
|
+
# The figure unit. It handles the figure basics, like drawing itself
|
3
|
+
# making all sorts of manipulations like movements and rotations,
|
4
|
+
# plus it keeps the track of it's position and distance below
|
5
|
+
#
|
6
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
7
|
+
#
|
8
|
+
class Figure
|
9
|
+
attr_accessor :name, :color, :matrix, :pos_x, :pos_y, :distance
|
10
|
+
|
11
|
+
#
|
12
|
+
# Basic constructor
|
13
|
+
#
|
14
|
+
# NOTE! creates a random figure if there is no explicit config
|
15
|
+
#
|
16
|
+
def initialize(window, name=nil)
|
17
|
+
name ||= FIGURES.keys.shuffle[0]
|
18
|
+
config = FIGURES[name].split('|')
|
19
|
+
|
20
|
+
@pos_x = 0
|
21
|
+
@pos_y = 0
|
22
|
+
|
23
|
+
@name = name
|
24
|
+
@window = window
|
25
|
+
@color = COLORS[config.pop.to_sym]
|
26
|
+
@block = Block.new(window, @color)
|
27
|
+
|
28
|
+
@matrix = config.map{ |row| row.split('').map{|c| c == 'x'}}
|
29
|
+
|
30
|
+
# getting rid of empty colls and rows
|
31
|
+
@matrix.reject!{ |row| row.none? }
|
32
|
+
|
33
|
+
while @matrix.map{ |row| row.last }.none?
|
34
|
+
@matrix.each{ |row| row.pop }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Draws the blocks of the figure
|
40
|
+
#
|
41
|
+
def draw
|
42
|
+
@matrix.each_with_index do |row, y|
|
43
|
+
row.each_with_index do |visible, x|
|
44
|
+
@block.draw(@pos_x + x, @pos_y + y) if visible
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def size_x
|
50
|
+
@matrix[0].size
|
51
|
+
end
|
52
|
+
|
53
|
+
def size_y
|
54
|
+
@matrix.size
|
55
|
+
end
|
56
|
+
|
57
|
+
def drop
|
58
|
+
@window.glass.glue_in(self)
|
59
|
+
@window.show_next_figure
|
60
|
+
end
|
61
|
+
|
62
|
+
def move_to(x, y)
|
63
|
+
try_set(@matrix, x, y)
|
64
|
+
end
|
65
|
+
|
66
|
+
def move_left
|
67
|
+
move_to(@pos_x - 1, @pos_y)
|
68
|
+
end
|
69
|
+
|
70
|
+
def move_right
|
71
|
+
move_to(@pos_x + 1, @pos_y)
|
72
|
+
end
|
73
|
+
|
74
|
+
def move_down
|
75
|
+
move_to(@pos_x, @pos_y + 1)
|
76
|
+
end
|
77
|
+
|
78
|
+
def turn_left
|
79
|
+
new_matrix = (0..size_x-1).map do |i|
|
80
|
+
@matrix.map{ |row| row[size_x - 1 - i] }
|
81
|
+
end
|
82
|
+
|
83
|
+
try_set(new_matrix, @pos_x, @pos_y)
|
84
|
+
end
|
85
|
+
|
86
|
+
def turn_right
|
87
|
+
new_matrix = (0..size_x-1).map do |i|
|
88
|
+
@matrix.map{ |row| row[i] }.reverse
|
89
|
+
end
|
90
|
+
|
91
|
+
try_set(new_matrix, @pos_x, @pos_y)
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def try_set(matrix, pos_x, pos_y)
|
97
|
+
# adjusting x-position for tall figures
|
98
|
+
if matrix != @matrix
|
99
|
+
offset = (size_x - matrix[0].size).abs/2
|
100
|
+
pos_x += offset * (size_x > matrix[0].size ? 1 : -1)
|
101
|
+
end
|
102
|
+
|
103
|
+
if @window.glass.has_space_for?(matrix, pos_x, pos_y)
|
104
|
+
@pos_x = pos_x
|
105
|
+
@pos_y = pos_y
|
106
|
+
@matrix = matrix
|
107
|
+
|
108
|
+
@distance = @window.glass.spaces_below(self)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
FIGURES = {
|
113
|
+
:cross => ' x |xxx | x | |white',
|
114
|
+
:stairs => 'x |xx | xx | |yellow',
|
115
|
+
:batch => 'x x |xxx | | |purple',
|
116
|
+
:l_kuckan => 'xxx | x | x | |yellow',
|
117
|
+
:s_kuckan => 'xxx | x | | |yellow',
|
118
|
+
:l_corner => 'x |x |xxx | |white',
|
119
|
+
:s_corner => 'x |xx | | |white',
|
120
|
+
:l_square => 'xx |xx | | |blue',
|
121
|
+
:s_square => 'x | | | |blue',
|
122
|
+
:l_cap => ' xx |xxx | | |blue',
|
123
|
+
:r_cap => 'xx |xxx | | |blue',
|
124
|
+
:l_peris => 'xx | x | xx | |green',
|
125
|
+
:r_peris => ' xx | x |xx | |purple',
|
126
|
+
:l_zigota => 'x |xxx | x | |green',
|
127
|
+
:r_zigota => ' x |xxx | x | |cyan',
|
128
|
+
:ll_worm => 'xx | xxx | | |green',
|
129
|
+
:lr_worm => ' xx |xxx | | |cyan',
|
130
|
+
:sl_worm => 'xx | xx | | |green',
|
131
|
+
:sr_worm => ' xx |xx | | |cyan',
|
132
|
+
:l_crutch => 'x |xx |x |x |white',
|
133
|
+
:r_crutch => ' x |xx | x | x |yellow',
|
134
|
+
:sl_hook => 'xxx |x | | |cyan',
|
135
|
+
:sr_hook => 'x |xxx | | |white',
|
136
|
+
:ll_hook => 'xxxx |x | | |cyan',
|
137
|
+
:lr_hook => 'x |xxxx | | |yellow',
|
138
|
+
:t_stick => 'xx | | | |red',
|
139
|
+
:s_stick => 'xxx | | | |red',
|
140
|
+
:l_stick => 'xxxx | | | |red',
|
141
|
+
:h_stick => 'xxxxx| | | |red'
|
142
|
+
}.freeze
|
143
|
+
|
144
|
+
COLORS = {
|
145
|
+
:cyan => 0xFF00F7F1,
|
146
|
+
:blue => 0xFF0000FF,
|
147
|
+
:orange => 0xFFF69F00,
|
148
|
+
:yellow => 0xFFEAF800,
|
149
|
+
:green => 0xFF00FF00,
|
150
|
+
:purple => 0xFFB200F8,
|
151
|
+
:red => 0xFFFE0000,
|
152
|
+
:white => 0xFFFFFFFF
|
153
|
+
}.freeze
|
154
|
+
|
155
|
+
end
|
data/lib/finish.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# The Game-Over screen
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
5
|
+
#
|
6
|
+
class Finish
|
7
|
+
WIDTH = 27
|
8
|
+
HEIGHT = 27
|
9
|
+
|
10
|
+
BG_COLOR = Color::BLACK
|
11
|
+
|
12
|
+
OVER_FONT = ['Courier', Block::SIZE * 4, Color::RED].freeze
|
13
|
+
HEAD_FONT = ['Courier', Block::SIZE + 5, Color::GRAY].freeze
|
14
|
+
TEXT_FONT = ['Courier New', Block::SIZE, Color::GRAY].freeze
|
15
|
+
|
16
|
+
X_OFFSET = 3.5 * Block::SIZE
|
17
|
+
|
18
|
+
#
|
19
|
+
# Basic constructor
|
20
|
+
#
|
21
|
+
def initialize(window)
|
22
|
+
@window = window
|
23
|
+
@block = Block.new(window, BG_COLOR)
|
24
|
+
|
25
|
+
@over_font = Font.new(window, OVER_FONT[0], OVER_FONT[1])
|
26
|
+
@head_font = Font.new(window, HEAD_FONT[0], HEAD_FONT[1])
|
27
|
+
@text_font = Font.new(window, TEXT_FONT[0], TEXT_FONT[1])
|
28
|
+
|
29
|
+
@text_input = TextInput.new
|
30
|
+
|
31
|
+
reset!
|
32
|
+
end
|
33
|
+
|
34
|
+
def draw
|
35
|
+
# rendering the black background
|
36
|
+
(0..WIDTH).each do |x|
|
37
|
+
(0..HEIGHT).each do |y|
|
38
|
+
@block.draw(x, y)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@over_font.draw("GAME OVAR!",
|
43
|
+
X_OFFSET, 2 * Block::SIZE,
|
44
|
+
0, 1.0, 1.0, OVER_FONT[2])
|
45
|
+
|
46
|
+
@head_font.draw("Hiscores: ",
|
47
|
+
X_OFFSET, 7 * Block::SIZE,
|
48
|
+
0, 1.0, 1.0, HEAD_FONT[2])
|
49
|
+
|
50
|
+
score = @score.to_s
|
51
|
+
text = @text_input.text.slice(0, 32) + (@window.text_input == @text_input ? '_' : '')
|
52
|
+
@text_font.draw(
|
53
|
+
text.ljust(43 - score.size, '.') + score,
|
54
|
+
X_OFFSET, 9 * Block::SIZE, 0, 1.0, 1.0, TEXT_FONT[2])
|
55
|
+
|
56
|
+
@hiscores.each_with_index do |record, i|
|
57
|
+
score = record[1].to_s
|
58
|
+
|
59
|
+
@text_font.draw(
|
60
|
+
record[0].ljust(43 - score.size, '.') + score,
|
61
|
+
X_OFFSET, (10 + i) * Block::SIZE,
|
62
|
+
0, 1.0, 1.0, TEXT_FONT[2])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def score=(score)
|
67
|
+
@score = score
|
68
|
+
@hiscores = Records.top(14)
|
69
|
+
@window.text_input = @text_input
|
70
|
+
end
|
71
|
+
|
72
|
+
def reset!
|
73
|
+
@score = 0
|
74
|
+
@hiscores = []
|
75
|
+
@window.text_input = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def enter!
|
79
|
+
name = @text_input.text.strip
|
80
|
+
unless name == ''
|
81
|
+
Records.add(name, @score)
|
82
|
+
@window.text_input = nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/game.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#
|
2
|
+
# The main game window class
|
3
|
+
#
|
4
|
+
# NOTE: all the sizes in the code are _in_blocks_
|
5
|
+
#
|
6
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
7
|
+
#
|
8
|
+
class Game < Window
|
9
|
+
|
10
|
+
attr_accessor :glass, :status
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super(27 * Block::SIZE, 27 * Block::SIZE, false)
|
14
|
+
self.caption = "Pentix"
|
15
|
+
|
16
|
+
@glass = Glass.new(self, 1, 1)
|
17
|
+
@status = Status.new(self, 16, 1)
|
18
|
+
|
19
|
+
@controls = Controls.new # keybindings
|
20
|
+
@finish = Finish.new(self) # the gameover screen
|
21
|
+
|
22
|
+
reset!
|
23
|
+
end
|
24
|
+
|
25
|
+
def draw
|
26
|
+
if @playing
|
27
|
+
@glass.draw
|
28
|
+
@status.draw
|
29
|
+
@figure.draw
|
30
|
+
else
|
31
|
+
@finish.draw
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def update
|
36
|
+
if @playing && time_to_move
|
37
|
+
if @figure.distance > 0
|
38
|
+
@figure.move_down
|
39
|
+
else
|
40
|
+
@figure.drop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def reset!
|
46
|
+
@status.reset!
|
47
|
+
@glass.reset!
|
48
|
+
@finish.reset!
|
49
|
+
|
50
|
+
show_next_figure
|
51
|
+
|
52
|
+
@time_offset = 1000 / @status.level # blocks per second
|
53
|
+
@next_time = 0
|
54
|
+
|
55
|
+
time_to_move # precalculating the next time to move
|
56
|
+
|
57
|
+
@playing = true
|
58
|
+
end
|
59
|
+
|
60
|
+
def its_over!
|
61
|
+
@finish.score = @status.score
|
62
|
+
@playing = false
|
63
|
+
end
|
64
|
+
|
65
|
+
def button_down(button)
|
66
|
+
case @controls.command_for(button)
|
67
|
+
when :drop then @figure.drop if @playing
|
68
|
+
when :left then @figure.move_left if @playing
|
69
|
+
when :right then @figure.move_right if @playing
|
70
|
+
when :turn_left then @figure.turn_left if @playing
|
71
|
+
when :turn_right then @figure.turn_right if @playing
|
72
|
+
when :reset then reset!
|
73
|
+
when :quit then close
|
74
|
+
else
|
75
|
+
@finish.enter! if button == Button::KbReturn
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def show_next_figure
|
80
|
+
@figure = @status.figure || Figure.new(self)
|
81
|
+
@status.figure = Figure.new(self)
|
82
|
+
|
83
|
+
x = (Glass::WIDTH - @figure.size_x)/2 + 2
|
84
|
+
y = @glass.pos_y
|
85
|
+
|
86
|
+
if @glass.has_space_for?(@figure.matrix, x, y)
|
87
|
+
@figure.move_to(x, y)
|
88
|
+
else
|
89
|
+
@figure.pos_x = x
|
90
|
+
@figure.pos_y = y
|
91
|
+
|
92
|
+
its_over!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def time_to_move
|
97
|
+
if Gosu::milliseconds > @next_time
|
98
|
+
@next_time = Gosu::milliseconds + @time_offset
|
99
|
+
else
|
100
|
+
false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
data/lib/glass.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
#
|
2
|
+
# The glass thing. It handles the figure position calculcations
|
3
|
+
# watches the available space, removes full lines, etc, etc.
|
4
|
+
#
|
5
|
+
# Copyright (C) 2011 Nikolay Nemshilov
|
6
|
+
#
|
7
|
+
class Glass
|
8
|
+
attr_accessor :pos_x, :pos_y, :matrix
|
9
|
+
|
10
|
+
WIDTH = 12
|
11
|
+
HEIGHT = 24
|
12
|
+
COLOR = Color::GRAY
|
13
|
+
|
14
|
+
#
|
15
|
+
# Basic constructor
|
16
|
+
#
|
17
|
+
def initialize(window, x, y)
|
18
|
+
@window = window
|
19
|
+
@block = Block.new(window, COLOR)
|
20
|
+
|
21
|
+
@pos_x = x
|
22
|
+
@pos_y = y
|
23
|
+
|
24
|
+
reset!
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Empties the glass by creating a new blocks matrix
|
29
|
+
#
|
30
|
+
def reset!
|
31
|
+
@matrix = (0..HEIGHT-1).map do
|
32
|
+
Array.new(WIDTH)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Draws the class walls and content
|
38
|
+
#
|
39
|
+
def draw
|
40
|
+
@block.color = COLOR
|
41
|
+
|
42
|
+
# drawing the walls
|
43
|
+
(0..HEIGHT).each do |i|
|
44
|
+
@block.draw(@pos_x, @pos_y + i)
|
45
|
+
@block.draw(@pos_x + WIDTH + 1, @pos_y + i)
|
46
|
+
end
|
47
|
+
|
48
|
+
# drawing the bottom
|
49
|
+
(1..WIDTH).each do |i|
|
50
|
+
@block.draw(@pos_x + i, @pos_y + HEIGHT)
|
51
|
+
end
|
52
|
+
|
53
|
+
# drawing the blocks inside
|
54
|
+
@matrix.each_with_index do |row, y|
|
55
|
+
row.each_with_index do |color, x|
|
56
|
+
unless color == nil
|
57
|
+
@block.color = color
|
58
|
+
@block.draw(@pos_x + x + 1, @pos_y + y)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Calculates the available space (in blocks)
|
66
|
+
# below the figure
|
67
|
+
#
|
68
|
+
def spaces_below(figure)
|
69
|
+
fig_x = figure.pos_x - @pos_x - 1
|
70
|
+
fig_y = figure.pos_y - @pos_y
|
71
|
+
|
72
|
+
(0..figure.size_x-1).map do |x|
|
73
|
+
column_height = 0
|
74
|
+
|
75
|
+
figure.matrix.each_with_index do |row, y|
|
76
|
+
column_height = y + 1 if row[x]
|
77
|
+
end
|
78
|
+
|
79
|
+
lowest_point = fig_y + column_height
|
80
|
+
|
81
|
+
x += fig_x
|
82
|
+
distance = HEIGHT - lowest_point
|
83
|
+
|
84
|
+
# checking if it interescts with any existing blocks
|
85
|
+
@matrix.each_with_index do |row, y|
|
86
|
+
if nil != row[x]
|
87
|
+
distance = y - lowest_point
|
88
|
+
break
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
distance
|
93
|
+
end.min
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Checks if this figure matrix can fit the glass at the given
|
98
|
+
# position. Used for prechecks on figure manipulations to
|
99
|
+
# enforce movement constraints
|
100
|
+
#
|
101
|
+
def has_space_for?(matrix, pos_x, pos_y)
|
102
|
+
if pos_x > @pos_x && pos_x < (@pos_x + WIDTH + 2 - matrix[0].size)
|
103
|
+
if pos_y >= @pos_y && pos_y < (@pos_y + HEIGHT + 1 - matrix.size)
|
104
|
+
matrix.each_with_index do |row, y|
|
105
|
+
row.each_with_index do |visible, x|
|
106
|
+
if visible && nil != @matrix[pos_y - @pos_y + y][pos_x - @pos_x + x - 1]
|
107
|
+
return false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
return true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
false
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Glues the figure into the glass. The figure might hang above
|
121
|
+
# or virtually get through the stack, it doesn't matter. This
|
122
|
+
# method uses the horizontal position only
|
123
|
+
#
|
124
|
+
def glue_in(figure)
|
125
|
+
@window.status.count_drop(figure)
|
126
|
+
|
127
|
+
(0..figure.size_x - 1).each do |x|
|
128
|
+
(0..figure.size_y-1).each do |y|
|
129
|
+
if figure.matrix[y][x]
|
130
|
+
@matrix[
|
131
|
+
y + figure.pos_y - @pos_y + figure.distance
|
132
|
+
][
|
133
|
+
x + figure.pos_x - @pos_x - 1
|
134
|
+
] = figure.color
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
remove_full_lines
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Checks the glass for full lines, removes them
|
144
|
+
# and refills the glass
|
145
|
+
#
|
146
|
+
def remove_full_lines
|
147
|
+
lines = @matrix.map do |row|
|
148
|
+
row.all? ? row : nil
|
149
|
+
end.compact!
|
150
|
+
|
151
|
+
lines.each do |row|
|
152
|
+
@matrix.delete(row)
|
153
|
+
@matrix.unshift Array.new(WIDTH)
|
154
|
+
end
|
155
|
+
|
156
|
+
@window.status.count_kill(lines)
|
157
|
+
end
|
158
|
+
end
|
data/lib/records.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# A little wrapper to handle hiscore records
|
3
|
+
#
|
4
|
+
class Records < Array
|
5
|
+
FILENAME = File.join(ENV['HOME'] || ENV['USERPROFILE'], ".pentix")
|
6
|
+
|
7
|
+
def self.new
|
8
|
+
@@instance ||= super # no need to bother HDD all the time
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.top(size)
|
12
|
+
new.sort{ |a, b| b[1] <=> a[1] }.uniq.slice(0, size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.add(name, score)
|
16
|
+
list = self.new
|
17
|
+
list << [name.strip, score]
|
18
|
+
list.save
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
super *args
|
23
|
+
|
24
|
+
File.read(FILENAME).split("\n").each do |line|
|
25
|
+
if match = line.match(/^\s*(.*?)\s+(\d+)\s*$/)
|
26
|
+
self << [match[1].strip, match[2].to_i]
|
27
|
+
end
|
28
|
+
end if File.exists?(FILENAME)
|
29
|
+
end
|
30
|
+
|
31
|
+
def save
|
32
|
+
File.open(FILENAME, "w") do |file|
|
33
|
+
each do |entry|
|
34
|
+
if entry[1] > 0
|
35
|
+
file.write "#{entry[0].ljust(40, ' ')} #{entry[1]}\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|