DEV Community

Andy Maleh
Andy Maleh

Posted on

Glimmer Battleship

Glimmer DSL for SWT 4.20.13.15 just shipped with Glimmer Battleship.

Mid-game battle

Glimmer Battleship

Placing ships on grid using drag and drop

Placement

All ships placed. Ready for battle.

Ready for Battle

Finished game, beating the enemy.

Finished Win

Game over message box dialog.

Game Over

You may check out the code over here.

# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#battleship

require 'glimmer-dsl-swt'
require 'facets/string/titlecase'
require 'facets/string/underscore'

require_relative 'battleship/model/game'

require_relative 'battleship/view/grid'
require_relative 'battleship/view/ship_collection'
require_relative 'battleship/view/action_panel'

class Battleship
  include Glimmer::UI::CustomShell

  COLOR_WATER = rgb(156, 211, 219)
  COLOR_SHIP = :dark_gray

  before_body do
    @game = Model::Game.new
  end

  after_body do
    observe(@game, :over) do |game_over_value|
      if game_over_value
        game_over_message = if game_over_value == :you
          "Game over!\nYou Won!"
        else
          "Game over!\nYou Lost!"
        end

        message_box {
          text 'Game Over!'
          message game_over_message
        }.open
      end
    end
  end

  body {
    shell(:no_resize) {
      grid_layout(2, false) {
        horizontal_spacing 15
        vertical_spacing 15
      }

      text 'Glimmer Battleship'

      @enemy_grid = grid(game: @game, player: :enemy)
      @enemy_ship_collection = ship_collection(game: @game, player: :enemy)

      @player_grid = grid(game: @game, player: :you)
      @player_ship_collection = ship_collection(game: @game, player: :you)

      action_panel(game: @game)
    }
  }
end

Battleship.launch
Enter fullscreen mode Exit fullscreen mode
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#battleship

require_relative '../model/grid'

require_relative 'cell'

class Battleship
  module View
    class Grid
      include Glimmer::UI::CustomWidget

      options :game, :player

      body {
        composite {
          grid_layout(Model::Grid::WIDTH + 1, true) {
            margin_width 0
            margin_height 0
            horizontal_spacing 0
            vertical_spacing 0
          }

          label(:center) {
            layout_data(:fill, :center, true, false) {
              horizontal_span (Model::Grid::WIDTH + 1)
            }

            text player.to_s.capitalize
            font height: 20, style: :bold
          }

          label # filler
          Model::Grid::WIDTH.times do |column_index|
            label {
              text (column_index + 1).to_s
              font height: 16
            }
          end

          Model::Grid::HEIGHT.times do |row_index|
            label {
              text Model::Grid::ROW_ALPHABETS[row_index]
              font height: 16
            }
            Model::Grid::WIDTH.times do |column_index|
              cell(game: game, player: player, row_index: row_index, column_index: column_index) {
                layout_data {
                  width_hint 25
                  height_hint 25
                }
              }
            end
          end
        }
      }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#battleship

class Battleship
  module View
    class Cell
      include Glimmer::UI::CustomWidget

      class << self
        attr_accessor :dragging
        alias dragging? dragging
      end

      COLOR_WATER = rgb(156, 211, 219)
      COLOR_SHIP = :gray
      COLOR_PLACED = :white
      COLOR_EMPTY = :black
      COLOR_NO_HIT = :white
      COLOR_HIT = :red

      options :game, :player, :row_index, :column_index, :ship
      option :type, default: :grid # other type is :ship

      body {
        canvas {
          if type == :grid
            if player == :you
              background <= [model, :ship, on_read: ->(s) {s ? COLOR_SHIP : COLOR_WATER}]
            else
              background COLOR_WATER
            end
          else
            background <= [ship, :sunk, on_read: ->(s) {s ? COLOR_HIT : COLOR_PLACED}]
            background <= [ship, :top_left_cell, on_read: ->(c) {c ? COLOR_PLACED : COLOR_SHIP}]
          end

          rectangle(0, 0, [:max, -1], [:max, -1])
          oval(:default, :default, 10, 10) {
            foreground <= [model, :hit, on_read: ->(h) {h == nil ? COLOR_EMPTY : (h ? COLOR_HIT : COLOR_NO_HIT)}]
          }
          oval(:default, :default, 5, 5) {
            background <= [model, :hit, on_read: ->(h) {h == nil ? COLOR_EMPTY : (h ? COLOR_HIT : COLOR_NO_HIT)}]
          }

          on_mouse_move do |event|
            if game.started?
              if type == :grid
                if player == :enemy
                  body_root.cursor = :cross
                else
                  body_root.cursor = :arrow
                end
              else
                body_root.cursor = :arrow
              end
            end
          end

          if player == :enemy
            on_mouse_up do
              game.attack!(row_index, column_index)
            end
          end

          if player == :you
            on_drag_detected do |event|
              unless game.started? || game.over?
                Cell.dragging = true
                body_root.cursor = :hand if type == :grid
              end
            end

            on_drag_set_data do |event|
              the_ship = ship || model&.ship
              if the_ship && !game.started? && !game.over? && !(type == :ship && the_ship.top_left_cell)
                event.data = the_ship.name.to_s
              else
                event.doit = false
                Cell.dragging = false
              end
            end

            on_mouse_up do
              unless game.started? || game.over?
                Cell.dragging = false
                change_cursor
              end
            end

            if type == :grid
              on_mouse_move do |event|
                unless game.started? || game.over?
                  change_cursor
                end
              end

              on_mouse_hover do |event|
                unless game.started? || game.over?
                  change_cursor
                end
              end

              on_mouse_up do |event|
                unless game.started? || game.over?
                  begin
                    model.ship&.toggle_orientation!
                  rescue => e
                    Glimmer::Config.logger.debug e.full_message
                  end
                  change_cursor
                end
              end

              on_drop do |event|
                unless game.started? || game.over?
                  ship_name = event.data
                  place_ship(ship_name.to_s.to_sym) if ship_name
                  Cell.dragging = false
                  change_cursor
                end
              end
            end
          end
        }
      }

      def model
        game.grids[player].cell_rows[row_index][column_index] if type == :grid
      end

      def place_ship(ship_name)
        ship = game.ship_collections[player].ships[ship_name]
        model.place_ship!(ship)
      end

      def change_cursor
        if type == :grid && model.ship && !Cell.dragging?
          body_root.cursor = model.ship.orientation == :horizontal ? :sizens : :sizewe
        elsif !Cell.dragging?
          body_root.cursor = :arrow
        end
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#battleship

require_relative '../model/game'
require_relative '../model/ship_collection'

require_relative 'ship'

class Battleship
  module View
    class ShipCollection
      include Glimmer::UI::CustomWidget

      options :game, :player

      body {
        composite {
          row_layout(:vertical) {
            fill true
            margin_width 0
            margin_height 0
          }

          Model::ShipCollection::BATTLESHIPS.each do |ship_name, ship_length|
            ship(game: game, player: player, ship_name: ship_name, ship_length: ship_length)
          end
        }
      }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#battleship

require_relative 'cell'

class Battleship
  module View
    class Ship
      include Glimmer::UI::CustomWidget

      options :game, :player, :ship_name, :ship_length

      body {
        composite {
          row_layout(:vertical) {
            fill true
            margin_width 0
            margin_height 0
          }

          label {
            text ship_name.to_s.titlecase
            font height: 16
          }

          composite {
            grid_layout(ship_length, true) {
              margin_width 0
              margin_height 0
              horizontal_spacing 0
              vertical_spacing 0
            }

            ship_length.times do |column_index|
              cell(game: game, player: player, type: :ship, column_index: column_index, ship: game.ship_collections[player].ships[ship_name]) {
                layout_data {
                  width_hint 25
                  height_hint 25
                }
              }
            end
          }
        }
      }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode
# From: https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#battleship

class Battleship
  module View
    class ActionPanel
      include Glimmer::UI::CustomWidget

      options :game

      body {
        composite {
          row_layout(:horizontal) {
            margin_width 0
            margin_height 0
          }

          layout_data(:center, :center, true, false)

          @battle_button = button {
            text 'Battle!'
            enabled <= [game.ship_collections[:you], :placed_count, on_read: ->(c) {c == 5}]

            on_widget_selected do
              game.battle!
              @battle_button.enabled = false
            end
          }

          button {
            text 'Restart'

            on_widget_selected do
              game.reset!
            end
          }
        }
      }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

This is another reminder that Glimmer DSL for SWT lets you build apps that normally take months and years in just days and weeks.

Happy Glimmering!

Discussion (0)