ThatsW0lfy

CC BlackJack

Feb 15th, 2019
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.80 KB | None | 0 0
  1. local wdir = shell and shell.dir() or ""
  2. if term.current().setTextScale then
  3.   term.current().setTextScale(0.5)
  4. end
  5.  
  6. local json = require("json")
  7.  
  8. local wapi = require("w")
  9. local rapi = require("r")
  10. local kapi = require("k")
  11. local jua  = require("jua")
  12.  
  13. -- Load local config
  14. local configHandle = fs.open(wdir .. "/config.lua", "r")
  15. if not configHandle then
  16.   configHandle = fs.open(wdir .. "/.config", "r")
  17.  
  18.   if not configHandle then
  19.     error("No config file found at 'config.lua', please create one")
  20.   end
  21. end
  22.  
  23. local config
  24. local configData = configHandle.readAll()
  25. if not configData:match("^return") then
  26.   configData = "return " .. configData
  27. end
  28. local configFunc, err = loadstring(configData)
  29. if not configFunc then
  30.   error("Invalid config: Line " .. (err:match(":(%d+:.+)") or err))
  31. else
  32.   config = configFunc()
  33. end
  34.  
  35. configHandle.close()
  36.  
  37. local Surface = dofile(wdir .. "/surface.lua")
  38.  
  39. local display = Surface.create(term.getSize())
  40. local buffer = Surface.create(display.width * 2, display.height * 3, colors.black)
  41.  
  42. local function loadRF(name)
  43.   local ims = Surface.load(wdir .. "/res/" .. name .. ".rif")
  44.   ims:toRGB(Surface.palette.riko4)
  45.   ims:toPalette(Surface.palette.cc)
  46.  
  47.   return ims
  48. end
  49.  
  50. local fontMapping = {
  51.   [" "] = 32,
  52.   ["0"] = 33,
  53.   ["1"] = 34,
  54.   ["2"] = 35,
  55.   ["3"] = 36,
  56.   ["4"] = 37,
  57.   ["5"] = 38,
  58.   ["6"] = 39,
  59.   ["7"] = 40,
  60.   ["8"] = 41,
  61.   ["9"] = 42,
  62.   ["A"] = 43,
  63.   ["J"] = 44,
  64.   ["Q"] = 45,
  65.   ["K"] = 46
  66. }
  67.  
  68. local sleepTime = 0 -- 0.1
  69.  
  70. local function gChar(char)
  71.   return string.char(fontMapping[char])
  72. end
  73.  
  74. local bigFontSurf = Surface.load(wdir .. "/res/font.bmp")
  75. local bigFont = Surface.loadFont(bigFontSurf)
  76.  
  77. local fontSurf = loadRF("font")
  78. local font = Surface.loadFont(fontSurf)
  79.  
  80. local cardFirst = loadRF("card")
  81. local cardMost = loadRF("card2")
  82. local cardLast = loadRF("card-s")
  83.  
  84. local heart = loadRF("heart")
  85. local diamond = loadRF("diamond")
  86. local spade = loadRF("spade")
  87. local club = loadRF("club")
  88.  
  89. local cardBack = loadRF("cardfill")
  90.  
  91. local function checkBounds(surf, x, y, cx, cy)
  92.   local w, h = surf.width / 2, surf.height / 3
  93.   return cx >= x and cx < x + w and cy >= y and cy < y + h
  94. end
  95.  
  96. local function writeCenter(surf, text, y, b, t)
  97.   surf:drawString(text, math.floor((surf.width - #text) / 2), y, b, t)
  98. end
  99.  
  100. local function writeBigCenter(surf, text, y, b, t, ns)
  101.   local fw, fh = Surface.getTextSize(text, bigFont)
  102.   local oSurf = Surface.create(math.ceil(fw / 2) * 2, math.ceil(fh / 3) * 3, b or colors.black)
  103.   oSurf:drawText(text, bigFont, 0, 0, t or colors.white)
  104.  
  105.   if ns then
  106.     surf:drawSurface(oSurf, math.floor((surf.width - oSurf.width) / 2), y)
  107.   else
  108.     surf:drawSurfaceSmall(oSurf, math.floor((surf.width - (oSurf.width / 2)) / 2), y)
  109.   end
  110. end
  111.  
  112. local function getDeckDims(deckCnt)
  113.   if deckCnt == 0 then
  114.     return 0
  115.   end
  116.  
  117.   return cardFirst.width + (deckCnt - 1) * cardMost.width + cardLast.width, cardFirst.height
  118. end
  119.  
  120. local function drawDeck(surf, deck, x, y, firstOver)
  121.   if #deck == 0 then
  122.     return x
  123.   end
  124.  
  125.   if not surf then
  126.     surf = setmetatable({}, {__index = function() return function() end end})
  127.   end
  128.  
  129.   local suits = {hearts=heart,diamonds=diamond,spades=spade,clubs=club}
  130.   local colors = {hearts=colors.red,diamonds=colors.red,spades=colors.black,clubs=colors.black}
  131.  
  132.   local function drawFaceValue(v, c, x, y)
  133.     if v == "10" then
  134.       surf:drawText(gChar("1") .. gChar("0"), font, x - 2, y, c)
  135.     else
  136.       surf:drawText(gChar(v), font, x, y, c)
  137.     end
  138.   end
  139.  
  140.   local firstCard = deck[1]
  141.   surf:drawSurface(firstOver and cardMost or cardFirst, x, y)
  142.   if firstOver then
  143.     x = x + 1
  144.   end
  145.  
  146.   if firstCard.hidden then
  147.     surf:drawSurface(cardBack, x + 2, y + 2)
  148.     surf:drawSurface(cardBack, x + 2, y + 10)
  149.     surf:drawSurface(cardBack, x + 2, y + 18)
  150.   else
  151.     surf:drawSurface(suits[firstCard.suit], x + 2, y + 2)
  152.     drawFaceValue(firstCard.value, colors[firstCard.suit], x + 4, y + 19)
  153.   end
  154.   x = x + 10
  155.  
  156.   for i = 2, #deck do
  157.     local thisCard = deck[i]
  158.     surf:drawSurface(cardMost, x, y)
  159.     if thisCard.hidden then
  160.       surf:drawSurface(cardBack, x + 3, y + 2)
  161.       surf:drawSurface(cardBack, x + 3, y + 10)
  162.       surf:drawSurface(cardBack, x + 3, y + 18)
  163.     else
  164.       surf:drawSurface(suits[thisCard.suit], x + 3, y + 2)
  165.       drawFaceValue(thisCard.value, colors[thisCard.suit], x + 5, y + 19)
  166.     end
  167.     x = x + 11
  168.   end
  169.  
  170.   local lastCard = deck[#deck]
  171.   surf:drawSurface(cardLast, x, y)
  172.   if lastCard.hidden then
  173.     surf:drawSurface(cardBack, x, y + 2)
  174.     surf:drawSurface(cardBack, x, y + 10)
  175.     surf:drawSurface(cardBack, x, y + 18)
  176.   else
  177.     surf:drawSurfaceRotated(suits[lastCard.suit], x, y + 18, 7, 7, math.pi)
  178.     drawFaceValue(lastCard.value, colors[lastCard.suit], x + 2, y + 3)
  179.   end
  180.  
  181.   return x
  182. end
  183.  
  184. local hitBox = Surface.create(50, 24, colors.black)
  185. hitBox:drawRect(0, 0, 50, 24, colors.green)
  186. writeBigCenter(hitBox, "HIT", 10, nil, colors.green, true)
  187.  
  188. local standBox = Surface.create(50, 24, colors.black)
  189. standBox:drawRect(0, 0, 50, 24, colors.red)
  190. writeBigCenter(standBox, "STAND", 10, nil, colors.red, true)
  191.  
  192. local function outputBanner(text, color)
  193.   display:fillRect(0, 16, display.width, 6, color)
  194.   writeBigCenter(display, text, 18, color, colors.white)
  195.   writeCenter(display, "Click Anywhere to Finish...", 20, color, colors.white)
  196.  
  197.   display:output()
  198. end
  199.  
  200. local suitMap = {"hearts", "spades", "diamonds", "clubs"}
  201. local valueMap = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
  202. local baseValue = {
  203.   ["2"] = 2,
  204.   ["3"] = 3,
  205.   ["4"] = 4,
  206.   ["5"] = 5,
  207.   ["6"] = 6,
  208.   ["7"] = 7,
  209.   ["8"] = 8,
  210.   ["9"] = 9,
  211.   ["J"] = 10,
  212.   ["Q"] = 10,
  213.   ["K"] = 10,
  214.   ["10"] = 10
  215. }
  216.  
  217. -- GAME STATE
  218. local totalDeck = {}
  219. local playerHand, playerY = {}, 83
  220. local houseHand, houseY = {}, 4
  221. local wager = 0
  222. local account = {}
  223.  
  224. local function getHandValue(hand, trueValue)
  225.   local aces = 0
  226.   local value = 0
  227.  
  228.   -- First add up all non-aces
  229.   for i = 1, #hand do
  230.     if trueValue or not hand[i].hidden then
  231.       if hand[i].value == "A" then
  232.         aces = aces + 1
  233.       else
  234.         value = value + baseValue[hand[i].value]
  235.       end
  236.     end
  237.   end
  238.  
  239.   -- Now find the least number of aces that need to count as 1 for it to not be a bust
  240.   local finalValue
  241.   for i = aces, 0, -1 do
  242.     finalValue = value + (i * 11) + (aces - i)
  243.     if finalValue <= 21 then
  244.       break
  245.     end
  246.   end
  247.  
  248.   return finalValue
  249. end
  250.  
  251. local function drawGame(extraCard, ex, ey, flag)
  252.   buffer:clear(colors.black)
  253.   display:clear()
  254.  
  255.   drawDeck(buffer, houseHand, math.floor(houseHand.x or 0), houseY)
  256.   drawDeck(buffer, playerHand, math.floor(playerHand.x or 0), playerY)
  257.  
  258.   if extraCard then
  259.     drawDeck(buffer, {extraCard}, math.floor(ex), math.floor(ey), flag)
  260.   end
  261.  
  262.   writeBigCenter(buffer, "House: " .. getHandValue(houseHand), houseY + 28, nil, nil, true)
  263.   writeBigCenter(buffer, "You: " .. getHandValue(playerHand), playerY - 6, nil, nil, true)
  264.  
  265.   display:drawSurfaceSmall(buffer, 0, 0)
  266.   display:output()
  267. end
  268.  
  269. local function resetDeck()
  270.   totalDeck = {}
  271.   playerHand = {}
  272.   houseHand = {}
  273.  
  274.   for suit = 1, 4 do
  275.     for v = 1, 13 do
  276.       totalDeck[#totalDeck + 1] = {suit = suitMap[suit], value = valueMap[v]}
  277.     end
  278.   end
  279.  
  280.   -- Shuffle the deck
  281.   for i = #totalDeck, 1, -1 do
  282.     local j = math.random(1, i)
  283.     totalDeck[i], totalDeck[j] = totalDeck[j], totalDeck[i]
  284.   end
  285. end
  286.  
  287. local function tween(x, d)
  288.   local val = x + (d - x) / 2
  289.   return val, math.abs(val - x) <= 1
  290. end
  291.  
  292. local function dealTo(hand, startY, handY, hidden)
  293.   local card = totalDeck[#totalDeck]
  294.   card.hidden = hidden
  295.   totalDeck[#totalDeck] = nil
  296.  
  297.   -- local cardX
  298.  
  299.   -- Animate in
  300.   local done = false
  301.  
  302.   -- Move deck over
  303.   local deckXDst = math.floor((buffer.width - getDeckDims(#hand + 1)) / 2)
  304.   if #hand == 0 then
  305.     hand.x = deckXDst
  306.   end
  307.  
  308.   repeat
  309.     hand.x, done = tween(hand.x, deckXDst)
  310.     drawGame()
  311.  
  312.     sleep(sleepTime)
  313.   until done
  314.  
  315.   -- Slide card in
  316.   local cx, cy = drawDeck(nil, hand, math.floor(hand.x), handY), startY
  317.   --deckXDst + getDeckDims(#hand) - cardLast.width, startY
  318.  
  319.   repeat
  320.     cy, done = tween(cy, handY)
  321.     drawGame(card, cx, cy, #hand > 0)
  322.  
  323.     sleep(sleepTime)
  324.   until done
  325.  
  326.   hand[#hand + 1] = card
  327.   drawGame()
  328. end
  329.  
  330. local deck21 = {
  331.   {value = "J", suit = "spades"},
  332.   {value = "A", suit = "diamonds"}
  333. }
  334.  
  335. local d21Surf = Surface.create(getDeckDims(#deck21))
  336. drawDeck(d21Surf, deck21, 0, 0)
  337.  
  338. local titleSurf
  339. do
  340.   local fw, fh = Surface.getTextSize(config.title, bigFont)
  341.   titleSurf = Surface.create(math.ceil(fw / 2) * 2, math.ceil(fh / 3) * 3, colors.green)
  342.   titleSurf:drawText(config.title, bigFont, 0, 0, colors.white)
  343. end
  344.  
  345. local function makePayout()
  346.   os.queueEvent("mktx", math.floor(wager * config.payout[1] / config.payout[2]), account[1], account[2], "message=Congratulations! Here are your winnings.")
  347.   wager = 0
  348. end
  349.  
  350. local state = "pregame"
  351.  
  352. local function gameThread()
  353.   while state ~= "quit" do
  354.     if state == "pregame" then
  355.       display:clear(colors.black)
  356.  
  357.       display:fillRect(0, 0, display.width, 5, colors.green)
  358.       display:drawSurfaceSmall(titleSurf, math.floor((display.width - titleSurf.width / 2) / 2), 2)
  359.  
  360.       display:drawSurfaceSmall(d21Surf, math.floor((display.width - d21Surf.width / 2) / 2), 10)
  361.  
  362.       writeBigCenter(display, config.payout[1] .. ":" .. config.payout[2] .. " Payout", 21)
  363.  
  364.       writeCenter(display, "To play, send your wager to:", 26)
  365.       writeBigCenter(display, config.kristName .. ".kst", 28)
  366.  
  367.       writeCenter(display, "Push is considered a loss, house wins. Dealer stops at 17.", 37)
  368.  
  369.       display:output()
  370.  
  371.       while true do
  372.         local e = os.pullEvent()
  373.         if e == "payment" and wager > 0 then
  374.           state = "gameInit"
  375.           break
  376.         end
  377.       end
  378.     elseif state == "gameInit" then
  379.       resetDeck()
  380.  
  381.       dealTo(houseHand, -40, houseY)
  382.       dealTo(playerHand, 120, playerY)
  383.  
  384.       dealTo(houseHand, -40, houseY, true)
  385.       dealTo(playerHand, 120, playerY)
  386.  
  387.       if getHandValue(playerHand) == 21 then
  388.         state = "reveal-stand"
  389.         -- outputBanner("BlackJack!", colors.green)
  390.  
  391.         -- os.pullEvent("mouse_click")
  392.         -- state = "quit"
  393.       else
  394.         state = "game"
  395.       end
  396.     elseif state == "game" then
  397.       local hbx, hby = 5, 15
  398.       local sbx, sby = display.width - standBox.width / 2 - 5, 15
  399.  
  400.       local function drawBoxes()
  401.         display:drawSurfaceSmall(hitBox, hbx, hby)
  402.  
  403.         display:drawSurfaceSmall(standBox, sbx, sby)
  404.         display:output()
  405.       end
  406.  
  407.       while true do
  408.         drawBoxes()
  409.         local e, b, mx, my = os.pullEvent("mouse_click")
  410.  
  411.         if checkBounds(hitBox, hbx, hby, mx, my) then
  412.           dealTo(playerHand, 120, playerY)
  413.  
  414.           if getHandValue(playerHand) > 21 then
  415.             state = "bust-player"
  416.             break
  417.           end
  418.         elseif checkBounds(standBox, sbx, sby, mx, my) then
  419.           state = "reveal-stand"
  420.           break
  421.         end
  422.       end
  423.     elseif state == "reveal-stand" then
  424.       -- Reveal the starter for the house
  425.       houseHand[#houseHand].hidden = false
  426.       drawGame()
  427.       sleep(0.1)
  428.  
  429.       while getHandValue(houseHand) < 17 do
  430.         dealTo(houseHand, -40, houseY)
  431.       end
  432.  
  433.       if getHandValue(houseHand) > 21 then
  434.         state = "bust-house"
  435.       else
  436.         state = "checkwin"
  437.       end
  438.     elseif state == "bust-player" then
  439.       outputBanner("Bust!", colors.red)
  440.  
  441.       os.pullEvent("mouse_click")
  442.       state = "quit"
  443.     elseif state == "bust-house" then
  444.       outputBanner("House Bust!", colors.green)
  445.       makePayout()
  446.  
  447.       os.pullEvent("mouse_click")
  448.       state = "quit"
  449.     elseif state == "checkwin" then
  450.       local player = getHandValue(playerHand)
  451.       local house = getHandValue(houseHand)
  452.       if player > house then
  453.         if player == 21 then
  454.           outputBanner("BlackJack!", colors.green)
  455.         else
  456.           outputBanner("You Win!", colors.green)
  457.         end
  458.  
  459.         makePayout()
  460.       elseif player == house then
  461.         outputBanner("Push, House Wins", colors.red)
  462.       else
  463.         if house == 21 then
  464.           outputBanner("BlackJack, House Wins", colors.red)
  465.         else
  466.           outputBanner("House Wins", colors.red)
  467.         end
  468.       end
  469.  
  470.       os.pullEvent("mouse_click")
  471.       state = "quit"
  472.     end
  473.  
  474.     if state == "quit" then
  475.       state = "pregame"
  476.       wager = 0
  477.     elseif state == "fatal" then
  478.       state = "quit"
  479.     end
  480.   end
  481. end
  482.  
  483. local function kristThread()
  484.   --== Krist Interface Setup ==--
  485.  
  486.   rapi.init(jua)
  487.   wapi.init(jua)
  488.   kapi.init(jua, json, wapi, rapi)
  489.  
  490.   jua.on("mktx", function(e, amount, tx, meta, msg)
  491.     if meta.meta and meta.meta["return"] then
  492.       jua.await(kapi.makeTransaction, config.pkey, meta.meta["return"], amount, msg)
  493.     else
  494.       jua.await(kapi.makeTransaction, config.pkey, tx.from, amount, msg)
  495.     end
  496.   end)
  497.  
  498.   local seenID = 0
  499.  
  500.   --== Handlers ==--
  501.   local function handleTransaction(data)
  502.     local tx = data.transaction
  503.  
  504.     if tx.to == config.host then
  505.       if tx.metadata then
  506.         local meta = kapi.parseMeta(tx.metadata)
  507.  
  508.         if tx.id > seenID then
  509.           seenID = tx.id
  510.  
  511.           if meta.domain == config.kristName then
  512.             -- print("Received " .. tx.value .. "kst from " .. tx.from .. " (Meta: " .. tx.metadata .. ")")
  513.  
  514.             -- for k, v in pairs(tx) do
  515.             --   print(k, v)
  516.             -- end
  517.  
  518.             -- processPayment(tx, meta)
  519.             if wager == 0 then
  520.               wager = tx.value
  521.               account = {tx, meta}
  522.               os.queueEvent("payment")
  523.             else
  524.               os.queueEvent("mktx", tx.value, account[1], account[2], "error=A game is currently underway, please wait your turn..")
  525.             end
  526.           end
  527.         end
  528.       end
  529.     end
  530.   end
  531.  
  532.   --== Main Loop ==--
  533.  
  534.   jua.go(function()
  535.     local success
  536.     if not config.pkey then
  537.       state = "fatal"
  538.       jua.stop()
  539.  
  540.       error("No pkey configured")
  541.     end
  542.  
  543.     if config.pkeyFormat == "kwallet" then
  544.       config.pkey = kapi.toKristWalletFormat(config.pkey)
  545.     end
  546.  
  547.     success, ws = await(kapi.connect, config.pkey or "no-pkey")
  548.  
  549.     if success then
  550.       ws.on("hello", function(helloData)
  551.         local subscribeSuccess = await(ws.subscribe, "transactions", handleTransaction)
  552.  
  553.         if not subscribeSuccess then
  554.           state = "fatal"
  555.           jua.stop()
  556.  
  557.           error("Failed to subscribe to Krist transactions")
  558.         end
  559.       end)
  560.  
  561.       ws.on("closed", function()
  562.         os.reboot()
  563.       end)
  564.     else
  565.       state = "fatal"
  566.       jua.stop()
  567.  
  568.       error("Failed to request a websocket url")
  569.     end
  570.   end)
  571. end
  572.  
  573. parallel.waitForAny(kristThread, gameThread)
Add Comment
Please, Sign In to add comment