glimmer-dsl-swt 4.22.2.1 → 4.22.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +26 -12
- data/VERSION +1 -1
- data/docs/reference/GLIMMER_COMMAND.md +3 -1
- data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +145 -0
- data/docs/reference/GLIMMER_SAMPLES.md +17 -9
- data/glimmer-dsl-swt.gemspec +0 -0
- data/lib/glimmer/dsl/swt/exec_expression.rb +1 -2
- data/lib/glimmer/launcher.rb +7 -4
- data/lib/glimmer/rake_task/scaffold.rb +28 -15
- data/lib/glimmer/swt/image_proxy.rb +1 -0
- data/lib/glimmer/swt/layout_data_proxy.rb +4 -0
- data/samples/elaborate/calculator.rb +12 -12
- data/samples/elaborate/contact_manager.rb +26 -14
- data/samples/elaborate/game_of_life.rb +8 -8
- data/samples/elaborate/klondike_solitaire/view/action_panel.rb +2 -2
- data/samples/elaborate/klondike_solitaire/view/klondike_solitaire_menu_bar.rb +15 -10
- data/samples/elaborate/login.rb +16 -10
- data/samples/elaborate/mandelbrot_fractal.rb +26 -20
- data/samples/elaborate/meta_sample.rb +6 -6
- data/samples/elaborate/metronome.rb +4 -4
- data/samples/elaborate/snake/model/apple.rb +33 -0
- data/samples/elaborate/snake/model/game.rb +68 -0
- data/samples/elaborate/snake/model/snake.rb +95 -0
- data/samples/elaborate/snake/model/vertebra.rb +22 -0
- data/samples/elaborate/snake/presenter/cell.rb +27 -0
- data/samples/elaborate/snake/presenter/grid.rb +49 -0
- data/samples/elaborate/snake.rb +103 -0
- data/samples/elaborate/stock_ticker.rb +12 -12
- data/samples/elaborate/tetris.rb +10 -10
- data/samples/elaborate/timer.rb +36 -26
- data/samples/elaborate/user_profile.rb +53 -14
- data/samples/elaborate/weather.rb +2 -2
- data/samples/hello/hello_button.rb +3 -3
- data/samples/hello/hello_canvas_animation.rb +2 -2
- data/samples/hello/hello_color_dialog.rb +4 -4
- data/samples/hello/hello_custom_shell.rb +2 -2
- data/samples/hello/hello_dialog.rb +4 -4
- data/samples/hello/hello_directory_dialog.rb +2 -2
- data/samples/hello/hello_expand_bar.rb +4 -5
- data/samples/hello/hello_file_dialog.rb +2 -2
- data/samples/hello/hello_font_dialog.rb +2 -2
- data/samples/hello/hello_link.rb +5 -4
- data/samples/hello/hello_list_multi_selection.rb +3 -1
- data/samples/hello/hello_list_single_selection.rb +3 -1
- data/samples/hello/hello_menu_bar.rb +28 -28
- data/samples/hello/hello_message_box.rb +2 -2
- data/samples/hello/hello_pop_up_context_menu.rb +18 -8
- data/samples/hello/hello_shell.rb +16 -16
- data/samples/hello/hello_styled_text.rb +2 -2
- data/samples/hello/hello_table.rb +4 -4
- data/samples/hello/hello_tray_item.rb +2 -2
- metadata +9 -2
@@ -40,6 +40,7 @@ class ContactManager
|
|
40
40
|
margin_width 0
|
41
41
|
margin_height 0
|
42
42
|
}
|
43
|
+
|
43
44
|
layout_data :fill, :center, true, false
|
44
45
|
text 'Lookup Contacts'
|
45
46
|
font height: 24
|
@@ -52,9 +53,10 @@ class ContactManager
|
|
52
53
|
text {
|
53
54
|
layout_data :fill, :center, true, false
|
54
55
|
text <=> [@contact_manager_presenter, :first_name]
|
55
|
-
|
56
|
+
|
57
|
+
on_key_pressed do |key_event|
|
56
58
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
57
|
-
|
59
|
+
end
|
58
60
|
}
|
59
61
|
|
60
62
|
label {
|
@@ -65,9 +67,10 @@ class ContactManager
|
|
65
67
|
text {
|
66
68
|
layout_data :fill, :center, true, false
|
67
69
|
text <=> [@contact_manager_presenter, :last_name]
|
68
|
-
|
70
|
+
|
71
|
+
on_key_pressed do |key_event|
|
69
72
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
70
|
-
|
73
|
+
end
|
71
74
|
}
|
72
75
|
|
73
76
|
label {
|
@@ -78,9 +81,10 @@ class ContactManager
|
|
78
81
|
text {
|
79
82
|
layout_data :fill, :center, true, false
|
80
83
|
text <=> [@contact_manager_presenter, :email]
|
81
|
-
|
84
|
+
|
85
|
+
on_key_pressed do |key_event|
|
82
86
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
83
|
-
|
87
|
+
end
|
84
88
|
}
|
85
89
|
|
86
90
|
composite {
|
@@ -94,18 +98,26 @@ class ContactManager
|
|
94
98
|
|
95
99
|
button {
|
96
100
|
text "&Find"
|
97
|
-
|
98
|
-
|
101
|
+
|
102
|
+
on_widget_selected do
|
103
|
+
@contact_manager_presenter.find
|
104
|
+
end
|
105
|
+
|
106
|
+
on_key_pressed do |key_event|
|
99
107
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
100
|
-
|
108
|
+
end
|
101
109
|
}
|
102
110
|
|
103
111
|
button {
|
104
112
|
text "&List All"
|
105
|
-
|
106
|
-
|
113
|
+
|
114
|
+
on_widget_selected do
|
115
|
+
@contact_manager_presenter.list
|
116
|
+
end
|
117
|
+
|
118
|
+
on_key_pressed do |key_event|
|
107
119
|
@contact_manager_presenter.list if key_event.keyCode == swt(:cr)
|
108
|
-
|
120
|
+
end
|
109
121
|
}
|
110
122
|
}
|
111
123
|
}
|
@@ -134,9 +146,9 @@ class ContactManager
|
|
134
146
|
|
135
147
|
items <=> [@contact_manager_presenter, :results, column_properties: [:first_name, :last_name, :email]]
|
136
148
|
|
137
|
-
on_mouse_up
|
149
|
+
on_mouse_up do |event|
|
138
150
|
table_proxy.edit_table_item(event.table_item, event.column_index)
|
139
|
-
|
151
|
+
end
|
140
152
|
}
|
141
153
|
}
|
142
154
|
}
|
@@ -50,9 +50,9 @@ class GameOfLife
|
|
50
50
|
rectangle(column_index*CELL_WIDTH, row_index*CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT) {
|
51
51
|
background <= [@grid.cell_rows[row_index][column_index], "alive", on_read: ->(a) {a ? :black : :white}]
|
52
52
|
|
53
|
-
on_mouse_down
|
53
|
+
on_mouse_down do
|
54
54
|
@grid.cell_rows[row_index][column_index].toggle_aliveness!
|
55
|
-
|
55
|
+
end
|
56
56
|
}
|
57
57
|
end
|
58
58
|
end
|
@@ -67,26 +67,26 @@ class GameOfLife
|
|
67
67
|
text 'Step'
|
68
68
|
enabled <= [@grid, :playing, on_read: :! ]
|
69
69
|
|
70
|
-
on_widget_selected
|
70
|
+
on_widget_selected do
|
71
71
|
@grid.step!
|
72
|
-
|
72
|
+
end
|
73
73
|
}
|
74
74
|
|
75
75
|
button {
|
76
76
|
text 'Clear'
|
77
77
|
enabled <= [@grid, :playing, on_read: :! ]
|
78
78
|
|
79
|
-
on_widget_selected
|
79
|
+
on_widget_selected do
|
80
80
|
@grid.clear!
|
81
|
-
|
81
|
+
end
|
82
82
|
}
|
83
83
|
|
84
84
|
button {
|
85
85
|
text <= [@grid, :playing, on_read: ->(p) { p ? 'Stop' : 'Play' }]
|
86
86
|
|
87
|
-
on_widget_selected
|
87
|
+
on_widget_selected do
|
88
88
|
@grid.toggle_playback!
|
89
|
-
|
89
|
+
end
|
90
90
|
}
|
91
91
|
|
92
92
|
label {
|
@@ -7,12 +7,13 @@ class KlondikeSolitaire
|
|
7
7
|
|
8
8
|
before_body do
|
9
9
|
@display = display {
|
10
|
-
on_about
|
10
|
+
on_about do
|
11
11
|
display_about_dialog
|
12
|
-
|
13
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
on_preferences do
|
14
15
|
display_about_dialog
|
15
|
-
|
16
|
+
end
|
16
17
|
}
|
17
18
|
end
|
18
19
|
|
@@ -20,30 +21,34 @@ class KlondikeSolitaire
|
|
20
21
|
menu_bar {
|
21
22
|
menu {
|
22
23
|
text '&Game'
|
24
|
+
|
23
25
|
menu_item {
|
24
26
|
text '&Restart'
|
25
27
|
accelerator (OS.mac? ? :command : :ctrl), :r
|
26
28
|
|
27
|
-
on_widget_selected
|
29
|
+
on_widget_selected do
|
28
30
|
game.restart!
|
29
|
-
|
31
|
+
end
|
30
32
|
}
|
33
|
+
|
31
34
|
menu_item {
|
32
35
|
text 'E&xit'
|
33
36
|
accelerator :alt, :f4
|
34
37
|
|
35
|
-
on_widget_selected
|
38
|
+
on_widget_selected do
|
36
39
|
exit(0)
|
37
|
-
|
40
|
+
end
|
38
41
|
}
|
39
42
|
}
|
40
43
|
menu {
|
41
44
|
text '&Help'
|
45
|
+
|
42
46
|
menu_item {
|
43
47
|
text '&About...'
|
44
|
-
|
48
|
+
|
49
|
+
on_widget_selected do
|
45
50
|
display_about_dialog
|
46
|
-
|
51
|
+
end
|
47
52
|
}
|
48
53
|
}
|
49
54
|
}
|
data/samples/elaborate/login.rb
CHANGED
@@ -81,9 +81,9 @@ class Login
|
|
81
81
|
text <=> [@presenter, :user_name]
|
82
82
|
enabled <= [@presenter, :logged_out?, computed_by: :status]
|
83
83
|
|
84
|
-
on_key_pressed
|
84
|
+
on_key_pressed do |event|
|
85
85
|
@password_text.set_focus if event.keyCode == swt(:cr)
|
86
|
-
|
86
|
+
end
|
87
87
|
}
|
88
88
|
|
89
89
|
label { text "Password:" }
|
@@ -91,9 +91,9 @@ class Login
|
|
91
91
|
text <=> [@presenter, :password]
|
92
92
|
enabled <= [@presenter, :logged_out?, computed_by: :status]
|
93
93
|
|
94
|
-
on_key_pressed
|
94
|
+
on_key_pressed do |event|
|
95
95
|
@presenter.login! if event.keyCode == swt(:cr)
|
96
|
-
|
96
|
+
end
|
97
97
|
}
|
98
98
|
|
99
99
|
label { text "Status:" }
|
@@ -103,25 +103,31 @@ class Login
|
|
103
103
|
text "Login"
|
104
104
|
enabled <= [@presenter, :logged_out?, computed_by: :status]
|
105
105
|
|
106
|
-
on_widget_selected
|
107
|
-
|
106
|
+
on_widget_selected do
|
107
|
+
@presenter.login!
|
108
|
+
end
|
109
|
+
|
110
|
+
on_key_pressed do |event|
|
108
111
|
if event.keyCode == swt(:cr)
|
109
112
|
@presenter.login!
|
110
113
|
end
|
111
|
-
|
114
|
+
end
|
112
115
|
}
|
113
116
|
|
114
117
|
button {
|
115
118
|
text "Logout"
|
116
119
|
enabled <= [@presenter, :logged_in?, computed_by: :status]
|
117
120
|
|
118
|
-
on_widget_selected
|
119
|
-
|
121
|
+
on_widget_selected do
|
122
|
+
@presenter.logout!
|
123
|
+
end
|
124
|
+
|
125
|
+
on_key_pressed do |event|
|
120
126
|
if event.keyCode == swt(:cr)
|
121
127
|
@presenter.logout!
|
122
128
|
@user_name_text.set_focus
|
123
129
|
end
|
124
|
-
|
130
|
+
end
|
125
131
|
}
|
126
132
|
}
|
127
133
|
}
|
@@ -186,10 +186,10 @@ class MandelbrotFractal
|
|
186
186
|
minimum_size mandelbrot.width + 29, mandelbrot.height + 77
|
187
187
|
image @mandelbrot_image
|
188
188
|
|
189
|
-
on_shell_closed
|
189
|
+
on_shell_closed do
|
190
190
|
@thread.kill # should not be dangerous in this case
|
191
191
|
puts "Mandelbrot background calculation stopped!"
|
192
|
-
|
192
|
+
end
|
193
193
|
|
194
194
|
progress_bar {
|
195
195
|
layout_data :fill, :center, true, false
|
@@ -205,27 +205,27 @@ class MandelbrotFractal
|
|
205
205
|
image @mandelbrot_image
|
206
206
|
cursor :no
|
207
207
|
|
208
|
-
on_mouse_down
|
208
|
+
on_mouse_down do
|
209
209
|
@drag_detected = false
|
210
210
|
@canvas.cursor = :hand
|
211
|
-
|
211
|
+
end
|
212
212
|
|
213
|
-
on_drag_detected
|
213
|
+
on_drag_detected do |drag_detect_event|
|
214
214
|
@drag_detected = true
|
215
215
|
@drag_start_x = drag_detect_event.x
|
216
216
|
@drag_start_y = drag_detect_event.y
|
217
|
-
|
217
|
+
end
|
218
218
|
|
219
|
-
on_mouse_move
|
219
|
+
on_mouse_move do |mouse_event|
|
220
220
|
if @drag_detected
|
221
221
|
origin = @scrolled_composite.origin
|
222
222
|
new_x = origin.x - (mouse_event.x - @drag_start_x)
|
223
223
|
new_y = origin.y - (mouse_event.y - @drag_start_y)
|
224
224
|
@scrolled_composite.set_origin(new_x, new_y)
|
225
225
|
end
|
226
|
-
|
226
|
+
end
|
227
227
|
|
228
|
-
on_mouse_up
|
228
|
+
on_mouse_up do |mouse_event|
|
229
229
|
if !@drag_detected
|
230
230
|
origin = @scrolled_composite.origin
|
231
231
|
@location_x = mouse_event.x
|
@@ -238,7 +238,7 @@ class MandelbrotFractal
|
|
238
238
|
end
|
239
239
|
@canvas.cursor = can_zoom_in? ? :cross : :no
|
240
240
|
@drag_detected = false
|
241
|
-
|
241
|
+
end
|
242
242
|
|
243
243
|
}
|
244
244
|
}
|
@@ -251,27 +251,33 @@ class MandelbrotFractal
|
|
251
251
|
text 'Zoom &In'
|
252
252
|
accelerator COMMAND, '+'
|
253
253
|
|
254
|
-
on_widget_selected
|
254
|
+
on_widget_selected do
|
255
|
+
zoom_in
|
256
|
+
end
|
255
257
|
}
|
256
258
|
|
257
259
|
menu_item {
|
258
260
|
text 'Zoom &Out'
|
259
261
|
accelerator COMMAND, '-'
|
260
262
|
|
261
|
-
on_widget_selected
|
263
|
+
on_widget_selected do
|
264
|
+
zoom_out
|
265
|
+
end
|
262
266
|
}
|
263
267
|
|
264
268
|
menu_item {
|
265
269
|
text '&Reset Zoom'
|
266
270
|
accelerator COMMAND, '0'
|
267
271
|
|
268
|
-
on_widget_selected
|
272
|
+
on_widget_selected do
|
273
|
+
perform_zoom(mandelbrot_zoom: 1.0)
|
274
|
+
end
|
269
275
|
}
|
270
276
|
}
|
271
277
|
menu {
|
272
278
|
text '&Cores'
|
273
279
|
|
274
|
-
Concurrent.physical_processor_count.times
|
280
|
+
Concurrent.physical_processor_count.times do |n|
|
275
281
|
processor_number = n + 1
|
276
282
|
menu_item(:radio) {
|
277
283
|
text "&#{processor_number}"
|
@@ -287,11 +293,11 @@ class MandelbrotFractal
|
|
287
293
|
|
288
294
|
selection true if processor_number == Concurrent.physical_processor_count
|
289
295
|
|
290
|
-
on_widget_selected
|
296
|
+
on_widget_selected do
|
291
297
|
Mandelbrot.processor_count = processor_number
|
292
|
-
|
298
|
+
end
|
293
299
|
}
|
294
|
-
|
300
|
+
end
|
295
301
|
}
|
296
302
|
menu {
|
297
303
|
text '&Help'
|
@@ -300,9 +306,9 @@ class MandelbrotFractal
|
|
300
306
|
text '&Instructions'
|
301
307
|
accelerator COMMAND, :shift, :i
|
302
308
|
|
303
|
-
on_widget_selected
|
309
|
+
on_widget_selected do
|
304
310
|
display_help_instructions
|
305
|
-
|
311
|
+
end
|
306
312
|
}
|
307
313
|
}
|
308
314
|
}
|
@@ -352,7 +358,7 @@ class MandelbrotFractal
|
|
352
358
|
def color_palette
|
353
359
|
if @color_palette.nil?
|
354
360
|
@color_palette = [[0, 0, 0]] + 40.times.map { |i| [255 - i*5, 255 - i*5, 55 + i*5] }
|
355
|
-
@color_palette = @color_palette.map {|color_data| rgb(*color_data).swt_color}
|
361
|
+
@color_palette = @color_palette.map { |color_data| rgb(*color_data).swt_color }
|
356
362
|
end
|
357
363
|
@color_palette
|
358
364
|
end
|
@@ -253,22 +253,22 @@ class MetaSampleApplication
|
|
253
253
|
font height: 25
|
254
254
|
enabled <= [SampleDirectory, 'selected_sample.launchable']
|
255
255
|
|
256
|
-
on_widget_selected
|
256
|
+
on_widget_selected do
|
257
257
|
begin
|
258
258
|
SampleDirectory.selected_sample.launch(@code_text.text)
|
259
259
|
rescue LoadError, StandardError, SyntaxError => launch_error
|
260
260
|
error_dialog(message: launch_error.full_message).open
|
261
261
|
end
|
262
|
-
|
262
|
+
end
|
263
263
|
}
|
264
264
|
button {
|
265
265
|
text 'Reset'
|
266
266
|
font height: 25
|
267
267
|
enabled <= [SampleDirectory, 'selected_sample.editable']
|
268
268
|
|
269
|
-
on_widget_selected
|
269
|
+
on_widget_selected do
|
270
270
|
SampleDirectory.selected_sample.reset_code!
|
271
|
-
|
271
|
+
end
|
272
272
|
}
|
273
273
|
}
|
274
274
|
}
|
@@ -319,9 +319,9 @@ class MetaSampleApplication
|
|
319
319
|
button {
|
320
320
|
text 'Close'
|
321
321
|
|
322
|
-
on_widget_selected
|
322
|
+
on_widget_selected do
|
323
323
|
dialog_proxy.close
|
324
|
-
|
324
|
+
end
|
325
325
|
}
|
326
326
|
}
|
327
327
|
end
|
@@ -101,13 +101,13 @@ class Metronome
|
|
101
101
|
|
102
102
|
@beat_container = beat_container
|
103
103
|
|
104
|
-
on_swt_show
|
104
|
+
on_swt_show do
|
105
105
|
start_metronome
|
106
|
-
|
106
|
+
end
|
107
107
|
|
108
|
-
on_widget_disposed
|
108
|
+
on_widget_disposed do
|
109
109
|
stop_metronome
|
110
|
-
|
110
|
+
end
|
111
111
|
}
|
112
112
|
}
|
113
113
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Snake
|
2
|
+
module Model
|
3
|
+
class Apple
|
4
|
+
attr_reader :game
|
5
|
+
attr_accessor :row, :column
|
6
|
+
|
7
|
+
def initialize(game)
|
8
|
+
@game = game
|
9
|
+
end
|
10
|
+
|
11
|
+
# generates a new location from scratch or via dependency injection of what cell is (for testing purposes)
|
12
|
+
def generate(initial_row: nil, initial_column: nil)
|
13
|
+
if initial_row && initial_column
|
14
|
+
self.row, self.column = initial_row, initial_column
|
15
|
+
else
|
16
|
+
self.row, self.column = @game.height.times.zip(@game.width.times).reject do |row, column|
|
17
|
+
@game.snake.vertebrae.map {|v| [v.row, v.column]}.include?([row, column])
|
18
|
+
end.sample
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove
|
23
|
+
self.row = nil
|
24
|
+
self.column = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# inspect is overridden to prevent printing very long stack traces
|
28
|
+
def inspect
|
29
|
+
"#{super[0, 120]}... >"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require_relative 'snake'
|
4
|
+
require_relative 'apple'
|
5
|
+
|
6
|
+
class Snake
|
7
|
+
module Model
|
8
|
+
class Game
|
9
|
+
WIDTH_DEFAULT = 20
|
10
|
+
HEIGHT_DEFAULT = 20
|
11
|
+
FILE_HIGH_SCORE = File.expand_path(File.join(Dir.home, '.glimmer-snake'))
|
12
|
+
|
13
|
+
attr_reader :width, :height
|
14
|
+
attr_accessor :snake, :apple, :over, :score, :high_score, :paused
|
15
|
+
alias over? over
|
16
|
+
alias paused? paused
|
17
|
+
|
18
|
+
def initialize(width = WIDTH_DEFAULT, height = HEIGHT_DEFAULT)
|
19
|
+
@width = width
|
20
|
+
@height = height
|
21
|
+
@snake = Snake.new(self)
|
22
|
+
@apple = Apple.new(self)
|
23
|
+
FileUtils.touch(FILE_HIGH_SCORE)
|
24
|
+
@high_score = File.read(FILE_HIGH_SCORE).to_i rescue 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def score=(new_score)
|
28
|
+
@score = new_score
|
29
|
+
self.high_score = @score if @score > @high_score
|
30
|
+
end
|
31
|
+
|
32
|
+
def high_score=(new_high_score)
|
33
|
+
@high_score = new_high_score
|
34
|
+
File.write(FILE_HIGH_SCORE, @high_score.to_s)
|
35
|
+
rescue => e
|
36
|
+
puts e.full_message
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
self.over = false
|
41
|
+
self.score = 0
|
42
|
+
self.snake.generate
|
43
|
+
self.apple.generate
|
44
|
+
end
|
45
|
+
|
46
|
+
def pause
|
47
|
+
self.paused = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def resume
|
51
|
+
self.paused = false
|
52
|
+
end
|
53
|
+
|
54
|
+
def toggle_pause
|
55
|
+
unless paused?
|
56
|
+
pause
|
57
|
+
else
|
58
|
+
resume
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# inspect is overridden to prevent printing very long stack traces
|
63
|
+
def inspect
|
64
|
+
"#{super[0, 75]}... >"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative 'vertebra'
|
2
|
+
|
3
|
+
class Snake
|
4
|
+
module Model
|
5
|
+
class Snake
|
6
|
+
SCORE_EAT_APPLE = 50
|
7
|
+
RIGHT_TURN_MAP = {
|
8
|
+
north: :east,
|
9
|
+
east: :south,
|
10
|
+
south: :west,
|
11
|
+
west: :north
|
12
|
+
}
|
13
|
+
LEFT_TURN_MAP = RIGHT_TURN_MAP.invert
|
14
|
+
|
15
|
+
attr_accessor :collided
|
16
|
+
alias collided? collided
|
17
|
+
|
18
|
+
attr_reader :game
|
19
|
+
# vertebrae and joins are ordered from tail to head
|
20
|
+
attr_accessor :vertebrae
|
21
|
+
|
22
|
+
def initialize(game)
|
23
|
+
@game = game
|
24
|
+
end
|
25
|
+
|
26
|
+
# generates a new snake location and orientation from scratch or via dependency injection of what head_cell and orientation are (for testing purposes)
|
27
|
+
def generate(initial_row: nil, initial_column: nil, initial_orientation: nil)
|
28
|
+
self.collided = false
|
29
|
+
initial_vertebra = Vertebra.new(snake: self, row: initial_row, column: initial_column, orientation: initial_orientation)
|
30
|
+
self.vertebrae = [initial_vertebra]
|
31
|
+
end
|
32
|
+
|
33
|
+
def length
|
34
|
+
@vertebrae.length
|
35
|
+
end
|
36
|
+
|
37
|
+
def head
|
38
|
+
@vertebrae.last
|
39
|
+
end
|
40
|
+
|
41
|
+
def tail
|
42
|
+
@vertebrae.first
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove
|
46
|
+
self.vertebrae.clear
|
47
|
+
self.joins.clear
|
48
|
+
end
|
49
|
+
|
50
|
+
def move
|
51
|
+
@old_tail = tail.dup
|
52
|
+
@new_head = head.dup
|
53
|
+
case @new_head.orientation
|
54
|
+
when :east
|
55
|
+
@new_head.column = (@new_head.column + 1) % @game.width
|
56
|
+
when :west
|
57
|
+
@new_head.column = (@new_head.column - 1) % @game.width
|
58
|
+
when :south
|
59
|
+
@new_head.row = (@new_head.row + 1) % @game.height
|
60
|
+
when :north
|
61
|
+
@new_head.row = (@new_head.row - 1) % @game.height
|
62
|
+
end
|
63
|
+
if @vertebrae.map {|v| [v.row, v.column]}.include?([@new_head.row, @new_head.column])
|
64
|
+
self.collided = true
|
65
|
+
@game.over = true
|
66
|
+
else
|
67
|
+
@vertebrae.append(@new_head)
|
68
|
+
@vertebrae.delete(tail)
|
69
|
+
if head.row == @game.apple.row && head.column == @game.apple.column
|
70
|
+
grow
|
71
|
+
@game.apple.generate
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def turn_right
|
77
|
+
head.orientation = RIGHT_TURN_MAP[head.orientation]
|
78
|
+
end
|
79
|
+
|
80
|
+
def turn_left
|
81
|
+
head.orientation = LEFT_TURN_MAP[head.orientation]
|
82
|
+
end
|
83
|
+
|
84
|
+
def grow
|
85
|
+
@game.score += SCORE_EAT_APPLE
|
86
|
+
@vertebrae.prepend(@old_tail)
|
87
|
+
end
|
88
|
+
|
89
|
+
# inspect is overridden to prevent printing very long stack traces
|
90
|
+
def inspect
|
91
|
+
"#{super[0, 150]}... >"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Snake
|
2
|
+
module Model
|
3
|
+
class Vertebra
|
4
|
+
ORIENTATIONS = %i[north east south west]
|
5
|
+
# orientation is needed for snake occuppied cells (but not apple cells)
|
6
|
+
attr_reader :snake
|
7
|
+
attr_accessor :row, :column, :orientation
|
8
|
+
|
9
|
+
def initialize(snake: , row: , column: , orientation: )
|
10
|
+
@row = row || rand(snake.game.height)
|
11
|
+
@column = column || rand(snake.game.width)
|
12
|
+
@orientation = orientation || ORIENTATIONS.sample
|
13
|
+
@snake = snake
|
14
|
+
end
|
15
|
+
|
16
|
+
# inspect is overridden to prevent printing very long stack traces
|
17
|
+
def inspect
|
18
|
+
"#{super[0, 150]}... >"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|