rubtris 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.
- checksums.yaml +7 -0
- data/lib/block.rb +59 -0
- data/lib/board.rb +176 -0
- data/lib/menu.rb +66 -0
- data/lib/pattern.rb +10 -0
- data/lib/rubtris.rb +144 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 66de87c292b7fa0fe1019576f8d2f3d91d63c9c4
|
4
|
+
data.tar.gz: f0230d50dd81c8adfa775fccd3294b0e4fdd1d92
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9bc0f5287f299847a6d9f8e51053b8b2143dfab4db280d1041d00808d852b3ab8a232f3683fc74f87750fa87f39f31c9fd8cb04d83fdccf89dcf19f1dc393ac3
|
7
|
+
data.tar.gz: 1e315db7ca55658f079a34483170607fad5e4b931279950d832f48c5d433c00cc7be0341df33daa44da4badc15ae13cf4c363ba0f63dfbb5e09a9eea49569aa8
|
data/lib/block.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'io/console'
|
3
|
+
require_relative 'pattern'
|
4
|
+
|
5
|
+
class Block
|
6
|
+
|
7
|
+
attr_accessor :upper_left, :rotation, :pattern
|
8
|
+
|
9
|
+
PATTERN = [ # Name, Pattern, Size, Color
|
10
|
+
Pattern.new(:square, [[0, 0], [0, 1], [1, 0], [1, 1]], 2, :red),
|
11
|
+
Pattern.new(:line, [[0, 1], [1, 1], [2, 1], [3, 1]], 4, :cyan),
|
12
|
+
Pattern.new(:left_l, [[0, 0], [1, 0], [2, 0], [2, 1]], 3, :blue),
|
13
|
+
Pattern.new(:right_l, [[0, 2], [1, 2], [2, 2], [2, 1]], 3, :green),
|
14
|
+
Pattern.new(:t_block, [[1, 1], [2, 0], [2, 1], [2, 2]], 3, :red),
|
15
|
+
Pattern.new(:n_block_1, [[0, 0], [1, 0], [1, 1], [2, 1]], 3, :yellow),
|
16
|
+
Pattern.new(:n_block_2, [[0, 1], [1, 1], [1, 0], [2, 0]], 3, :black)
|
17
|
+
]
|
18
|
+
|
19
|
+
|
20
|
+
def initialize(pattern, upper_left)
|
21
|
+
@pattern, @upper_left, @rotation = pattern, upper_left, 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.random(upper_left)
|
25
|
+
Block.new(PATTERN.sample, upper_left)
|
26
|
+
end
|
27
|
+
|
28
|
+
def spaces_occupied(options = {})
|
29
|
+
rotation = options[:rotation] || @rotation
|
30
|
+
pos = options[:pos] || @upper_left
|
31
|
+
get_vectors(rotation).map do |vector|
|
32
|
+
[vector.first + pos.first, vector.last + pos.last]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_vectors(rotation)
|
37
|
+
vectors = @pattern.pattern
|
38
|
+
|
39
|
+
(rotation.abs % 4 ).times do
|
40
|
+
vectors.map! do |coord|
|
41
|
+
rotation > 0 ? rotate_coord_right(coord) : rotate_coord_left(coord)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
vectors
|
46
|
+
end
|
47
|
+
|
48
|
+
def rotate_coord_right(coord)
|
49
|
+
i, j = coord
|
50
|
+
size = @pattern.size
|
51
|
+
[j , (size - 1) - i]
|
52
|
+
end
|
53
|
+
|
54
|
+
def rotate_coord_left(coord)
|
55
|
+
i, j = coord
|
56
|
+
size = @pattern.size
|
57
|
+
[(size - 1) - j, i]
|
58
|
+
end
|
59
|
+
end
|
data/lib/board.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require_relative 'block'
|
2
|
+
require_relative 'pattern'
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
class Board
|
6
|
+
|
7
|
+
attr_reader :completed_lines
|
8
|
+
|
9
|
+
WIDTH = 10
|
10
|
+
HEIGHT = 22
|
11
|
+
STARTING_POINT = [0, 4]
|
12
|
+
SCORE_MULTIPLIER = 2
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@grid, @over, @completed_lines = Array.new(HEIGHT) { Array.new(WIDTH) }, false, 0
|
16
|
+
@start_time, @time_limit, @line_limit = Time.now, nil, nil
|
17
|
+
@score, @level = 0, 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](coord)
|
21
|
+
@grid[coord.first][coord.last]
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(coord, val)
|
25
|
+
@grid[coord.first][coord.last] = val
|
26
|
+
end
|
27
|
+
|
28
|
+
def render
|
29
|
+
str = ""
|
30
|
+
@grid.each_with_index do |row, i|
|
31
|
+
row.each do |pos|
|
32
|
+
str += pos.nil? ? " " : " ".colorize(background: pos.pattern.color)
|
33
|
+
end
|
34
|
+
str+= "*"
|
35
|
+
if i == 1
|
36
|
+
str += " WASD to move. '[' and ']' to rotate."
|
37
|
+
elsif i == 2
|
38
|
+
str += " #{summary_s}"
|
39
|
+
end
|
40
|
+
str += "\n"
|
41
|
+
end
|
42
|
+
str = " " * (WIDTH + 1) + "\n" + str + "- " * (WIDTH + 1) + "\n"
|
43
|
+
puts str
|
44
|
+
end
|
45
|
+
|
46
|
+
def summary_s
|
47
|
+
"LINES: #{@completed_lines} LEVEL: #{level} SCORE: #{@score}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def level
|
51
|
+
@completed_lines / 10
|
52
|
+
end
|
53
|
+
|
54
|
+
def filled?(space)
|
55
|
+
!self[space].nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
def any_filled?(spaces)
|
59
|
+
spaces.any?{ |space| filled?(space) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def all_filled?(spaces)
|
63
|
+
spaces.all?{ |space| filled?(space) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_to_spaces(block, spaces)
|
67
|
+
spaces.each do |space|
|
68
|
+
self[space] = block
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_from_spaces(spaces)
|
73
|
+
spaces.each do |space|
|
74
|
+
self[space] = nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_block
|
79
|
+
block = Block.random(STARTING_POINT)
|
80
|
+
spaces = block.spaces_occupied
|
81
|
+
return false if any_filled?(spaces)
|
82
|
+
add_to_spaces(block, spaces)
|
83
|
+
@selected.upper_left = nil unless @selected.nil?
|
84
|
+
@selected = block
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
def change_direction(block, options = {})
|
89
|
+
i, j, turn = options[:i], options[:j], options[:turn]
|
90
|
+
new_upper_left = (i && j) ? [block.upper_left.first + i, block.upper_left.last + j] : block.upper_left
|
91
|
+
new_rotation = (turn) ? block.rotation + turn : block.rotation
|
92
|
+
old_spaces = block.spaces_occupied
|
93
|
+
new_spaces = block.spaces_occupied(pos: new_upper_left, rotation: new_rotation)
|
94
|
+
old_territory = old_spaces - new_spaces
|
95
|
+
new_territory = new_spaces - old_spaces
|
96
|
+
# p "i: #{i} j: #{j} turn: #{turn} new_upper_left: #{new_upper_left} new_rotation: #{new_rotation}"
|
97
|
+
# p "old_spaces: #{old_spaces} new_spaces: #{new_spaces} old_territory: #{old_territory} new_territory: #{new_territory}"
|
98
|
+
return false unless new_spaces.all? { |coord| on_board?(coord) }
|
99
|
+
return false if any_filled?(new_territory)
|
100
|
+
remove_from_spaces(old_territory)
|
101
|
+
add_to_spaces(block, new_territory)
|
102
|
+
block.upper_left, block.rotation = new_upper_left, new_rotation
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
def on_board?(space)
|
107
|
+
space.first.between?(0, HEIGHT - 1) && space.last.between?(0, WIDTH - 1)
|
108
|
+
end
|
109
|
+
|
110
|
+
def push_selected_down
|
111
|
+
unless change_direction(@selected, i: 5, j: 0)
|
112
|
+
move_selected_down
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def move_to_bottom
|
117
|
+
id = @selected.object_id
|
118
|
+
while change_direction(@selected, i: 1, j: 0) && id == @selected.object_id
|
119
|
+
move_selected_down
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def move_selected_down
|
124
|
+
unless change_direction(@selected, i: 1, j: 0)
|
125
|
+
remove_completed_line_and_increase_score
|
126
|
+
@over = !add_block
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def move_selected_left
|
131
|
+
change_direction(@selected, i: 0, j: -1)
|
132
|
+
end
|
133
|
+
|
134
|
+
def move_selected_right
|
135
|
+
change_direction(@selected, i: 0, j: 1)
|
136
|
+
end
|
137
|
+
|
138
|
+
def rotate_selected_left
|
139
|
+
change_direction(@selected, turn: -1)
|
140
|
+
end
|
141
|
+
|
142
|
+
def rotate_selected_right
|
143
|
+
change_direction(@selected, turn: 1)
|
144
|
+
end
|
145
|
+
|
146
|
+
def remove_and_shift(row_num)
|
147
|
+
@grid[row_num] = Array.new(WIDTH)
|
148
|
+
until @grid[row_num - 1].all?(&:nil?)
|
149
|
+
@grid[row_num] = @grid[row_num -= 1]
|
150
|
+
@grid[row_num] = Array.new(WIDTH)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def remove_completed_line_and_increase_score
|
155
|
+
lines = 0
|
156
|
+
i = HEIGHT - 1
|
157
|
+
until i == 0
|
158
|
+
spaces_on_row = [i].product((0...WIDTH).to_a)
|
159
|
+
if all_filled?(spaces_on_row)
|
160
|
+
remove_and_shift(i)
|
161
|
+
lines += 1
|
162
|
+
else
|
163
|
+
i -= 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
@completed_lines += lines
|
167
|
+
@score += (lines * SCORE_MULTIPLIER) ** 2
|
168
|
+
end
|
169
|
+
|
170
|
+
def over?
|
171
|
+
@over
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
end
|
data/lib/menu.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Menu
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(options, prompt)
|
7
|
+
@prompt = prompt.center(33)
|
8
|
+
@options = options
|
9
|
+
@selected = 0
|
10
|
+
@done = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def open
|
14
|
+
until done?
|
15
|
+
system "clear" or system "cls"
|
16
|
+
render
|
17
|
+
input = STDIN.getch
|
18
|
+
take_action(input)
|
19
|
+
end
|
20
|
+
return @options[@selected]
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
str = @options.map.with_index do |option, i|
|
25
|
+
if option[:type] == :increment
|
26
|
+
line = "◀ #{option[:title]}: #{option[:value]} #{option[:unit]} ►".center(33)
|
27
|
+
else
|
28
|
+
line = "#{option[:title]}".center(33)
|
29
|
+
end
|
30
|
+
i == @selected ? line.on_yellow : line
|
31
|
+
end.join("\n")
|
32
|
+
puts @prompt
|
33
|
+
puts str
|
34
|
+
end
|
35
|
+
|
36
|
+
def take_action(input)
|
37
|
+
case input
|
38
|
+
when "w"
|
39
|
+
@selected = (@selected - 1) % @options.length
|
40
|
+
when "a"
|
41
|
+
increment_selected(-1)
|
42
|
+
when "s"
|
43
|
+
@selected = (@selected + 1) % @options.length
|
44
|
+
when "d"
|
45
|
+
increment_selected(1)
|
46
|
+
when "\r"
|
47
|
+
@done = true
|
48
|
+
when "p"
|
49
|
+
@done = true
|
50
|
+
when "\e"
|
51
|
+
@done = true
|
52
|
+
end
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def increment_selected(num)
|
57
|
+
return nil unless @options[@selected][:type] == :increment
|
58
|
+
new_val = @options[@selected][:value] + num
|
59
|
+
@options[@selected][:value] = [new_val, 1].max
|
60
|
+
end
|
61
|
+
|
62
|
+
def done?
|
63
|
+
@done
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/pattern.rb
ADDED
data/lib/rubtris.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require_relative 'board'
|
2
|
+
require 'timeout'
|
3
|
+
require 'io/console'
|
4
|
+
require_relative 'menu'
|
5
|
+
|
6
|
+
class Rubtris
|
7
|
+
|
8
|
+
REFRESH_RATE = 0.1
|
9
|
+
BEGINNING_ADVANCE_RATE = 0.3
|
10
|
+
MINIMUM_ADVANCE_RATE = 0.05
|
11
|
+
DIF_BTWN_LEVEL = 0.04
|
12
|
+
|
13
|
+
|
14
|
+
def run
|
15
|
+
set_up_game
|
16
|
+
until over?
|
17
|
+
do_turn
|
18
|
+
end
|
19
|
+
end_game
|
20
|
+
run if !force_quit? && play_again
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_mode
|
24
|
+
options = [
|
25
|
+
{title: "Unlimited", type: :none, value: true},
|
26
|
+
{title: "Timed", type: :increment, value: 3, unit: 'min.'},
|
27
|
+
{title: "Lines", type: :increment, value: 40}
|
28
|
+
]
|
29
|
+
prompt = "Welcome to Rubtris.\nSelect the mode you want to play!"
|
30
|
+
menu = Menu.new(options, prompt)
|
31
|
+
@game_mode = menu.open
|
32
|
+
end
|
33
|
+
|
34
|
+
def play_again
|
35
|
+
options = [
|
36
|
+
{title: "Yes", type: :none, value: true},
|
37
|
+
{title: "No", type: :none, value: false}
|
38
|
+
]
|
39
|
+
prompt = "#{@board.summary_s}\nPlay again?"
|
40
|
+
menu = Menu.new(options, prompt)
|
41
|
+
menu.open[:value]
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_up_game
|
45
|
+
@board = Board.new
|
46
|
+
STDIN.echo = false
|
47
|
+
@force_quit = false
|
48
|
+
@board.add_block
|
49
|
+
@board.render
|
50
|
+
@start_time, @last_advanced, @level = Time.now, Time.now, 0
|
51
|
+
@time_limit, @line_limit = nil, nil
|
52
|
+
config = get_mode
|
53
|
+
add_win_condition(config[:title], config[:value])
|
54
|
+
end
|
55
|
+
|
56
|
+
def do_turn
|
57
|
+
auto_advance if auto_advance_needed?
|
58
|
+
system "clear" or system "cls"
|
59
|
+
@board.render
|
60
|
+
action = get_input
|
61
|
+
take_action(action) if action
|
62
|
+
end
|
63
|
+
|
64
|
+
def end_game
|
65
|
+
STDIN.echo = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def current_advance_rate
|
69
|
+
[BEGINNING_ADVANCE_RATE - (@level * DIF_BTWN_LEVEL), MINIMUM_ADVANCE_RATE].max
|
70
|
+
end
|
71
|
+
|
72
|
+
def auto_advance_needed?
|
73
|
+
Time.now - @last_advanced > current_advance_rate
|
74
|
+
end
|
75
|
+
|
76
|
+
def auto_advance
|
77
|
+
@last_advanced = Time.now
|
78
|
+
@board.move_selected_down
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_input
|
82
|
+
action = nil
|
83
|
+
begin
|
84
|
+
action = Timeout::timeout(REFRESH_RATE) { STDIN.getch }
|
85
|
+
rescue
|
86
|
+
end
|
87
|
+
action
|
88
|
+
end
|
89
|
+
|
90
|
+
def force_quit?
|
91
|
+
@force_quit
|
92
|
+
end
|
93
|
+
|
94
|
+
def take_action(action)
|
95
|
+
case action.downcase
|
96
|
+
when "w"
|
97
|
+
@board.move_to_bottom
|
98
|
+
when "a"
|
99
|
+
@board.move_selected_left
|
100
|
+
when "s"
|
101
|
+
@board.move_selected_down
|
102
|
+
when "d"
|
103
|
+
@board.move_selected_right
|
104
|
+
when "\r"
|
105
|
+
@board.push_selected_down
|
106
|
+
when "e"
|
107
|
+
@board.rotate_selected_right
|
108
|
+
when "q"
|
109
|
+
@board.rotate_selected_left
|
110
|
+
when "["
|
111
|
+
@board.rotate_selected_left
|
112
|
+
when "]"
|
113
|
+
@board.rotate_selected_right
|
114
|
+
when "\e"
|
115
|
+
@force_quit = true
|
116
|
+
end
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def over?
|
121
|
+
force_quit? || @board.over? || over_time? || over_lines?
|
122
|
+
end
|
123
|
+
|
124
|
+
def over_time?
|
125
|
+
@time_limit && (@last_advanced - @start_time) >= @time_limit
|
126
|
+
end
|
127
|
+
|
128
|
+
def over_lines?
|
129
|
+
@line_limit && @board.completed_lines >= @line_limit
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_win_condition(condition, value)
|
133
|
+
case condition
|
134
|
+
when "Timed"
|
135
|
+
@time_limit = value * 60
|
136
|
+
when "Lines"
|
137
|
+
@line_limit = value
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
t = Rubtris.new
|
144
|
+
t.run
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubtris
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Henderson
|
8
|
+
- Danny Burt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-12-09 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Command line Tetrino based puzzle game!
|
15
|
+
email: benjamin.c.henderson@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/block.rb
|
21
|
+
- lib/board.rb
|
22
|
+
- lib/menu.rb
|
23
|
+
- lib/pattern.rb
|
24
|
+
- lib/rubtris.rb
|
25
|
+
homepage: http://rubygems.org/gems/rubtris
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.2.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Command line Tetrino based puzzle game!
|
49
|
+
test_files: []
|