Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --# 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