Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local modem = peripheral.find("modem")
- if not modem then error("No modem found. Please attach a wireless modem.") end
- local speaker = peripheral.find("speaker")
- if not speaker then print("Warning: No speaker found. Sound effects will be disabled.") end
- modem.open(1) -- Use channel 1 for communication with the server
- --------------------------------------------------------------------------------
- -- SERVER COMMUNICATION & AUTHENTICATION (from Slot Machine program)
- --------------------------------------------------------------------------------
- -- Simple encryption and decryption functions
- local function encrypt(text)
- local result = ""
- for i = 1, #text do
- result = result .. string.char((string.byte(text, i) + 5) % 256)
- end
- return result
- end
- local function decrypt(text)
- local result = ""
- for i = 1, #text do
- result = result .. string.char((string.byte(text, i) - 5) % 256)
- end
- return result
- end
- -- Function to send requests to the server and wait for a response
- local function sendRequest(request)
- modem.transmit(1, 1, request)
- local timer = os.startTimer(5) -- 5-second timeout
- while true do
- local event, param1, _, _, message = os.pullEvent()
- if event == "modem_message" and type(message) == "table" then
- return message
- elseif event == "timer" and param1 == timer then
- print("Server communication timed out.")
- return nil
- end
- end
- end
- -- Function to save admin credentials to a hidden file
- local function saveAdminCredentials(id, password)
- local file = fs.open(".admin_cred", "w")
- file.write(encrypt(id .. ":" .. password))
- file.close()
- end
- -- Function to load admin credentials from the hidden file
- local function loadAdminCredentials()
- if fs.exists(".admin_cred") then
- local file, err = fs.open(".admin_cred", "r")
- if not file then print("Error reading credentials: "..tostring(err)); return nil, nil end
- local content = decrypt(file.readAll())
- file.close()
- local id, password = content:match("(.+):(.+)")
- return id, password
- end
- return nil, nil
- end
- -- Admin login on first boot
- local function adminLogin()
- term.clear()
- term.setCursorPos(1, 1)
- print("One-Time Admin Setup")
- print("This account will hold the house funds.")
- write("Enter Admin ID: ")
- local adminId = read()
- write("Enter Admin Password: ")
- local adminPassword = read("*")
- print()
- local response = sendRequest({ type = "login", studentId = adminId, password = adminPassword })
- if response and response.success then
- saveAdminCredentials(adminId, adminPassword)
- print("Admin credentials verified and saved.")
- sleep(2)
- return adminId, adminPassword
- else
- print("Admin login failed. The server may be down or credentials incorrect.")
- sleep(3)
- error("Admin login failed. Please restart the program.")
- end
- end
- -- Load or set up admin credentials
- local adminId, adminPassword = loadAdminCredentials()
- if not adminId or not adminPassword then
- adminId, adminPassword = adminLogin()
- end
- -- Player login screen
- local function login()
- term.clear()
- term.setCursorPos(1, 1)
- print("Blackjack - Player Login")
- write("Enter your ID: ")
- local studentId = read()
- write("Enter your password: ")
- local password = read("*")
- print()
- local response = sendRequest({ type = "login", studentId = studentId, password = password })
- if response and response.success then
- print("Login successful!")
- sleep(1)
- return studentId, password
- else
- print("Login failed. Please check your credentials and try again.")
- sleep(2)
- return nil, nil
- end
- end
- -- Function to get a user's balance from the server
- local function getBalance(studentId, password)
- local response = sendRequest({ type = "get_balance", studentId = studentId, password = password })
- if response and response.type == "balance_response" then
- return tonumber(response.balance)
- else
- print("Failed to retrieve balance.")
- return nil
- end
- end
- -- Function to update a user's balance on the server
- local function updateBalance(studentId, password, action, amount)
- local response = sendRequest({
- type = "update_balance",
- studentId = studentId,
- password = password,
- action = action,
- amount = amount
- })
- if response and response.type == "balance_update_response" and response.success then
- return tonumber(response.result)
- else
- -- This is a critical failure, could indicate server issues or insufficient funds
- return nil
- end
- end
- --------------------------------------------------------------------------------
- -- BLACKJACK GAME LOGIC
- --------------------------------------------------------------------------------
- local deck = {}
- -- Card suits and ranks definitions
- local suits = { "♥", "♦", "♣", "♠" } -- Suits are kept for logic, but not displayed
- local ranks = {
- { rank = "2", value = 2 }, { rank = "3", value = 3 }, { rank = "4", value = 4 },
- { rank = "5", value = 5 }, { rank = "6", value = 6 }, { rank = "7", value = 7 },
- { rank = "8", value = 8 }, { rank = "9", value = 9 }, { rank = "10", value = 10 },
- { rank = "J", value = 10 }, { rank = "Q", value = 10 }, { rank = "K", value = 10 },
- { rank = "A", value = 11 } -- Ace is initially 11
- }
- -- Creates a standard 52-card deck
- local function createDeck()
- local newDeck = {}
- for _, suit in ipairs(suits) do
- for _, rankInfo in ipairs(ranks) do
- table.insert(newDeck, { rank = rankInfo.rank, suit = suit, value = rankInfo.value })
- end
- end
- return newDeck
- end
- -- Shuffles the deck using Fisher-Yates algorithm
- local function shuffleDeck(deckToShuffle)
- for i = #deckToShuffle, 2, -1 do
- local j = math.random(i)
- deckToShuffle[i], deckToShuffle[j] = deckToShuffle[j], deckToShuffle[i]
- end
- return deckToShuffle
- end
- -- Deals one card from the top of the deck
- local function dealCard()
- -- If deck is getting low (e.g., less than 15 cards), create and shuffle a new one
- if #deck < 15 then
- print("Reshuffling the deck...")
- if speaker then speaker.playSound("minecraft:item.book.page_turn", 1, 0.8) end
- sleep(1.5)
- deck = createDeck()
- deck = shuffleDeck(deck)
- end
- return table.remove(deck, 1)
- end
- -- Calculates the total value of a hand, handling Aces correctly
- local function getHandValue(hand)
- local value = 0
- local numAces = 0
- for _, card in ipairs(hand) do
- value = value + card.value
- if card.rank == "A" then
- numAces = numAces + 1
- end
- end
- -- If the hand is a bust and contains Aces, change Ace value from 11 to 1
- while value > 21 and numAces > 0 do
- value = value - 10
- numAces = numAces - 1
- end
- return value
- end
- -- Plays a sound based on the game event
- local function playSound(event)
- if not speaker then return end
- if event == "win" then
- speaker.playSound("minecraft:entity.player.levelup", 1, 1.2)
- elseif event == "lose" then
- speaker.playSound("minecraft:entity.villager.no", 1, 1)
- elseif event == "push" then
- speaker.playSound("minecraft:block.note_block.harp", 1, 0.8)
- elseif event == "blackjack" then
- speaker.playSound("minecraft:ui.toast.challenge_complete", 1, 1)
- elseif event == "bust" then
- speaker.playSound("minecraft:entity.goat.scream", 0.5, 0.7)
- elseif event == "hit" then
- speaker.playSound("minecraft:block.wood.place", 1, 1.5)
- end
- end
- --------------------------------------------------------------------------------
- -- UI DRAWING AND GAME FLOW
- --------------------------------------------------------------------------------
- -- Draws a simplified card with just an outline and the rank
- local function drawCard(card, x, y)
- term.setCursorPos(x, y)
- term.write("┌──┐")
- term.setCursorPos(x, y + 1)
- -- Add padding to center single-character ranks
- term.write("│"..(card.rank == "10" and "10" or " "..card.rank).."│")
- term.setCursorPos(x, y + 2)
- term.write("└──┘")
- end
- -- Draws the back of a card, matching the new simplified dimensions
- local function drawHiddenCard(x, y)
- term.setCursorPos(x, y)
- term.setBackgroundColor(colors.gray)
- term.write(" ")
- term.setCursorPos(x, y + 1)
- term.write(" ")
- term.setCursorPos(x, y + 2)
- term.write(" ")
- term.setBackgroundColor(colors.black)
- end
- -- Main function to draw the entire game table
- local function drawTable(playerHand, dealerHand, balance, currentBet, message, hideDealerCard)
- local w, h = term.getSize()
- term.clear()
- term.setCursorPos(1, 1)
- term.write("Blackjack | Balance: " .. balance .. " | Bet: " .. currentBet)
- term.setCursorPos(1, 2)
- term.write(string.rep("=", w))
- -- Draw Dealer's Hand
- local dealerValue = hideDealerCard and dealerHand[1].value or getHandValue(dealerHand)
- term.setCursorPos(3, 4)
- term.write("Dealer's Hand (" .. (hideDealerCard and dealerValue.." + ?" or dealerValue) .. "):")
- for i, card in ipairs(dealerHand) do
- if hideDealerCard and i == 2 then
- drawHiddenCard(3 + (i - 1) * 6, 5)
- else
- drawCard(card, 3 + (i - 1) * 6, 5)
- end
- end
- -- Draw Player's Hand
- term.setCursorPos(3, 9)
- term.write("Your Hand (" .. getHandValue(playerHand) .. "):")
- for i, card in ipairs(playerHand) do
- drawCard(card, 3 + (i - 1) * 6, 10)
- end
- -- Display message and options
- term.setCursorPos(1, h - 2)
- term.write(string.rep("=", w))
- term.setCursorPos(2, h - 1)
- term.write(message)
- end
- -- The main game loop for a single player
- local function playBlackjack(studentId, password)
- deck = createDeck()
- deck = shuffleDeck(deck)
- while true do
- local balance = getBalance(studentId, password)
- if balance == nil then
- print("Could not retrieve balance. Returning to main menu.")
- sleep(3)
- return
- end
- if balance == 0 then
- term.clear()
- term.setCursorPos(1,1)
- print("Your balance is 0. You cannot play.")
- print("Please add funds to your account.")
- print("\nPress any key to return to the main menu.")
- os.pullEvent("key")
- return
- end
- -- 1. Betting Phase
- local bet = 0
- while true do
- term.clear()
- term.setCursorPos(1, 1)
- print("Balance: " .. balance .. " diamonds")
- print("Place your bet (or type 'quit' to exit):")
- local input = read()
- if input:lower() == 'quit' then return end
- local num_bet = tonumber(input)
- if num_bet and num_bet > 0 and num_bet <= balance then
- bet = num_bet
- break
- else
- print("Invalid bet. Please enter a number between 1 and " .. balance .. ".")
- sleep(2)
- end
- end
- -- Attempt to place the bet
- local playerBalance = updateBalance(studentId, password, "withdraw", bet)
- if not playerBalance then
- print("Error processing bet. Transaction failed. Try again.")
- sleep(3)
- goto continue_game -- Skip to next iteration of outer while loop
- end
- local adminBalanceUpdate = updateBalance(adminId, adminPassword, "deposit", bet)
- if not adminBalanceUpdate then
- print("Error processing bet. House account error.")
- print("Refunding your bet...")
- updateBalance(studentId, password, "deposit", bet) -- Refund player
- sleep(3)
- goto continue_game
- end
- -- NEW: Verify admin can cover potential payout
- local currentAdminBalance = getBalance(adminId, adminPassword)
- local maxWinnings = math.floor(bet * 1.5) -- Winnings for a blackjack
- local maxTotalPayout = bet + maxWinnings -- Original bet returned + winnings
- if currentAdminBalance < maxTotalPayout then
- term.clear()
- term.setCursorPos(1,1)
- print("The house cannot cover the potential payout for this bet.")
- print("Your bet of " .. bet .. " has been refunded.")
- print("Please place a smaller bet.")
- -- Refund the player and deduct from admin
- updateBalance(studentId, password, "deposit", bet)
- updateBalance(adminId, adminPassword, "withdraw", bet)
- sleep(5)
- goto continue_game
- end
- -- 2. Dealing Phase
- local playerHand = { dealCard(), dealCard() }
- local dealerHand = { dealCard(), dealCard() }
- -- 3. Check for initial Blackjacks
- local playerValue = getHandValue(playerHand)
- if playerValue == 21 then -- Player has Blackjack
- drawTable(playerHand, dealerHand, playerBalance, bet, "", false)
- playSound("blackjack")
- local payout = math.floor(bet * 1.5) -- Standard 3:2 payout for Blackjack
- term.setCursorPos(2, term.getSize()-1)
- print("Blackjack! You win " .. payout .. " extra diamonds!")
- -- Payout from admin to player (original bet + winnings)
- updateBalance(adminId, adminPassword, "withdraw", bet + payout)
- updateBalance(studentId, password, "deposit", bet + payout)
- sleep(4)
- goto continue_game
- end
- -- 4. Player's Turn
- local playerBusted = false
- while true do
- drawTable(playerHand, dealerHand, playerBalance, bet, "H: Hit | S: Stand", true)
- local event, key = os.pullEvent("key")
- if key == keys.h then
- playSound("hit")
- table.insert(playerHand, dealCard())
- playerValue = getHandValue(playerHand)
- if playerValue > 21 then
- playerBusted = true
- break
- end
- elseif key == keys.s then
- break
- end
- end
- drawTable(playerHand, dealerHand, playerBalance, bet, "Dealer's turn...", false)
- sleep(1)
- -- 5. Outcome Phase
- if playerBusted then
- playSound("bust")
- drawTable(playerHand, dealerHand, playerBalance, bet, "You busted! You lose " .. bet .. " diamonds.", false)
- -- Money was already transferred to admin, so no action needed.
- sleep(4)
- goto continue_game
- end
- -- 6. Dealer's Turn
- while getHandValue(dealerHand) < 17 do
- sleep(1.5)
- table.insert(dealerHand, dealCard())
- drawTable(playerHand, dealerHand, playerBalance, bet, "Dealer hits...", false)
- end
- local finalPlayerValue = getHandValue(playerHand)
- local finalDealerValue = getHandValue(dealerHand)
- local resultMessage = ""
- sleep(1)
- if finalDealerValue > 21 then
- playSound("win")
- resultMessage = "Dealer busted! You win " .. bet .. " diamonds."
- -- Payout: Refund bet + winnings
- updateBalance(adminId, adminPassword, "withdraw", bet * 2)
- updateBalance(studentId, password, "deposit", bet * 2)
- elseif finalPlayerValue > finalDealerValue then
- playSound("win")
- resultMessage = "You win " .. bet .. " diamonds!"
- -- Payout: Refund bet + winnings
- updateBalance(adminId, adminPassword, "withdraw", bet * 2)
- updateBalance(studentId, password, "deposit", bet * 2)
- elseif finalPlayerValue < finalDealerValue then
- playSound("lose")
- resultMessage = "Dealer wins. You lose " .. bet .. " diamonds."
- -- No balance change needed
- else -- Push
- playSound("push")
- resultMessage = "Push! It's a tie. Your bet is returned."
- -- Refund bet
- updateBalance(adminId, adminPassword, "withdraw", bet)
- updateBalance(studentId, password, "deposit", bet)
- end
- drawTable(playerHand, dealerHand, getBalance(studentId, password), bet, resultMessage, false)
- print("\nPress any key to play another round.")
- os.pullEvent("key")
- ::continue_game::
- end
- end
- --------------------------------------------------------------------------------
- -- MAIN PROGRAM LOOP
- --------------------------------------------------------------------------------
- while true do
- term.clear()
- term.setCursorPos(1, 1)
- paintutils.drawFilledBox(1, 1, term.getSize(), 1, colors.green)
- term.setTextColor(colors.white)
- term.setCursorPos(3, 1)
- term.write("Welcome to MtnLabs Blackjack")
- term.setTextColor(colors.white)
- term.setBackgroundColor(colors.black)
- term.setCursorPos(3, 4)
- print("1. Play Blackjack")
- term.setCursorPos(3, 5)
- print("2. Exit")
- term.setCursorPos(3, 7)
- write("Choose an option: ")
- local choice = read()
- if choice == "1" then
- local studentId, password = login()
- if studentId then
- playBlackjack(studentId, password)
- end
- elseif choice == "2" then
- print("Thank you for playing!")
- break
- else
- print("Invalid option.")
- sleep(1)
- end
- end
Add Comment
Please, Sign In to add comment