Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --# Notes
- --[[
- there are two Main tabs, each with a different card game
- Codea will always run whichever is on the right, because it compiles from left to right and effectively writes over the left hand one. So swap the tabs around to change the game.
- --]]
- --# Main
- -- 21 card game
- -- Use this function to perform your initial setup
- function setup()
- --b=Backup("Cards v105")
- img=readImage("Dropbox:Felix") --image for back of card
- deal21() --simple version of 21 without betting, doesn't handle things like A being 1 or 11 properly
- end
- function touched(touch)
- end
- --===== 21 ========
- function deal21()
- parameter.watch("Score")
- parameter.watch("bankScore")
- parameter.action("Hold", function() playBank() end)
- parameter.action("Another card", function() AddMyCard() end)
- parameter.action("Play again",function() play21Again() end)
- c=Card(200,img) --big cards
- t=CardTable(10,HEIGHT-10,c,false,1,2.1)
- p=Pack()
- p:shuffle()
- Score=0
- bankScore=0
- cards=2
- for i=1,2 do
- local crd=t:dealCard(p,i,3.2,true) --lay out cards at fractional row intervals to look nice
- Score=GetScore(Score,crd)
- end
- status="PLAY"
- end
- function playBank()
- bankCards=2
- bankScore=0
- for i=1,2 do
- local crd=t:dealCard(p,i,1,true)
- bankScore=GetScore(bankScore,crd)
- end
- while bankScore<16 do
- bankScore=addBankCard()
- end
- if bankScore>=Score and bankScore<=21 then status="LOSE" else status ="WIN" end
- end
- function GetScore(score,crd)
- local i,j,v,s=c:SuitValue(crd.c)
- if v==1 then
- if score<=10 then score=score+11 else score=score+1 end
- else
- score=score+math.min(10,v)
- end
- return score
- end
- function AddMyCard()
- cards = cards + 1
- local crd=t:dealCard(p,cards,3.2,true)
- Score=GetScore(Score,crd)
- if Score>21 then status="LOSE" end
- end
- function addBankCard()
- bankCards = bankCards + 1
- local crd=t:dealCard(p,bankCards,1,true)
- bankScore=GetScore(bankScore,crd)
- return bankScore
- end
- function play21Again()
- deal21()
- end
- -- This function gets called once every frame
- function draw()
- background(69, 146, 59, 255)
- t:draw()
- if status=="LOSE" or status=="WIN" then
- pushStyle()
- font("Copperplate")
- fontSize(48)
- fill(255)
- text("You "..status.."!",300,50)
- popStyle()
- end
- end
- --# Main2
- -- Patience
- -- Use this function to perform your initial setup
- function setup()
- --b=Backup("Cards v105")
- img=readImage("Dropbox:Felix") --image for back of card
- dealPatience() --deal a patience hand. Touching a card shows its value at left. Nothing else yet
- end
- function dealPatience()
- c=Card(120,img) --small card to fit on screen
- --deal cards
- t=CardTable(10,HEIGHT-10,c,true,8,1)
- p=Pack()
- p:shuffle()
- for i=1,7 do
- for j=1,8-i do
- t:dealCard(p,j,i,j==8-i)
- end
- end
- end
- function touched(touch)
- local crd1,crd2=t:touched(touch)
- if crd1=="pack" then print("Touched pack")
- elseif crd1~=nil then
- if crd2~=nil then
- print("Move card(s) from col,row "..crd1.col..","..crd1.row.." to "..crd2.col)
- local a,b,v1,s1=c:SuitValue(crd1.c)
- local a,b,v2,s2=c:SuitValue(crd2.c)
- if c:isOppositeColor(s1,s2) and v1==v2-1 then
- local result=t:moveCardColumn(crd1.col,crd1.row,crd2.col,true)
- if type(result)=="string" then print(result) end
- else
- print("Invalid move")
- end
- else
- print(c:SuitValue(crd1.c))
- end
- end
- end
- function draw()
- background(69, 146, 59, 255)
- t:draw()
- end
- --# 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(154, 199, 203, 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*25/35+.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)
- if face then fill(self.faceColor) else fill(self.backColor) end
- ellipse(self.width-c,self.height-c,c*2)
- ellipse(self.width-c,c,c*2)
- ellipse(c,self.height-c,c*2)
- ellipse(c,c,c*2)
- if face then stroke(self.faceColor) else stroke(self.backColor) end
- rect(0,c,self.width,self.height-c*2)
- rect(c,0,self.width-c*2,self.height)
- 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) end
- else
- sprite(self.cardBack,0,0)
- end
- popMatrix()
- end
- --draws the pack of cards at x,y (actually just 4 cards but it looks like a pack)
- function Card:drawPack(x,y)
- for i=1,4 do
- local s=(i-1)*3
- self:draw(x+s,y-s,1,false)
- end
- 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,.3*h)
- text(u,w*.18,.3*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 Card:isOppositeColor(s1,s2)
- if s1==s2 or s1+s2==5 then return false else return true end
- 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
- --# CardTable
- CardTable = class()
- --[[
- This class handles tabular card games like patience, where all the cards are in columns and rows
- Most position references are in cols and rows rather than pixels, and they can be fractional
- The class has two tables to keep track of cards on the table.
- cards - is simply a hash table so we can scroll through all the cards quickly
- tableMap - is a table laid out like the table itself, so tableMap[2][3] is the card in col 2, row 3
- Currently, what is stored in these tables is a "card" which is an array with
- self.counter=unique id (unused)
- x,y=actual pixel position
- col,row=column and row
- c=value of card (1-52)
- face=true if face showing
- --]]
- --x,y are top left of table
- --c is a reference to the card class
- --s=whether cards stack (overlay) on each other
- --pc,ps are the location of the pack, pc=col, ps=row
- function CardTable:init(x,y,c,s,pc,ps)
- self.x,self.y = x,y
- self.stacked=s
- self.card=c
- self.margin=4
- self.cards={}
- self.tableMap={}
- self.packCol,self.packRow=pc,ps
- self.counter=0
- end
- --deals the next card to col,row, with face=true for face up, false for face down
- --returns the card dealt
- function CardTable:dealCard(pack,col,row,face)
- local c=pack:nextCard()
- if c==nil then return nil end
- return self:addToCol(c,col,row,face)
- end
- --add card to column, row can be set to nil if you want the next available row
- function CardTable:addToCol(c,col,row,face)
- if row==nil then row=self:findEmptyRowInCol(col) end --if row omitted,find next available
- local x=self:colToX(col)
- local y=self:rowToY(row)
- self.counter = self.counter + 1
- local crd={id=self.counter,x=x,y=y,col=col,row=row,c=c,face=face}
- --table.insert(self.cards,crd)
- table.insert(self.cards,crd)
- self:updateTable(crd)
- return crd
- end
- function CardTable:findEmptyRowInCol(col)
- if self.tableMap[col]==nil then return 1 end
- local r=1
- while self.tableMap[col][r]~=nil do r = r + 1 end
- return r
- end
- function CardTable:removeFromCol(col,row)
- if self.tableMap[col][row]==nil then
- return "Invalid column or row"
- end
- local crd=self.tableMap[col][row]
- for i=1,#self.cards do
- if self.cards[i]==crd then table.remove(self.cards,i) break end
- end
- self.tableMap[col][row]=nil
- return crd
- end
- function CardTable:UpdateCard(oldC, newC)
- end
- --tableMap
- function CardTable:updateTable(crd,action)
- if self.tableMap[crd.col]==nil then self.tableMap[crd.col]={} end
- self.tableMap[crd.col][crd.row]={}
- self.tableMap[crd.col][crd.row]=crd
- end
- --helper function returns x value from a col reference
- --dontStack=true if cards don't overlay
- function CardTable:colToX(col,dontStack)
- local h
- if self.stacked and dontStack~=true then h=self.card.height*.25 else h=self.card.height end
- return self.x+(self.card.width+self.margin)*(col-1)
- end
- --helper function returns y value from a row reference
- function CardTable:rowToY(row,dontStack)
- local h
- if self.stacked and dontStack~=true then h=self.card.height*.25 else h=self.card.height end
- return self.y-(h+self.margin)*(row-1)
- end
- --this finds which card is under x,y, very useful for touches on the screen
- --it is actually quite difficult to do this when cards overlap, because all the cards in
- --a column are truncated except the last one
- --however this can be overcome by looking at which card(s) are under x,yn and keeping
- --the card that is furthest down (lowest y value)
- function CardTable:cardFromXY(x,y)
- local w,h=self.card.width,self.card.height
- local c
- for i,crd in pairs(self.cards) do
- if x>=crd.x and x<=crd.x+w and y<=crd.y and y>=crd.y-h then
- if c==nil then c=crd elseif c.row<crd.row then c=crd end
- end
- end
- if c==nil then
- local px,py=self:colToX(self.packCol,true),self:rowToY(self.packRow,true)
- if x>=px and x<=px+w and y<=py and y>=py-h then
- c="pack"
- end
- end
- return c
- end
- --moves a stack of cards from the bottom of one column to the bottom of another
- --the last optional parameter makes it illegal to move a stack unless the top card is face up, ie handles
- function CardTable:moveCardColumn(startCol,startRow,newCol,failIfTopCardHidden)
- if self.tableMap[startCol][startRow]==nil then
- return "Invalid column or row"
- end
- if failIfTopCardHidden and self.tableMap[startCol][startRow].face==false then
- return "Hidden card cannot be moved"
- end
- --move cards one by one
- local r=startRow
- crd=self.tableMap[startCol][r]
- while crd~=nil do
- self:removeFromCol(startCol,r)
- self:addToCol(crd.c,newCol,nil,crd.face)
- r=r+1
- crd=self.tableMap[startCol][r]
- end
- --turn over exposed card in original col if there is one
- if startRow>1 then
- self:turnOverCard(startCol,startRow-1)
- end
- end
- function CardTable:turnOverCard(col,row)
- crd=self.tableMap[col][row]
- crd.face=true
- end
- function CardTable:draw()
- for i,card in ipairs(self.cards) do
- self.card:draw(card.x,card.y,card.c,card.face)
- end
- if self.packCol~=nil then
- local x,y=self:colToX(self.packCol,true),self:rowToY(self.packRow,true)
- self.card:drawPack(x,y)
- end
- if self.moving~=nil then
- self.card:draw(self.moving.x,self.moving.y,self.moving.c,self.moving.face)
- end
- end
- function CardTable:touched(touch)
- if touch.state==BEGAN then
- self.cardTouched=self:cardFromXY(touch.x,touch.y)
- elseif touch.state==MOVING and self.cardTouched~=nil then
- self.moving={x=touch.x-c.width/2,y=touch.y+c.height/2,c=self.cardTouched.c,face=self.cardTouched.face}
- elseif touch.state==ENDED then
- self.moving=nil
- crd=self:cardFromXY(touch.x,touch.y)
- if self.cardTouched==crd then
- return crd
- else
- return self.cardTouched,crd
- end
- end
- end
- --# Pack
- Pack = class()
- --[[
- Manages a pack of cards
- Functions:
- shuffle - shuffles the pack
- nextCard - returns the next card in the pack
- --]]
- function Pack:init(p)
- self.pack={}
- self:shuffle()
- end
- function Pack:shuffle()
- self.pack={}
- local t={}
- for i=1,52 do t[i]=i end
- for i=1,52 do
- local x=math.random(1,53-i)
- self.pack[i]=t[x]
- t[x]=53-i
- end
- return self.pack
- end
- function Pack:nextCard()
- if #self.pack==0 then return nil end
- local c=self.pack[1]
- table.remove(self.pack,1)
- return c
- end
- function Pack:cards()
- return #self.pack
- end
- --# Ideas
- --[[
- flip card over
- remove card
- move x layered cards
- fan cards like poker hand
- n packs or infinite pack, eg casino
- manage discard pile
- --]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement