home

Paperlang Language Guide

Contents

  1. The Basics
  2. The Game
  3. Variables
  4. Declaring Cards
  5. Move Commands
  6. Simple Moves & Cuts
  7. Create Random Cards
  8. Create Specific Cards
  9. Player Chooses a Card
  10. Choosing between decks
  11. Choosing between options
  12. Control flow
  13. Stacklists (lists of stacks)
  14. Filtering source & destination stacks
  15. Ordered Sequence Matchers
  16. Sequence Matchers & Stack Lists Move Commands
  17. Moving cards based on their attributes
  18. Summing up card attributes
  19. Detecting Card Attribute Sequences in unordered stacks
  20. Shuffling
  21. Card movement hooks
  22. Declaring a winner
  23. Declaring a loser
  24. phases
  25. while loops

You can open any projects in the Paper Toy tool by entering the main menu with escape, clicking "Load":

and pasting the path to your project from the clipboard by pressing the "Paste from clipboard" button.

Then Selecting the game file, and hitting the load button

press 'r' on your keyboard to start or restart the game once it has been loaded (this also recompiles the code if you've made any changes)

The Basics

Paperlang is a programming language for card games.

The two basic concepts in Paperlang are Cards and Stacks. Cards & Stacks may both contain attributes. Stacks contain some builtin attributes that hold important state, like size for the size, top for the top card, bottom for the bottom card.

            card MyFirstCard start
                int h = 0
            end

            MyFirstCard.h ; access 'h'

            visiblestack hand

            place MyFirstCard -> hand 1

            hand.size == 1 ; hand contains 1 card, so it's size is 1
            hand.top == MyFirstCard ; the top card in a is 'MyFirstCard'
            hand.bottom == B ; the bottom card in a is also 'MyFirstCard'

            card MySecondCard start
                int h
            end

            place MySecondCard -> hand 1
            hand.size == 2 ; a now contains 2 cards
            hand.top-1 == MyFirstCard ; the second from top is still MyFirstCard
            hand.top == MySecondCard ;the top card is now MySecondCard

            int hand.customVariable ; stacks can have attributes assigned to them
            hand.customVariable = 5 ; and assigned to
        

There are two kinds of stacks, visible and hidden:

            game Example start

                card A start end

                visiblestack a
                hiddenstack b

                place A -> a 5
                place A -> b 5

                setup start

                end

                turn start

                end
            end
        

Here a is visible (faceup), and b is hidden (facedown)

You can chage how the stacks & cards are displayed using the design tool. You can access the design tool from the escape menu

Lets see what happens when we change the orientation of stack b to 'Horizontal'...

You can also assign sprites to cards from here. Sprite paths are relative to the game code directory.


The Game

This is the simplest game you can create
            game EmptyGame start
                setup start

                end

                turn start

                end
            end
        

Notice the setup and turn blocks denoted buy the start and end tags.

Any code in the setup block will run at the start of the game. And any code in the turn block will run every turn.

You can place code outside of the setup block, and that will get run before the game starts. However, anything that happens in the setup block will be fully animated and visible to the player.

For example, when dealing out cards, maybe you want the player to see the cards being dealt out one by one, instead of starting the game with a full hand. Then you would put the dealing code inside the setup start ... end block

If you load this example into PaperToy, you will notice that the turn counter goes up automatically, this is because nothing inside the turn block is asking the user for any input. If no user input is nessecary, a turn will proceed automatically.

This game is empty, so we end up with an empty screen

Variables

As in most programming languages, paperlang has variables. There are different types of variables. Right now only integer numbers (whole numbers) and booleans (true or false) are available. Strings (text) are planned for a future update. floats (decimal numbers like 1.25) will added if there is demand for it.

variables are declared like so:

            game mygame start
                int i
                i = 5

                int j = 4

                ...

                turn start
                    bool weWon = false
                end
                ...
            end
        

As you can see, you can declare and assign attributes in the same line, or on different lines. Variables declared inside blocks are not visible to code outside of that block, but variables declared outside a block are visible to code inside that block. For instance in the above example i & j can both be accessed from the turn block, but weWon cannot be accessed outside the turn block.

Small technical detail (for interest only): the following two peices of code compile to exactly the same bytecode:

            int i
            i = 5
        
            int i = 5
        

Declaring Cards card MyCard start ... end

The first thing we should cover here is how to make a card. Cards are declared like so:

                    card Monster start
                        int defence
                        int attack
                    end
                

This delcares a Monster card. But what if we want multiple types of monsters? And what if we want to easily fill a stack of cards with random monsters? Well that's where 'parent' cards come in.

                card Monster start
                    int defence
                    int attack
                end

                card Goblin Monster start
                    defense=5
                    attack=5
                end

                card Hobold Monster start
                    defense=10
                    attack=2
                end

                card Elf Monster start
                    defense=2
                    attack=8
                end
            

Here we create three different kinds of Monster: Goblin Hobold Elf each with different defense and attack stats.

You cannot change an attribute for one individual card, changing an attribute for one card will change that value for all cards of that type. for instance this code:

                monsterStack.top.defense = 0
            

Will take the top card of the stack monsterStack, and change it's defense value to 0. But it also will change the defense value of all the other cards of the same type to 0. So if the top card happens to be an Elf then all Elf cards will have a defense value of 0. This is currently just a limitation of the engine. 'Singleton cards' are planned for a future release.

But how would we fill a stack with random Monster cards? Like this:

                hiddenstack monsters
                random Monster -> monsters 100
            

Premade Standard Playing Card Deck & the #use command

When looking through the example code, you will often see this a line like this:

                #use "deck"
            

if you have a look in the same folder as the code file with this line, you will find a file called deck, if you open this file up in an editor like notepad, you will see a bunch of playing card declarations, and commands filling a hiddenstack named 'deck'.

                ...
                card aceH number start v = 1 suit = 1 end
                card twoH number start v = 2 suit = 1 end
                card threeH number start v = 3 suit = 1 end
                ...

                hiddenstack deck
                place aceH -> deck 1
                place aceD -> deck 1
                place aceC -> deck 1
                ...
            

The deck stack represents a standard deck of 52 playing cards. These are already defined for you (along with their sprites), so there's no need to make them yourself. All you have to do is make sure the deck file (and the matching design.txt file, we'll cover this later) is next to your game file, and you're ready to use the standard deck of playing cards.

Move Commandsa -> b

Simple Moves & Cuts

The next thing we want to cover are move commands. The syntax is:

                SOURCE_STACK -> DESTINATION_STACK top/bottom NUMBER_TO_MOVE
            
                SOURCE_STACK /> DESTINATION_STACK top/bottom NUMBER_TO_CUT
            

A move -> moves cards from one stack to the other, one at a time. So if you are moving from the top of one stack to the top of another, the order of the cards will be reversed.

A cut /> moves multiple cards at the same time from one stack to the other, preserving order.

Create Random Cards

This line means, move NUMBER_TO_MOVE cards from either the top or bottom of SOURCE_STACK to the top of DESTINATION_STACK.
If you want to move a card to the bottom of DESTINATION_STACK, you can replace the '->' (move over) with an '->_' (move under)

random Monster -> a 20 means "Create twenty random cards of type 'Monster' and place them on the top of the 'a' stack"

Create Specific Cards

To place create specific cards and place them into a dack, you can use the place keyword

                place Goblin -> a 5
            

The above code creates 5 Goblin cards and places them on the top of stack a

Player Chooses a Card

This feature can be quite useful when a player needs to choose a card from their hand to play.

You can also let the player decide which cards specifically to move with choose SOURCE_STACK -> DESTINATION_STACK NUMBER_TO_MOVE

                ...
                    random Monster -> a 3
                    random Monster -> b 3

                    setup start

                    end

                    turn start
                        pickone start
                            pick choose a -> c 1 then
                            pick choose b -> c 1 then
                        end
                    end
                ...
            

if you replace lines 21 to 33 in cards&stack.txt with this code snippet, each time you choose a stack to move a card from, the game will let you choose which card from that stack is moved. If that stack is a visiblestack the front of the cards will be visible, else they will be hidden:

Choosing between decks

There's a much simpler way to express this part:

                    pickone start
                        pick choose a -> c 1 then
                        pick choose b -> c 1 then
                    end
                

and that's like this:

                    choose a,b -> c 1
                

If you specify multiple source or destination stacks like this, the user will get to choose between those options. The 'choose' keyword at the start of the line here makes it so that once the user has selected a source stack, they will need to select which card to move too.

                    a,b,c -> e,f,g top 1
                

This means: "Move the top card from either of stacks a, b or c TO either of stacks e, f or g."

there's another way to specify multiple stacks in a stack move, and that's with stacklists. These let you do powerful things like positional queries & indexing. These will be covered in a later section.

Choosing between options

You may ask then, what is the difference between a pick block pickone start ... end block and just specifying multiple stacks seperated by a comma a,b -> c,d ?

The answer is effects. Say you wanted to let the user choose a deck to move a card from, but then you wanted something to happen based on their choice. You could use a pickone ... block to acheive this:

Imagine a game of blackjack (in fact you don't have to imagine very hard, as a full blackjack implementation is included as an example project. examples/blackjack)

Here, we implement hit and stick "buttons" using two different stacks. When the user 'chooses' either stack, we send the top card, to the top of the same stack (essentially moving nothing), but then depending on the users choice, we either deal more cards to the dealer & player, or we end the game.


                    card Hit start end
                    card Stick start end

                    visiblestack hitbutton
                    visiblestack stickbutton

                    place Hit -> hitbutton 1
                    place Stick -> stickbutton 1

                    ...

                    pickone start
                        pick hitbutton -> hitbutton top 1 then
                            deck -> hand top 1
                            deck -> dealerHidden top 1
                        pick stickbutton -> stickbutton top 1 then
                            ; calculate the winner
                            ...
                    end
                    ; check if we've lost ...
                

The dealers hand is mostly hidden to the player, apart from their first card. The players hand is visible at the bottom. If the player chooses to stick, the dealers hand is revealed. If the players score exceeds 21, then the player automatically looses. If you're interested in looking at the code, it's available in the example blackjack project docs/examples/blackjack

Control flow if ... start ... elseif ... else ... end

an if/elseif/else structure exists in paperlang. Leaning on the blackjack example project again:

                    if dealerScore > 21 start
                      winneris currentPlayer
                    elseif (21 - playerScore) < (21 - dealerScore) then
                      winneris currentPlayer
                    else
                      looseris currentPlayer
                    end
                

Stacklists (lists of stacks)

It is possible to define a list of stacks, this is usefull where you have multiple stacks that need to interact with eachother in a positional way. EG in the solitaire game Accordion

For an example of stacklist use, please see the Accordion Worked Example Project

                    stacklist list[5]

                    place CardA -> list[0] 1
                    place CardA -> list[1] 1
                    place CardA -> list[2] 1
                    place CardA -> list[3] 1
                    place CardA -> list[4] 1

                    list -> list[:-1] top all
                

The above code defines a list of 5 stacks, places a card on each stack, and then asks the player to move all the cards from any stack to the stack directly preceding it.

In the Accordion game, the player can choose to move a card 1 to the left, or three to the left, so the move command looks like this:

                    list -> list[:-1:-3] top all
                

Meaning -1, or -3. Relative [:N] indexing is always relative to the source deck, so the following code does not make sense and will not run:

                    list[:1] -> list[-1] top 1
                

You can also directly specify stacks in a list by index:

                    list[4] -> list[0] top 1
                

Move the top card from the 5th stack in the list, to the first stack in the list (indexing begins at zero)

Filtering source & destination stacks a,b,c -> d,e,f top 1 where from{...}, to{...}

In situations where you are moving either to or from multiple stacks, you may want to filter available choices by certain criteria. We use as an example, the Accordion Solitaire implementation.

                    row /> row[:-1:-3] top all where from{from.size > 0}, to{(from.top.v == to.top.v) || (from.top.suit == to.top.suit)}
                

In Accordion solitaire, you may only move a card to a stack where the top card on that stack matches the card you want to move, either in value, or in suit. We define that rule as a move filter:

                    where from{from.size > 0}
                

the stack you're moving from, must contain at least one card

                    , to{(from.top.v == to.top.v) || (from.top.suit == to.top.suit)}
                

the top card of the stack you're moving to must match the top card of the source deck either in suit, or value (v)

the two filters must be seperated by a ',' comma, and must follow the where keyword

Ordered Sequence Matchers

You can test whether a stack contains a certain sequence of cards, in order. You can use an equality test against a stack and a sequence matcher [...].

                    if a == [A B C D] start
                    ...
                

Within the square branckets you specify a sequence of cards by name. You can also match any card once: '_', and match any number (including 0) of cards with ':'. Please see the following examples:

"Does stack contain cards A B C and D in that order. And no other cards?"

                    a == [A B C D]
                

"Does stack contain cards A B C and D in that order at the top of the deck? Any or no cards may follow after D"

                    a == [A B C D :]
                

"The top card may be any card, then the sequence in the deck must be B, C & D"

                    a == [_ B C D]
                

"Does a contain C anywhere in the stack?"

                    a == [: C :]
                

"Does stack a start with A on top, then contain the sequence B, anycard, D anywhere in the deck, and then end with another D card?"

                    a == [A : B _ D : D]
                

Sequence Matchers & Stack Lists Move Commands

You can use ordered sequence matchers to select stacks that you can move from & to:

a{[A A A A]} -> b top all means, ask the user to select a stack matching the sequence [A A A A] from the list to move from:

                    card A start end
                    card B start end

                    stacklist a[2]
                    visiblestack b

                    place B -> a[0] 4
                    place A -> a[1] 4

                    a{[A A A A]} -> b top all
                

You can see that only the second stack here is selectable:

You can also pick the first/last stack that matches the sequence:

                    card A start end

                    stacklist a[2]
                    visiblestack b

                    place A -> a[0] 4
                    place A -> a[1] 4

                    a{|[A A A A]} -> b top all
                

To select the last stack in the sequence, simply put the '|' after the sequence: a{[A A A A]|} -> b top all.

Here you can see that even though both stacks match the sequence, only the first stack is selectable.

You can also specify destination stacks:

                    card A start end
                    card B start end

                    stacklist a[2]
                    stacklist b[2]

                    place A -> a[0] 4
                    place B -> a[1] 4
                    place B -> b[0] 4
                    place A -> b[1] 4

                    a{[A A A A]} -> b{[A A A A]} top all
                

Moving all of one kind of card

You can move all of one kind of card: a{C} -> b top all

                    game EmptyGame start

                        card A start end
                        card B start end
                        card C start end
                        card D start end
                        card E start end

                        visiblestack a
                        place A -> a 5
                        place B -> a 5
                        place C -> a 5
                        place D -> a 5
                        place E -> a 5

                        shuffle a

                        visiblestack b

                        setup start

                        end

                        turn start
                            a{C} -> b top all
                        end
                    end
                

Move all C cards from a to b

The expression a{C} actually counts as a 'logical stack' so you can do things like this:

                    ; if there are 5 C's in a, move them all into b
                    if (a{C}).size == 5 start
                        a{C} -> b top all
                    end
                

Moving cards based on their attributes

You can also put logical expressions inside braces: a{x > 5}

                    ...
                    card A start
                        int x = 5
                    end
                    card B start
                        int x = 2
                    end
                    ...
                    a{x > 4} -> b top all
                

Here we move cards who's x attribute is greater than 4 to the b stack

Summing up card attributes

In blackjack, it's important to keep track of the total score of cards in play. You can do this by summing over attributes of cards in a stack. The following code uses the prebuilt 52 card deck. hand{v} means sum all of v attributes of the cards in hand. In our blackjack game, we assign that to our playerScore variable so that we can track it. Cards in the premade deck have two attributes, v for value, and suit for suit.

                    #use "deck"
                    visiblestack hand
                    int playerScore

                    ...

                    playerScore = hand{v}
                

Detecting Card Attribute Sequences in unordered stacks

Sometimes it's usefull to detect sequences of cards in stacks that may be unoredered. Consider the game rummy, where you need to have sequences of cards EG [A 2 3] [9 10 j] in your hand, your hand may not be sorted, and whats's more, you don't want to explicitally write down every possible legal three sequence. This is where Orderless Sequence Matches come in.

Sequences construct 'logical' stacks. You can treat these stacks just like ordinary stacks, you can check their size, move from them and match them against sequences like [: A 2 3 :].

The syntax is (STACKNAME { sq N A EXPRESSION }) where STACKNAME is the name of your stack, N is the expected size of the sequence, A is the attribute you want the stack to be sorted by during the search, and EXPRESSION is the expression to test previously matched card in the sequence against the next one.

In the expression, the last matched card and next card to test are bound to l and n respecitively. When the search is executed, the stack will be sorted by the attribute A, and every possible sequence matching your search will be tested for.

At the moment, only sorting in ascending order is supported.

In the following example, 5 cards are dealt into the players hand, and if any continuous sequence of 3 cards is detected, the player wins. hand{sq 3 v (l.v+1) == n.v}) We sort the stack hand by the attribute v, then we only match cards where the next card's v value is one greater than the last.

The resulting logical stack will contain 3 cards if a matching sequence has been found, otherwise it will be empty.

                    game OrderlessSequences start

                        #use "deck"

                        visiblestack hand

                        shuffle deck

                        setup start
                            deck -> hand top 5
                        end

                        turn start

                            if (hand{sq 3 v (l.v+1) == n.v}).size == 3 start
                                winneris currentPlayer
                            end
                            ...
                        end
                    end
                

If the player wins, lets move the winning sequence from the hand to a done stack

                    if (hand{sq 3 v (l.v+1) == n.v}).size == 3 start
                        hand{sq 3 v (l.v+1) == n.v} -> done top all
                        winneris currentPlayer
                    end
                

Shuffling

Stacks can be shuffled:

                    shuffle a
                

"shuffle stack a"

Card movement hooks (not available in demo version)

Many collectable card games have mechanics where individual cards has special rules that play out when they enter or exit certain stacks. In Paperlang, you can use onmoved and premove hooks to run a bit of code every time a specific card has just been moved, or is just about to be moved.

inside the onmoved and premove hooks, to references the stack the card moved in to, and references the stack the card just moved out of. this references the card being moved.

Hint! Please note that currently, attempting to get user input during a movement hook will result in the error: 'You cannot wait on user input from a movement hook'

                    ...
                    visiblestack graveyard
                    visiblestack play
                    card Necromancer start end

                    onmoved Necromancer start
                        if to == graveyard start
                            ; raise the dead!
                            graveyard -> play top all
                        end
                    end
                    ...
                

When the Necromancer enters the graveyard, everything in the graveyard gets moved into the play stack

But this code will also bring the Necromancer back from the graveyard. Maybe we want to move all the cards from the graveyard just before the necromancer gets there, so the necromancer will still end up in the graveyard?

For that, we can use a premove hook

                    ...
                    visiblestack graveyard
                    visiblestack play
                    card Necromancer start end

                    premove Necromancer start
                        if to == graveyard start
                            ; raise the dead! Just before the Necromancer gets there ...
                            graveyard -> play top all
                        end
                    end
                    ...
                

Declaring a winner

The current player wins

                    winneris currentPlayer
                

Declaring a loser

The current player loses

                    looseris currentPlayer
                

Phases

sometimes, it's useful to define some behaviour once, and then run it from somewhere else. This could be because it needs to run in multiple locations, or it could just be to neeten up your code.

Syntax: do PHASE_NAME

                    ...
                    phase refreshHand start
                        deck -> hand top 1
                    end
                    ...
                    turn start
                        do refreshHand
                        ...
                    end
                    ...
                

Here, we delcare a phase called refreshHand, which adds a card to our hand from the deck We run it at the start of every turn

while loops

Sometimes, it's nessecary to repeat an action multiple times while something is true. If you have some experience with programing this idea shouldn't be new to you. It works in exactly the same way as in many other programing languages.

                    i = 0
                    while i < 5 start
                        deck -> hand top 1
                        i = i + 1
                    end
                

This will place 5 cards into the hand stack.

WARNING Infinite loops will cause the game to hang and become unresponsive.