Advertisement
Dermotb

Cards1.02

Mar 18th, 2013
370
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. --# Notes
  3. --[[
  4. there are two Main tabs, each with a different card game
  5.  
  6. 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.
  7. --]]
  8. --# Main
  9. -- 21 card game
  10.  
  11. -- Use this function to perform your initial setup
  12. function setup()
  13. --b=Backup("Cards v105")
  14. img=readImage("Dropbox:Felix") --image for back of card
  15.  
  16. deal21() --simple version of 21 without betting, doesn't handle things like A being 1 or 11 properly
  17. end
  18.  
  19. function touched(touch)
  20.  
  21. end
  22.  
  23. --===== 21 ========
  24. function deal21()
  25. parameter.watch("Score")
  26. parameter.watch("bankScore")
  27. parameter.action("Hold", function() playBank() end)
  28. parameter.action("Another card", function() AddMyCard() end)
  29. parameter.action("Play again",function() play21Again() end)
  30. c=Card(200,img) --big cards
  31. t=CardTable(10,HEIGHT-10,c,false,1,2.1)
  32. p=Pack()
  33. p:shuffle()
  34. Score=0
  35. bankScore=0
  36. cards=2
  37. for i=1,2 do
  38. local crd=t:dealCard(p,i,3.2,true) --lay out cards at fractional row intervals to look nice
  39. Score=GetScore(Score,crd)
  40. end
  41. status="PLAY"
  42. end
  43.  
  44. function playBank()
  45. bankCards=2
  46. bankScore=0
  47. for i=1,2 do
  48. local crd=t:dealCard(p,i,1,true)
  49. bankScore=GetScore(bankScore,crd)
  50. end
  51. while bankScore<16 do
  52. bankScore=addBankCard()
  53. end
  54. if bankScore>=Score and bankScore<=21 then status="LOSE" else status ="WIN" end
  55. end
  56.  
  57. function GetScore(score,crd)
  58. local i,j,v,s=c:SuitValue(crd.c)
  59. if v==1 then
  60. if score<=10 then score=score+11 else score=score+1 end
  61. else
  62. score=score+math.min(10,v)
  63. end
  64. return score
  65. end
  66.  
  67. function AddMyCard()
  68. cards = cards + 1
  69. local crd=t:dealCard(p,cards,3.2,true)
  70. Score=GetScore(Score,crd)
  71. if Score>21 then status="LOSE" end
  72. end
  73.  
  74. function addBankCard()
  75. bankCards = bankCards + 1
  76. local crd=t:dealCard(p,bankCards,1,true)
  77. bankScore=GetScore(bankScore,crd)
  78. return bankScore
  79. end
  80.  
  81. function play21Again()
  82. deal21()
  83. end
  84.  
  85. -- This function gets called once every frame
  86. function draw()
  87. background(69, 146, 59, 255)
  88. t:draw()
  89. if status=="LOSE" or status=="WIN" then
  90. pushStyle()
  91. font("Copperplate")
  92. fontSize(48)
  93. fill(255)
  94. text("You "..status.."!",300,50)
  95. popStyle()
  96. end
  97. end
  98.  
  99.  
  100.  
  101. --# Main2
  102. -- Patience
  103.  
  104. -- Use this function to perform your initial setup
  105. function setup()
  106. --b=Backup("Cards v105")
  107. img=readImage("Dropbox:Felix") --image for back of card
  108.  
  109. dealPatience() --deal a patience hand. Touching a card shows its value at left. Nothing else yet
  110.  
  111. end
  112.  
  113. function dealPatience()
  114. c=Card(120,img) --small card to fit on screen
  115. --deal cards
  116. t=CardTable(10,HEIGHT-10,c,true,8,1)
  117. p=Pack()
  118. p:shuffle()
  119. for i=1,7 do
  120. for j=1,8-i do
  121. t:dealCard(p,j,i,j==8-i)
  122. end
  123. end
  124. end
  125.  
  126. function touched(touch)
  127. local crd1,crd2=t:touched(touch)
  128. if crd1=="pack" then print("Touched pack")
  129. elseif crd1~=nil then
  130. if crd2~=nil then
  131. print("Move card(s) from col,row "..crd1.col..","..crd1.row.." to "..crd2.col)
  132. local a,b,v1,s1=c:SuitValue(crd1.c)
  133. local a,b,v2,s2=c:SuitValue(crd2.c)
  134. if c:isOppositeColor(s1,s2) and v1==v2-1 then
  135. local result=t:moveCardColumn(crd1.col,crd1.row,crd2.col,true)
  136. if type(result)=="string" then print(result) end
  137. else
  138. print("Invalid move")
  139. end
  140. else
  141. print(c:SuitValue(crd1.c))
  142. end
  143. end
  144. end
  145.  
  146. function draw()
  147. background(69, 146, 59, 255)
  148. t:draw()
  149. end
  150.  
  151.  
  152. --# Card
  153. Card = class()
  154. --[[
  155. this class builds and draws the card images from scratch and provides some utilities
  156.  
  157. --]]
  158.  
  159. --class initialises by creating card design
  160. --Parameters:
  161. --height of card in pixels
  162. --the image to be used on the back
  163. --background color on the face of the card
  164. --(optional) background color on the back of the card
  165. -- (optional) color along the border ofthe card
  166.  
  167. --returns nothing
  168. function Card:init(height,backImg,faceColor,backColor,borderColor)
  169. self.height = height
  170. self.backImg=backImg
  171. self.f=height/200 --scale font size to card size
  172. self.faceColor=faceColor or color(255)
  173. local x=100 --default border color
  174. self.borderColor=borderColor or color(x, x, x, 255)
  175. x=x*1.5 --corners are drawn with circles which appear darker than lines, so lighten colour for them
  176. self.cornerColor=borderColor or color(x,x,x, 150)
  177. self.backColor=backColor or color(154, 199, 203, 255)
  178. self:createCard()
  179. self:createSuits()
  180. self.value={"A","2","3","4","5","6","7","8","9","10","J","Q","K"}
  181. self.suit={"S","H","D","C"}
  182. end
  183.  
  184. --this and the next function build just the front and back of the card itself
  185. --the main problem is rounded corners
  186. --this is done by drawing circles at the corners and then overlapping rectangles forthe final effect
  187. function Card:createCard()
  188. self.cardFace=self:createOutline(true)
  189. self.cardBack=self:createOutline(false)
  190. end
  191.  
  192. function Card:createOutline(face)
  193. --use standard 25/35 ratio
  194. self.width=math.floor(self.height*25/35+.5)
  195. local img=image(self.width,self.height)
  196. --create rounded corner on top right
  197. local corner=0.05 --distance from end of card as percent of height
  198. local c=math.floor(corner*self.height+0.5)
  199. setContext(img)
  200. pushStyle()
  201. strokeWidth(1)
  202. stroke(self.cornerColor)
  203. if face then fill(self.faceColor) else fill(self.backColor) end
  204. ellipse(self.width-c,self.height-c,c*2)
  205. ellipse(self.width-c,c,c*2)
  206. ellipse(c,self.height-c,c*2)
  207. ellipse(c,c,c*2)
  208. if face then stroke(self.faceColor) else stroke(self.backColor) end
  209. rect(0,c,self.width,self.height-c*2)
  210. rect(c,0,self.width-c*2,self.height)
  211. stroke(self.borderColor)
  212. line(0,c,0,self.height-c)
  213. line(c,0,self.width-c,0)
  214. line(self.width,c,self.width,self.height-c)
  215. line(c,self.height,self.width-c,self.height)
  216. --do picture on back
  217. if face~=true then
  218. sprite(self.backImg,img.width/2,img.height/2,img.width*.9)
  219. end
  220. popStyle()
  221. setContext()
  222. return img
  223. end
  224.  
  225. --the suit images come from emoji
  226. function Card:createSuits()
  227. font("AppleColorEmoji")
  228. self.suits={unicode2UTF8(9824),unicode2UTF8(9829),unicode2UTF8(9830),unicode2UTF8(9827)}
  229. end
  230.  
  231. --draws a card at x,y with value of card (1-52), face=true if face up, a=angle in degrees (default 0)
  232. function Card:draw(x,y,card,face,a)
  233. pushMatrix()
  234. translate(x+self.width/2,y-self.height/2)
  235. if a==nil then a=0 end
  236. rotate(a)
  237. if face then
  238. if card>0 then self:drawDetails(card) end
  239. else
  240. sprite(self.cardBack,0,0)
  241. end
  242. popMatrix()
  243. end
  244.  
  245. --draws the pack of cards at x,y (actually just 4 cards but it looks like a pack)
  246. function Card:drawPack(x,y)
  247. for i=1,4 do
  248. local s=(i-1)*3
  249. self:draw(x+s,y-s,1,false)
  250. end
  251. end
  252.  
  253. --draws the numbers and symbols on the front of the card
  254. --one parameter = card value
  255. function Card:drawDetails(card)
  256. sprite(self.cardFace,0,0)
  257. pushStyle()
  258. font("SourceSansPro-Regular")
  259. fontSize(24*self.f)
  260. --calculate suit and value of card
  261. card=card-1
  262. local v=card%13
  263. local s=(card-v)/13+1
  264. v=v+1
  265. local w=self.cardFace.width
  266. local h=self.cardFace.height
  267. if s==1 or s==4 then fill(0,0, 0, 255) else fill(255,0,0,255) end --fill is red or black
  268.  
  269. --half the images on a card are upside down
  270. --so we do them in two loops, turning the card upside down between them
  271. --where the card is not exactly symmetrical, eg for odd numbers, we only draw on the first loop
  272. for i=1,2 do
  273. if i==2 then rotate(180) end --turn 180 degrees to do second loop
  274. local u=self.suits[s]
  275. text(self.value[v],-w*.4,h*.4) --text in corner of card
  276. fontSize(16*self.f)
  277. text(u,-w*.4,h*.28) --suit image
  278. fontSize(28*self.f)
  279. local ss=.13*h
  280. --now all the symbols arranged in the middle of the card
  281. if v==1 then
  282. if i==1 then text(u,0,0) end
  283. elseif v==2 then
  284. text(u,0,.3*h)
  285. elseif v==3 then
  286. if i==1 then text(u,0,0) end
  287. text(u,0,.3*h)
  288. elseif v==4 then
  289. text(u,-w*.18,.3*h)
  290. text(u,w*.18,.3*h)
  291. elseif v==5 then
  292. text(u,-w*.18,.3*h)
  293. text(u,w*.18,.3*h)
  294. if i==1 then text(u,0,0) end
  295. elseif v==6 then
  296. text(u,-w*.18,.3*h)
  297. text(u,w*.18,.3*h)
  298. text(u,-w*.18,0)
  299. elseif v==7 then
  300. text(u,-w*.18,.3*h)
  301. text(u,w*.18,.3*h)
  302. text(u,-w*.18,0)
  303. if i==1 then text(u,0,.15*h) end
  304. elseif v==8 then
  305. text(u,-w*.18,.3*h)
  306. text(u,w*.18,.3*h)
  307. text(u,-w*.18,0)
  308. text(u,0,.15*h)
  309. elseif v==9 then
  310. text(u,-w*.18,.3*h)
  311. text(u,w*.18,.3*h)
  312. if i==1 then text(u,0,0) end
  313. text(u,-w*.18,.1*h)
  314. text(u,w*.18,.1*h)
  315. elseif v==10 then
  316. text(u,-w*.18,.3*h)
  317. text(u,w*.18,.3*h)
  318. text(u,-w*.18,.1*h)
  319. text(u,w*.18,.1*h)
  320. text(u,0,.2*h)
  321. else --royalty
  322. pushStyle()
  323. font("AppleColorEmoji")
  324. fill(255)
  325. fontSize(84*self.f)
  326. if i==1 then
  327. if v==11 then text(unicode2UTF8(128113))
  328. elseif v==12 then text(unicode2UTF8(128120))
  329. else text(unicode2UTF8(128116))
  330. end
  331. end
  332. popStyle()
  333. end
  334. end
  335. popStyle()
  336. end
  337.  
  338. --used by Main and other classes
  339. --given a value 1-52, it returns the value and suit in text and numbers, eg S,J,1,11
  340. function Card:SuitValue(c)
  341. local v=(c-1)%13
  342. local s=(c-1-v)/13+1
  343. v = v + 1
  344. return self.value[v],self.suit[s],v,s
  345. end
  346.  
  347. function Card:isOppositeColor(s1,s2)
  348. if s1==s2 or s1+s2==5 then return false else return true end
  349. end
  350.  
  351. function unicode2UTF8(u) --needed for emoji
  352. u = math.max(0, math.floor(u)) -- A positive integer
  353. local UTF8
  354. if u < 0x80 then -- less than 8 bits
  355. UTF8 = string.char(u)
  356. elseif u < 0x800 then -- less than 12 bits
  357. local b2 = u % 0x40 + 0x80
  358. local b1 = math.floor(u/0x40) + 0xC0
  359. UTF8 = string.char(b1, b2)
  360. elseif u < 0x10000 then -- less than 16 bits
  361. local b3 = u % 0x40 + 0x80
  362. local b2 = math.floor(u/0x40) % 0x40 + 0x80
  363. local b1 = math.floor(u/0x1000) + 0xE0
  364. UTF8 = string.char(b1, b2, b3)
  365. elseif u < 0x200000 then -- less than 22 bits
  366. local b4 = u % 0x40 + 0x80
  367. local b3 = math.floor(u/0x40) % 0x40 + 0x80
  368. local b2 = math.floor(u/0x1000) % 0x40 + 0x80
  369. local b1 = math.floor(u/0x40000) + 0xF0
  370. UTF8 = string.char(b1, b2, b3, b4)
  371. elseif u < 0x800000 then -- less than 24 bits
  372. local b5 = u % 0x40 + 0x80
  373. local b4 = math.floor(u/0x40) % 0x40 + 0x80
  374. local b3 = math.floor(u/0x1000) % 0x40 + 0x80
  375. local b2 = math.floor(u/0x40000) % 0x40 + 0x80
  376. local b1 = math.floor(u/0x1000000) + 0xF8
  377. UTF8 = string.char(b1, b2, b3, b4, b5)
  378. else
  379. print("Error: Code point too large for Codea's Lua.")
  380. end
  381. return UTF8
  382. end
  383.  
  384.  
  385. --# CardTable
  386. CardTable = class()
  387. --[[
  388. This class handles tabular card games like patience, where all the cards are in columns and rows
  389.  
  390. Most position references are in cols and rows rather than pixels, and they can be fractional
  391. The class has two tables to keep track of cards on the table.
  392. cards - is simply a hash table so we can scroll through all the cards quickly
  393. tableMap - is a table laid out like the table itself, so tableMap[2][3] is the card in col 2, row 3
  394. Currently, what is stored in these tables is a "card" which is an array with
  395. self.counter=unique id (unused)
  396. x,y=actual pixel position
  397. col,row=column and row
  398. c=value of card (1-52)
  399. face=true if face showing
  400.  
  401. --]]
  402.  
  403. --x,y are top left of table
  404. --c is a reference to the card class
  405. --s=whether cards stack (overlay) on each other
  406. --pc,ps are the location of the pack, pc=col, ps=row
  407. function CardTable:init(x,y,c,s,pc,ps)
  408. self.x,self.y = x,y
  409. self.stacked=s
  410. self.card=c
  411. self.margin=4
  412. self.cards={}
  413. self.tableMap={}
  414. self.packCol,self.packRow=pc,ps
  415. self.counter=0
  416. end
  417.  
  418. --deals the next card to col,row, with face=true for face up, false for face down
  419. --returns the card dealt
  420. function CardTable:dealCard(pack,col,row,face)
  421. local c=pack:nextCard()
  422. if c==nil then return nil end
  423. return self:addToCol(c,col,row,face)
  424. end
  425.  
  426. --add card to column, row can be set to nil if you want the next available row
  427. function CardTable:addToCol(c,col,row,face)
  428. if row==nil then row=self:findEmptyRowInCol(col) end --if row omitted,find next available
  429. local x=self:colToX(col)
  430. local y=self:rowToY(row)
  431. self.counter = self.counter + 1
  432. local crd={id=self.counter,x=x,y=y,col=col,row=row,c=c,face=face}
  433. --table.insert(self.cards,crd)
  434. table.insert(self.cards,crd)
  435. self:updateTable(crd)
  436. return crd
  437. end
  438.  
  439. function CardTable:findEmptyRowInCol(col)
  440. if self.tableMap[col]==nil then return 1 end
  441. local r=1
  442. while self.tableMap[col][r]~=nil do r = r + 1 end
  443. return r
  444. end
  445.  
  446. function CardTable:removeFromCol(col,row)
  447. if self.tableMap[col][row]==nil then
  448. return "Invalid column or row"
  449. end
  450. local crd=self.tableMap[col][row]
  451. for i=1,#self.cards do
  452. if self.cards[i]==crd then table.remove(self.cards,i) break end
  453. end
  454. self.tableMap[col][row]=nil
  455. return crd
  456. end
  457.  
  458. function CardTable:UpdateCard(oldC, newC)
  459.  
  460. end
  461.  
  462. --tableMap
  463. function CardTable:updateTable(crd,action)
  464. if self.tableMap[crd.col]==nil then self.tableMap[crd.col]={} end
  465. self.tableMap[crd.col][crd.row]={}
  466. self.tableMap[crd.col][crd.row]=crd
  467. end
  468.  
  469. --helper function returns x value from a col reference
  470. --dontStack=true if cards don't overlay
  471. function CardTable:colToX(col,dontStack)
  472. local h
  473. if self.stacked and dontStack~=true then h=self.card.height*.25 else h=self.card.height end
  474. return self.x+(self.card.width+self.margin)*(col-1)
  475. end
  476. --helper function returns y value from a row reference
  477. function CardTable:rowToY(row,dontStack)
  478. local h
  479. if self.stacked and dontStack~=true then h=self.card.height*.25 else h=self.card.height end
  480. return self.y-(h+self.margin)*(row-1)
  481. end
  482.  
  483. --this finds which card is under x,y, very useful for touches on the screen
  484. --it is actually quite difficult to do this when cards overlap, because all the cards in
  485. --a column are truncated except the last one
  486. --however this can be overcome by looking at which card(s) are under x,yn and keeping
  487. --the card that is furthest down (lowest y value)
  488. function CardTable:cardFromXY(x,y)
  489. local w,h=self.card.width,self.card.height
  490. local c
  491. for i,crd in pairs(self.cards) do
  492. if x>=crd.x and x<=crd.x+w and y<=crd.y and y>=crd.y-h then
  493. if c==nil then c=crd elseif c.row<crd.row then c=crd end
  494. end
  495. end
  496. if c==nil then
  497. local px,py=self:colToX(self.packCol,true),self:rowToY(self.packRow,true)
  498. if x>=px and x<=px+w and y<=py and y>=py-h then
  499. c="pack"
  500. end
  501. end
  502. return c
  503. end
  504.  
  505. --moves a stack of cards from the bottom of one column to the bottom of another
  506. --the last optional parameter makes it illegal to move a stack unless the top card is face up, ie handles
  507. function CardTable:moveCardColumn(startCol,startRow,newCol,failIfTopCardHidden)
  508. if self.tableMap[startCol][startRow]==nil then
  509. return "Invalid column or row"
  510. end
  511. if failIfTopCardHidden and self.tableMap[startCol][startRow].face==false then
  512. return "Hidden card cannot be moved"
  513. end
  514. --move cards one by one
  515. local r=startRow
  516. crd=self.tableMap[startCol][r]
  517. while crd~=nil do
  518. self:removeFromCol(startCol,r)
  519. self:addToCol(crd.c,newCol,nil,crd.face)
  520. r=r+1
  521. crd=self.tableMap[startCol][r]
  522. end
  523. --turn over exposed card in original col if there is one
  524. if startRow>1 then
  525. self:turnOverCard(startCol,startRow-1)
  526. end
  527. end
  528.  
  529. function CardTable:turnOverCard(col,row)
  530. crd=self.tableMap[col][row]
  531. crd.face=true
  532. end
  533.  
  534. function CardTable:draw()
  535. for i,card in ipairs(self.cards) do
  536. self.card:draw(card.x,card.y,card.c,card.face)
  537. end
  538. if self.packCol~=nil then
  539. local x,y=self:colToX(self.packCol,true),self:rowToY(self.packRow,true)
  540. self.card:drawPack(x,y)
  541. end
  542. if self.moving~=nil then
  543. self.card:draw(self.moving.x,self.moving.y,self.moving.c,self.moving.face)
  544. end
  545. end
  546.  
  547. function CardTable:touched(touch)
  548. if touch.state==BEGAN then
  549. self.cardTouched=self:cardFromXY(touch.x,touch.y)
  550. elseif touch.state==MOVING and self.cardTouched~=nil then
  551. self.moving={x=touch.x-c.width/2,y=touch.y+c.height/2,c=self.cardTouched.c,face=self.cardTouched.face}
  552. elseif touch.state==ENDED then
  553. self.moving=nil
  554. crd=self:cardFromXY(touch.x,touch.y)
  555. if self.cardTouched==crd then
  556. return crd
  557. else
  558. return self.cardTouched,crd
  559. end
  560. end
  561. end
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568. --# Pack
  569. Pack = class()
  570. --[[
  571. Manages a pack of cards
  572. Functions:
  573.  
  574. shuffle - shuffles the pack
  575.  
  576. nextCard - returns the next card in the pack
  577. --]]
  578.  
  579. function Pack:init(p)
  580. self.pack={}
  581. self:shuffle()
  582. end
  583.  
  584. function Pack:shuffle()
  585. self.pack={}
  586. local t={}
  587. for i=1,52 do t[i]=i end
  588. for i=1,52 do
  589. local x=math.random(1,53-i)
  590. self.pack[i]=t[x]
  591. t[x]=53-i
  592. end
  593. return self.pack
  594. end
  595.  
  596. function Pack:nextCard()
  597. if #self.pack==0 then return nil end
  598. local c=self.pack[1]
  599. table.remove(self.pack,1)
  600. return c
  601. end
  602.  
  603. function Pack:cards()
  604. return #self.pack
  605. end
  606.  
  607. --# Ideas
  608.  
  609. --[[
  610.  
  611. flip card over
  612. remove card
  613. move x layered cards
  614. fan cards like poker hand
  615. n packs or infinite pack, eg casino
  616. manage discard pile
  617.  
  618. --]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement