Advertisement
BombBloke

BBCards (ComputerCraft)

May 4th, 2014
1,416
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.32 KB | None | 0 0
  1. -- +---------------------+------------+---------------------+
  2. -- |                     |            |                     |
  3. -- |                     |   BBCards  |                     |
  4. -- |                     |            |                     |
  5. -- +---------------------+------------+---------------------+
  6.  
  7. local version = "Version 0.3.0"
  8.  
  9. -- Card games for ComputerCraft, by Jeffrey Alexander (aka Bomb Bloke).
  10. -- Currently offers Solitaire and FreeCell.
  11. -- http://www.computercraft.info/forums2/index.php?/topic/18480-bbcards
  12.  
  13. ---------------------------------------------
  14. ------------Variable Declarations------------
  15. ---------------------------------------------
  16.  
  17. local myEvent, clickTimer, mon
  18.  
  19. local game = "Solitaire"
  20.  
  21. local kingHead
  22. if _HOST then
  23.     kingHead = {{{{32,146,143,158,32},{32,129,133,133,32},{32,144,129,133,132},{32,136,130,131,32},{32,32,139,32,32}},
  24.     {"dde4d","dd88d","dd7f6","d966d","de11d"},{"d44dd","d777d","d766d","dfffd","de31d"}},
  25.     {{{32,146,143,158,32},{32,138,138,130,32},{136,138,130,159,32},{32,131,129,132,32},{32,32,135,32,32}},
  26.     {"dde4d","d88dd","6f77d","d669d","d11ed"},{"d44dd","d777d","d66dd","dfffd","d13ed"}}}
  27.    
  28.     for j = 1, #kingHead do
  29.         local rows = kingHead[j][1]
  30.         for i = 1, 5 do rows[i] = string.char(unpack(rows[i])) end
  31.     end
  32. else kingHead = {{4,4,4},{7,7,7},{6,6,6},{15,15,15},{14,3,14}} end
  33.  
  34. ---------------------------------------------
  35. ------------Function Declarations------------
  36. ---------------------------------------------
  37.  
  38. os.loadAPI("card")
  39. if not card then error("Unable to initialise card API - ensure it's present on the root of your drive.") end
  40.  
  41. -- Returns true if the current event represents a mouse click we should listen to.
  42. local function touchedMe()
  43.     if myEvent[1] == "mouse_click" then return true
  44.     elseif myEvent[1] ~= "monitor_touch" or not mon then return false
  45.     else return mon.side == myEvent[2] end
  46. end
  47.  
  48. -- Returns true if the current event represents one of the passed keys being pressed.
  49. local function pressedKey(...)
  50.     if myEvent[1] ~= "key" then return false end
  51.  
  52.     local args = {...}
  53.     for i = 1, #args do if args[i] == myEvent[2] then return true end end
  54.     return false
  55. end
  56.  
  57. -- Draws the watching king:
  58. local function drawKingHead()
  59.     if _HOST then
  60.         local kingHead = kingHead[myEvent[3] < 35 and 2 or 1]
  61.         for y = 1, 5 do
  62.             term.setCursorPos(33, y + 2)
  63.             term.blit(kingHead[1][y], kingHead[2][y], kingHead[3][y])
  64.         end
  65.     else
  66.         for y = 1, 5 do
  67.             term.setCursorPos(34, y + 2)
  68.             for x = 1, 3 do
  69.                 term.setBackgroundColor(bit.blshift(1,kingHead[y][x]))
  70.                 term.write(" ")
  71.             end
  72.         end
  73.  
  74.         term.setCursorPos(34 + (myEvent[3] < 35 and 2 or 0), 5)
  75.         term.setBackgroundColor(colours.grey)
  76.         term.write(" ")
  77.     end
  78. end
  79.  
  80. -- Sets up the layout of the play area.
  81. local prepareBoard = {["Solitaire"] = function()
  82.         for i = 0, 3 do card.defineCell(8 * i + 26, 2, card.straight, card.victoryPile) end  -- Cells 1-4, targets.
  83.  
  84.         for i = 0, 6 do card.defineCell(8 * i + 2, 10, card.down) end                        -- Cells 5-11, the main play area.
  85.  
  86.         card.defineCell(2, 2, card.straight, card.drawPile, 13, 3)                           -- Cells 12-13, draw piles.
  87.         card.defineCell(10, 2, card.left, card.discardPile)
  88.     end,
  89.    
  90.     ["FreeCell"] = function()
  91.         for i = 0, 3 do card.defineCell(8 * i + 38, 2, card.straight, card.victoryPile) end  -- Cells 1-4, targets.
  92.  
  93.         for i = 0, 7 do card.defineCell(8 * i + 4, 10, card.down) end                        -- Cells 5-12, the main play area.
  94.  
  95.         for i = 0, 3 do card.defineCell(8 * i + 2, 2, card.straight, card.discardPile) end   -- Cells 13-16, additional free cells.
  96.     end}
  97.  
  98. -- Deals out a new game.
  99. local deal = {["Solitaire"] = function()
  100.         card.shuffle()
  101.  
  102.         for i = 6, 11 do card.displayCell(i,false) end
  103.  
  104.         for i = 1, 7 do
  105.             for j = 7, i + 1, -1 do card.dealCard(j+4,true) end
  106.             card.dealCard(i+4,false)
  107.         end
  108.  
  109.         for i = 6, 11 do card.displayCell(i,true) end
  110.  
  111.         -- And the rest of the cards can go in the face-down draw pile.
  112.         card.displayCell(12,false)
  113.         for i = 1, card.getRemainingDeckSize() do card.dealCard(12,true) end
  114.         card.displayCell(12,true)
  115.     end,
  116.    
  117.     ["FreeCell"] = function()
  118.         card.shuffle()
  119.        
  120.         for i = 5, 12 do card.displayCell(i,false) end
  121.        
  122.         local curCell = 5
  123.         for i = 1, 52 do
  124.             card.dealCard(curCell,false)
  125.             curCell = curCell == 12 and 5 or (curCell + 1)
  126.         end
  127.        
  128.         for i = 5, 12 do card.displayCell(i,true) end
  129.        
  130.         myEvent = {0, 0, 40}
  131.         drawKingHead()
  132.     end}
  133.  
  134. -- Handle gameplay.
  135. local function handleClick()
  136.     thisCard, thisCell = card.checkClick(myEvent[3],myEvent[4])
  137.    
  138.     local selected = card.getSelected()
  139.     card.deselect()
  140.    
  141.     if not thisCell then return end
  142.    
  143.     -- At this point, we have determined which cell was clicked ("thisCell"), and
  144.     -- if any cards were in that cell, which specific one was clicked ("thisCard").
  145.  
  146.     -- React according to the cell's type and the card's placement:
  147.     if selected then
  148.         -- Attempt to place pre-selected cards elsewhere.
  149.  
  150.         -- Moving to a victory pile:
  151.         if (card.getSpecial(thisCell) == card.victoryPile and (not card.getHigherCard(selected)) and
  152.         ((card.getValue(selected) == 1 and not thisCard) or
  153.         (thisCard and card.getValue(selected) == card.getValue(thisCard) + 1 and card.getSuit(selected) == card.getSuit(thisCard)))) or
  154.        
  155.         -- Moving to a FreeCell upper-free cell:
  156.         (game == "FreeCell" and card.getSpecial(thisCell) == card.discardPile and
  157.         not (card.getHigherCard(selected) or card.getTopCard(thisCell))) then
  158.             card.moveTo(selected,thisCell)
  159.             return
  160.            
  161.         -- Moving to a regular pile:
  162.         elseif ((not card.getSpecial(thisCell)) and
  163.         (((card.getValue(selected) == 13 or game == "FreeCell") and not thisCard) or
  164.         (thisCard and (not card.getHigherCard(thisCard)) and card.getValue(thisCard) == card.getValue(selected) + 1 and bit.band(card.getSuit(selected),1) ~= bit.band(card.getSuit(thisCard),1)))) then
  165.             if game == "FreeCell" then
  166.                 local moving, freeCells, temp = 1, 1, selected
  167.                
  168.                 while card.getHigherCard(temp) do
  169.                     temp = card.getHigherCard(temp)
  170.                     moving = moving + 1
  171.                 end
  172.                
  173.                 for i = 5, 16 do if i ~= thisCell and not card.getTopCard(i) then freeCells = freeCells + 1 end end
  174.                
  175.                 if moving > freeCells then return end
  176.             end
  177.            
  178.             card.moveTo(selected,thisCell)
  179.             return
  180.            
  181.         -- Quick-move to victory pile:
  182.         elseif clickTimer and thisCard == selected and (not card.getHigherCard(thisCard)) and card.getSpecial(thisCell) ~= card.victoryPile then
  183.             for i = 1, 4 do
  184.                 if (card.getTopCard(i) and card.getValue(card.getTopCard(i)) + 1 == card.getValue(selected) and card.getSuit(selected) == card.getSuit(card.getTopCard(i))) or
  185.                 (card.getValue(selected) == 1 and not card.getTopCard(i)) then
  186.                     card.moveTo(selected,i)
  187.                     return
  188.                 end
  189.             end
  190.            
  191.             if game == "FreeCell" and card.getSpecial(thisCell) ~= card.discardPile  then for i=13,16 do if not card.getTopCard(i) then
  192.                     card.moveTo(selected,i)
  193.                     return
  194.             end end end
  195.         end
  196.     end
  197.  
  198.     if thisCard and (card.getSpecial(thisCell) == card.victoryPile or card.getSpecial(thisCell) == card.discardPile or not card.getSpecial(thisCell)) then
  199.         -- Standard play cell.
  200.         -- Select cards for movement elsewhere, or flip upside-down cards to reveal them.
  201.  
  202.         if card.isFaceDown(thisCard) then
  203.             -- Attempt to reveal a card.
  204.             if not card.getHigherCard(thisCard) then card.setFaceDown(thisCard, false) end
  205.         else
  206.             -- Attempt to select a card.
  207.             if (card.getSpecial(thisCell) == card.discardPile and card.getTopCard(thisCell) ~= thisCard) or (game == "FreeCell" and card.getSpecial(thisCell) == card.victoryPile) then return end
  208.  
  209.             selected = thisCard
  210.  
  211.             while card.getHigherCard(selected) do
  212.                 if card.getValue(selected) ~= card.getValue(card.getHigherCard(selected)) + 1 or bit.band(card.getSuit(selected),1) == bit.band(card.getSuit(card.getHigherCard(selected)),1) then return end
  213.                 selected = card.getHigherCard(selected)
  214.             end
  215.  
  216.             card.select(thisCard)                  
  217.         end
  218.     elseif card.getSpecial(thisCell) == card.drawPile then
  219.         card.displayCell(thisCell,false)
  220.         card.displayCell(card.getTarget(thisCell),false)
  221.  
  222.         if thisCard then
  223.             -- Flip cards to the target pile.
  224.             card.moveTo(card.getTopCard(thisCell),card.getTarget(thisCell),false)
  225.             card.straightenCell(card.getTarget(thisCell))
  226.  
  227.             for i=1,card.getFlipAmount(thisCell)-1 do
  228.                 if not card.getTopCard(thisCell) then break end
  229.                 card.moveTo(card.getTopCard(thisCell),card.getTarget(thisCell),false)
  230.             end
  231.         else
  232.             -- Flip cards back from the target pile.
  233.             while card.getTopCard(card.getTarget(thisCell)) do
  234.                 card.moveTo(card.getTopCard(card.getTarget(thisCell)),thisCell,true,true)
  235.             end
  236.         end
  237.  
  238.         card.displayCell(thisCell,true)
  239.         card.displayCell(card.getTarget(thisCell),true)
  240.     end
  241. end
  242.  
  243. -- Automatically moves cards up to the target cells, if it's appropriate to do so.
  244. local function autoMove()
  245.     local moving = true
  246.     while moving do
  247.         local least = 13
  248.         for i = 1, 4 do
  249.             if card.getTopCard(i) then
  250.                 if card.getValue(card.getTopCard(i)) < least then least = card.getValue(card.getTopCard(i)) end
  251.             else least = 0 end
  252.         end
  253.        
  254.         moving = false
  255.         for i = 5, 16 do if card.getTopCard(i) and card.getValue(card.getTopCard(i)) <= least + 2 then
  256.             for j = 1, 4 do if (card.getValue(card.getTopCard(i)) == 1 and not card.getTopCard(j)) or
  257.             (card.getTopCard(j) and card.getSuit(card.getTopCard(i)) == card.getSuit(card.getTopCard(j)) and
  258.             card.getValue(card.getTopCard(i)) == card.getValue(card.getTopCard(j)) + 1) then
  259.                 moving = true
  260.                 if card.getSelected() == card.getTopCard(i) then card.deselect() end
  261.                 card.moveTo(card.getTopCard(i),j)
  262.                 break
  263.             end end
  264.         end end
  265.     end
  266. end
  267.  
  268. ---------------------------------------------
  269. ------------         Init        ------------
  270. ---------------------------------------------
  271.  
  272. term.clear()
  273. term.setCursorPos(1,1)
  274.  
  275. do
  276.     local temp = peripheral.getNames()
  277.     for i=1,#temp do if peripheral.getType(temp[i]) == "monitor" and peripheral.call(temp[i],"isColor") then
  278.         mon = peripheral.wrap(temp[i])
  279.         mon.side = temp[i]
  280.         if not term.restore then mon.restoreTo = term.current() end
  281.         break
  282.     end end
  283. end
  284.  
  285. if not mon then
  286.     print("Sorry, BBCards requires access to an advanced monitor.")
  287.     print("At least 4x3 blocks is recommended.")
  288.     error()
  289. end
  290.  
  291. print("BBCards is running.")
  292. print("")
  293. print("Keys:")
  294. print("G     - Change game (Solitaire / FreeCell)")
  295. print("S / L - Save / Load")
  296. print("R     - Redeal")
  297. print("Q / X - Exit")
  298. print("")
  299. print("Play commencing on attached monitor \""..mon.side.."\". Recommended size is 4x3.")
  300.  
  301. mon.setTextScale(0.5)
  302. term.redirect(mon)
  303.  
  304. prepareBoard[game]()
  305. deal[game]()
  306.  
  307. term.setCursorPos(1,1)
  308.  
  309. ---------------------------------------------
  310. ------------     Main Program    ------------
  311. ---------------------------------------------
  312.  
  313. local function main()
  314.     while true do
  315.         myEvent = {os.pullEvent()}
  316.  
  317.         if touchedMe() then
  318.             handleClick()
  319.             os.queueEvent("startTimer")
  320.            
  321.             if game == "FreeCell" then
  322.                 autoMove()
  323.                 drawKingHead()
  324.             end
  325.            
  326.             myEvent = true
  327.             for i = 1, 4 do if not card.getTopCard(i) or card.getValue(card.getTopCard(i)) ~= 13 then
  328.                 myEvent = false
  329.                 break
  330.             end end
  331.            
  332.             if myEvent then
  333.                 card.victoryAnimation(mon.side)
  334.                 deal[game]()
  335.             end
  336.  
  337.         elseif pressedKey(keys.q,keys.x) then
  338.             os.pullEvent("char")
  339.             return
  340.  
  341.         elseif pressedKey(keys.r) then
  342.             deal[game]()
  343.  
  344.         elseif pressedKey(keys.s) then
  345.             card.save("Solitaire")
  346.  
  347.         elseif pressedKey(keys.l) then
  348.             game = card.load()
  349.  
  350.         elseif pressedKey(keys.g) then
  351.             game = game == "FreeCell" and "Solitaire" or "FreeCell"
  352.  
  353.             card.reset()
  354.             prepareBoard[game]()
  355.             deal[game]()
  356.  
  357.         elseif myEvent[1] == "monitor_resize" then
  358.             card.redraw()
  359.  
  360.         end
  361.     end
  362. end
  363.  
  364. local function timerCheck()
  365.     local timerEvent
  366.     while true do
  367.         timerEvent = {os.pullEvent()}
  368.         if timerEvent[1] == "timer" and timerEvent[2] == clickTimer then
  369.             clickTimer = nil
  370.         elseif timerEvent[1] == "startTimer" then clickTimer = os.startTimer(0.5) end
  371.     end
  372. end
  373.  
  374. parallel.waitForAny(main, timerCheck)
  375.  
  376. ---------------------------------------------
  377. ------------     Termination     ------------
  378. ---------------------------------------------
  379.  
  380. os.unloadAPI("card")
  381. if term.restore then term.restore() else term.redirect(mon.restoreTo) end
  382.  
  383. term.clear()
  384. term.setCursorPos(1,1)
  385. print("Thanks for playing!")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement