The player deals out the cards one by one face up, in a row from left to right, as many at a time as space allows.
Any card may be placed on top of the next card at its left, or the third card at its left, if the cards are of the same suit or of the same rank.
The goal is to get all the cards in one pile.
Rules Reference: bicyclecards
This is all the code needed for the game, as you can see it's very short.
Paperlang was designed exclusively for card-games, so common mechanics such as moving cards between stacks of cards are very easy to express.
game accordion start
#use "deck"
shuffle deck
stacklist row[20]
setup start end
turn start
pickone start
pick row /> row[:-1:-3] top all where from{from.size > 0}, to{(from.top.v == to.top.v) || (from.top.suit == to.top.suit)} then
pick deck -> deck top 0 then
auto deck -> row{|[]} top 1
end
every row /> row[:-1] top all where to{(to.size == 0) && (from.size > 0)}
if (row[0]).size == 52 start
winneris currentPlayer
end
end
end
As this is the first example, we'll start with some basics.
the whole game is contained within the game start & end lines:
game accordion start
...
end
You will see a lot of start ... end pairs, together they define a 'block' of code.
This is similar in many ways to other programming languages if you are familiar with software development (variable scope, for instance)
#use "deck"
This simply means that we are going to use the standard 52 card deck of paying cards. They come with all the properly configured card sprites too.
shuffle deck
The previous line actually gave us access to a 'stack' of cards called "deck". You can shuffle any stack of cards, meaning to randomly re-order the cards in the stack.
By default, the deck is structured in order of Rank,Suit.
setup start end
A setup block is something that runs once at the start of every game. We could have put the shuffle command into the setup block, but this would have made no difference to the game
Any code that is placed directly under the
game accordion start end
blocks will run once at the start of the game too, the only difference is that no animations will be triggered for code that runs outside of the setup block. As this code runs before the game renders for the first time.
So if you want any opening setup of your game to be animated, put that code into the setup block.
There is nothing between the
start ... end
in this setup block, meaning the block is empty.
turn start
...
end
Anything inside the turn block will run every single turn. Once a turn is over, the next turn will automatically begin. At the moment this autoplay behaviour cannot be changed.
pickone start
pick row /> row[:-1:-3] top all where from{from.size > 0}, to{(from.top.v == to.top.v) || (from.top.suit == to.top.suit)} then
pick deck -> deck top 0 then
auto deck -> row{|[]} top 1
end
This is a "pick block". The pick block is used whenever the player must choose between moving cards from multiple stacks.
There's quite a lot going on here so lets break it down line by line.
pick row /> row[:-1:-3] top all . . . then
Give the user the option to pick a card from any stack in the stacklist defined above as row and place that card in the stack imeediately before it, or three spaces before it (row[:-1:-3]) the : before the number here means that we are selecting a position in the stacklist relative to where the cards are being moved from.
top means move cards from the top of the selected stack, and all means move all of the cards from the selected stack. (all could have been replaced by a number 5, or a variable X for instance)
the where clause specifies constraints on what can be selected, we will cover this later.
Importantly, anything after the then and before the next pick or end of the pick block will run after the player has selected that option.
You must always write the word then even if nothing will run. In this case, the first thing after then is the next pick option, so nothing will run after the user selects this option.
There are two ways of moving cards between stacks. Cutting /> and moving ->.
Cutting preserves the order of the cards, where as moving reverses them.
Imagine taking a card from one stack and placing it ontop of another stack. Then repeat that 3 times. The top three cards from the source stack are now in the destination stack in the opposite order than they were in before. This is a move ->.
Now imagine you had taken the same three cards from the source stack together, and moved them all at once to the destination stack. The same three cards are now in the same order. This is a cut />.
This is the difference between moving and cutting.
Let's focus on the where clause in the pick block above
pick ... where from{from.size > 0}, to{(from.top.v == to.top.v) || (from.top.suit == to.top.suit)} ...
This might be more obvious to you if you are familiar with SQL. The where clause constrains what stacks the user can select as the source (from) and destination (to) stacks.
in a where clause, the variable from references the source stack, while the variable to references the destination stack.
from{from.size > 0}
This means, constrain the source stack to stacks that contain at least one card (more than 0 cards)
to{(from.top.v == to.top.v) || (from.top.suit == to.top.suit)}
This means, constrain the set of possible destination stacks to stacks where the top card has either the same v value (from.top.v == to.top.v) OR (||) the same suit value (from.top.suit == to.top.suit)
Note the comma ... > 0}, to{ ... that must separate the two clauses
auto simply means that the move or cut will happen without needing any user input.
Notice in the second pick block, in the then block (gets executed after the user selects the deck -> deck top 0 option)
we move the top card (top 1) from deck to one of the stacks in row
The {...} means select a stack from this stacklist that satisfies the follwing condition.
The condition we specify is: |[] - meaning the first stack | that matches this sequence []. [] is an empty sequence.
So what this means is, move the top card from deck to the first empty stack in row.
after the end of the pick block, we have another /> cut command.
every row /> row[:-1] top all where to{(to.size == 0) && (from.size > 0)}
When the source of a move is a stacklist, we can use the every keyword to specify that the move must happen for every stack in the stacklist
We always want to make sure that there are no empty gaps in our accordion, that is, there cannot be an empty stack in between two stacks with cards in them.
This line says: For every stack in the stacklist row, if there are any cards in that stack, and no cards in the stack immediately to the left of it row[:-1], cut all of it's cards one stack to the left.
the next section should be self explanetory if you are at all familiar with programming.
if (row[0]).size == 52 start
winneris currentPlayer
end
If the first stack in the row contains all 52 cards, then the player has won.
The game code does not detect when there are no moves available to the player, but if it did it could set a loosing state with looseris currentPlayer
The currentPlayer variable simply refurs to the player whos turn it is now. For a single player game, this is always the same player.