MtnMCG

blackjack machine

Jun 6th, 2025 (edited)
25
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.70 KB | None | 0 0
  1. local modem = peripheral.find("modem")
  2. if not modem then error("No modem found. Please attach a wireless modem.") end
  3.  
  4. local speaker = peripheral.find("speaker")
  5. if not speaker then print("Warning: No speaker found. Sound effects will be disabled.") end
  6.  
  7. modem.open(1) -- Use channel 1 for communication with the server
  8.  
  9. --------------------------------------------------------------------------------
  10. -- SERVER COMMUNICATION & AUTHENTICATION (from Slot Machine program)
  11. --------------------------------------------------------------------------------
  12.  
  13. -- Simple encryption and decryption functions
  14. local function encrypt(text)
  15. local result = ""
  16. for i = 1, #text do
  17. result = result .. string.char((string.byte(text, i) + 5) % 256)
  18. end
  19. return result
  20. end
  21.  
  22. local function decrypt(text)
  23. local result = ""
  24. for i = 1, #text do
  25. result = result .. string.char((string.byte(text, i) - 5) % 256)
  26. end
  27. return result
  28. end
  29.  
  30. -- Function to send requests to the server and wait for a response
  31. local function sendRequest(request)
  32. modem.transmit(1, 1, request)
  33. local timer = os.startTimer(5) -- 5-second timeout
  34. while true do
  35. local event, param1, _, _, message = os.pullEvent()
  36. if event == "modem_message" and type(message) == "table" then
  37. return message
  38. elseif event == "timer" and param1 == timer then
  39. print("Server communication timed out.")
  40. return nil
  41. end
  42. end
  43. end
  44.  
  45. -- Function to save admin credentials to a hidden file
  46. local function saveAdminCredentials(id, password)
  47. local file = fs.open(".admin_cred", "w")
  48. file.write(encrypt(id .. ":" .. password))
  49. file.close()
  50. end
  51.  
  52. -- Function to load admin credentials from the hidden file
  53. local function loadAdminCredentials()
  54. if fs.exists(".admin_cred") then
  55. local file, err = fs.open(".admin_cred", "r")
  56. if not file then print("Error reading credentials: "..tostring(err)); return nil, nil end
  57. local content = decrypt(file.readAll())
  58. file.close()
  59. local id, password = content:match("(.+):(.+)")
  60. return id, password
  61. end
  62. return nil, nil
  63. end
  64.  
  65. -- Admin login on first boot
  66. local function adminLogin()
  67. term.clear()
  68. term.setCursorPos(1, 1)
  69. print("One-Time Admin Setup")
  70. print("This account will hold the house funds.")
  71. write("Enter Admin ID: ")
  72. local adminId = read()
  73. write("Enter Admin Password: ")
  74. local adminPassword = read("*")
  75. print()
  76.  
  77. local response = sendRequest({ type = "login", studentId = adminId, password = adminPassword })
  78. if response and response.success then
  79. saveAdminCredentials(adminId, adminPassword)
  80. print("Admin credentials verified and saved.")
  81. sleep(2)
  82. return adminId, adminPassword
  83. else
  84. print("Admin login failed. The server may be down or credentials incorrect.")
  85. sleep(3)
  86. error("Admin login failed. Please restart the program.")
  87. end
  88. end
  89.  
  90. -- Load or set up admin credentials
  91. local adminId, adminPassword = loadAdminCredentials()
  92. if not adminId or not adminPassword then
  93. adminId, adminPassword = adminLogin()
  94. end
  95.  
  96. -- Player login screen
  97. local function login()
  98. term.clear()
  99. term.setCursorPos(1, 1)
  100. print("Blackjack - Player Login")
  101. write("Enter your ID: ")
  102. local studentId = read()
  103. write("Enter your password: ")
  104. local password = read("*")
  105. print()
  106.  
  107. local response = sendRequest({ type = "login", studentId = studentId, password = password })
  108. if response and response.success then
  109. print("Login successful!")
  110. sleep(1)
  111. return studentId, password
  112. else
  113. print("Login failed. Please check your credentials and try again.")
  114. sleep(2)
  115. return nil, nil
  116. end
  117. end
  118.  
  119. -- Function to get a user's balance from the server
  120. local function getBalance(studentId, password)
  121. local response = sendRequest({ type = "get_balance", studentId = studentId, password = password })
  122. if response and response.type == "balance_response" then
  123. return tonumber(response.balance)
  124. else
  125. print("Failed to retrieve balance.")
  126. return nil
  127. end
  128. end
  129.  
  130. -- Function to update a user's balance on the server
  131. local function updateBalance(studentId, password, action, amount)
  132. local response = sendRequest({
  133. type = "update_balance",
  134. studentId = studentId,
  135. password = password,
  136. action = action,
  137. amount = amount
  138. })
  139. if response and response.type == "balance_update_response" and response.success then
  140. return tonumber(response.result)
  141. else
  142. -- This is a critical failure, could indicate server issues or insufficient funds
  143. return nil
  144. end
  145. end
  146.  
  147.  
  148. --------------------------------------------------------------------------------
  149. -- BLACKJACK GAME LOGIC
  150. --------------------------------------------------------------------------------
  151. local deck = {}
  152.  
  153. -- Card suits and ranks definitions
  154. local suits = { "♥", "♦", "♣", "♠" } -- Suits are kept for logic, but not displayed
  155. local ranks = {
  156. { rank = "2", value = 2 }, { rank = "3", value = 3 }, { rank = "4", value = 4 },
  157. { rank = "5", value = 5 }, { rank = "6", value = 6 }, { rank = "7", value = 7 },
  158. { rank = "8", value = 8 }, { rank = "9", value = 9 }, { rank = "10", value = 10 },
  159. { rank = "J", value = 10 }, { rank = "Q", value = 10 }, { rank = "K", value = 10 },
  160. { rank = "A", value = 11 } -- Ace is initially 11
  161. }
  162.  
  163. -- Creates a standard 52-card deck
  164. local function createDeck()
  165. local newDeck = {}
  166. for _, suit in ipairs(suits) do
  167. for _, rankInfo in ipairs(ranks) do
  168. table.insert(newDeck, { rank = rankInfo.rank, suit = suit, value = rankInfo.value })
  169. end
  170. end
  171. return newDeck
  172. end
  173.  
  174. -- Shuffles the deck using Fisher-Yates algorithm
  175. local function shuffleDeck(deckToShuffle)
  176. for i = #deckToShuffle, 2, -1 do
  177. local j = math.random(i)
  178. deckToShuffle[i], deckToShuffle[j] = deckToShuffle[j], deckToShuffle[i]
  179. end
  180. return deckToShuffle
  181. end
  182.  
  183. -- Deals one card from the top of the deck
  184. local function dealCard()
  185. -- If deck is getting low (e.g., less than 15 cards), create and shuffle a new one
  186. if #deck < 15 then
  187. print("Reshuffling the deck...")
  188. if speaker then speaker.playSound("minecraft:item.book.page_turn", 1, 0.8) end
  189. sleep(1.5)
  190. deck = createDeck()
  191. deck = shuffleDeck(deck)
  192. end
  193. return table.remove(deck, 1)
  194. end
  195.  
  196. -- Calculates the total value of a hand, handling Aces correctly
  197. local function getHandValue(hand)
  198. local value = 0
  199. local numAces = 0
  200. for _, card in ipairs(hand) do
  201. value = value + card.value
  202. if card.rank == "A" then
  203. numAces = numAces + 1
  204. end
  205. end
  206. -- If the hand is a bust and contains Aces, change Ace value from 11 to 1
  207. while value > 21 and numAces > 0 do
  208. value = value - 10
  209. numAces = numAces - 1
  210. end
  211. return value
  212. end
  213.  
  214. -- Plays a sound based on the game event
  215. local function playSound(event)
  216. if not speaker then return end
  217. if event == "win" then
  218. speaker.playSound("minecraft:entity.player.levelup", 1, 1.2)
  219. elseif event == "lose" then
  220. speaker.playSound("minecraft:entity.villager.no", 1, 1)
  221. elseif event == "push" then
  222. speaker.playSound("minecraft:block.note_block.harp", 1, 0.8)
  223. elseif event == "blackjack" then
  224. speaker.playSound("minecraft:ui.toast.challenge_complete", 1, 1)
  225. elseif event == "bust" then
  226. speaker.playSound("minecraft:entity.goat.scream", 0.5, 0.7)
  227. elseif event == "hit" then
  228. speaker.playSound("minecraft:block.wood.place", 1, 1.5)
  229. end
  230. end
  231.  
  232. --------------------------------------------------------------------------------
  233. -- UI DRAWING AND GAME FLOW
  234. --------------------------------------------------------------------------------
  235.  
  236. -- Draws a simplified card with just an outline and the rank
  237. local function drawCard(card, x, y)
  238. term.setCursorPos(x, y)
  239. term.write("┌──┐")
  240. term.setCursorPos(x, y + 1)
  241. -- Add padding to center single-character ranks
  242. term.write("│"..(card.rank == "10" and "10" or " "..card.rank).."│")
  243. term.setCursorPos(x, y + 2)
  244. term.write("└──┘")
  245. end
  246.  
  247. -- Draws the back of a card, matching the new simplified dimensions
  248. local function drawHiddenCard(x, y)
  249. term.setCursorPos(x, y)
  250. term.setBackgroundColor(colors.gray)
  251. term.write(" ")
  252. term.setCursorPos(x, y + 1)
  253. term.write(" ")
  254. term.setCursorPos(x, y + 2)
  255. term.write(" ")
  256. term.setBackgroundColor(colors.black)
  257. end
  258.  
  259. -- Main function to draw the entire game table
  260. local function drawTable(playerHand, dealerHand, balance, currentBet, message, hideDealerCard)
  261. local w, h = term.getSize()
  262. term.clear()
  263. term.setCursorPos(1, 1)
  264. term.write("Blackjack | Balance: " .. balance .. " | Bet: " .. currentBet)
  265. term.setCursorPos(1, 2)
  266. term.write(string.rep("=", w))
  267.  
  268. -- Draw Dealer's Hand
  269. local dealerValue = hideDealerCard and dealerHand[1].value or getHandValue(dealerHand)
  270. term.setCursorPos(3, 4)
  271. term.write("Dealer's Hand (" .. (hideDealerCard and dealerValue.." + ?" or dealerValue) .. "):")
  272. for i, card in ipairs(dealerHand) do
  273. if hideDealerCard and i == 2 then
  274. drawHiddenCard(3 + (i - 1) * 6, 5)
  275. else
  276. drawCard(card, 3 + (i - 1) * 6, 5)
  277. end
  278. end
  279.  
  280. -- Draw Player's Hand
  281. term.setCursorPos(3, 9)
  282. term.write("Your Hand (" .. getHandValue(playerHand) .. "):")
  283. for i, card in ipairs(playerHand) do
  284. drawCard(card, 3 + (i - 1) * 6, 10)
  285. end
  286.  
  287. -- Display message and options
  288. term.setCursorPos(1, h - 2)
  289. term.write(string.rep("=", w))
  290. term.setCursorPos(2, h - 1)
  291. term.write(message)
  292. end
  293.  
  294. -- The main game loop for a single player
  295. local function playBlackjack(studentId, password)
  296. deck = createDeck()
  297. deck = shuffleDeck(deck)
  298.  
  299. while true do
  300. local balance = getBalance(studentId, password)
  301. if balance == nil then
  302. print("Could not retrieve balance. Returning to main menu.")
  303. sleep(3)
  304. return
  305. end
  306. if balance == 0 then
  307. term.clear()
  308. term.setCursorPos(1,1)
  309. print("Your balance is 0. You cannot play.")
  310. print("Please add funds to your account.")
  311. print("\nPress any key to return to the main menu.")
  312. os.pullEvent("key")
  313. return
  314. end
  315.  
  316. -- 1. Betting Phase
  317. local bet = 0
  318. while true do
  319. term.clear()
  320. term.setCursorPos(1, 1)
  321. print("Balance: " .. balance .. " diamonds")
  322. print("Place your bet (or type 'quit' to exit):")
  323. local input = read()
  324. if input:lower() == 'quit' then return end
  325.  
  326. local num_bet = tonumber(input)
  327. if num_bet and num_bet > 0 and num_bet <= balance then
  328. bet = num_bet
  329. break
  330. else
  331. print("Invalid bet. Please enter a number between 1 and " .. balance .. ".")
  332. sleep(2)
  333. end
  334. end
  335.  
  336. -- Attempt to place the bet
  337. local playerBalance = updateBalance(studentId, password, "withdraw", bet)
  338. if not playerBalance then
  339. print("Error processing bet. Transaction failed. Try again.")
  340. sleep(3)
  341. goto continue_game -- Skip to next iteration of outer while loop
  342. end
  343. local adminBalanceUpdate = updateBalance(adminId, adminPassword, "deposit", bet)
  344. if not adminBalanceUpdate then
  345. print("Error processing bet. House account error.")
  346. print("Refunding your bet...")
  347. updateBalance(studentId, password, "deposit", bet) -- Refund player
  348. sleep(3)
  349. goto continue_game
  350. end
  351.  
  352. -- NEW: Verify admin can cover potential payout
  353. local currentAdminBalance = getBalance(adminId, adminPassword)
  354. local maxWinnings = math.floor(bet * 1.5) -- Winnings for a blackjack
  355. local maxTotalPayout = bet + maxWinnings -- Original bet returned + winnings
  356.  
  357. if currentAdminBalance < maxTotalPayout then
  358. term.clear()
  359. term.setCursorPos(1,1)
  360. print("The house cannot cover the potential payout for this bet.")
  361. print("Your bet of " .. bet .. " has been refunded.")
  362. print("Please place a smaller bet.")
  363. -- Refund the player and deduct from admin
  364. updateBalance(studentId, password, "deposit", bet)
  365. updateBalance(adminId, adminPassword, "withdraw", bet)
  366. sleep(5)
  367. goto continue_game
  368. end
  369.  
  370. -- 2. Dealing Phase
  371. local playerHand = { dealCard(), dealCard() }
  372. local dealerHand = { dealCard(), dealCard() }
  373.  
  374. -- 3. Check for initial Blackjacks
  375. local playerValue = getHandValue(playerHand)
  376.  
  377. if playerValue == 21 then -- Player has Blackjack
  378. drawTable(playerHand, dealerHand, playerBalance, bet, "", false)
  379. playSound("blackjack")
  380. local payout = math.floor(bet * 1.5) -- Standard 3:2 payout for Blackjack
  381. term.setCursorPos(2, term.getSize()-1)
  382. print("Blackjack! You win " .. payout .. " extra diamonds!")
  383.  
  384. -- Payout from admin to player (original bet + winnings)
  385. updateBalance(adminId, adminPassword, "withdraw", bet + payout)
  386. updateBalance(studentId, password, "deposit", bet + payout)
  387.  
  388. sleep(4)
  389. goto continue_game
  390. end
  391.  
  392. -- 4. Player's Turn
  393. local playerBusted = false
  394. while true do
  395. drawTable(playerHand, dealerHand, playerBalance, bet, "H: Hit | S: Stand", true)
  396. local event, key = os.pullEvent("key")
  397. if key == keys.h then
  398. playSound("hit")
  399. table.insert(playerHand, dealCard())
  400. playerValue = getHandValue(playerHand)
  401. if playerValue > 21 then
  402. playerBusted = true
  403. break
  404. end
  405. elseif key == keys.s then
  406. break
  407. end
  408. end
  409.  
  410. drawTable(playerHand, dealerHand, playerBalance, bet, "Dealer's turn...", false)
  411. sleep(1)
  412.  
  413. -- 5. Outcome Phase
  414. if playerBusted then
  415. playSound("bust")
  416. drawTable(playerHand, dealerHand, playerBalance, bet, "You busted! You lose " .. bet .. " diamonds.", false)
  417. -- Money was already transferred to admin, so no action needed.
  418. sleep(4)
  419. goto continue_game
  420. end
  421.  
  422. -- 6. Dealer's Turn
  423. while getHandValue(dealerHand) < 17 do
  424. sleep(1.5)
  425. table.insert(dealerHand, dealCard())
  426. drawTable(playerHand, dealerHand, playerBalance, bet, "Dealer hits...", false)
  427. end
  428.  
  429. local finalPlayerValue = getHandValue(playerHand)
  430. local finalDealerValue = getHandValue(dealerHand)
  431. local resultMessage = ""
  432.  
  433. sleep(1)
  434.  
  435. if finalDealerValue > 21 then
  436. playSound("win")
  437. resultMessage = "Dealer busted! You win " .. bet .. " diamonds."
  438. -- Payout: Refund bet + winnings
  439. updateBalance(adminId, adminPassword, "withdraw", bet * 2)
  440. updateBalance(studentId, password, "deposit", bet * 2)
  441. elseif finalPlayerValue > finalDealerValue then
  442. playSound("win")
  443. resultMessage = "You win " .. bet .. " diamonds!"
  444. -- Payout: Refund bet + winnings
  445. updateBalance(adminId, adminPassword, "withdraw", bet * 2)
  446. updateBalance(studentId, password, "deposit", bet * 2)
  447. elseif finalPlayerValue < finalDealerValue then
  448. playSound("lose")
  449. resultMessage = "Dealer wins. You lose " .. bet .. " diamonds."
  450. -- No balance change needed
  451. else -- Push
  452. playSound("push")
  453. resultMessage = "Push! It's a tie. Your bet is returned."
  454. -- Refund bet
  455. updateBalance(adminId, adminPassword, "withdraw", bet)
  456. updateBalance(studentId, password, "deposit", bet)
  457. end
  458.  
  459. drawTable(playerHand, dealerHand, getBalance(studentId, password), bet, resultMessage, false)
  460. print("\nPress any key to play another round.")
  461. os.pullEvent("key")
  462.  
  463. ::continue_game::
  464. end
  465. end
  466.  
  467. --------------------------------------------------------------------------------
  468. -- MAIN PROGRAM LOOP
  469. --------------------------------------------------------------------------------
  470. while true do
  471. term.clear()
  472. term.setCursorPos(1, 1)
  473. paintutils.drawFilledBox(1, 1, term.getSize(), 1, colors.green)
  474. term.setTextColor(colors.white)
  475. term.setCursorPos(3, 1)
  476. term.write("Welcome to MtnLabs Blackjack")
  477. term.setTextColor(colors.white)
  478. term.setBackgroundColor(colors.black)
  479.  
  480. term.setCursorPos(3, 4)
  481. print("1. Play Blackjack")
  482. term.setCursorPos(3, 5)
  483. print("2. Exit")
  484. term.setCursorPos(3, 7)
  485. write("Choose an option: ")
  486.  
  487. local choice = read()
  488. if choice == "1" then
  489. local studentId, password = login()
  490. if studentId then
  491. playBlackjack(studentId, password)
  492. end
  493. elseif choice == "2" then
  494. print("Thank you for playing!")
  495. break
  496. else
  497. print("Invalid option.")
  498. sleep(1)
  499. end
  500. end
  501.  
Add Comment
Please, Sign In to add comment