glimmer_tetris 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6657cf18edfb6aa0f9074f5b3912cd0b820fc4c33ebb86d24158dded8d7acbb8
4
+ data.tar.gz: 17da3824fec9eaaa7991a79f581aff7a684bced04aed6a805e871e4a354c9c36
5
+ SHA512:
6
+ metadata.gz: a047a900db53a7c5d1a80285e5bc7317dbd08f8fb3011968e8e4b585f12910e6ceefbf013addf9de1e7684d53f72470d05a29537ba603eef577ac2cdae528524
7
+ data.tar.gz: cb32ca5bbfd720c079b39709fc050cb2437b411a2b89b6c03cec6383a2624ee570c83778cd0b0d8c2f70b6353adc256f332063eafaa54bacaf63ef0c4180a6e6
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2021 Andy Maleh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # <img alt="Glimmer Tetris Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer_tetris/master/package/linux/Glimmer%20Tetris.png" height=85 /> Glimmer Tetris
2
+ ## [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=40 /> Glimmer Application](https://github.com/AndyObtiva/glimmer-dsl-swt)
3
+
4
+ [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) External Sample.
5
+
6
+ ![Glimmer Tetris](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/v4.20.0.0/images/glimmer-tetris.png)
7
+
8
+ <img alt="Glimmer Tetris Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer_tetris/master/package/linux/Glimmer%20Tetris.png" height=40 /> [Download v1.0.0 for the Mac (x64 Big Sur and older)](https://www.dropbox.com/s/gul4kyt3m86thw8/Glimmer%20Tetris-1.0.0.dmg?dl=1)
9
+
10
+ Explained in [Best of Ruby Gems Series](https://github.com/planetruby/gems).
11
+
12
+ Contributing to glimmer_tetris
13
+ ------------------------------------------
14
+
15
+ - Check out the latest master to make sure the feature hasn't been
16
+ implemented or the bug hasn't been fixed yet.
17
+ - Check out the issue tracker to make sure someone already hasn't
18
+ requested it and/or contributed it.
19
+ - Fork the project.
20
+ - Start a feature/bugfix branch.
21
+ - Commit and push until you are happy with your contribution.
22
+ - Make sure to add tests for it. This is important so I don't break it
23
+ in a future version unintentionally.
24
+ - Please try not to mess with the Rakefile, version, or history. If
25
+ you want to have your own version, or is otherwise necessary, that
26
+ is fine, but please isolate to its own commit so I can cherry-pick
27
+ around it.
28
+
29
+ Copyright
30
+ ---------
31
+
32
+ [MIT](LICENSE.txt)
33
+
34
+ Copyright (c) 2021 Andy Maleh.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
23
+
24
+ begin
25
+ require 'bundler/setup'
26
+ Bundler.require(:default)
27
+ rescue
28
+ require 'glimmer-dsl-swt'
29
+ require 'glimmer-cp-bevel'
30
+ end
31
+ require 'glimmer_tetris/view/app_view'
32
+
33
+ class GlimmerTetris
34
+ APP_ROOT = File.expand_path('../..', __FILE__)
35
+ VERSION = File.read(File.join(APP_ROOT, 'VERSION'))
36
+ LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
37
+ BLOCK_SIZE = 25
38
+ FONT_NAME = 'Menlo'
39
+ FONT_TITLE_HEIGHT = 32
40
+ FONT_TITLE_STYLE = :bold
41
+ BEVEL_CONSTANT = 20
42
+ end
@@ -0,0 +1,3 @@
1
+ require_relative '../glimmer_tetris'
2
+
3
+ GlimmerTetris::View::AppView.launch
@@ -0,0 +1,48 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ class GlimmerTetris
23
+ module Model
24
+ class Block
25
+ COLOR_CLEAR = :white
26
+
27
+ attr_accessor :color
28
+
29
+ # Initializes with color. Default color (gray) signifies an empty block
30
+ def initialize(color = COLOR_CLEAR)
31
+ @color = color
32
+ end
33
+
34
+ # Clears block color. `quietly` option indicates if it should not notify observers by setting value quietly via variable not attribute writer.
35
+ def clear
36
+ self.color = COLOR_CLEAR unless self.color == COLOR_CLEAR
37
+ end
38
+
39
+ def clear?
40
+ self.color == COLOR_CLEAR
41
+ end
42
+
43
+ def occupied?
44
+ !clear?
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,306 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'fileutils'
23
+ require 'json'
24
+ require 'glimmer/data_binding/observer'
25
+ require 'glimmer/config'
26
+
27
+ require_relative 'block'
28
+ require_relative 'tetromino'
29
+ require_relative 'past_game'
30
+
31
+ class GlimmerTetris
32
+ module Model
33
+ class Game
34
+ PLAYFIELD_WIDTH = 10
35
+ PLAYFIELD_HEIGHT = 20
36
+ PREVIEW_PLAYFIELD_WIDTH = 4
37
+ PREVIEW_PLAYFIELD_HEIGHT = 2
38
+ SCORE_MULTIPLIER = {1 => 40, 2 => 100, 3 => 300, 4 => 1200}
39
+
40
+ attr_reader :playfield_width, :playfield_height
41
+ attr_accessor :game_over, :paused, :preview_tetromino, :lines, :score, :level, :high_scores, :beeping, :added_high_score, :show_high_scores, :up_arrow_action
42
+ alias game_over? game_over
43
+ alias paused? paused
44
+ alias beeping? beeping
45
+ alias added_high_score? added_high_score
46
+
47
+ def initialize(playfield_width = PLAYFIELD_WIDTH, playfield_height = PLAYFIELD_HEIGHT)
48
+ @playfield_width = playfield_width
49
+ @playfield_height = playfield_height
50
+ @high_scores = []
51
+ @show_high_scores = false
52
+ @beeping = true
53
+ @up_arrow_action = :rotate_left
54
+ load_high_scores!
55
+ end
56
+
57
+ def configure_beeper(&beeper)
58
+ @beeper = beeper
59
+ end
60
+
61
+ def game_in_progress?
62
+ !game_over? && !paused?
63
+ end
64
+
65
+ def start!
66
+ self.show_high_scores = false
67
+ self.paused = false
68
+ self.level = 1
69
+ self.score = 0
70
+ self.lines = 0
71
+ reset_playfield
72
+ reset_preview_playfield
73
+ reset_tetrominoes
74
+ preview_next_tetromino!
75
+ consider_adding_tetromino
76
+ self.game_over = false
77
+ end
78
+ alias restart! start!
79
+
80
+ def game_over!
81
+ add_high_score!
82
+ beep
83
+ self.game_over = true
84
+ end
85
+
86
+ def clear_high_scores!
87
+ high_scores.clear
88
+ end
89
+
90
+ def add_high_score!
91
+ self.added_high_score = true
92
+ high_scores.prepend(PastGame.new("Player #{high_scores.count + 1}", score, lines, level))
93
+ end
94
+
95
+ def save_high_scores!
96
+ high_score_file_content = @high_scores.map {|past_game| past_game.to_a.join("\t") }.join("\n")
97
+ FileUtils.mkdir_p(tetris_dir)
98
+ File.write(tetris_high_score_file, high_score_file_content)
99
+ rescue => e
100
+ # Fail safely by keeping high scores in memory if unable to access disk
101
+ Glimmer::Config.logger.error {"Failed to save high scores in: #{tetris_high_score_file}\n#{e.full_message}"}
102
+ end
103
+
104
+ def load_high_scores!
105
+ if File.exist?(tetris_high_score_file)
106
+ self.high_scores = File.read(tetris_high_score_file).split("\n").map {|line| PastGame.new(*line.split("\t")) }
107
+ end
108
+ rescue => e
109
+ # Fail safely by keeping high scores in memory if unable to access disk
110
+ Glimmer::Config.logger.error {"Failed to load high scores from: #{tetris_high_score_file}\n#{e.full_message}"}
111
+ end
112
+
113
+ def tetris_dir
114
+ @tetris_dir ||= File.join(File.expand_path('~'), '.glimmer-tetris')
115
+ end
116
+
117
+ def tetris_high_score_file
118
+ File.join(tetris_dir, "high_scores.txt")
119
+ end
120
+
121
+ def down!(instant: false)
122
+ return unless game_in_progress?
123
+ current_tetromino.down!(instant: instant)
124
+ game_over! if current_tetromino.row <= 0 && current_tetromino.stopped?
125
+ end
126
+
127
+ def right!
128
+ return unless game_in_progress?
129
+ current_tetromino.right!
130
+ end
131
+
132
+ def left!
133
+ return unless game_in_progress?
134
+ current_tetromino.left!
135
+ end
136
+
137
+ def rotate!(direction)
138
+ return unless game_in_progress?
139
+ current_tetromino.rotate!(direction)
140
+ end
141
+
142
+ def current_tetromino
143
+ tetrominoes.last
144
+ end
145
+
146
+ def tetrominoes
147
+ @tetrominoes ||= reset_tetrominoes
148
+ end
149
+
150
+ # Returns blocks in the playfield
151
+ def playfield
152
+ @playfield ||= @original_playfield = @playfield_height.times.map {
153
+ @playfield_width.times.map {
154
+ Block.new
155
+ }
156
+ }
157
+ end
158
+
159
+ # Executes a hypothetical scenario without truly changing playfield permanently
160
+ def hypothetical(&block)
161
+ @playfield = hypothetical_playfield
162
+ block.call
163
+ @playfield = @original_playfield
164
+ end
165
+
166
+ # Returns whether currently executing a hypothetical scenario
167
+ def hypothetical?
168
+ @playfield != @original_playfield
169
+ end
170
+
171
+ def hypothetical_playfield
172
+ @playfield_height.times.map { |row|
173
+ @playfield_width.times.map { |column|
174
+ playfield[row][column].clone
175
+ }
176
+ }
177
+ end
178
+
179
+ def preview_playfield
180
+ @preview_playfield ||= PREVIEW_PLAYFIELD_HEIGHT.times.map {|row|
181
+ PREVIEW_PLAYFIELD_WIDTH.times.map {|column|
182
+ Block.new
183
+ }
184
+ }
185
+ end
186
+
187
+ def preview_next_tetromino!
188
+ self.preview_tetromino = Tetromino.new(self)
189
+ end
190
+
191
+ def calculate_score!(eliminated_lines)
192
+ new_score = SCORE_MULTIPLIER[eliminated_lines] * (level + 1)
193
+ self.score += new_score
194
+ end
195
+
196
+ def level_up!
197
+ self.level += 1 if lines >= self.level*10
198
+ end
199
+
200
+ def delay
201
+ [1.1 - (level.to_i * 0.1), 0.001].max
202
+ end
203
+
204
+ def beep
205
+ @beeper&.call if beeping
206
+ end
207
+
208
+ def instant_down_on_up=(value)
209
+ self.up_arrow_action = :instant_down if value
210
+ end
211
+
212
+ def instant_down_on_up
213
+ self.up_arrow_action == :instant_down
214
+ end
215
+
216
+ def rotate_right_on_up=(value)
217
+ self.up_arrow_action = :rotate_right if value
218
+ end
219
+
220
+ def rotate_right_on_up
221
+ self.up_arrow_action == :rotate_right
222
+ end
223
+
224
+ def rotate_left_on_up=(value)
225
+ self.up_arrow_action = :rotate_left if value
226
+ end
227
+
228
+ def rotate_left_on_up
229
+ self.up_arrow_action == :rotate_left
230
+ end
231
+
232
+ def reset_tetrominoes
233
+ @tetrominoes = []
234
+ end
235
+
236
+ def reset_playfield
237
+ playfield.each do |row|
238
+ row.each do |block|
239
+ block.clear
240
+ end
241
+ end
242
+ end
243
+
244
+ def reset_preview_playfield
245
+ preview_playfield.each do |row|
246
+ row.each do |block|
247
+ block.clear
248
+ end
249
+ end
250
+ end
251
+
252
+ def consider_adding_tetromino
253
+ if tetrominoes.empty? || current_tetromino.stopped?
254
+ preview_tetromino.launch!
255
+ preview_next_tetromino!
256
+ end
257
+ end
258
+
259
+ def consider_eliminating_lines
260
+ eliminated_lines = 0
261
+ playfield.each_with_index do |row, playfield_row|
262
+ if row.all? {|block| !block.clear?}
263
+ eliminated_lines += 1
264
+ shift_blocks_down_above_row(playfield_row)
265
+ end
266
+ end
267
+ if eliminated_lines > 0
268
+ beep
269
+ self.lines += eliminated_lines
270
+ level_up!
271
+ calculate_score!(eliminated_lines)
272
+ end
273
+ end
274
+
275
+ def playfield_remaining_heights(tetromino = nil)
276
+ @playfield_width.times.map do |playfield_column|
277
+ bottom_most_block = tetromino.bottom_most_block_for_column(playfield_column)
278
+ (playfield.each_with_index.detect do |row, playfield_row|
279
+ !row[playfield_column].clear? &&
280
+ (
281
+ tetromino.nil? ||
282
+ bottom_most_block.nil? ||
283
+ (playfield_row > tetromino.row + bottom_most_block[:row_index])
284
+ )
285
+ end || [nil, @playfield_height])[1]
286
+ end.to_a
287
+ end
288
+
289
+ private
290
+
291
+ def shift_blocks_down_above_row(row)
292
+ row.downto(0) do |playfield_row|
293
+ playfield[playfield_row].each_with_index do |block, playfield_column|
294
+ previous_row = playfield[playfield_row - 1]
295
+ previous_block = previous_row[playfield_column]
296
+ block.color = previous_block.color unless block.color == previous_block.color
297
+ end
298
+ end
299
+ playfield[0].each(&:clear)
300
+ end
301
+
302
+ end
303
+
304
+ end
305
+
306
+ end