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