Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Created April 16, 2025 23:44
Show Gist options
  • Save amirrajan/62c6b2f0f71678c4235c7acde7f34159 to your computer and use it in GitHub Desktop.
Save amirrajan/62c6b2f0f71678c4235c7acde7f34159 to your computer and use it in GitHub Desktop.
DragonRuby primer for Love2D

functions

lua

function foo(a, b)
  return a + b
end

ruby

def foo(a, b)
  a + b
end

function with named parameters

Lua:

function foo(params)
  local a = params.a
  local b = params.b
  return a + b
end

-- Usage
local result = foo({ a = 1, b = 2 })
print(result)  -- Output: 3

Ruby (required parameters):

# required parameters
def foo(a:, b:)
  a + b
end

# Usage
result = foo(a: 1, b: 2)
puts result  # Output: 3

Ruby (optional parameters):

def foo(a:, b: 0)
  a + b
end

# Usage
result = foo(a: 1)
puts result  # Output: 1

# Usage
result = foo(a: 1, b: 2)
puts result  # Output: 3

create an array and add 3 letters (“a”, “b”, “c”)

Lua:

letters = {}
table.insert(letters, "a")
table.insert(letters, "b")
table.insert(letters, "c")

Ruby:

letters = []
letters << "a"
letters << "b"
letters << "c"
# or
letters = []
letters.push("a")
letters.push("b")
letters.push("c")

initializing an array, and print out each character on a new line

Lua:

letters = {"a", "b", "c"}
for i = 1, #letters do
  print(letters[i])
end

Ruby:

letters = ["a", "b", "c"]
for i in 0..letters.length - 1 do
  puts letters[i]
end

creating a table/hash with first name and last name print each field on a new line:

Lua:

person = { first_name = "John", last_name = "Doe" }
print(person.first_name)
print(person.last_name)

Ruby:

person = { first_name: "John", last_name: "Doe" }
puts person.first_name
puts person.last_name

if/else statement

Lua:

function compare(a, b)
  if a > b then
    print("a is greater than b")
  elseif a < b then
    print("a is less than b")
  else
    print("a is equal to b")
  end
end

Ruby:

def compare(a, b)
  if a > b
    puts "a is greater than b"
  elsif a < b
    puts "a is less than b"
  else
    puts "a is equal to b"
  end
end

creating a class (OOP)

Lua:

Person = {}
Person.__index = Person

function Person:new(first_name, last_name)
  local obj = { first_name = first_name, last_name = last_name }
  setmetatable(obj, Person)
  return obj
end

function Person:full_name()
  return self.first_name .. " " .. self.last_name
end

local john = Person:new("John", "Doe")
print(john:full_name())

Ruby:

class Person
  attr :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def full_name
    "#{@first_name} #{@last_name}"
  end
end

john = Person.new("John", "Doe")
puts john.full_name

creating an array of 10 numbers between 1 and 100 and creat a new array with only numbers greater than 50:

Lua:

numbers = {}
for i = 1, 10 do
  table.insert(numbers, math.random(1, 100))
end

greater_than_50 = {}
for i = 1, #numbers do
  if numbers[i] > 50 then
    table.insert(greater_than_50, numbers[i])
  end
end

-- Print the new array
for i = 1, #greater_than_50 do
  print(greater_than_50[i])
end

Ruby (conventional for loop)

numbers = []

for i in 1..10 do
  numbers << rand(1..100)
end

greater_than_50 = []

for num in numbers do
  if num > 50
    greater_than_50 << num
  end
end

for num in greater_than_50 do
    puts num
end

Ruby (using lambdas):

numbers = Array.new(10) do
  rand(1..100)
end

greater_than_50 = numbers.find_all do |num|
  num > 50
end

greater_than_50.each do |num|
  puts num
end

Ruby (lambda short hand):

numbers = Array.new(10) { rand(1..100) }
greater_than_50 = numbers.find_all { |num| num > 50 }
greater_than_50.each { |num| puts num }

printing hello 10 times

Lua:

for i = 1, 10 do
  print("Hello")
end

Ruby (conventional for loop):

for i in 1..10 do
  puts "Hello"
end

Ruby (using lambdas):

10.times { puts "Hello" }

generating a random list of numbers between 1 and 100, sorted first by even/odd, then each group sorted by increasing order in Lua:

numbers = {}
for i = 1, 10 do
  table.insert(numbers, math.random(1, 100))
end

-- Separate even and odd numbers
even_numbers = {}
odd_numbers = {}

for i = 1, #numbers do
  if numbers[i] % 2 == 0 then
    table.insert(even_numbers, numbers[i])
  else
    table.insert(odd_numbers, numbers[i])
  end
end

-- Sort the arrays
table.sort(even_numbers)
table.sort(odd_numbers)

-- Print the sorted arrays
print("Even numbers:")
for i = 1, #even_numbers do
  print(even_numbers[i])
end

print("Odd numbers:")
for i = 1, #odd_numbers do
  print(odd_numbers[i])
end

Ruby (lambdas with { }, or you can use do/end):

numbers = Array.new(10) { rand(1..100) }
even_numbers = numbers.find_all { |num| num.even? }
odd_numbers = numbers.find_all { |num| num.odd? }

puts "Even numbers:"
even_numbers.each { |num| puts num }

puts "Odd numbers:"
odd_numbers.each { |num| puts num }

Ruby (lambda chaining):

numbers = Array.new(10) { rand(1..100) }
puts "Even numbers:"
numbers.find_all { |num| num.even? }
       .sort
       .each { |num| puts num }

puts "Odd numbers:"
numbers.find_all { |num| num.odd? }
         .sort
         .each { |num| puts num }

definition of random loot. each with a min max value, and probability of dropping, then a “get loot” function that returns a random loot item:

in Lua:

loot = {
  { name = "Sword",  min = 1, max = 10, probability = 0.5 },
  { name = "Shield", min = 5, max = 15, probability = 0.3 },
  { name = "Potion", min = 2, max = 8,  probability = 0.2 }
}

function get_loot(loot)
  local result = {}
  for i, item in ipairs(loot) do
    if item.probability > math.random() then
      table.insert(result,
                   {
                     name = item.name,
                     quantity = math.random(item.min, item.max)
                   })
    end
  end
  return result
end

item = get_loot(loot)
print("loot received")
for i, loot_item in ipairs(item) do
  print("You got a " .. loot_item.name .. " of quantity " .. loot_item.quantity)
end

print("original loot and probabilities")
for i, loot_item in ipairs(loot) do
  print(loot_item.name .. " probability: " .. loot_item.probability .. ", min: " .. loot_item.min .. ", max: " .. loot_item.max)
end

Ruby:

loot = [
  { name: "Sword",  min: 1, max: 10, probability: 0.5 },
  { name: "Shield", min: 5, max: 15, probability: 0.3 },
  { name: "Potion", min: 2, max: 8,  probability: 0.2 }
]

def get_loot(loot)
  loot.find_all { |item| item[:probability] > rand }
      .map do |item|
        {
          name: item[:name],
          quantity: Numeric.rand(item[:min]..item[:max])
        }
      end
end

item = get_loot(loot)
puts "loot recieved"
item.each do |i|
  puts "You got a #{i[:name]} of quantity #{i[:quantity]}"
end

puts "original loot and probabilities"
loot.each do |item|
  puts "#{item[:name]} probability: #{item[:probability]}, min: #{item[:min]}, max: #{item[:max]}"
end

pythagorean triples

Lua:

-- All triangle combinations of a, b, c:
-- - with lengths between 1 and 100
-- - where a and b are the two sides, and c is the hypotenuse
-- - and a^2 + b^2 = c^2 where a, b, c are all whole numbers
-- - remove duplicates
-- - sort by area
-- Also known as pythagorean triples: https://en.wikipedia.org/wiki/Pythagorean_triple

triples = {}
one_to_hundred = {}
for i = 1, 100 do
  table.insert(one_to_hundred, i)
end

for _,a in ipairs(one_to_hundred) do
  for _,b in ipairs(one_to_hundred) do
    -- Calculate the hypotenuse
    c = math.sqrt(a*a + b*b)
    -- If the hypotenuse is a whole number (pythagorean triple)
    if math.floor(c) == c then
      -- Check if a similar triangle already exists
      exists = false
      for _,triangle in ipairs(triples) do
        if (triangle.a == a and triangle.b == b and triangle.c == c) or (triangle.a == b and triangle.b == a and triangle.c == c) then
          exists = true
          break
        end
      end

      if not exists then
        -- Calculate the area
        area = (a*c) / 2
        -- Append the triangle to the list
        table.insert(triples, {a=a, b=b, c=c, area=area})
      end
    end
  end
end

-- Sort triangles by area
table.sort(triples, function(a, b)
  return a.area < b.area
end)

for _,triangle in ipairs(triples) do
  print(triangle.a, triangle.b, triangle.c, triangle.area)
end

Ruby:

# all triangle combinations of a, b, c:
# - with lengths between 1 and 100
# - where a and b are the two sides, and c is the hypotenuse
# - and a^2 + b^2 = c^2 where a, b, c are all whole numbers
# - remove duplicates
# - sort by area
# also known as pythagorean triples: https://en.wikipedia.org/wiki/Pythagorean_triple
one_to_hundred = (1..100).to_a
triples = one_to_hundred.product(one_to_hundred).map do |a, b|
            # given permutations of side a, and side b (product)
            # calculate the hypotenuse
            { a:, b:, c: Math.sqrt(a ** 2 + b ** 2) }
          end.find_all do |triangle|
            # where area is a whole number (pythagaroean triple)
            triangle[:c].floor == triangle[:c]
          end.uniq do |triangle|
            # unique based on sorted sides
            triangle.values.sort
          end.map do |triangle|
            # calculate the area
            triangle.merge(area: (triangle[:a] * triangle[:c]) / 2)
          end.sort_by do |triangle|
            # sort by area
            triangle[:area]
          end

puts triples

Simple Game

render a sprite with path “sprites/square/blue.png” at the center of the screen and make it rotate. if space is pressed, stop the rotation, if space is pressed again, start the rotation again:

Lua (LOVE2D):

function love.conf(t)
    t.window.width = 1280
    t.window.height = 720
end

-- Game class
Game = {}
Game.__index = Game

function Game.new()
    local self = setmetatable({}, Game)
    self.square = Square.new()
    return self
end

function Game:update(dt)
    self.square:update(dt)
end

function Game:draw()
    self.square:draw()
end

-- Square class
Square = {}
Square.__index = Square

function Square.new()
    local self = setmetatable({}, Square)

    self.image = love.graphics.newImage("sprites/square/blue.png")
    self.x = love.graphics.getWidth() / 2
    self.y = love.graphics.getHeight() / 2
    self.rotation = 0
    self.rotate = true
    self.wasPressed = false

    self.scaleX, self.scaleY = 100/self.image:getWidth(), 100/self.image:getHeight()

    return self
end

function Square:update(dt)
    if love.keyboard.isDown('space') then
        if not self.wasPressed then
            self.rotate = not self.rotate
            self.wasPressed = true
        end
    else
        self.wasPressed = false
    end

    if self.rotate then
        self.rotation = self.rotation + dt
        if self.rotation >= 2 * math.pi then
            self.rotation = self.rotation - 2 * math.pi
        end
    end
end

function Square:draw()
    love.graphics.draw(self.image, self.x, self.y, self.rotation, self.scaleX, self.scaleY, self.image:getWidth() / 2, self.image:getHeight() / 2)
end

-- Main functions
function love.load()
    game = Game.new()
end

function love.update(dt)
    game:update(dt)
end

function love.draw()
    game:draw()
end

Ruby (DragonRuby):

class Square
  attr :x, :y, :w, :h, :angle, :rotating, :rotation_speed

  def initialize(x:, y:, w:, h:)
    @x = x
    @y = y
    @w = w
    @h = h
    @angle = 0
    @rotating = true
    @rotation_speed = 5
  end

  def tick(space_was_pressed)
    @rotating = !@rotating if space_was_pressed

    @angle += @rotation_speed if @rotating
  end

  def prefab
    {
      x: @x, y: @y, w: @w, h: @h,
      angle: @angle, anchor_x: 0.5, anchor_y: 0.5,
      path: "sprites/blue/square.png"
    }
  end
end

class Game
  attr_gtk

  def initialize
    @square = Square.new(x: 640, y: 360, w: 100, h: 100)
  end

  def tick
    @square.tick(inputs.keyboard.key_down.space)
    outputs.sprites << @square.prefab
  end
end

def tick(args)
  $game ||= Game.new
  $game.args = args
  $game.tick
end

Release game

create a web, pc, mac, linux, rasberry pi build of the lua game

Lua (LOVE2D):

Ruby (DragonRuby):

  1. update the 6 values in `./mygame/metadata/game_metadata.txt` (name of game, game id, version, and icon)
  2. run `./dragonruby-publish –package`, all packages will be created and zipped under `./builds`

uh… that’s it.

you can test the web version by running `./dragonruby-httpd ./builds/path-to-html-directory`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment