glimmer-dsl-libui 0.2.21 → 0.2.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47a6ef9ebfc0564ae4489dc2981baad90d307aa8beaf0b109c8830370db4f02b
4
- data.tar.gz: 3752be2f3972bc7ab9c054444ff99934d4e1e2cb47bb7724059e69b96d6793b0
3
+ metadata.gz: 9ed417f87bfb329535969efe60a61a444d59c7b0e2bdb68196ecf29642b9d628
4
+ data.tar.gz: b0cb2a08fe3ef7d666408bc2dbfa9f6a7ada5151257b55aca2cd11abda09253d
5
5
  SHA512:
6
- metadata.gz: e20361b29c5ccbe1f7a9de7a4929c7384d3db8ca8deafe016ee35e1f239dd2152500018919586e4ad173bb67bfb746fc2a062db449817d33a745a8871b82d455
7
- data.tar.gz: 98b75a7e30e0d05f79b7669e26d3f4afeeeaf19ed57e1acfb56a760c5eb052739f4f4d890840c47eb43b2b841e849f44dd727d0ba0bc06189cfea606cec38380
6
+ metadata.gz: 12e49121d599a00cdf5c3e6638081c8c3d2c6aea263428a1966ce221949d8602fe27c5a03f7506809e5ce877465c5b4b03b909909b8e8c2bba39e698b25f25da
7
+ data.tar.gz: c0e345c1c9e3d1348a6add04910ecd6a4b58934094d2b688253688e8aa860af00613a0c275aac6ace4a97e46f7341b66dc2eebeec511eaa6e36feb4bcfe8d19b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.2.22
4
+
5
+ - examples/snake.rb implemented test-first
6
+
3
7
  ## 0.2.21
4
8
 
5
9
  - examples/tic_tac_toe.rb
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.2.21
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.2.22
2
2
  ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
4
4
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -218,9 +218,10 @@ NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is fe
218
218
  Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
219
219
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
220
220
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
221
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
222
+ - [glimmer-dsl-gtk](https://github.com/AndyObtiva/glimmer-dsl-gtk): Ruby-GNOME Desktop Development GUI Library
221
223
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
222
224
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
223
- - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
224
225
 
225
226
  ## Table of Contents
226
227
 
@@ -279,6 +280,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
279
280
  - [Method-Based Custom Keyword](#method-based-custom-keyword)
280
281
  - [Tetris](#tetris)
281
282
  - [Tic Tac Toe](#tic-tac-toe)
283
+ - [Snake](#snake)
282
284
  - [Applications](#applications)
283
285
  - [Manga2PDF](#manga2pdf)
284
286
  - [Befunge98 GUI](#befunge98-gui)
@@ -371,7 +373,7 @@ gem install glimmer-dsl-libui
371
373
  Or install via Bundler `Gemfile`:
372
374
 
373
375
  ```ruby
374
- gem 'glimmer-dsl-libui', '~> 0.2.21'
376
+ gem 'glimmer-dsl-libui', '~> 0.2.22'
375
377
  ```
376
378
 
377
379
  Add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
@@ -1221,7 +1223,7 @@ class MetaExample
1221
1223
 
1222
1224
  def run_example(example)
1223
1225
  Thread.new do
1224
- command = "ruby -r #{glimmer_dsl_libui_file} #{example} 2>&1"
1226
+ command = "#{RbConfig.ruby} -r #{glimmer_dsl_libui_file} #{example} 2>&1"
1225
1227
  result = ''
1226
1228
  IO.popen(command) do |f|
1227
1229
  sleep(0.0001) # yield to main thread
@@ -6765,6 +6767,125 @@ end
6765
6767
  TicTacToe.new.launch
6766
6768
  ```
6767
6769
 
6770
+ ### Snake
6771
+
6772
+ Snake provides an example of building a desktop application [test-first](/spec/examples/snake/model/game_spec.rb) following the MVP ([Model](/examples/snake/model/game.rb) / [View](/examples/snake.rb) / [Presenter](/examples/snake/presenter/grid.rb)) architectural pattern.
6773
+
6774
+ [examples/snake.rb](examples/snake.rb)
6775
+
6776
+ Run with this command from the root of the project if you cloned the project:
6777
+
6778
+ ```
6779
+ ruby -r './lib/glimmer-dsl-libui' examples/snake.rb
6780
+ ```
6781
+
6782
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
6783
+
6784
+ ```
6785
+ ruby -r glimmer-dsl-libui -e "require 'examples/snake'"
6786
+ ```
6787
+
6788
+ Mac
6789
+
6790
+ ![glimmer-dsl-libui-mac-snake.png](images/glimmer-dsl-libui-mac-snake.png)
6791
+
6792
+ ![glimmer-dsl-libui-mac-snake-game-over.png](images/glimmer-dsl-libui-mac-snake-game-over.png)
6793
+
6794
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6795
+
6796
+ ```ruby
6797
+ require 'glimmer-dsl-libui'
6798
+ require 'glimmer/data_binding/observer'
6799
+
6800
+ require_relative 'snake/presenter/grid'
6801
+
6802
+ class Snake
6803
+ CELL_SIZE = 15
6804
+ SNAKE_MOVE_DELAY = 0.1
6805
+ include Glimmer
6806
+
6807
+ def initialize
6808
+ @game = Model::Game.new
6809
+ @grid = Presenter::Grid.new(@game)
6810
+ @game.start
6811
+ create_gui
6812
+ register_observers
6813
+ end
6814
+
6815
+ def launch
6816
+ @main_window.show
6817
+ end
6818
+
6819
+ def register_observers
6820
+ @game.height.times do |row|
6821
+ @game.width.times do |column|
6822
+ Glimmer::DataBinding::Observer.proc do |new_color|
6823
+ @cell_grid[row][column].fill = new_color
6824
+ end.observe(@grid.cells[row][column], :color)
6825
+ end
6826
+ end
6827
+
6828
+ Glimmer::DataBinding::Observer.proc do |game_over|
6829
+ Glimmer::LibUI.queue_main do
6830
+ if game_over
6831
+ msg_box('Game Over!', "Score: #{@game.score}")
6832
+ @game.start
6833
+ end
6834
+ end
6835
+ end.observe(@game, :over)
6836
+
6837
+ Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
6838
+ unless @game.over?
6839
+ @game.snake.move
6840
+ @main_window.title = "Glimmer Snake (Score: #{@game.score})"
6841
+ end
6842
+ end
6843
+ end
6844
+
6845
+ def create_gui
6846
+ @cell_grid = []
6847
+ @main_window = window('Glimmer Snake', @game.width * CELL_SIZE, @game.height * CELL_SIZE) {
6848
+ resizable false
6849
+
6850
+ vertical_box {
6851
+ padded false
6852
+
6853
+ @game.height.times do |row|
6854
+ @cell_grid << []
6855
+ horizontal_box {
6856
+ padded false
6857
+
6858
+ @game.width.times do |column|
6859
+ area {
6860
+ @cell_grid.last << path {
6861
+ square(0, 0, CELL_SIZE)
6862
+
6863
+ fill Presenter::Cell::COLOR_CLEAR
6864
+ }
6865
+
6866
+ on_key_up do |area_key_event|
6867
+ orientation_and_key = [@game.snake.head.orientation, area_key_event[:ext_key]]
6868
+ case orientation_and_key
6869
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
6870
+ @game.snake.turn_right
6871
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
6872
+ @game.snake.turn_left
6873
+ else
6874
+ # No Op
6875
+ end
6876
+ end
6877
+ }
6878
+ end
6879
+ }
6880
+ end
6881
+ }
6882
+ }
6883
+ end
6884
+ end
6885
+
6886
+ Snake.new.launch
6887
+ ```
6888
+
6768
6889
  ## Applications
6769
6890
 
6770
6891
  Here are some applications built with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.21
1
+ 0.2.22
@@ -43,7 +43,7 @@ class MetaExample
43
43
 
44
44
  def run_example(example)
45
45
  Thread.new do
46
- command = "ruby -r #{glimmer_dsl_libui_file} #{example} 2>&1"
46
+ command = "#{RbConfig.ruby} -r #{glimmer_dsl_libui_file} #{example} 2>&1"
47
47
  result = ''
48
48
  IO.popen(command) do |f|
49
49
  sleep(0.0001) # yield to main thread
@@ -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,52 @@
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 = 40
10
+ HEIGHT_DEFAULT = 40
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
15
+ alias over? over
16
+ # TODO implement scoring on snake eating apples
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
+ # inspect is overridden to prevent printing very long stack traces
47
+ def inspect
48
+ "#{super[0, 75]}... >"
49
+ end
50
+ end
51
+ end
52
+ 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
@@ -0,0 +1,27 @@
1
+ class Snake
2
+ module Presenter
3
+ class Cell
4
+ COLOR_CLEAR = :white
5
+ COLOR_SNAKE = :green
6
+ COLOR_APPLE = :red
7
+
8
+ attr_reader :row, :column, :grid
9
+ attr_accessor :color
10
+
11
+ def initialize(grid: ,row: ,column: )
12
+ @row = row
13
+ @column = column
14
+ @grid = grid
15
+ end
16
+
17
+ def clear
18
+ self.color = COLOR_CLEAR unless color == COLOR_CLEAR
19
+ end
20
+
21
+ # inspect is overridden to prevent printing very long stack traces
22
+ def inspect
23
+ "#{super[0, 150]}... >"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ require 'glimmer/data_binding/observer'
2
+ require_relative '../model/game'
3
+ require_relative 'cell'
4
+
5
+ class Snake
6
+ module Presenter
7
+ class Grid
8
+ attr_reader :game, :cells
9
+
10
+ def initialize(game = Model::Game.new)
11
+ @game = game
12
+ @cells = @game.height.times.map do |row|
13
+ @game.width.times.map do |column|
14
+ Cell.new(grid: self, row: row, column: column)
15
+ end
16
+ end
17
+ Glimmer::DataBinding::Observer.proc do |new_vertebrae|
18
+ occupied_snake_positions = @game.snake.vertebrae.map {|v| [v.row, v.column]}
19
+ @cells.each_with_index do |row_cells, row|
20
+ row_cells.each_with_index do |cell, column|
21
+ if [@game.apple.row, @game.apple.column] == [row, column]
22
+ cell.color = Cell::COLOR_APPLE
23
+ elsif occupied_snake_positions.include?([row, column])
24
+ cell.color = Cell::COLOR_SNAKE
25
+ else
26
+ cell.clear
27
+ end
28
+ end
29
+ end
30
+ end.observe(@game.snake, :vertebrae)
31
+ end
32
+
33
+ def clear
34
+ @cells.each do |row_cells|
35
+ row_cells.each do |cell|
36
+ cell.clear
37
+ end
38
+ end
39
+ end
40
+
41
+ # inspect is overridden to prevent printing very long stack traces
42
+ def inspect
43
+ "#{super[0, 75]}... >"
44
+ end
45
+ end
46
+ end
47
+ end
data/examples/snake.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'glimmer-dsl-libui'
2
+ require 'glimmer/data_binding/observer'
3
+
4
+ require_relative 'snake/presenter/grid'
5
+
6
+ class Snake
7
+ CELL_SIZE = 15
8
+ SNAKE_MOVE_DELAY = 0.1
9
+ include Glimmer
10
+
11
+ def initialize
12
+ @game = Model::Game.new
13
+ @grid = Presenter::Grid.new(@game)
14
+ @game.start
15
+ create_gui
16
+ register_observers
17
+ end
18
+
19
+ def launch
20
+ @main_window.show
21
+ end
22
+
23
+ def register_observers
24
+ @game.height.times do |row|
25
+ @game.width.times do |column|
26
+ Glimmer::DataBinding::Observer.proc do |new_color|
27
+ @cell_grid[row][column].fill = new_color
28
+ end.observe(@grid.cells[row][column], :color)
29
+ end
30
+ end
31
+
32
+ Glimmer::DataBinding::Observer.proc do |game_over|
33
+ Glimmer::LibUI.queue_main do
34
+ if game_over
35
+ msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
36
+ @game.start
37
+ end
38
+ end
39
+ end.observe(@game, :over)
40
+
41
+ Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
42
+ unless @game.over?
43
+ @game.snake.move
44
+ @main_window.title = "Glimmer Snake (Score: #{@game.score} | High Score: #{@game.high_score})"
45
+ end
46
+ end
47
+ end
48
+
49
+ def create_gui
50
+ @cell_grid = []
51
+ @main_window = window('Glimmer Snake', @game.width * CELL_SIZE, @game.height * CELL_SIZE) {
52
+ resizable false
53
+
54
+ vertical_box {
55
+ padded false
56
+
57
+ @game.height.times do |row|
58
+ @cell_grid << []
59
+ horizontal_box {
60
+ padded false
61
+
62
+ @game.width.times do |column|
63
+ area {
64
+ @cell_grid.last << path {
65
+ square(0, 0, CELL_SIZE)
66
+
67
+ fill Presenter::Cell::COLOR_CLEAR
68
+ }
69
+
70
+ on_key_up do |area_key_event|
71
+ orientation_and_key = [@game.snake.head.orientation, area_key_event[:ext_key]]
72
+ case orientation_and_key
73
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
74
+ @game.snake.turn_right
75
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
76
+ @game.snake.turn_left
77
+ else
78
+ # No Op
79
+ end
80
+ end
81
+ }
82
+ end
83
+ }
84
+ end
85
+ }
86
+ }
87
+ end
88
+ end
89
+
90
+ Snake.new.launch
@@ -113,7 +113,7 @@ class Tetris
113
113
  end
114
114
 
115
115
  def tetris_dir
116
- @tetris_dir ||= File.join(File.expand_path('~'), '.glimmer-tetris')
116
+ @tetris_dir ||= File.join(Dir.home, '.glimmer-tetris')
117
117
  end
118
118
 
119
119
  def tetris_high_score_file
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-libui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.21
4
+ version: 0.2.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-05 00:00:00.000000000 Z
11
+ date: 2021-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -252,6 +252,13 @@ files:
252
252
  - examples/method_based_custom_keyword.rb
253
253
  - examples/midi_player.rb
254
254
  - examples/simple_notepad.rb
255
+ - examples/snake.rb
256
+ - examples/snake/model/apple.rb
257
+ - examples/snake/model/game.rb
258
+ - examples/snake/model/snake.rb
259
+ - examples/snake/model/vertebra.rb
260
+ - examples/snake/presenter/cell.rb
261
+ - examples/snake/presenter/grid.rb
255
262
  - examples/tetris.rb
256
263
  - examples/tetris/model/block.rb
257
264
  - examples/tetris/model/game.rb