Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Card Game revised (one sprite to download)

edited September 2017 in Code Sharing Posts: 122

Necessary spritesheet: https://opengameart.org/content/playing-cards-0

I've tried to code this decently well. I'm not totally happy: The Field.touched(touch) and Field.move functions took me over an hour.. but it went pretty well. It looks a lot better than the first time i did this, I actually commented a lot of everything, it was done in less than 3 hours, and it will fit in your scratchpad :)

Although, I forgot to program the score hahaha

Oh! Another thing that i forgot to put in is instructions. Basically, you select a card, and then you move it to the left one or three indexes. And if suit or rank matches, that makes a valid move! sorry for not animating it, but I suppose I can work on that. I just got so excited and posted this as soon as it was playable

-- Created by Tommy
-- Second version of this game (first version is messier code, and took me two weeks. This took two and a half hours to make)
-- Note that this requires you to download a single spritesheet for the cards to play the game. i do not rememer where i got it from originally (\_ツ_/) shrugs
-- I named it sheet, but you can rename it in the Field.draw() function, at line 63
displayMode( FULLSCREEN )
function setup()
    Deck.setup()
    Field.deal()
    print(Field[1], "suit "..Field[1]/4, "rank ".. Field[1]/13)
end

function draw()
    background(40,40,50,1)
    Deck.draw()
    Field.draw()
end

function touched(touch)
    Deck.touched(touch)
    Field.touched(touch)
end
-- Timezone GMT-10 Hawaii Time, Date format follows MM/DD/YYYY
-- Started 4:34 PM 09-02-2017 Saturday
-- Last Modified 7:16 PM 09-02-2017
Deck = {}   --52 length array, holds cards in deck
Field = {}  --16 length array, holds cards in play
Score = {0,0,0,0,0} --ten thousands, thousands, hundreds, tens, ones places
local numberOfRanks = 13
local numberOfSuits = 4
--[[ ^Cards are represented by number values of 1-52. Rank and suit are determined with modulus 13 and modulus 4, if you change the spritesheet, make sure to change these variables. 
]]--
local width, height = 80,112    -- Card Width and Card Height. Note they use a 5:7 ratio, in real cards this would be 2.5" x 3.5"
-- used in Deck.draw, Deck.touched, Field.draw, Field.touched

--[[ If your device screen is too small or you simply don't like the way cards are draw, change the lookup table for positioning cards, has two rows. Feel free to modify 
]]--
local position = {
    vec2(50,668), vec2(150,668), vec2(250,668), vec2(350,668), 
    vec2(450,668), vec2(550,668), vec2(650,668), vec2(750,668), 
    vec2(50,338), vec2(150,338), vec2(250,338), vec2(350,338), 
    vec2(450,338), vec2(550,338), vec2(650,338), vec2(750,338) }

--positions the deck of cards
local deckposition = vec2(WIDTH-100, 168)


function Deck.setup()   --called in setup()
    for i = 1, 52 do
        Deck[i] = i
    end
    local j
    for i = 52, 2, -1 do
        j = math.random(52)
        Deck[i], Deck[j] = Deck[j], Deck[i]
    end
end

function Field.draw()   --called in draw()
    local mesh=mesh()
    for i,v in ipairs(Field) do --index determines position
        local rank = (1/numberOfRanks)*(v % numberOfRanks)
        local suit = (1/numberOfSuits)*(v % numberOfSuits)
        mesh.texture = "Project:sheet"
        mesh:addRect(position[i].x, position[i].y, width, height)
        mesh:setRectTex(i, rank, suit, 1/numberOfRanks, 1/numberOfSuits)
    end
    mesh:draw()
end

function Field.deal()   --called in Deck.touched(touch)
    if #Field < 16 and #Deck > 0 then
        Deck[#Deck], Field[#Field + 1] = nil, Deck[#Deck]
    end
end

function Deck.draw()    --called in draw()
    local mesh = mesh()
    mesh:addRect(deckposition.x, deckposition.y, 80, 112)
    if #Deck > 0 then
        mesh:setColors(0,168,0)
    else
        mesh:setColors(0)
    end
    mesh:draw()
end

function Deck.touched(touch)    --called in touched(touch)
    if touch.x > deckposition.x - width*.5 and touch.x < deckposition.x + width*.5
    and touch.y > deckposition.y - height*.5 and touch.y < deckposition.y + height*.5
    and touch.state == ENDED then
        Field.deal()    -- is this bad practice to handle field.deal inside deck.touched?
    end
end

--[[ Field.move is the game rules, index1 must be 1 or 3 higher than ind2, and either ranks or suits must be equal. During a move, the card originally at index 2 is elimnated from the game
all other cards will move up
Here's a gamemode idea: Rank up, where cards change ranks based on your moves
]]--
function Field.move(index1, index2) --called in Field.touched(touch)
    print "Starting move"
    if index1 - index2 ~= 3 and index1 - index2 ~= 1 then
        print "Illegal Move: difference between indexes must be equal to 1 or 3"
    else
        local rank1, suit1 = Field[index1]%numberOfRanks, Field[index1]%numberOfSuits
        local rank2, suit2 = Field[index2]%numberOfRanks, Field[index2]%numberOfSuits
        if rank1 ~= rank2 and suit1 ~= suit2 then
            print "Illegal Move. Either the card suits or the card ranks must match."
        else
            Field[index2] = Field[index1]
            table.remove(Field, index1)
        end
    end
end

function Field.touched(touch)   --called in touched(touch)
    -- This function took an hour. Once I threw out this code and started over, it was done in ten minutes
    for i,v in ipairs(position) do
        if touch.x > v.x - width*.5 and touch.x < v.x + width*.5
        and touch.y > v.y - height*.5 and touch.y < v.y + height*.5 then
            if touch.state == BEGAN then
                index1 = i
                break
            elseif touch.state == ENDED then
                index2 = i
                if index1 and index2 and index1 ~= index2 then
                    Field.move(index1, index2)
                end
                print(index1, index2)
                index1, index2 = nil, nil
                break
            end
        end
    end
end

Could someone give me sdvice on making an instructions ofr this game? I've never had to do that before.

Comments

  • Can you post the entire Sprite sheet as an image instead of a .zip file?

    Downloading, extracting, and importing an image from a .zip file is non-trivial for most of us, and involves multiple apps.

    On the other hand, an image on a webpage can be imported into the camera roll and then into a Codea project without ever leaving the Codea app.

  • Thanks!

  • I didn't realize what game this was!

    This is a solitaire game my uncle taught me and that, in my experience, very few people know. One of the reasons he liked it is that it's a solitaire game you can play without taking up much space--you can actually play the whole game while just holding all the drawn cards in one hand.

    Anyway, a nice blast from the past to see this. Nice job. And yeah, you should animate it!

  • edited September 2017 Posts: 122

    https://github.com/ThomasofHilo/solitare/tree/master/tabs

    my ipad was refusing to charge, so i recoded this from scratch on my dads ipad, without the spritesheet, thinking my ipad would die forever (spoiler: it didnt, dirty battery port)

    there's no working installer, so its not easy to download and run....I can't figure out how to make a working installer. apparently, its not as simple as just taking the soda installer and putting my url :pensive: if you have any knowledge on making a raw downloadable folder in ios, would be nice.

    @UberGoober

    also: [quote]On the other hand, an image on a webpage can be imported into the camera roll and then into a Codea project without ever leaving the Codea app.[/quote]

    are you browsing from inside Codea? lol

  • @xThomas, yes I'm browsing from inside Codea. Why the lol?

  • Posts: 122

    found it funny, I didnt think of doing that.

    anyway... Hmm, did you look at the second version (without sprite)? It has implemented dx and dy, but I did it without the sprite so cards are just colors and numbers

  • I found and used the sprite, hence my comment above.
  • I tried the game, all I see is blank white rectangles(16). I loaded the "sheet", but still nothing...I changed line #63 (Documents:sheet).. Two rows of 8 rectangles only come on the screen, with one green rectangle in the lower right corner(the deck)....Please Help ! I'm only interested in card games, and the fourm has very little in this area..

  • edited September 2017 Posts: 122

    @kendog400 Did you put it in quotes? Try readImage('Documents:sheet')

    I have modified this code to not need a spritesheet, and also to have deltaX and deltaY.

    -- Created by Tommy
    -- Second version of this game (first version is messier code, and took me two weeks. This took two and a half hours to make)
    -- This version does not use a spritesheet
    function setup()
        Deck.setup(52)
        Field.deal()
        print(Field[1], "suit "..Field[1]/4, "rank ".. Field[1]/13)
    end
    
    function draw()
        background(40,40,50,1)
        Deck.draw()
        Field.draw()
    end
    
    function touched(touch)
        Deck.touched(touch)
        Field.touched(touch)
    end
    -- Timezone GMT-10 Hawaii Time, Date format follows MM/DD/YYYY
    -- Started 4:34 PM 09-02-2017 Saturday
    -- Last Modified 8:11 PM 09-26-2017
    Deck = {}   --52 length array, holds cards in deck
    Field = {}  --16 length array, holds cards in play
    Score = {0,0,0,0,0} --ten thousands, thousands, hundreds, tens, ones places
    local numberOfRanks = 13
    local numberOfSuits = 4
    --[[ ^Cards are represented by number values of 1-52. Rank and suit are determined with modulus 13 and modulus 4, if you change the spritesheet, make sure to change these variables. 
    ]]--
    local width, height = WIDTH/8, WIDTH/8*1.4    -- Card Width and Card Height. Note they use a 5:7 ratio, in real cards this would be 2.5" x 3.5"
    -- used in Deck.draw, Deck.touched, Field.draw, Field.touched
    
    --[[ If your device screen is too small or you simply don't like the way cards are draw, change the lookup table for positioning cards, has two rows. Feel free to modify 
    ]]--
    local position = {}
    do local x,y
        for a = 1,0,-1 do
            y = height*a
            for b = 0,7 do
                x = width*b
                table.insert(position, vec2(x,y))
            end
        end
    end
    local deltaX, deltaY = 0,0
    --positions the deck of cards
    local deckposition = vec2(0, height*2)
    
    
    function Deck.setup(n)   --called in setup()
        for i = 1, n do
            Deck[i] = i
        end
        local j
        for i = n, 2, -1 do
            j = math.random(n)
            Deck[i], Deck[j] = Deck[j], Deck[i]
        end
    end
    --[[ There are four suitcolors, with the fifth index being the deck color
    
    ]]
    local suitColors = {
        color(168,168,0)      , 
        color(0,168,0)      , 
        color(0,0,168)      , 
        color(0,168,168)    , 
        color(168,0,0)              }
    function Field.draw()   --called in draw()
        for i,v in ipairs(Field) do --index determines position
            if index1 ~= i then
                fill(suitColors[v%4+1])
                RoundedRectangle(position[i].x, position[i].y, width, height, 10)
                fill(0)
                fontSize(64)
                text(v%13, position[i].x + width*.5, position[i].y + height*.5)
            end
        end
        if index1 then
            local r = Field[index1]%4+1
            fill(suitColors[r])
            RoundedRectangle(position[index1].x + deltaX, position[index1].y + deltaY, width, height, 10)
            fill(100,200)
            RoundedRectangle(position[index1].x + deltaX, position[index1].y + deltaY, width, height, 10)
            fill(0)
            fontSize(64)
            text(Field[index1]%13, position[index1].x + width*.5 + deltaX, position[index1].y + height*.5 + deltaY)
        end
    end
    
    function Field.deal()   --called in Deck.touched(touch)
        if #Field < 16 and #Deck > 0 then
            Deck[#Deck], Field[#Field + 1] = nil, Deck[#Deck]
        end
    end
    
    function Deck.draw()    --called in draw()
        fill(suitColors[5])
        RoundedRectangle(deckposition.x, deckposition.y, width, height, 10)
        fill(0)
        fontSize(32)
        text('#'..#Deck, deckposition.x + width*.5, deckposition.y + height*.5)
    end
    
    function Deck.touched(touch)    --called in touched(touch)
        if touch.x > deckposition.x and touch.x < deckposition.x + width
        and touch.y > deckposition.y and touch.y < deckposition.y + height
        and touch.state == ENDED then
            Field.deal()    -- is this bad practice to handle field.deal inside deck.touched?
        end
    end
    
    --[[ Field.move is the game rules, index1 must be 1 or 3 higher than ind2, and either ranks or suits must be equal. During a move, the card originally at index 2 is elimnated from the game
    all other cards will move up
    Here's a gamemode idea: Rank up, where cards change ranks based on your moves
    ]]--
    function Field.move(index1, index2) --called in Field.touched(touch)
        print "Starting move"
        if index1 - index2 ~= 3 and index1 - index2 ~= 1 then
            print "Illegal Move: difference between indexes must be equal to 1 or 3"
        else
            local rank1, suit1 = Field[index1]%numberOfRanks, Field[index1]%numberOfSuits
            local rank2, suit2 = Field[index2]%numberOfRanks, Field[index2]%numberOfSuits
            if rank1 ~= rank2 and suit1 ~= suit2 then
                print "Illegal Move. Either the card suits or the card ranks must match."
            else
                Field[index2] = Field[index1]
                table.remove(Field, index1)
            end
        end
    end
    
    function Field.touched(touch)   --called in touched(touch)
        for i,v in ipairs(position) do
            if touch.x > v.x and touch.x < v.x + width
            and touch.y > v.y and touch.y < v.y + height then
                if touch.state == BEGAN then
                    index1 = i
                    break
                elseif touch.state == ENDED then
                    index2 = i
                    if index1 and index2 and index1 ~= index2 then
                        Field.move(index1, index2)
                    end
                    print(index1, index2)
                    index1, index2 = nil, nil
                    break
                end
            end
        end
        --added deltaXY on Sep 26
        if touch.state == MOVING and index1 then
            deltaX = deltaX + touch.deltaX
            deltaY = deltaY + touch.deltaY
        elseif touch.state == ENDED then
            deltaX, deltaY = 0,0
        end
    end
    
    --[[
    This is an auxilliary function for drawing a rectangle with possibly
    curved cornders.  The first four parameters specify the rectangle.
    The fifth is the radius of the corner rounding.  The optional sixth is
    a way for specifying which corners should be rounded by passing a
    number between 0 and 15.  The first bit corresponds to the lower-left
    corner and it procedes clockwise from there.
    --]]
    
    local __RRects = {}
    
    
    
    function RoundedRectangle(x,y,w,h,s,c,a)
        c = c or 0
        w = w or 0
        h = h or 0
        if w < 0 then
            x = x + w
            w = -w
        end
        if h < 0 then
            y = y + h
            h = -h
        end
        w = math.max(w,2*s)
        h = math.max(h,2*s)
        a = a or 0
        pushMatrix()
        translate(x,y)
        rotate(a)
        local label = table.concat({w,h,s,c},",")
        if __RRects[label] then
            __RRects[label]:setColors(fill())
            __RRects[label]:draw()
        else
        local rr = mesh()
        local v = {}
        local ce = vec2(w/2,h/2)
        local n = 4
        local o,dx,dy
        for j = 1,4 do
            dx = -1 + 2*(j%2)
            dy = -1 + 2*(math.floor(j/2)%2)
            o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
            if math.floor(c/2^(j-1))%2 == 0 then
        for i = 1,n do
            table.insert(v,o)
            table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
            table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
        end
        else
            table.insert(v,o)
            table.insert(v,o + vec2(dx * s,0))
            table.insert(v,o + vec2(dx * s,dy * s))
            table.insert(v,o)
            table.insert(v,o + vec2(0,dy * s))
            table.insert(v,o + vec2(dx * s,dy * s))
        end
        end
        rr.vertices = v
        rr:addRect(ce.x,ce.y,w,h-2*s)
        rr:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
        rr:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
        rr:setColors(fill())
        rr:draw()
        __RRects[label] = rr
        end
        popMatrix()
    end
    
  • I dont think i put the quotes in, I'll try that....

  • Posts: 122

    I beleive I found the name of this game. It's Accordion Solitaire :)

  • edited September 2018 Posts: 7

    Made some changes to original code incorporating some features of suggested code, used a separate mesh for the deck and added some scoring

    -- Card file is read in the Field.draw() function, at line 84
    
    displayMode( FULLSCREEN )
    function setup()
        highscore = readLocalData("highscore", 0) -- default to 0 if no highscore set  
        gameState = "Started"  
        Deck.setup()
        Field.deal()
    --    print(Field[1], "suit "..Field[1]/4, "rank ".. Field[1]/13)
        DisplayText = "Start: "
    end
    
    function draw()
        background(40,40,50,1)
        Deck.draw()
        Field.draw()
        if gameState == "GAMEOVER" then
            if Score > highscore then
                 saveLocalData( "highscore", Score )
                 -- congratulate player
                DisplayText = "Congratulations a new high score!"
            end
        end
        text("HighScore= "..math.tointeger(highscore), WIDTH/2,25)
    end
    
    function touched(touch)
        Deck.touched(touch)
        Field.touched(touch)
        if #Field == 1 and #Deck == 0 then
            Score = Score + 100
            DisplayText = "Congratulations you have Won! with a score of "..Score
            gameState = GAMEOVER
        elseif #Field >> 1 and #Deck == 0 then
            DisplayText = "Keep trying you have "..#Field.." Cards left, and a Score of: "..Score   
            gameState = "GAMEOVER"
        end
    end
    
    Deck = {}   --52 length array, holds cards in deck
    Field = {}  --16 length array, holds cards in play
    Score = 0 -- each card remove has a face and suit value added to score
    local numberOfRanks = 13 -- Ace through to King
    local numberOfSuits = 4
    -- ^Cards are represented by number values of 1-52. 
    
    local width, height = 90,126   -- was 80,112
    -- Card Width and Card Height. Note they use a 5:7 ratio, in real cards this would be 2.5" x 3.5"
    -- used in Deck.draw, Deck.touched, Field.draw, Field.touched
    
    --[[ If your device screen is too small or you simply don't like the way cards are drawn, change the lookup table for positioning cards, has two rows. Feel free to modify 
    ]]--
    local position = {
        vec2(50,600), vec2(150,600), vec2(250,600), vec2(350,600), 
        vec2(450,600), vec2(550,600), vec2(650,600), vec2(750,600), 
        vec2(50,450), vec2(150,450), vec2(250,450), vec2(350,450), 
        vec2(450,450), vec2(550,450), vec2(650,450), vec2(750,450) }
    
    --positions the deck of cards
    
    local deckposition = vec2(WIDTH-100, 168)
    
    function Deck.setup()   --called in setup()
        for i = 1, 52 do
            Deck[i] = i
        end
        local j
        for i = 52, 2, -1 do
            j = math.random(52)
            Deck[i], Deck[j] = Deck[j], Deck[i]
        end
    end
    
    function Field.draw()   --called in draw()
        textAlign(CENTER)
        fill(246, 246, 246, 255)
        fontSize(32)
        text(DisplayText, WIDTH/2, HEIGHT-50)
        local mesh=mesh()
        for i,v in ipairs(Field) do --index determines position
            local rank = (1/numberOfRanks)*(v % numberOfRanks)
            local suit = (1/numberOfSuits)*(v % numberOfSuits)
            mesh.texture = "Documents:sheet"
            mesh:addRect(position[i].x, position[i].y, width, height)
            mesh:setRectTex(i, rank, suit, 1/numberOfRanks, 1/numberOfSuits)
        end
        mesh:draw()
    end
    
    function Field.deal()   --called in Deck.touched(touch)
        if #Field < 16 and #Deck > 0 then
            Deck[#Deck], Field[#Field + 1] = nil, Deck[#Deck]
        end
    end
    
    function Deck.draw()    --called in draw()
        local mesh = mesh()
            mesh.texture = "Documents:CardBack"
            mesh:addRect(deckposition.x, deckposition.y, width, height)
        mesh:draw()
        -- number of cards remaining in deck
        fill(241, 240, 240, 255)
        fontSize(32) 
        text('#'..#Deck, deckposition.x, deckposition.y)
    end
    
    
    function Deck.touched(touch)    --called in touched(touch)
        if touch.x > deckposition.x - width*.5 and touch.x < deckposition.x + width*.5
        and touch.y > deckposition.y - height*.5 and touch.y < deckposition.y + height*.5
        and touch.state == ENDED then
            Field.deal()    -- is this bad practice to handle field.deal inside deck.touched?
        end
    end
    
    --[[ Field.move defines the game rules, index1 must be 1 or 3 higher than index2, and either ranks or suits must be equal. During a move, the card originally at index 2 is elimnated from the game, and is scored for both rank and suit, all other cards will move up
    ]]--
    function Field.move(index1, index2) --called in Field.touched(touch)
        DisplayText = "Starting Move"
        if index1 - index2 ~= 3 and index1 - index2 ~= 1 then
            DisplayText = "Illegal Move: difference between indexes must be equal to 1 or 3"
        else
            local rank1, suit1 = Field[index1]%numberOfRanks, Field[index1]%numberOfSuits
            local rank2, suit2 = Field[index2]%numberOfRanks, Field[index2]%numberOfSuits
            print("Rank:"..rank2.."  Suit:"..suit2)
            if rank1 ~= rank2 and suit1 ~= suit2 then
                DisplayText = "Invalid Move. Either the card suits or the card rank must match."  
            else
                Field[index2] = Field[index1]
                -- suit2 order is clubs diamonds hearts spades A to king
                -- suits and ranks count from 0
                if suit2 == 0 then -- Clubs
                    Score = Score + 10
                elseif suit2 == 1 then -- Diamonds
                    Score = Score + 30
                elseif suit2 == 2 then -- Hearts
                    Score = Score + 40
                elseif suit2 == 3 then -- Spades
                    Score = Score + 20
                end
                Score = Score + rank2 + 1            
                table.remove(Field, index1)
                DisplayText = " Score = "..Score
            end
        end
    end
    
    function Field.touched(touch)   --called in touched(touch)
        for i,v in ipairs(position) do
            if touch.x > v.x - width*.5 and touch.x < v.x + width*.5
            and touch.y > v.y - height*.5 and touch.y < v.y + height*.5 then
                if touch.state == BEGAN then
                    index1 = i
                    break
                elseif touch.state == ENDED then
                    index2 = i
                    if index1 and index2 and index1 ~= index2 then
                        Field.move(index1, index2)
                    end
                    index1, index2 = nil, nil
                    break
                end
            end
        end
    end
    
  • em2em2
    Posts: 194
    @colincooper when you post code, leave three '~' before and after it so that it displays nicely
  • dave1707dave1707 Mod
    Posts: 8,491

    I added the three ~~~

  • Posts: 197
    Somehow I can't seem to get the sprite sheet to load...
    PGM says : Card file is read in the Field.draw()....(png file)
    --*******************************
    mesh.texture = "Documents:sheet" -- The sprite sheet
    --*******************************

    I tried to make changes but I can't seem to get the sprite sheet to load..
    Is this the correct way ?.....
    img=readImage(asset.documents.Deck)

    I know PGM was written long ago and their may have been changes and updates to codea since then....But looks like a gud PGM, and I would like to get it to work..
    I also like the idea of putting in lots of comments, and making it easier to break the PGM down....Any help is greatly appreicated.....
  • dave1707dave1707 Mod
    Posts: 8,491

    You should be able to tap in the () of img=readImage() and select what you want to read. It will put the correct name format in the ().

  • Posts: 197
    Original PGM doesn’t have a a readImage ( ), so I placed one in the setup fx...
    pic 01....I got the results of pic4

    In the feild draw & Deck draw fx's, pics 3 & 4, shows changes I made...
    I see ::mesh.texture = "Documents:CardBack", so I put a
    mesh.texture = img....and I got the whole sprite sheet not a card...pic4
    I took some freeze frames to show...

    I uploaded 2 sprite sheets..if needed...
    01.png 119.9K
    02.png 140.2K
    03.png 116.6K
    04.png 88.3K
    Deck2.png 158.7K
    Solitare.jpg 810.7K
  • dave1707dave1707 Mod
    edited July 11 Posts: 8,491

    I found the game interesting and thought I’d try writing another version. The difference with this game is the first card selected replaces the second card selected by tapping the first card, then tapping the second. Another difference is you can go 1 or 3 cards to the left or right. The original only let you go to the left. By going either way, you get a little more strategy on your selection. I also added a score. The game starts with the sum of all the cards, 1-13 for each suit. Ace is 1 and King is 13. As you replace a card, that card value is subtracted from the score. Try to get a low score. I was able to get to 7. The lowest score is 1, but you might never get there. Best played in landscape orientation. To select a card, tap on it. If you make a mistake, tap the same card again.

    PS. I was able to get down to a score of 3. The cards, Ace of spades and 2 of hearts were left. If it would have been a 2 of spades, I could have gotten a 1.

    Code removed, see updated version below.
    
  • dave1707dave1707 Mod
    Posts: 8,491

    Here’s an updated version of the code I wrote earlier. I removed it to save space here. In this version, I added an option using a parameter slider that allows left only moves or the default left and right 1 or 3 position moves. I also added code to enter your name or initials for a high score, in this game, a low score. It will show the lowest 10 scores with name/initials. When the game ends (you can’t make any more moves), tap the Save score button. I leave it up to you to determine when you can’t move anymore. Tap the New game button to enter a new name/initials or continue with the current name. I’ve managed to get a low score of 1, the lowest, but most of the time I’m well above 10. See my farther above post for more instructions.

    displayMode(FULLSCREEN)
    
    function setup()
        --clearProjectData()
        showKeyboard()
        parameter.boolean("justLeft",false)
        parameter.text("initials","")
        rectMode(CENTER)
        gameSetup()
    end
    
    function draw()
        background(229, 189, 97)
        if getInitials then
            initialMessage()
        elseif showScoreFlag then
            scoreDraw()
        else       
            gameDraw()
        end
    end
    
    function touched(t)
        if t.state==BEGAN then
            dealCards(t)
            selectCards(t)
            scoreButtonPressed(t)
            newGameButtonPressed(t)
        end    
    end
    
    function keyboard(k)
        if k==BACKSPACE then
            initials=string.sub(initials,1,#initials-1)
        elseif k==RETURN then
            if initials~="" then
                hideKeyboard()
                getInitials=false
            end
        else
            initials=initials..string.upper(k)
        end
    end
    
    function initialMessage()
        pushStyle()
        fill(255)
        fontSize(30)
        text("Enter name or initials, then press return.",WIDTH/2,HEIGHT*.9)
        text("Or press return for same person.",WIDTH/2,HEIGHT*.9-50)
        text(initials,WIDTH/2,HEIGHT*.9-150)
        popStyle()
    end
    
    function showScoreButton()
        fill(255)
        rect(WIDTH/2,60,180,60)
        fill(0)
        text("Save score",WIDTH/2,60) 
    end
    
    function scoreButtonPressed(t)
        if t.x>WIDTH/2-90 and t.x<WIDTH/2+90 and
                t.y>60-30 and t.y<60+30 then
            scoreSave()
            showScoreFlag=true
        end    
    end
    
    function scoreSave()
        local str=string.format("%03d",scoreTotal)
        saveProjectData(str,initials)
    end
    
    function scoreDraw()
        pushStyle()
        textMode(CORNER)
        fontSize(30)
        font("Courier-Bold")
        local z=listProjectData()
        table.sort(z)
        for a,b in pairs(z) do
            local v=readProjectData(b)
            if a>10 then
                saveProjectData(b,nil)
            else
                str=string.format("%2d)   %3d...%s",a,b,v)
                text(str,WIDTH*.35,HEIGHT-a*50)
            end
        end 
        popStyle()  
        newGameButton()
    end
    
    function gameSetup()
        play={}
        pos=0
        cardsTotal=52
        select=0
        scoreTotal=364
        shuffleCards()
        scoreTab={}
        showScoreFlag=false  
        getInitials=true  
    end
    
    function gameDraw()
        fill(255)
        fontSize(30)
        text("Player  "..initials,WIDTH/2,HEIGHT-200)
        text("Cards remaining  "..cardsTotal,WIDTH/2,HEIGHT-100)
        text(string.format("Score  %d",scoreTotal),WIDTH/2,HEIGHT-150)
        if justLeft then
            text("Move Just Left, 1 or 3",WIDTH/2,HEIGHT-50)
        else
            text("Move Left or Right, 1 or 3",WIDTH/2,HEIGHT-50)
        end
        rect(WIDTH/2,HEIGHT/2-200,120,80)
        fill(0)
        text("Deal",WIDTH/2,HEIGHT/2-200) 
        if select>0 then
            fill(0, 142, 255)
            rect(select*65,HEIGHT/2,60,90)
        end
        showScoreButton()
        showCards() 
    end
    
    function dealCards(t)
        if t.x>WIDTH/2-60 and t.x<WIDTH/2+60 and
                t.y>HEIGHT/2-240 and t.y<HEIGHT/2-160 then
            if #play<16 and cardsTotal>0 then
                pos=pos+1
                local z=shuffled[pos]-1
                local s=z//13+1
                local v=z%13+1
                table.insert(play,vec2(s,v))
                cardsTotal=cardsTotal-1
                card1=vec2(0,0)
                card2=vec2(0,0)
                select=0
            end
        end   
    end
    
    function selectCards(t)
        local diff
        if t.y>HEIGHT/2-40 and t.y<HEIGHT/2+40 then
            for z=1,#play do
                if t.x>z*65-25 and t.x<z*65+25 then
                    if card1.x==0 then
                        card1=vec3(play[z].x,play[z].y,z)
                        select=z
                    else
                        card2=vec3(play[z].x,play[z].y,z)  
                        if justLeft then                      
                            diff=card1.z-card2.z
                        else
                            diff=math.abs(card1.z-card2.z)
                        end
                        if diff==1 or diff==3 then
                            if card2.x==card1.x or card2.y==card1.y then
                                play[card2.z]=play[card1.z]
                                scoreTotal=scoreTotal-card2.y
                                table.remove(play,card1.z)                               
                            end
                        end
                        card1,card2=vec3(0,0,0),vec3(0,0,0)
                        select=0
                    end
                end
            end
        end
    end
    
    function newGameButton()
        fill(255)
        rect(WIDTH-100,60,180,60)
        fill(0)
        text("New game",WIDTH-100,60) 
    end
    
    function newGameButtonPressed(t)
        if t.x>WIDTH-100-90 and t.x<WIDTH-100+90 and
                t.y>60-30 and t.y<60+30 then
            showKeyboard()
            gameSetup()    
        end   
    end
    
    
    function showCards()
        fontSize(20)
        for a,b in pairs(play) do
            local s=b.x
            local v=b.y
            fill(255)
            rect(a*65,HEIGHT/2,50,80)
            fill(0)
            if s==1 or s==3 then
                fill(255,0,0)
            end
            text(value[v],a*65-13,HEIGHT/2+25)
            text(value[v],a*65+13,HEIGHT/2-25)
            text(suit[s],a*65,HEIGHT/2)
        end
    end
    
    function shuffleCards()
        value={"A","2","3","4","5","6","7","8","9","10","J","Q","K"}
        suit={"♥️","♣️","♦️","♠️"} 
        shuffled={} 
        local temp={}
        for z=1,52 do
            table.insert(temp,z)        
        end
        for z=1,52 do
            r=math.random(#temp)
            table.insert(shuffled,temp[r])
            table.remove(temp,r) 
        end
    end
    
Sign In or Register to comment.