Solitaire
The solitaire example is where Ruflet starts to feel like a serious app framework instead of a small demo toolkit.
This tutorial does not ask you to memorize the whole file at once. Instead, use the example as a study path:
- understand the data model
- understand slots
- understand cards
- understand layout and dealing
- understand drag and drop
- understand game rules
The full example lives here:
examples/solitaire.rbStep 1: model the game data
The example starts with plain Ruby structures:
Suite = Struct.new(:name, :color)
Rank = Struct.new(:name, :value)This is a great reminder that Ruflet does not replace Ruby. You still model your domain with normal Ruby objects first.
Step 2: build slot objects
Slots represent places where cards can live:
- stock
- waste
- foundation piles
- tableau columns
The Slot class stores:
- position
- size
- type
- pile contents
That means the UI is backed by a clean game model instead of random visual state.
Step 3: build the card object
The Card class is where game data and interactive UI meet.
It stores:
- suit and rank
- left and top position
- current slot
- whether the card is face up
- whether the card is visible
- the Ruflet control reference used for updates
This is a very important Ruflet lesson:
- app state and control references can live together when needed
page.update(...)can then patch position or image state without rebuilding everything
Step 4: render a card as a control
Each card becomes a gesture-aware control:
@control ||= gesture_detector(
left: left,
top: top,
visible: visible,
on_pan_start: ->(_e) { @game.start_drag(self) },
on_pan_update: ->(e) { @game.drag(self, e) },
on_pan_end: ->(_e) { @game.drop(self) },
on_tap: ->(_e) { @game.card_tap(self) },
on_double_tap: ->(_e) { @game.card_double_tap(self) },
content: image(image_url, width: @w, height: @h)
)This step alone teaches a lot:
- Ruflet can attach drag gestures directly to controls
- the game object handles the rules
- the card object knows how to represent itself visually
Step 5: compute the board layout
The setup_layout method is worth studying carefully.
It calculates:
- viewport-aware widths
- card size
- gaps between columns
- tableau offsets
- drop proximity thresholds
This is one of the best parts of the example because it shows how Ruflet apps can adapt layout using live client details rather than fixed assumptions.
Step 6: create slots and deck
The example then separates setup into small steps:
create_slotscreate_deckdeal
That separation is excellent practice for any larger Ruflet app:
- layout first
- model objects second
- initial state third
Step 7: understand dealing
The deal method teaches a useful pattern for many stateful apps:
- clear old state
- reset model objects
- place objects into their starting containers
- reveal only the parts that should be visible
Even if you are not building a card game, this reset-and-rebuild pattern is useful for dashboards, workflows, editors, and board-style apps.
Step 8: understand drag and drop
The drag flow is split into three methods:
start_dragdragdrop
This is the right way to study the example.
start_drag
This decides whether the selected card can move and remembers its original position.
drag
This updates the dragged card group as the pointer moves.
drop
This finds a valid destination and either places the cards or sends them back.
That separation keeps the interactive logic understandable even though the app is much larger than the earlier tutorials.
Step 9: understand the rules
Rule methods are intentionally small:
valid_foundation?valid_tableau?candidate_slots
This is a strong design choice. The UI code is complicated enough already, so the game rules stay isolated and readable.
Step 10: understand rendering
The board is finally mounted like this:
board = container(width: @board_w, height: @board_h, content: stack(children: controls))
body = container(
expand: true,
padding: @outer,
content: column(
expand: true,
spacing: 10,
children: [@status_control, board]
)
)
@page.add(body, appbar: app_bar(...))That is the architectural payoff:
- a normal app bar
- a board container
- a
stackfor absolute-positioned card controls - page updates for card movement and flip state
How to study this example productively
Do not try to rewrite the whole file in one pass.
Use this order:
- read
Suite,Rank,Slot, andCard - read
setup_layout,create_slots,create_deck, anddeal - read
start_drag,drag, anddrop - read
valid_foundation?andvalid_tableau? - read
render
That order makes the example far easier to absorb.
Run the example
-function">cd examples
-function">bundle install
-function">bundle exec -function">ruflet run solitaireWhat you learned
- how larger Ruflet apps can still stay organized
- how to combine Ruby domain models with interactive controls
- how gesture handling works in practice
- how
stack,container, and targeted updates support game-like interfaces