Advertisement
Dermotb

Cards 1.01

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