Example Projects
Example projects demonstrating different scripting patterns and game mechanics.
Tic-Tac-Toe
A classic tic-tac-toe game against a simple AI opponent.
Concepts demonstrated:
- Component states for square visuals (NEUTRAL/BLUE/RED)
- Tags for grouping squares by row, column, and diagonal (
#row1,#col1,#dia1) - Custom actions for game logic (
checkWin,computerMove,reset) - Bulk operations with
foreach,first,all,any,count - Game variables for turn tracking and win detection
ChromaStack
A 2048-style matching game where pieces on a grid move together in cardinal directions. When same-colored pieces collide, they merge and change color.
Concepts demonstrated:
- Grid functions:
emptyCells()for finding unoccupied cells - Array functions:
shuffle()for random placement, indexing witharr[i].property - Foreach with tags:
foreach piece in #piece - Grid-based movement with
snapToGrid,movable,blocking - Dynamic blocking expressions:
set self.blocking "mover.color != self.color" - Combined keyboard events:
onKeyDown "ArrowUp" | onKeyDown "ArrowDown" | ...
15 Puzzle (Distributed Scripts)
The classic sliding puzzle where you arrange numbered tiles in order by sliding them into the empty space.
Approach: Each piece has its own onClick script. A Controller object handles keyboard input with custom onKeyDown handlers that find and move the appropriate piece.
Concepts demonstrated:
- Components as game pieces (visual children, no scripts on children)
- Tags for piece iteration:
foreach p in #piece - Grid functions:
isEmpty(),emptyCells() - Manual position control:
set p.cellX,set p.cellY - Custom actions:
checkWin restartcommand for replay
Script pattern:
# On each piece
onClick:
set x self.cellX
set y self.cellY
if isEmpty("Grid", x - 1, y):
set self.cellX x - 1
# ... check other directions
Controller.checkWin
# On Controller - keyboard support
onKeyDown "ArrowLeft":
set empty emptyCells("Grid")
if length(empty) > 0:
set ex empty[0].x
set ey empty[0].y
foreach p in #piece {
if p.cellX == ex + 1 and p.cellY == ey:
set p.cellX ex
Controller.checkWin
}
15 Puzzle (Dynamics-Based)
Same 15 puzzle game, but using the dynamics system instead of manual movement scripts.
Approach: All logic in one cell script. Pieces have no scripts. Uses movable capability with dynamic enable/disable based on adjacency to empty cell.
Concepts demonstrated:
- Centralized game logic (cell script only)
- Dynamic
movableassignment based on game state wait movementfor position synchronization- Separation of concerns: pieces are pure visuals, logic is centralized
Script pattern:
onEnter:
# Setup pieces on grid
foreach piece in #piece {
set piece.snapToGrid "Grid"
set piece.blocking true
# ... position randomly
}
self.updateMovable
onKeyDown "ArrowUp" | onKeyDown "ArrowDown" | onKeyDown "ArrowLeft" | onKeyDown "ArrowRight":
wait movement
self.updateMovable
self.checkWin
action updateMovable {
set empty emptyCells("Grid")
set ex empty[0].x
set ey empty[0].y
foreach p in #piece {
set isAdjacent false
if p.cellX == ex - 1 and p.cellY == ey: set isAdjacent true
if p.cellX == ex + 1 and p.cellY == ey: set isAdjacent true
# ... check other directions
if isAdjacent:
set p movable style slide
else:
set p.movable false
}
}
Comparison:
| Aspect | Distributed Scripts | Dynamics-Based |
|---|---|---|
| Script location | Each piece + Controller | Cell only |
| Movement | Manual cellX/cellY |
Dynamics system |
| Piece scripts | Required | None |
| Flexibility | Full control per piece | Centralized logic |
| Modification | Edit multiple scripts | Edit one script |
When to Use Each Pattern
Distributed scripts (like fifteen.purl):
- Different pieces need different behaviors
- Complex per-piece logic
- Click-based interactions on individual objects
Dynamics-based (like fifteen-movable.purl):
- Uniform piece behavior
- Movement-focused games
- Cleaner separation of visuals and logic
- Easier to modify/reskin