AndyObtiva/glimmer-dsl-swt

View on GitHub
samples/elaborate/snake.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'glimmer-dsl-swt'

require_relative 'snake/presenter/grid'

class Snake
  include Glimmer::UI::CustomShell
  
  CELL_SIZE = 15
  SNAKE_MOVE_DELAY = 100 # millis
  
  before_body do
    @game = Model::Game.new
    @grid = Presenter::Grid.new(@game)
    @game.start
    @keypress_queue = []
    
    display {
      on_swt_keydown do |key_event|
        if key_event.keyCode == 32
          @game.toggle_pause
        else
          @keypress_queue << key_event.keyCode
        end
      end
    }
  end
  
  after_body do
    register_observers
  end
  
  body {
    shell(:no_resize) {
      grid_layout(@game.width, true) {
        margin_width 0
        margin_height 0
        horizontal_spacing 0
        vertical_spacing 0
      }
    
      # data-bind window title to game score, converting it to a title string on read from the model
      text <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
      minimum_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
      
      @game.height.times do |row|
        @game.width.times do |column|
          canvas {
            layout_data {
              width_hint CELL_SIZE
              height_hint CELL_SIZE
            }
            
            rectangle(0, 0, CELL_SIZE, CELL_SIZE) {
              background <= [@grid.cells[row][column], :color] # data-bind square fill to grid cell color
            }
          }
        end
      end
    }
  }
  
  def register_observers
    observe(@game, :over) do |game_over|
      async_exec do
        if game_over
          message_box {
            text 'Game Over!'
            message "Score: #{@game.score} | High Score: #{@game.high_score}"
          }.open
          @game.start
        end
      end
    end
    
    timer_exec(SNAKE_MOVE_DELAY, &method(:move_snake))
  end
  
  def move_snake
    unless @game.paused? || @game.over?
      process_queued_keypress
      @game.snake.move
    end
    timer_exec(SNAKE_MOVE_DELAY, &method(:move_snake))
  end
  
  def process_queued_keypress
    # key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
    key = @keypress_queue.shift
    if [@game.snake.head.orientation, key] == [:north, swt(:arrow_right)] ||
       [@game.snake.head.orientation, key] == [:east, swt(:arrow_down)] ||
       [@game.snake.head.orientation, key] == [:south, swt(:arrow_left)] ||
       [@game.snake.head.orientation, key] == [:west, swt(:arrow_up)]
      @game.snake.turn_right
    elsif [@game.snake.head.orientation, key] == [:north, swt(:arrow_left)] ||
          [@game.snake.head.orientation, key] == [:west, swt(:arrow_down)] ||
          [@game.snake.head.orientation, key] == [:south, swt(:arrow_right)] ||
          [@game.snake.head.orientation, key] == [:east, swt(:arrow_up)]
      @game.snake.turn_left
    end
  end
end

Snake.launch