Skip to content
Tags

39. Working with playing cards

April 22, 2013

I’m going to do a couple of posts on working with playing cards, which will include creating your own pack from scratch, like this
http://instagram.com/p/YZWRURBHZW/

The basics

Before you start worrying about how to create the actual cards, you need some basics behind the scenes. Specifically, you need to be able to

  • shuffle one or more packs of cards randomly
  • deal those cards one by one until they are gone

and that’s what I’ll deal with in this post.

Shuffling a pack
Shuffling a pack (or any list of items) is quite easy in Lua/Codea. There are several ways to do it, and the method below is quite efficient.

First, you create a list with all the values you want to shuffle, which in our case is a number from 1 to 52 (we can turn these numbers into suits and card values later). So we create a table and add items from 1 to 52.

Then, we pick a number between 1 and 52 at random, and take that item from our table to be the first card. It is added to a new table containing the shuffled list. We then remove the item we picked from our original list.

After that, we keep choosing a random item between 1 and the number of remaining cards, adding that item to our shuffled list, and removing it from the original list. It is exactly like picking a card at random from a face down stack of cards.

So if we picked 18 the first time, it will also have the value 18 (because we numbered them in sequence. We add 18 to our shuffled list and remove it from the original list. If we pick 24 the next time, it will have the value 25, because all the items above 17 have moved up by one. If the next pick is 36, it will contain the number 38, and so on. The important thing is that each number can only be picked once.

If you want to shuffle multiple packs, then you simply create a bigger list to start with, so after creating a list containing the numbers 1-52, you then add the numbers 1-52 for the next pack, and the next pack (which is exactly like putting all the packs on top of each other to make one big pile. Then you pick cards at random until there are no more. So it’s very simple to allow for multiple packs.

This shuffling approach should work with any list of items containing numbers, text, anything you like, and (to me, anyway) it makes sense because it is similar to how we would do it by hand.

Anyway, here is the code

function Pack1:shuffle(p)
    n=p or 1 --number of packs, defaults to 1
    --set up pile with all the packs in order first
    local t={}
    for i=1,n do
        for j=1,52 do
           t[#t+1]=j   --add to end of list
        end
    end
    --now sort the pile randomly by pulling cards out at random
    local pack={}  --table to hold shuffled cards
    while #t>0 do
       --pick a random item, add to shuffled list, remove from table
       local i=math.random(1,#t)
       pack[#pack+1]=t[i]
       table.remove(t,i)
    end
    return pack
end

Dealing cards
Dealing cards is pretty simple. Just return the first item from the shuffled list, and delete that item.

But if all we return is a number between 1 and 52, then our main program has to figure out the suit and the value of the card. It would be friendlier to provide that information.

What is nice about Codea (Lua, actually) is that you can return extra values from a function and they can be ignored if you don’t want them. So we can provide a number of items of information about the card we’ve dealt, and the main program can use what it needs.

The function below takes the first card from the pack, figures out its suit and value, and returns all the information in a mini table.

function Pack1:nextCard(p)
    if #self.pack==0 then return nil end
    local value={'A','2','3','4','5','6','7','8','9','10','J','Q','K'}
    local suit={'S','H','D','C'}
    local c=self.pack[1]
    table.remove(self.pack,1)
    local v=(c-1)%13+1
    local s=(c-v)/13+1
    return {value=value[v],suit=suit[s],valueIndex=v,suitIndex=s,cardIndex=c}
end

The value of returning a table like this is that it makes it easy for the main program to use what it needs using names, instead of trying to figure out if it should use the third or fourth item returned.

So you can use the two functions above to shuffle and deal a whole pack, printing the suit and value (eg 7H, QD), like so

    p=Pack()
    --I'm assuming the init function in Pack
    --shuffles the cards for us
    --now we print them out one by one
    repeat --a form of do loop
       v=p:nextCard() --get details of next card
       --print details if provided
       if v then print(v.value,v.suit) end
    until v==nil

Full code is here.

Using closures – for extra credit 😉

A few posts ago, I described a strange feature of Lua, called closures, which are functions with zombie functions that won’t die. They can be quite useful, so I thought I’d try using one for the shuffling and dealing to see how well it works.

If you don’t care, skip this, because I won’t be using it in my continuing posts on playing cards – but hopefully, like me, you want to understand this weird feature a little better in case it is useful one day. There’s a bit more about ‘self’ below, too.

Here is the code. I’ve omitted all the lines which are the same as before so you can see what is going on, and what I’ve changed.

function Pack2:ShuffleAndDeal(p)
    --code to shuffle pack(s) and create a shuffled
    --table called pack
    --this is exactly the same code as above
    --instead of exiting, we now carry on.....

    local value={"A","2","3","4","5","6","7","8","9","10","J","Q","K"}
    local suit={"S","H","D","C"}
    return function ()
                --code to return info about first card
                --in shuffled table
                --this is the same code as before
            end
end

To set this up, we need this

--p=number of packs
function Pack:init(p)
    --set up variable to hold function
    --that will deal cards for us
    --(the p or 1 bit just defaults the
    --number of packs to 1 if not provided)
    self.deal=self:ShuffleAndDeal(p or 1)
end

and the main program uses it like this, much the same as before

    p=Pack()
    repeat
       --note we use a dot below
       v=p.deal()
       if v then print(v.value,v.suit) end
    until v==nil

There are a couple of things to note. First, why bother? Well, doing it this way means that all the shuffling, and the tables containing lists of suits and values, are locked away in zombie values that cannot be accessed by any code. The only thing the user can do is ask for more cards. So if you want your data or formulae protected, this is a good way to do it. I’m not suggesting it was actually a better way to shuffle and deal cards, but it is always interesting to experiment.

Note that ShuffleAndDeal returns a function (which is how you create closures), and we assign it to self.deal, a variable belonging to the Pack class. So when Main wants the next card, all it has to do is call self.deal() – we use brackets because it is a function, not a value – and the closure function runs, retrieving the next card from our zombie stack and looking up its value in our zombie tables.

Note too that when calling self.deal() from Main, you use a dot, ie p.deal(), rather than a colon, p:deal(), because deal is defined as self.deal with a dot.

Only for people who wonder “what if” :I tried p:deal() and it works, but only because we aren’t passing any parameters. If, say, there was one parameter, x, we would get an error if we called p:deal(x) because Codea would add ‘self’ as the first parameter, so x would end up being given self (which is a table).

To make it work, you’d have to change the closure function inside ShuffleAndDeal so it took two parameters, the first one being a dummy that you ignore.

You would never do this, of course, but I find it helps me understand things if I experiment a little, because otherwise, dots, colons, self and closures tend to just swirl round in my head…

Advertisement

From → animation, Games

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: