Squib

Edit. Compile. Playtest.

Squib is a Ruby DSL for prototyping card and board games. Write a little bit of Ruby, define your deck’s stats, then compile your game into a series of images ready for print-and-play or even print-on-demand. Squib is very data-driven and built on the principle of Don’t Repeat Yourself. Think of it like nanDeck done “the Ruby way”. Squib supports:

  • A concise set of rules for laying out your cards
  • Loading PNGs and SVGs
  • Complex text rendering using Pango
  • Reading xlsx and csv files
  • Rendering to PNGs, PDFs, and SVGs (sheets or individual files)
  • Flexible, data-driven layouts in Yaml
  • Basic shape drawing, blending operators, gradients, etc.
  • Unit conversion
  • The full power of Ruby!

Squib is based on the Cairo graphics rendering engine, the library of choice for WebKit, Gecko, Inkscape and many, many others.

Check this out.

require 'squib'

Squib::Deck.new(cards: 2) do
  background color: 'white'
  text str: ['Hello', 'World!']
  save_png prefix: 'basic_'
end

Perhaps something a little prettier…

require 'squib'

Squib::Deck.new(cards: 2) do
  background color: '#230602'

  text str: %w( Attack Defend ),
       color: '#F3EFE3', font: 'ChunkFive Roman,Sans 72',
       y: '2.5in', width: '2.75in', align: :center

  svg file: %w(attack.svg defend.svg),
      width: 500, height: 500,
      x: 150, y: 250 # icons adapted from game-icons.net

  save_png prefix: 'better_'
end

Let’s go bigger.

Suppose we have an excel sheet like this:

Title Attack Defend
Sword 5 4
Dagger 3 3
Axe 8 1
Shield 1 8

And a layout.yml file like this:

AllText:
  font: ChunkFive Roman,Sans 72
  align: center
  color: '#F3EFE3'

Title:
  extends: AllText
  width: 825
  y: 150

AttackText:
  extends: AllText
  x: 300
  y: 500
  font_size: 84
AttackIcon:
  extends: AttackText
  x: += 75
  y: -= 15
  width: 150
  height: 150

DefendText:
  extends: AttackText
  y: += 200
DefendIcon:
  extends: AttackIcon
  y: += 200

And then this deck.rb file:

require 'squib'

Squib::Deck.new(cards: 4, layout: 'layout.yml') do
  background color: '#230602'
  deck = xlsx file: 'bigger.xlsx'

  text str: deck['Title'], layout: :Title

  svg file: 'attack.svg', layout: :AttackIcon
  text str: deck['Attack'], layout: :AttackText

  svg file: 'defend.svg', layout: :DefendIcon
  text str: deck['Defend'], layout: :DefendText

  save_png prefix: 'bigger_'
end

Even Bigger…

Ok that was cool, but let’s go bigger. This example uses our built-in layout called hand.yml as a base template (found here).

Title Attack Defend Health Description Snark Art
Sword 5 4 3 You may draw 1 card. My father always told me that the sword is more bargaining chip than weapon. broadsword.svg
Daggers 3 3 1 If your opponent attacks, you may counter +1 Damage Darkness is my cloak. daggers.svg
Battle Axe 8 1 3 Cannot be used against Dwarves. You might see me coming, but you can’t stop me. battle-axe.svg
Wooden Shield 1 8 2 Breaks apart against an attack of 5 or more. This will have to do for now. round-shield.svg

And a layout.yml file like this:

Title:
  extends: title # from hand.yml
  font: ChunkFive Roman,Sans 58
  color: '#F3EFE3'
  align: center
  spacing: -15

Art:
  extends: art

Description:
  extends: description # from hand.yml
  font: Sans 36
  color: '#F3EFE3'
  align: center

Snark:
  extends: snark # from hand.yml
  font: Sans italic 18
  align: center
  valign: middle
  color: '#F3EFE3'

Attack:
  extends: bonus1 # from hand.yml
  font: ChunkFive Roman,Sans 48
  color: '#F3EFE3'
  y: += 70
AttackIcon:
  extends: bonus1 # from hand.yml
  x: += 40
  width: -= 40
  height: -= 40

Defend:
  extends: Attack
  y: += 200
DefendIcon:
  extends: AttackIcon
  y: += 200

Health:
  extends: Defend
  y: += 200
HealthIcon:
  extends: DefendIcon
  y: += 200

And then this deck.rb file:

require 'squib'

Squib::Deck.new(cards: 4, layout: %w(hand.yml even-bigger.yml)) do
  background color: '#230602'
  deck = xlsx file: 'even-bigger.xlsx'
  svg file: deck['Art'], layout: 'Art'

  %w(Title Description Snark).each do |key|
    text str: deck[key], layout: key
  end

  %w(Attack Defend Health).each do |key|
    svg file: "#{key.downcase}.svg", layout: "#{key}Icon"
    text str: deck[key], layout: key
  end

  save_png prefix: 'even_bigger_'
  showcase file: 'showcase.png', fill_color: '#0000'
  hand file: 'hand.png', trim: 37.5, trim_radius: 25, fill_color: '#0000'
end

Ready to get started?