Like a turned card

Here is another version of code to shuffle a deck of cards. This one is smaller than my original one and also allows you to shuffle the deck x number of times to get a better random order of the deck. This also causes a problem that I’m not sure what’s happening. Even though I’m calling shuffleCards with a value of 5, which results in the loop doing 5 shuffles, the size of table d2 never gets above 53, even though I do a table.insert 260 (5x52) times. If I put the statement print(#d2) after table.insert(d2,d1[s]), I see the size of d2 rise from 1 to 53 and stay at 53. I think I know what’s happening, but I’m not sure. I’ll wait for other explanations.


function setup()
    suit={"Diamonds","Hearts","Spades","Clubs"}
    value={"2","3","4","5","6","7","8","9","10","Jack","Queen","King","Ace"}
    
    Shuffled=shuffleCards(5)    -- shuffle cards 10 times
    
    for z=1,52 do    -- print the shuffled deck
        s=math.ceil(Shuffled[z]/13)   -- get suit 
        v=Shuffled[z]%13+1    -- get value
        print(value[v].." of "..suit[s])
    end
end

function shuffleCards(x)
    local d1, d2, s, y, z = {}, {}
    for z=1,52 do
        d1[z]=z     -- fill table d1 with the numbers 1 to 52
    end      
    for y=1,x do    -- shuffle x number of times
        for z=1,52 do  -- loop thru 52 cards
            s=math.random(1,#d1)    -- get a random number from table d1
            table.insert(d2,d1[s])    -- insert it in table d2
            table.remove(d1,s)    -- remove it from table d1
        end
        d1=d2  -- make d1 equal to d2 for the next shuffle of d1
    end
    return d2    -- return the shuffled deck in d2
end

@dave1707 Shuffling five times does not make it more random! You really, truly do not need to do that.

Oh, and the problem is that after one shuffle you are only using one table. So on the second shuffle you are adding to the same table that you are deleting from. You probably meant to do:

d1,d2 = d2,{}

@Andrew_Stacey I don’t have any way to prove or disprove if shuffling 5 times makes it more random or not, but it only added 2 lines of code to do multiple shuffles. When I shuffle a real deck of cards, I usually shuffle multiple times, so I thought I would add that option. As for the tables being one, you are correct. When I printed the address of the 2 tables, they had different addresses to start, but the same address after that. That had me a little confused until I realized that. I like your line of code, d1,d2=d2,{} . When I added my fix, I had it as 2 lines, d1=d2 d2={} even though I used your form when I defined the 2 tables the 1st time.

@dave1707 @Andrew_Stacey - I’m with Andrew on this, Dave. As a statistician, I can assure you that randomising an already random shuffle doesn’t add anything. The reason we often do multiple manual shuffles in real life, is that our shuffles aren’t very random. The computer does a much better job.

I remember an amusing story from when bridge players first started using computer shuffled packs. They complained about the weird hands they were getting until they realised it was because their own shuffling had been non random.

@Ignatz After I sent the post I realized that wasn’t what I meant to say, but I didn’t want to try and correct it. Either something is random or it isn’t. It can’t be more random. I added multiple shuffles because it was only 2 more lines of code and I shuffle multiple times in real life.

@dave1707 - fair enough - if your shuffling is anything like mine, it takes a few of them to get some resemblance to being random!

@dave1707 but in your code you are then doing something for no reason at all so it’s a waste of time.

(Incidentally, it takes 7 riffle shuffles to randomise a pack. Unless you are a magician and can do “perfect” riffle shuffles.)

Either something is random or it isn’t. It can’t be more random.

Err, not quite. The point here is the probability distribution. If the thing you are doing is random, it might not be the right random but maybe doing it a few times gets you the right one. For example, if you have a biased coin then you can simulate a fair coin by flipping it in batches of two and taking only HT or TH as the two values.

So if your method of shuffling only introduces a little randomness then you need to shuffle multiple times to spread that through the whole pack. But the shuffling routines we’ve been discussing are already the right ones so doing them again is simply a waste of time.

This site give a lot of info on randomness. I like the cartoon in this section. http://www.random.org/analysis/

@ignatz ive tried out the code but it didn’t work.
Do you got a better solution?

@Draakk - without seeing your code, I can’t comment. My solution should work fine if you did it right. What is happening now?

@ignatz yes im sorry my bad, i forgot an extra end.
It is working now, but the following problem is, the cards keep repeating. It wont stop after card 52.
Maybe you can explain that?

Thnx

@Draakk, your deal function is a bit odd…

cards={}
for i=1,1 do
    cards[i]=p:nextCard()
end

Every time you deal you are clearing the cards table. That for loop makes no sense either as it starts at 1 and always ends at 1, so every time you are only inputting a card into index 1 of the table. If that was meant to be that way, you could accomplish the same thing by using cards[1]=p:nextCard().

I’m assuming Pack is your class to create your deck, including the randomized shuffle. If so, you don’t want to call that in the same function you use to draw a card, which I’m assuming p:nextCard() draws a card.

Let us see your Card and Pack classes and we can further investigate the issue.

@slashin8r
Here is my pack class

Pack = class()
--[[
Manages one or more packs of cards
Functions:
 
shuffle - shuffles the pack
 
nextCard - returns the next card in the pack
--]]
 
function Pack:init(p)
    self.pack=self:shuffle(p)
end
 
function Pack: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     
        end
    end
    --now sort the pile randomly by pulling cards out at random
    local pack={}  --table to hold shuffled cards
    while #t>0 do
       local i=math.random(1,#t)
       pack[#pack+1]=t[i]
       table.remove(t,i) 
    end
    return pack
end
 
function Pack: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
 
function Pack:cards()
    return #self.pack
end

And here is my card class

--# Card
Card = class()
--[[this class builds and draws the card images from scratch and provides some utilities
 
--]]
 
--class initialises by creating card design
--Parameters:
--height of card in pixels
--the image to be used on the back
--background color on the face of the card
--(optional) background color on the back of the card
-- (optional) color along the border ofthe card
 
--returns nothing
function Card:init(height,backImg,faceColor,backColor,borderColor)
    self.height = height
    self.backImg=backImg
    self.f=height/200 --scale font size to card size
    self.faceColor=faceColor or color(255)
    local x=100    --default border color
    self.borderColor=borderColor or color(x, x, x, 255)
    x=x*1.5 --corners are drawn with circles which appear darker than lines, so lighten colour for them
    self.cornerColor=borderColor or color(x,x,x, 150)
    self.backColor=backColor or color(240, 239, 237, 255)
    self:createCard()
    self:createSuits()
    self.value={"A","2","3","4","5","6","7","8","9","10","J","Q","K"}
    self.suit={"S","H","D","C"}
end
 
--this and the next function build just the front and back of the card itself
--the main problem is rounded corners
--this is done by drawing circles at the corners and then overlapping rectangles forthe final effect
function Card:createCard()
    self.cardFace=self:createOutline(true)
    self.cardBack=self:createOutline(false)
end
 
function Card:createOutline(face)
    --use standard 25/35 ratio
    self.width=math.floor(self.height*30/45+.5)
    local img=image(self.width,self.height)
    --create rounded corner on top right
    local corner=0,05 --distance from end of card as percent of height
    local c=math.floor(corner*self.height+0.5)
    setContext(img)
    pushStyle()
    strokeWidth(1)
    stroke(self.cornerColor)
    --set background colour
    if face then fill(self.faceColor) else fill(self.backColor) end
    --draw small circles at corners
    ellipse(self.width-c+1,self.height-c+1,c*2)
    ellipse(self.width-c+1,c-1,c*2)
    ellipse(c-1,self.height-c+1,c*2)
    ellipse(c-1,c-1,c*2)
    if face then stroke(self.faceColor) else stroke(self.backColor) end
    --now rectangles to fill in thre centre of the card
    rect(0,c,self.width,self.height-c*2)
    rect(c,0,self.width-c*2,self.height)
    --now a border round the card
    stroke(self.borderColor)
    line(0,c,0,self.height-c)
    line(c,0,self.width-c,0)
    line(self.width,c,self.width,self.height-c)
    line(c,self.height,self.width-c,self.height)
    --do picture on back 
    if face~=true then
        sprite(self.backImg,img.width/2,img.height/2,img.width*.9)
    end
    popStyle()
    setContext()
    return img
end
 
--the suit images come from emoji
function Card:createSuits()
    font("AppleColorEmoji")
    self.suits={unicode2UTF8(9824),unicode2UTF8(9829),unicode2UTF8(9830),unicode2UTF8(9827)}
end
 
--draws a card at x,y with value of card (1-52), face=true if face up, a=angle in degrees (default 0)
function Card:draw(x,y,card,face,a)
    pushMatrix()
    translate(x+self.width/2,y-self.height/2)
    if a==nil then a=0 end
    rotate(a)
    if face then 
        if card>0 then self:drawDetails(card) else sprite(self.cardFace,0,0) end
    else
        sprite(self.cardBack,0,0)
    end
    popMatrix()
end
 
--draws the numbers and symbols on the front of the card
--one parameter = card value
function Card:drawDetails(card)
    sprite(self.cardFace,0,0)
    pushStyle()
    font("SourceSansPro-Regular")
    fontSize(24*self.f)
    --calculate suit and value of card
    card=card-1
    local v=card%13 
    local s=(card-v)/13+1
    v=v+1
    local w=self.cardFace.width
    local h=self.cardFace.height
    if s==1 or s==4 then fill(0,0, 0, 255) else fill(255,0,0,255) end   --fill is red or black
    
    --half the images on a card are upside down
    --so we do them in two loops, turning the card upside down between them
    --where the card is not exactly symmetrical, eg for odd numbers, we only draw on the first loop
    for i=1,2 do
        if i==2 then rotate(180) end --turn 180 degrees to do second loop
        local u=self.suits[s]
        text(self.value[v],-w*.4,h*.4) --text in corner of card
        fontSize(16*self.f)
        text(u,-w*.4,h*.28) --suit image
        fontSize(28*self.f)
        local ss=.13*h
        --now all the symbols arranged in the middle of the card
        if v==1 then
            if i==1 then text(u,0,0) end
        elseif v==2 then
            text(u,0,.3*h)
        elseif v==3 then
            if i==1 then text(u,0,0) end
            text(u,0,.3*h)
        elseif v==4 then
            text(u,-w*.18,.15*h)
            text(u,w*.18,.15*h)
        elseif v==5 then
            text(u,-w*.18,.3*h)
            text(u,w*.18,.3*h)
            if i==1 then text(u,0,0) end
        elseif v==6 then
            text(u,-w*.18,.3*h)
            text(u,w*.18,.3*h)
            text(u,-w*.18,0) 
        elseif v==7 then
            text(u,-w*.18,.3*h)
            text(u,w*.18,.3*h)
            text(u,-w*.18,0) 
            if i==1 then text(u,0,.15*h) end
        elseif v==8 then
            text(u,-w*.18,.3*h)
            text(u,w*.18,.3*h)
            text(u,-w*.18,0)
            text(u,0,.15*h)
        elseif v==9 then 
            text(u,-w*.18,.3*h)
            text(u,w*.18,.3*h)
            if i==1 then text(u,0,0) end
            text(u,-w*.18,.1*h)
            text(u,w*.18,.1*h)
        elseif v==10 then
            text(u,-w*.18,.3*h)
            text(u,w*.18,.3*h)
            text(u,-w*.18,.1*h)
            text(u,w*.18,.1*h)
            text(u,0,.2*h)
        else --royalty
            pushStyle()
            font("AppleColorEmoji")
            fill(255) 
            fontSize(84*self.f)
            if i==1 then 
                if v==11 then text(unicode2UTF8(128113)) 
                elseif v==12 then text(unicode2UTF8(128120)) 
                else text(unicode2UTF8(128116)) 
                end
            end
            popStyle()
        end
    end
    popStyle()
end
 
--used by Main and other classes
--given a value 1-52, it returns the value and suit in text and numbers, eg S,J,1,11
function Card:SuitValue(c)
    local v=(c-1)%13
    local s=(c-1-v)/13+1
    v = v + 1
    return self.value[v],self.suit[s],v,s
end
 
function unicode2UTF8(u) --needed for emoji
    u = math.max(0, math.floor(u)) -- A positive integer
    local UTF8
    if u < 0x80 then          -- less than  8 bits
        UTF8 = string.char(u)
    elseif u < 0x800 then     -- less than 12 bits
        local b2 = u % 0x40 + 0x80
        local b1 = math.floor(u/0x40) + 0xC0
        UTF8 = string.char(b1, b2)
    elseif u < 0x10000 then   -- less than 16 bits
        local b3 = u % 0x40 + 0x80
        local b2 = math.floor(u/0x40) % 0x40 + 0x80
        local b1 = math.floor(u/0x1000) + 0xE0
        UTF8 = string.char(b1, b2, b3)
    elseif u < 0x200000 then  -- less than 22 bits
        local b4 = u % 0x40 + 0x80
        local b3 = math.floor(u/0x40) % 0x40 + 0x80
        local b2 = math.floor(u/0x1000) % 0x40 + 0x80
        local b1 = math.floor(u/0x40000) + 0xF0
        UTF8 = string.char(b1, b2, b3, b4)
    elseif u < 0x800000 then -- less than 24 bits
        local b5 = u % 0x40 + 0x80
        local b4 = math.floor(u/0x40) % 0x40 + 0x80
        local b3 = math.floor(u/0x1000) % 0x40 + 0x80
        local b2 = math.floor(u/0x40000) % 0x40 + 0x80
        local b1 = math.floor(u/0x1000000) + 0xF8
        UTF8 = string.char(b1, b2, b3, b4, b5)
    else
        print("Error: Code point too large for Codea's Lua.")
    end
    return UTF8
end



Here is some pseudo code you could possibly work with.

shuffle 52 cards to make a deck


check if deck is empty (#deck ~= 0)


drawCard function to remove a card from your deck (cards[53-#deck] = table.remove(deck,1))

@Draakk, quick and easy fix for you.

Change the deal() line in touched function to:

cards[53-#p.pack]=p:nextCard()

My fix only works for 1 deck, make a variable for number of decks and then instead of running Pack(1) in deal(), run Pack(numDecks), then change my above fix to:

cards[((52*numDecks)+1)-#p.pack]=p:nextCard()

Add print(#p.pack) after above line to verify the card count is going down

    --draw a pack face down
    for i=1,math.min(4,#p.pack) do
       c:draw(312+i*2,590-i*2,1,false) 
    end

This code will show your deck disappear when you use up all the cards.

@slashin8r hey thnx for the fix.
But the last one where do i need to put that?
And my following problem is; the turned cards are not placed at the same place anymore.
With 15 cards the turned cards, the next cards are leaving the screen to the right.