TechManDylan

CCblackjack

Apr 30th, 2026 (edited)
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 33.30 KB | None | 0 0
  1. -- Blackjack Turtle Casino
  2. -- Centered-card UI with left info panel
  3. -- ComputerCraft / CC:Tweaked style program
  4. -- Designed for Advanced Monitor wall, 3 tall x 7 wide
  5. -- Uses a turtle as the diamond bank/payout machine
  6. -- Uses speaker if available
  7.  
  8. --------------------------------------------------
  9. -- CONFIG
  10. --------------------------------------------------
  11.  
  12. local DIAMOND_ID = "minecraft:diamond"
  13.  
  14. local TEXT_SCALE = 0.5
  15.  
  16. local MIN_BET = 1
  17. local MAX_BET = 999
  18. local MAX_BET_AMOUNT = 999
  19.  
  20. local DEALER_HITS_SOFT_17 = true
  21.  
  22. local BLACKJACK_PAYS_NUMERATOR = 3
  23. local BLACKJACK_PAYS_DENOMINATOR = 2
  24. -- Blackjack profit is 3:2.
  25. -- Total payout = bet + floor(bet * 3 / 2).
  26. -- Example:
  27. -- 2 diamond bet blackjack pays 5 total.
  28. -- 1 diamond bet blackjack pays 2 total because diamonds cannot split.
  29.  
  30. local SHOE_DECKS = 6
  31. local SHUFFLE_AT_CARDS_LEFT = 30
  32.  
  33. local CASINO_MENU_PROGRAM = "casino"
  34. local CASINO_BANK_FILE = "casino_bank_base.txt"
  35.  
  36. --------------------------------------------------
  37. -- PERIPHERALS
  38. --------------------------------------------------
  39.  
  40. local mon = peripheral.find("monitor")
  41.  
  42. if not mon then
  43. error("No monitor found. Attach/wrap an advanced monitor wall.")
  44. end
  45.  
  46. local speaker = nil
  47.  
  48. for _, name in ipairs(peripheral.getNames()) do
  49. local p = peripheral.wrap(name)
  50. if p then
  51. if type(p.playNote) == "function" or type(p.playSound) == "function" then
  52. speaker = p
  53. break
  54. end
  55. end
  56. end
  57.  
  58. mon.setTextScale(TEXT_SCALE)
  59. mon.setBackgroundColor(colors.green)
  60. mon.setTextColor(colors.white)
  61. mon.clear()
  62.  
  63. local W, H = mon.getSize()
  64.  
  65. --------------------------------------------------
  66. -- UTILS
  67. --------------------------------------------------
  68.  
  69. local function clamp(v, a, b)
  70. if v < a then return a end
  71. if v > b then return b end
  72. return v
  73. end
  74.  
  75. local function writeAt(x, y, text, fg, bg)
  76. if bg then mon.setBackgroundColor(bg) end
  77. if fg then mon.setTextColor(fg) end
  78. mon.setCursorPos(x, y)
  79. mon.write(text)
  80. end
  81.  
  82. local function fillBox(x, y, w, h, bg)
  83. if w <= 0 or h <= 0 then return end
  84.  
  85. mon.setBackgroundColor(bg)
  86.  
  87. for yy = y, y + h - 1 do
  88. if yy >= 1 and yy <= H then
  89. mon.setCursorPos(x, yy)
  90. mon.write(string.rep(" ", math.max(0, math.min(w, W - x + 1))))
  91. end
  92. end
  93. end
  94.  
  95. local function centerText(x, y, w, text, fg, bg)
  96. text = tostring(text)
  97.  
  98. if #text > w then
  99. text = string.sub(text, 1, w)
  100. end
  101.  
  102. local pad = math.floor((w - #text) / 2)
  103. writeAt(x + pad, y, text, fg, bg)
  104. end
  105.  
  106. local function drawButton(x, y, w, h, label, bg, fg)
  107. fillBox(x, y, w, h, bg)
  108. local cy = y + math.floor(h / 2)
  109. centerText(x, cy, w, label, fg or colors.white, bg)
  110. end
  111.  
  112. local function inBox(px, py, box)
  113. return px >= box.x and px < box.x + box.w and py >= box.y and py < box.y + box.h
  114. end
  115.  
  116. local function money(n)
  117. return tostring(math.floor(n or 0))
  118. end
  119.  
  120. --------------------------------------------------
  121. -- DIAMOND BANKING
  122. --------------------------------------------------
  123.  
  124. local function countDiamonds()
  125. local total = 0
  126.  
  127. for slot = 1, 16 do
  128. local d = turtle.getItemDetail(slot)
  129. if d and d.name == DIAMOND_ID then
  130. total = total + d.count
  131. end
  132. end
  133.  
  134. return total
  135. end
  136.  
  137. local function selectDiamondSlot()
  138. for slot = 1, 16 do
  139. local d = turtle.getItemDetail(slot)
  140.  
  141. if d and d.name == DIAMOND_ID and d.count > 0 then
  142. turtle.select(slot)
  143. return true
  144. end
  145. end
  146.  
  147. return false
  148. end
  149.  
  150. local function pullDiamondsFromAboveUntil(amountNeeded)
  151. local tries = 0
  152.  
  153. while countDiamonds() < amountNeeded and tries < 64 do
  154. tries = tries + 1
  155.  
  156. local pulled = turtle.suckUp(64)
  157.  
  158. if not pulled then
  159. break
  160. end
  161.  
  162. sleep(0.05)
  163. end
  164.  
  165. return countDiamonds() >= amountNeeded
  166. end
  167.  
  168. local function dropDiamondsDown(amount)
  169. local remaining = amount
  170.  
  171. while remaining > 0 do
  172. if not selectDiamondSlot() then
  173. return false, amount - remaining
  174. end
  175.  
  176. local d = turtle.getItemDetail()
  177.  
  178. if not d or d.name ~= DIAMOND_ID then
  179. return false, amount - remaining
  180. end
  181.  
  182. local toDrop = math.min(remaining, d.count)
  183. local ok = turtle.dropDown(toDrop)
  184.  
  185. if not ok then
  186. return false, amount - remaining
  187. end
  188.  
  189. remaining = remaining - toDrop
  190. sleep(0.03)
  191. end
  192.  
  193. return true, amount
  194. end
  195.  
  196. local function loadBankBase()
  197. if fs.exists(CASINO_BANK_FILE) then
  198. local f = fs.open(CASINO_BANK_FILE, "r")
  199. if f then
  200. local raw = f.readAll()
  201. f.close()
  202.  
  203. local n = tonumber(raw)
  204. if n and n >= 0 then
  205. return n
  206. end
  207. end
  208. end
  209.  
  210. return countDiamonds()
  211. end
  212.  
  213. local bankBase = loadBankBase()
  214.  
  215. local function saveBankBase()
  216. local f = fs.open(CASINO_BANK_FILE, "w")
  217. if f then
  218. f.write(tostring(math.floor(bankBase)))
  219. f.close()
  220. end
  221. end
  222.  
  223. saveBankBase()
  224.  
  225. --------------------------------------------------
  226. -- SOUND
  227. --------------------------------------------------
  228.  
  229. local function playNoteSafe(instrument, volume, pitch)
  230. if not speaker then return end
  231.  
  232. instrument = instrument or "harp"
  233. volume = volume or 1
  234. pitch = pitch or 12
  235.  
  236. if type(speaker.playNote) == "function" then
  237. local ok = pcall(function()
  238. speaker.playNote(instrument, volume, pitch)
  239. end)
  240.  
  241. if ok then
  242. return
  243. end
  244. end
  245.  
  246. if type(speaker.playSound) == "function" then
  247. pcall(function()
  248. speaker.playSound("note.harp", volume, 1)
  249. end)
  250. end
  251. end
  252.  
  253. local function soundClick()
  254. playNoteSafe("hat", 0.8, 12)
  255. end
  256.  
  257. local function soundDeal()
  258. playNoteSafe("hat", 0.7, 14)
  259. sleep(0.04)
  260. end
  261.  
  262. local function soundWin()
  263. playNoteSafe("bell", 1, 16)
  264. sleep(0.08)
  265. playNoteSafe("bell", 1, 20)
  266. sleep(0.08)
  267. playNoteSafe("bell", 1, 24)
  268. end
  269.  
  270. local function soundBlackjack()
  271. playNoteSafe("bell", 1, 16)
  272. sleep(0.06)
  273. playNoteSafe("bell", 1, 20)
  274. sleep(0.06)
  275. playNoteSafe("bell", 1, 24)
  276. sleep(0.06)
  277. playNoteSafe("bell", 1, 28)
  278. end
  279.  
  280. local function soundLoss()
  281. playNoteSafe("bass", 1, 6)
  282. sleep(0.12)
  283. playNoteSafe("bass", 1, 4)
  284. end
  285.  
  286. local function soundPush()
  287. playNoteSafe("harp", 1, 12)
  288. sleep(0.08)
  289. playNoteSafe("harp", 1, 12)
  290. end
  291.  
  292. --------------------------------------------------
  293. -- CARD / SHOE LOGIC
  294. --------------------------------------------------
  295.  
  296. math.randomseed(os.epoch and os.epoch("utc") or os.clock() * 100000)
  297.  
  298. local suits = {
  299. {name = "S", color = colors.black},
  300. {name = "H", color = colors.red},
  301. {name = "D", color = colors.red},
  302. {name = "C", color = colors.black}
  303. }
  304.  
  305. local ranks = {
  306. {name = "A", value = 11},
  307. {name = "2", value = 2},
  308. {name = "3", value = 3},
  309. {name = "4", value = 4},
  310. {name = "5", value = 5},
  311. {name = "6", value = 6},
  312. {name = "7", value = 7},
  313. {name = "8", value = 8},
  314. {name = "9", value = 9},
  315. {name = "10", value = 10},
  316. {name = "J", value = 10},
  317. {name = "Q", value = 10},
  318. {name = "K", value = 10}
  319. }
  320.  
  321. local shoe = {}
  322.  
  323. local function shuffleShoe()
  324. shoe = {}
  325.  
  326. for deck = 1, SHOE_DECKS do
  327. for _, suit in ipairs(suits) do
  328. for _, rank in ipairs(ranks) do
  329. table.insert(shoe, {
  330. rank = rank.name,
  331. value = rank.value,
  332. suit = suit.name,
  333. color = suit.color
  334. })
  335. end
  336. end
  337. end
  338.  
  339. for i = #shoe, 2, -1 do
  340. local j = math.random(1, i)
  341. shoe[i], shoe[j] = shoe[j], shoe[i]
  342. end
  343. end
  344.  
  345. local function drawCardFromShoe()
  346. if #shoe <= SHUFFLE_AT_CARDS_LEFT then
  347. shuffleShoe()
  348. end
  349.  
  350. local card = table.remove(shoe)
  351. soundDeal()
  352. return card
  353. end
  354.  
  355. local function handValue(hand)
  356. local total = 0
  357. local aces = 0
  358.  
  359. for _, card in ipairs(hand) do
  360. total = total + card.value
  361. if card.rank == "A" then
  362. aces = aces + 1
  363. end
  364. end
  365.  
  366. while total > 21 and aces > 0 do
  367. total = total - 10
  368. aces = aces - 1
  369. end
  370.  
  371. local soft = aces > 0
  372. return total, soft
  373. end
  374.  
  375. local function isBlackjack(hand)
  376. if #hand ~= 2 then
  377. return false
  378. end
  379.  
  380. local value = handValue(hand)
  381. return value == 21
  382. end
  383.  
  384. --------------------------------------------------
  385. -- GAME STATE
  386. --------------------------------------------------
  387.  
  388. local STATE_BETTING = "betting"
  389. local STATE_PLAYER = "player"
  390. local STATE_DEALER = "dealer"
  391. local STATE_ROUND_OVER = "round_over"
  392.  
  393. local state = STATE_BETTING
  394.  
  395. local playerHand = {}
  396. local dealerHand = {}
  397.  
  398. local currentBet = 0
  399. local originalBet = 0
  400. local betAmount = 1
  401.  
  402. local message = "Set bet amount, then press DEAL."
  403. local revealDealer = false
  404. local lastOutcome = nil
  405.  
  406. local hitBoxes = {}
  407. local confirmMode = nil
  408.  
  409. local drawAll
  410.  
  411. local function availableCredits()
  412. local current = countDiamonds()
  413. local credits = current - bankBase - currentBet
  414.  
  415. if credits < 0 then
  416. credits = 0
  417. end
  418.  
  419. return credits
  420. end
  421.  
  422. local function roundIsActive()
  423. return state == STATE_PLAYER or state == STATE_DEALER
  424. end
  425.  
  426. --------------------------------------------------
  427. -- BET AMOUNT CONTROLS
  428. --------------------------------------------------
  429.  
  430. local function changeBetAmount(delta)
  431. if roundIsActive() or confirmMode then
  432. message = "Cannot change bet amount during a hand."
  433. soundLoss()
  434. return
  435. end
  436.  
  437. betAmount = betAmount + delta
  438. betAmount = clamp(betAmount, MIN_BET, MAX_BET_AMOUNT)
  439.  
  440. message = "Bet amount set to " .. betAmount .. " diamond(s). Press DEAL to start."
  441. soundClick()
  442. end
  443.  
  444. local function resetBetAmount()
  445. if roundIsActive() or confirmMode then
  446. message = "Cannot reset bet amount during a hand."
  447. soundLoss()
  448. return
  449. end
  450.  
  451. betAmount = 1
  452. message = "Bet amount reset to 1 diamond."
  453. soundClick()
  454. end
  455.  
  456. local function clearBet()
  457. if roundIsActive() then
  458. message = "Cannot clear bet during an active hand."
  459. soundLoss()
  460. return
  461. end
  462.  
  463. currentBet = 0
  464. originalBet = 0
  465.  
  466. if state == STATE_ROUND_OVER then
  467. playerHand = {}
  468. dealerHand = {}
  469. revealDealer = false
  470. lastOutcome = nil
  471. state = STATE_BETTING
  472. end
  473.  
  474. message = "Bet cleared. Press DEAL to use current bet amount."
  475. soundClick()
  476. end
  477.  
  478. --------------------------------------------------
  479. -- CASH OUT / MENU LOGIC
  480. --------------------------------------------------
  481.  
  482. local function resetRoundForExitOrCashout()
  483. currentBet = 0
  484. originalBet = 0
  485. playerHand = {}
  486. dealerHand = {}
  487. revealDealer = false
  488. lastOutcome = nil
  489. state = STATE_BETTING
  490. end
  491.  
  492. local function cashOutCredits()
  493. if roundIsActive() then
  494. message = "Cannot cash out directly during a hand."
  495. soundLoss()
  496. return
  497. end
  498.  
  499. local amount = availableCredits()
  500.  
  501. if amount <= 0 then
  502. message = "No unbet credits to cash out."
  503. soundLoss()
  504. return
  505. end
  506.  
  507. local ok, dropped = dropDiamondsDown(amount)
  508.  
  509. if ok then
  510. message = "Cashed out " .. dropped .. " diamond(s) below."
  511. soundWin()
  512. else
  513. message = "Cash out blocked! Dropped " .. dropped .. "/" .. amount .. "."
  514. soundLoss()
  515. end
  516.  
  517. saveBankBase()
  518. end
  519.  
  520. local function requestCashOut()
  521. if currentBet > 0 or roundIsActive() then
  522. confirmMode = "cashout"
  523. message = "Confirm cash out. Active bet/hand will be cleared first."
  524. soundLoss()
  525. return
  526. end
  527.  
  528. cashOutCredits()
  529. end
  530.  
  531. local function exitToMenuNow()
  532. resetRoundForExitOrCashout()
  533. confirmMode = nil
  534. saveBankBase()
  535.  
  536. mon.setBackgroundColor(colors.black)
  537. mon.setTextColor(colors.white)
  538. mon.clear()
  539.  
  540. if shell then
  541. if fs.exists(CASINO_MENU_PROGRAM) then
  542. shell.run(CASINO_MENU_PROGRAM)
  543. elseif fs.exists(CASINO_MENU_PROGRAM .. ".lua") then
  544. shell.run(CASINO_MENU_PROGRAM .. ".lua")
  545. else
  546. print("Casino menu not found.")
  547. print("Expected: " .. CASINO_MENU_PROGRAM)
  548. end
  549. end
  550.  
  551. error("Exited to casino menu", 0)
  552. end
  553.  
  554. local function requestExit()
  555. if currentBet > 0 or roundIsActive() then
  556. confirmMode = "exit"
  557. message = "Confirm menu. Active bet/hand will be cleared."
  558. soundLoss()
  559. return
  560. end
  561.  
  562. exitToMenuNow()
  563. end
  564.  
  565. --------------------------------------------------
  566. -- PAYOUT HANDLING
  567. --------------------------------------------------
  568.  
  569. local function payPlayer(amount, outcomeMessage, soundFunc)
  570. local unbetCredits = availableCredits()
  571.  
  572. if amount <= 0 then
  573. message = outcomeMessage or "No payout."
  574. if soundFunc then soundFunc() end
  575. return
  576. end
  577.  
  578. message = outcomeMessage .. " Paying " .. amount .. " diamonds."
  579. if soundFunc then soundFunc() end
  580.  
  581. local enough = pullDiamondsFromAboveUntil(amount)
  582.  
  583. if enough then
  584. local ok, dropped = dropDiamondsDown(amount)
  585.  
  586. if ok then
  587. message = outcomeMessage .. " Paid " .. dropped .. " diamonds below."
  588. else
  589. message = "Payout blocked! Dropped " .. dropped .. "/" .. amount
  590. end
  591. else
  592. local available = countDiamonds()
  593. local _, dropped = dropDiamondsDown(available)
  594. message = "HOUSE EMPTY! Paid only " .. dropped .. "/" .. amount .. "."
  595. end
  596.  
  597. local after = countDiamonds()
  598. bankBase = after - unbetCredits
  599.  
  600. if bankBase < 0 then
  601. bankBase = 0
  602. end
  603.  
  604. saveBankBase()
  605. end
  606.  
  607. local function houseKeepsBet()
  608. bankBase = bankBase + currentBet
  609. saveBankBase()
  610. end
  611.  
  612. local function finishRound(outcome, payoutAmount, msg, soundFunc)
  613. lastOutcome = outcome
  614. revealDealer = true
  615.  
  616. if payoutAmount and payoutAmount > 0 then
  617. payPlayer(payoutAmount, msg, soundFunc)
  618. else
  619. if outcome == "loss" then
  620. houseKeepsBet()
  621. end
  622.  
  623. message = msg
  624. if soundFunc then soundFunc() end
  625. end
  626.  
  627. currentBet = 0
  628. originalBet = 0
  629. state = STATE_ROUND_OVER
  630. end
  631.  
  632. --------------------------------------------------
  633. -- ROUND LOGIC
  634. --------------------------------------------------
  635.  
  636. local function dealCardTo(hand)
  637. table.insert(hand, drawCardFromShoe())
  638. drawAll()
  639. sleep(0.10)
  640. end
  641.  
  642. local function startDeal()
  643. if state == STATE_PLAYER or state == STATE_DEALER then
  644. return
  645. end
  646.  
  647. if state == STATE_ROUND_OVER then
  648. playerHand = {}
  649. dealerHand = {}
  650. revealDealer = false
  651. lastOutcome = nil
  652. state = STATE_BETTING
  653. end
  654.  
  655. if currentBet < MIN_BET then
  656. if betAmount < MIN_BET then
  657. message = "Bet amount must be at least " .. MIN_BET .. "."
  658. soundLoss()
  659. return
  660. end
  661.  
  662. if availableCredits() < betAmount then
  663. message = "Not enough credits. Need " .. betAmount .. " diamond(s)."
  664. soundLoss()
  665. return
  666. end
  667.  
  668. currentBet = betAmount
  669. originalBet = currentBet
  670. else
  671. originalBet = currentBet
  672. end
  673.  
  674. playerHand = {}
  675. dealerHand = {}
  676. revealDealer = false
  677. lastOutcome = nil
  678.  
  679. message = "Dealing..."
  680. drawAll()
  681.  
  682. dealCardTo(playerHand)
  683. dealCardTo(dealerHand)
  684. dealCardTo(playerHand)
  685. dealCardTo(dealerHand)
  686.  
  687. local playerBJ = isBlackjack(playerHand)
  688. local dealerBJ = isBlackjack(dealerHand)
  689.  
  690. if playerBJ or dealerBJ then
  691. revealDealer = true
  692. drawAll()
  693.  
  694. if playerBJ and dealerBJ then
  695. finishRound(
  696. "push",
  697. currentBet,
  698. "Both have blackjack. Push.",
  699. soundPush
  700. )
  701. elseif playerBJ then
  702. local profit = math.floor(currentBet * BLACKJACK_PAYS_NUMERATOR / BLACKJACK_PAYS_DENOMINATOR)
  703. local payout = currentBet + profit
  704.  
  705. finishRound(
  706. "blackjack",
  707. payout,
  708. "BLACKJACK!",
  709. soundBlackjack
  710. )
  711. else
  712. finishRound(
  713. "loss",
  714. 0,
  715. "Dealer has blackjack. You lose.",
  716. soundLoss
  717. )
  718. end
  719.  
  720. return
  721. end
  722.  
  723. state = STATE_PLAYER
  724. message = "Your turn. Hit, Stand, or Double."
  725. end
  726.  
  727. local function playerHit()
  728. if state ~= STATE_PLAYER then
  729. return
  730. end
  731.  
  732. dealCardTo(playerHand)
  733.  
  734. local value = handValue(playerHand)
  735.  
  736. if value > 21 then
  737. revealDealer = true
  738. finishRound(
  739. "loss",
  740. 0,
  741. "Bust! You lose.",
  742. soundLoss
  743. )
  744. else
  745. message = "You drew a card. Hand value: " .. value
  746. end
  747. end
  748.  
  749. local function dealerShouldHit()
  750. local value, soft = handValue(dealerHand)
  751.  
  752. if value < 17 then
  753. return true
  754. end
  755.  
  756. if value == 17 and soft and DEALER_HITS_SOFT_17 then
  757. return true
  758. end
  759.  
  760. return false
  761. end
  762.  
  763. local function resolveDealer()
  764. state = STATE_DEALER
  765. revealDealer = true
  766. message = "Dealer's turn."
  767. drawAll()
  768. sleep(0.25)
  769.  
  770. while dealerShouldHit() do
  771. dealCardTo(dealerHand)
  772. sleep(0.20)
  773. end
  774.  
  775. local playerValue = handValue(playerHand)
  776. local dealerValue = handValue(dealerHand)
  777.  
  778. if dealerValue > 21 then
  779. finishRound(
  780. "win",
  781. currentBet * 2,
  782. "Dealer busts. You win!",
  783. soundWin
  784. )
  785. elseif playerValue > dealerValue then
  786. finishRound(
  787. "win",
  788. currentBet * 2,
  789. "You win!",
  790. soundWin
  791. )
  792. elseif playerValue < dealerValue then
  793. finishRound(
  794. "loss",
  795. 0,
  796. "Dealer wins.",
  797. soundLoss
  798. )
  799. else
  800. finishRound(
  801. "push",
  802. currentBet,
  803. "Push. Bet returned.",
  804. soundPush
  805. )
  806. end
  807. end
  808.  
  809. local function playerStand()
  810. if state ~= STATE_PLAYER then
  811. return
  812. end
  813.  
  814. resolveDealer()
  815. end
  816.  
  817. local function playerDouble()
  818. if state ~= STATE_PLAYER then
  819. return
  820. end
  821.  
  822. if #playerHand ~= 2 then
  823. message = "Double only allowed on your first 2 cards."
  824. soundLoss()
  825. return
  826. end
  827.  
  828. if availableCredits() < originalBet then
  829. message = "Need " .. originalBet .. " more diamonds to double."
  830. soundLoss()
  831. return
  832. end
  833.  
  834. currentBet = currentBet + originalBet
  835. message = "Double down! Bet is now " .. currentBet .. "."
  836. soundClick()
  837. drawAll()
  838. sleep(0.12)
  839.  
  840. dealCardTo(playerHand)
  841.  
  842. local value = handValue(playerHand)
  843.  
  844. if value > 21 then
  845. revealDealer = true
  846. finishRound(
  847. "loss",
  848. 0,
  849. "Double bust! You lose.",
  850. soundLoss
  851. )
  852. else
  853. resolveDealer()
  854. end
  855. end
  856.  
  857. --------------------------------------------------
  858. -- DRAWING
  859. --------------------------------------------------
  860.  
  861. local function resetHitBoxes()
  862. hitBoxes = {}
  863. end
  864.  
  865. local function addHit(id, x, y, w, h)
  866. table.insert(hitBoxes, {
  867. id = id,
  868. x = x,
  869. y = y,
  870. w = w,
  871. h = h
  872. })
  873. end
  874.  
  875. local function drawFrame()
  876. fillBox(1, 1, W, H, colors.green)
  877. fillBox(1, 1, W, 1, colors.black)
  878. centerText(1, 1, W, "BLACKJACK | DIAMOND CASINO", colors.lime, colors.black)
  879. end
  880.  
  881. local function drawLeftInfoPanel()
  882. local x = 2
  883. local y = 3
  884. local w = 38
  885. local h = 13
  886.  
  887. fillBox(x, y, w, h, colors.black)
  888. centerText(x, y, w, "GAME INFO", colors.yellow, colors.black)
  889.  
  890. writeAt(x + 2, y + 2, "Credits: " .. money(availableCredits()), colors.lime, colors.black)
  891. writeAt(x + 2, y + 3, "Current Bet: " .. money(currentBet), colors.yellow, colors.black)
  892. writeAt(x + 2, y + 4, "Bet Amount: " .. money(betAmount), colors.orange, colors.black)
  893. writeAt(x + 2, y + 5, "Diamonds In Turtle: " .. money(countDiamonds()), colors.white, colors.black)
  894. writeAt(x + 2, y + 6, "House Bank Base: " .. money(bankBase), colors.lightGray, colors.black)
  895. writeAt(x + 2, y + 7, "Shoe Left: " .. tostring(#shoe), colors.lightGray, colors.black)
  896.  
  897. local soft17Text = "Dealer hits soft 17: "
  898. if DEALER_HITS_SOFT_17 then
  899. soft17Text = soft17Text .. "YES"
  900. else
  901. soft17Text = soft17Text .. "NO"
  902. end
  903. writeAt(x + 2, y + 8, soft17Text, colors.lightGray, colors.black)
  904.  
  905. fillBox(x + 1, y + 10, w - 2, 2, colors.gray)
  906. local msg = message
  907. if #msg > w - 4 then
  908. msg = string.sub(msg, 1, w - 4)
  909. end
  910. writeAt(x + 2, y + 10, msg, colors.white, colors.gray)
  911. end
  912.  
  913. local function drawCardBox(x, y, card, hidden)
  914. local w = 8
  915. local h = 5
  916.  
  917. if hidden then
  918. fillBox(x, y, w, h, colors.blue)
  919. centerText(x, y + 2, w, "???", colors.white, colors.blue)
  920. return
  921. end
  922.  
  923. local bg = colors.white
  924. local fg = card.color
  925.  
  926. fillBox(x, y, w, h, bg)
  927. writeAt(x + 1, y, card.rank, fg, bg)
  928. centerText(x, y + 2, w, card.suit, fg, bg)
  929. writeAt(x + w - #card.rank, y + h - 1, card.rank, fg, bg)
  930. end
  931.  
  932. local function drawCardPlaceholder(x, y)
  933. local w = 8
  934. local h = 5
  935.  
  936. fillBox(x, y, w, h, colors.lightGray)
  937. centerText(x, y + 2, w, "--", colors.gray, colors.lightGray)
  938. end
  939.  
  940. local function handAreaBounds()
  941. local leftEdge = 43
  942. local rightEdge = W - 2
  943.  
  944. if leftEdge > rightEdge then
  945. leftEdge = 2
  946. rightEdge = W - 2
  947. end
  948.  
  949. return leftEdge, rightEdge, rightEdge - leftEdge + 1
  950. end
  951.  
  952. local function getCenteredHandX(count)
  953. local cardW = 8
  954. local gap = 1
  955.  
  956. local leftEdge, _, areaW = handAreaBounds()
  957. local totalW = count * cardW + math.max(0, count - 1) * gap
  958.  
  959. if totalW > areaW then
  960. gap = 0
  961. totalW = count * cardW + math.max(0, count - 1) * gap
  962. end
  963.  
  964. local startX = leftEdge + math.floor((areaW - totalW) / 2)
  965. if startX < leftEdge then
  966. startX = leftEdge
  967. end
  968.  
  969. return startX, cardW, gap
  970. end
  971.  
  972. local function drawCenteredHand(title, hand, labelY, hideDealerSecondCard)
  973. local valueText
  974.  
  975. if hideDealerSecondCard and #hand > 0 then
  976. local shown = hand[1].value
  977. if hand[1].rank == "A" then
  978. shown = 11
  979. end
  980. valueText = "Showing: " .. shown
  981. elseif #hand > 0 then
  982. local v, soft = handValue(hand)
  983. valueText = "Value: " .. v
  984. if soft and v <= 21 then
  985. valueText = valueText .. " soft"
  986. end
  987. else
  988. valueText = "Value: --"
  989. end
  990.  
  991. local leftEdge, _, areaW = handAreaBounds()
  992. centerText(leftEdge, labelY, areaW, title .. " " .. valueText, colors.white, colors.green)
  993.  
  994. local cardY = labelY + 1
  995. local count = math.max(2, #hand)
  996. local startX, cardW, gap = getCenteredHandX(count)
  997.  
  998. if #hand == 0 then
  999. for i = 1, 2 do
  1000. local x = startX + (i - 1) * (cardW + gap)
  1001. drawCardPlaceholder(x, cardY)
  1002. end
  1003. return
  1004. end
  1005.  
  1006. for i, card in ipairs(hand) do
  1007. local x = startX + (i - 1) * (cardW + gap)
  1008. local hidden = false
  1009.  
  1010. if hideDealerSecondCard and i == 2 then
  1011. hidden = true
  1012. end
  1013.  
  1014. drawCardBox(x, cardY, card, hidden)
  1015. end
  1016. end
  1017.  
  1018. local function drawCardArea()
  1019. local bottomControlsY = H - 9
  1020. local actionRowY = H - 13
  1021.  
  1022. local playerLabelY = actionRowY - 8
  1023. local dealerLabelY = playerLabelY - 8
  1024.  
  1025. if dealerLabelY < 6 then
  1026. dealerLabelY = 6
  1027. playerLabelY = dealerLabelY + 8
  1028. end
  1029.  
  1030. drawCenteredHand("DEALER", dealerHand, dealerLabelY, (not revealDealer and #dealerHand > 0))
  1031. drawCenteredHand("PLAYER", playerHand, playerLabelY, false)
  1032.  
  1033. local leftEdge, _, areaW = handAreaBounds()
  1034.  
  1035. if state == STATE_PLAYER then
  1036. centerText(leftEdge, playerLabelY - 2, areaW, "YOUR TURN", colors.yellow, colors.green)
  1037. elseif state == STATE_DEALER then
  1038. centerText(leftEdge, playerLabelY - 2, areaW, "DEALER PLAYING...", colors.yellow, colors.green)
  1039. elseif state == STATE_ROUND_OVER and lastOutcome then
  1040. local outcomeText = "ROUND OVER"
  1041. if lastOutcome == "win" then
  1042. outcomeText = "YOU WIN"
  1043. elseif lastOutcome == "loss" then
  1044. outcomeText = "YOU LOSE"
  1045. elseif lastOutcome == "push" then
  1046. outcomeText = "PUSH"
  1047. elseif lastOutcome == "blackjack" then
  1048. outcomeText = "BLACKJACK!"
  1049. end
  1050.  
  1051. centerText(leftEdge, playerLabelY - 2, areaW, outcomeText, colors.yellow, colors.green)
  1052. end
  1053. end
  1054.  
  1055. local function drawActionRow()
  1056. local y = H - 13
  1057. local h = 3
  1058.  
  1059. local buttons = {}
  1060.  
  1061. if state == STATE_PLAYER then
  1062. buttons = {
  1063. {"HIT", "HIT", colors.lime, colors.black},
  1064. {"STAND", "STAND", colors.yellow, colors.black},
  1065. {"DOUBLE", "DOUBLE", colors.orange, colors.black},
  1066. {"EXIT_MENU", "MENU", colors.gray, colors.white}
  1067. }
  1068. else
  1069. buttons = {
  1070. {"CLEAR_BET", "CLEAR BET", colors.red, colors.white},
  1071. {"DEAL", "DEAL", colors.lime, colors.black},
  1072. {"EXIT_MENU", "MENU", colors.gray, colors.white}
  1073. }
  1074. end
  1075.  
  1076. local gap = 2
  1077. local count = #buttons
  1078. local btnW
  1079.  
  1080. if count == 4 then
  1081. btnW = 18
  1082. else
  1083. btnW = 22
  1084. end
  1085.  
  1086. local totalW = count * btnW + (count - 1) * gap
  1087. local x = math.floor((W - totalW) / 2)
  1088.  
  1089. for i, b in ipairs(buttons) do
  1090. local id, label, bg, fg = b[1], b[2], b[3], b[4]
  1091. local bx = x + (i - 1) * (btnW + gap)
  1092. drawButton(bx, y, btnW, h, label, bg, fg)
  1093. addHit(id, bx, y, btnW, h)
  1094. end
  1095. end
  1096.  
  1097. local function drawBottomBetControls()
  1098. local panelW = math.min(W - 6, 124)
  1099. local x = math.floor((W - panelW) / 2)
  1100. local y = H - 9
  1101. local panelH = 8
  1102.  
  1103. fillBox(x, y, panelW, panelH, colors.black)
  1104. centerText(x, y, panelW, "BET CONTROLS | CURRENT BET AMOUNT: " .. betAmount, colors.yellow, colors.black)
  1105.  
  1106. local gap = 1
  1107. local smallW = math.floor((panelW - 4 - gap * 2) / 3)
  1108.  
  1109. drawButton(x + 1, y + 1, smallW, 2, "+1", colors.lime, colors.black)
  1110. addHit("BETAMT:+1", x + 1, y + 1, smallW, 2)
  1111.  
  1112. drawButton(x + 1 + smallW + gap, y + 1, smallW, 2, "+5", colors.lime, colors.black)
  1113. addHit("BETAMT:+5", x + 1 + smallW + gap, y + 1, smallW, 2)
  1114.  
  1115. drawButton(x + 1 + (smallW + gap) * 2, y + 1, smallW, 2, "+10", colors.lime, colors.black)
  1116. addHit("BETAMT:+10", x + 1 + (smallW + gap) * 2, y + 1, smallW, 2)
  1117.  
  1118. drawButton(x + 1, y + 3, smallW, 2, "-1", colors.orange, colors.black)
  1119. addHit("BETAMT:-1", x + 1, y + 3, smallW, 2)
  1120.  
  1121. drawButton(x + 1 + smallW + gap, y + 3, smallW, 2, "-5", colors.orange, colors.black)
  1122. addHit("BETAMT:-5", x + 1 + smallW + gap, y + 3, smallW, 2)
  1123.  
  1124. drawButton(x + 1 + (smallW + gap) * 2, y + 3, smallW, 2, "-10", colors.orange, colors.black)
  1125. addHit("BETAMT:-10", x + 1 + (smallW + gap) * 2, y + 3, smallW, 2)
  1126.  
  1127. local halfW = math.floor((panelW - 3) / 2)
  1128.  
  1129. drawButton(x + 1, y + 5, halfW, 3, "RESET AMT", colors.gray, colors.white)
  1130. addHit("BETAMT:RESET", x + 1, y + 5, halfW, 3)
  1131.  
  1132. drawButton(x + 2 + halfW, y + 5, halfW, 3, "CASH OUT", colors.yellow, colors.black)
  1133. addHit("ACTION:CASHOUT", x + 2 + halfW, y + 5, halfW, 3)
  1134. end
  1135.  
  1136. local function drawConfirmScreen()
  1137. resetHitBoxes()
  1138. fillBox(1, 1, W, H, colors.black)
  1139.  
  1140. if confirmMode == "exit" then
  1141. centerText(1, 4, W, "CONFIRM RETURN TO GAME MENU", colors.yellow, colors.black)
  1142.  
  1143. if roundIsActive() then
  1144. centerText(1, 7, W, "A hand is currently active.", colors.white, colors.black)
  1145. centerText(1, 9, W, "Leaving now will cancel the hand and clear the bet back to credits.", colors.lightGray, colors.black)
  1146. elseif currentBet > 0 then
  1147. centerText(1, 7, W, "You have a " .. currentBet .. " diamond bet staged.", colors.white, colors.black)
  1148. centerText(1, 9, W, "Leaving will clear that bet back into your credits.", colors.lightGray, colors.black)
  1149. end
  1150.  
  1151. centerText(1, 11, W, "No diamonds will be paid out or removed.", colors.lightGray, colors.black)
  1152.  
  1153. elseif confirmMode == "cashout" then
  1154. centerText(1, 4, W, "CONFIRM CASH OUT", colors.yellow, colors.black)
  1155.  
  1156. if roundIsActive() then
  1157. centerText(1, 7, W, "A hand is currently active.", colors.white, colors.black)
  1158. centerText(1, 9, W, "Cash out will cancel the hand and clear the bet back to credits.", colors.lightGray, colors.black)
  1159. elseif currentBet > 0 then
  1160. centerText(1, 7, W, "You have a " .. currentBet .. " diamond bet staged.", colors.white, colors.black)
  1161. centerText(1, 9, W, "Cash out will clear the bet back into credits first.", colors.lightGray, colors.black)
  1162. end
  1163.  
  1164. centerText(1, 11, W, "Then all player credits will drop below the turtle.", colors.lightGray, colors.black)
  1165. end
  1166.  
  1167. local btnW = 32
  1168. local btnH = 5
  1169. local gap = 6
  1170. local totalW = btnW * 2 + gap
  1171. local startX = math.floor((W - totalW) / 2)
  1172. local y = 16
  1173.  
  1174. drawButton(startX, y, btnW, btnH, "CANCEL", colors.lime, colors.black)
  1175. addHit("CONFIRM_CANCEL", startX, y, btnW, btnH)
  1176.  
  1177. if confirmMode == "exit" then
  1178. drawButton(startX + btnW + gap, y, btnW, btnH, "MENU - CLEAR BET", colors.red, colors.white)
  1179. addHit("CONFIRM_YES", startX + btnW + gap, y, btnW, btnH)
  1180. elseif confirmMode == "cashout" then
  1181. drawButton(startX + btnW + gap, y, btnW, btnH, "CASH OUT ALL", colors.yellow, colors.black)
  1182. addHit("CONFIRM_YES", startX + btnW + gap, y, btnW, btnH)
  1183. end
  1184. end
  1185.  
  1186. drawAll = function()
  1187. if confirmMode then
  1188. drawConfirmScreen()
  1189. return
  1190. end
  1191.  
  1192. resetHitBoxes()
  1193. drawFrame()
  1194. drawLeftInfoPanel()
  1195. drawCardArea()
  1196. drawActionRow()
  1197. drawBottomBetControls()
  1198. end
  1199.  
  1200. --------------------------------------------------
  1201. -- INPUT
  1202. --------------------------------------------------
  1203.  
  1204. local function handleConfirmTouch(x, y)
  1205. for _, box in ipairs(hitBoxes) do
  1206. if inBox(x, y, box) then
  1207. if box.id == "CONFIRM_CANCEL" then
  1208. confirmMode = nil
  1209. message = "Action cancelled."
  1210. soundClick()
  1211. drawAll()
  1212. return true
  1213.  
  1214. elseif box.id == "CONFIRM_YES" then
  1215. if confirmMode == "exit" then
  1216. resetRoundForExitOrCashout()
  1217. confirmMode = nil
  1218. saveBankBase()
  1219. exitToMenuNow()
  1220. return true
  1221.  
  1222. elseif confirmMode == "cashout" then
  1223. resetRoundForExitOrCashout()
  1224. confirmMode = nil
  1225. saveBankBase()
  1226. cashOutCredits()
  1227. drawAll()
  1228. return true
  1229. end
  1230. end
  1231. end
  1232. end
  1233.  
  1234. return false
  1235. end
  1236.  
  1237. local function handleTouch(x, y)
  1238. if confirmMode then
  1239. handleConfirmTouch(x, y)
  1240. return
  1241. end
  1242.  
  1243. for _, box in ipairs(hitBoxes) do
  1244. if inBox(x, y, box) then
  1245. local id = box.id
  1246.  
  1247. if id == "BETAMT:+1" then
  1248. changeBetAmount(1)
  1249. elseif id == "BETAMT:+5" then
  1250. changeBetAmount(5)
  1251. elseif id == "BETAMT:+10" then
  1252. changeBetAmount(10)
  1253. elseif id == "BETAMT:-1" then
  1254. changeBetAmount(-1)
  1255. elseif id == "BETAMT:-5" then
  1256. changeBetAmount(-5)
  1257. elseif id == "BETAMT:-10" then
  1258. changeBetAmount(-10)
  1259. elseif id == "BETAMT:RESET" then
  1260. resetBetAmount()
  1261. elseif id == "CLEAR_BET" then
  1262. clearBet()
  1263. elseif id == "DEAL" then
  1264. startDeal()
  1265. elseif id == "HIT" then
  1266. playerHit()
  1267. elseif id == "STAND" then
  1268. playerStand()
  1269. elseif id == "DOUBLE" then
  1270. playerDouble()
  1271. elseif id == "ACTION:CASHOUT" then
  1272. requestCashOut()
  1273. elseif id == "EXIT_MENU" then
  1274. requestExit()
  1275. end
  1276.  
  1277. drawAll()
  1278. return
  1279. end
  1280. end
  1281. end
  1282.  
  1283. --------------------------------------------------
  1284. -- STARTUP
  1285. --------------------------------------------------
  1286.  
  1287. shuffleShoe()
  1288. drawAll()
  1289.  
  1290. while true do
  1291. local e, side, x, y = os.pullEvent()
  1292.  
  1293. if e == "monitor_touch" then
  1294. handleTouch(x, y)
  1295. elseif e == "peripheral" or e == "peripheral_detach" then
  1296. drawAll()
  1297. end
  1298. end
Add Comment
Please, Sign In to add comment