Advertisement
alexnolan

whackamole

Mar 26th, 2025 (edited)
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.07 KB | None | 0 0
  1. -- Whack-a-Mole for CC: Tweaked
  2. -- For a 4x3 monitor grid
  3.  
  4. -- Find the monitor
  5. local monitor = peripheral.find("monitor")
  6. if not monitor then
  7.   print("No monitor found! Please attach a monitor.")
  8.   return
  9. end
  10.  
  11. -- Set up the display
  12. monitor.setTextScale(0.5)
  13. local width, height = monitor.getSize()
  14. print("Monitor size: " .. width .. "x" .. height)
  15.  
  16. -- Game constants
  17. local GRID_WIDTH = 4  -- 4 mole holes across
  18. local GRID_HEIGHT = 3 -- 3 mole holes down
  19. local MAX_ACTIVE_MOLES = 3
  20. local GAME_DURATION = 30 -- seconds
  21.  
  22. -- Calculate cell dimensions
  23. local cellWidth = math.floor(width / GRID_WIDTH)
  24. local cellHeight = math.floor(height / GRID_HEIGHT)
  25.  
  26. -- Create the grid of mole holes
  27. local grid = {}
  28. for x = 1, GRID_WIDTH do
  29.   grid[x] = {}
  30.   for y = 1, GRID_HEIGHT do
  31.     grid[x][y] = {
  32.       x = (x-1) * cellWidth + 1,
  33.       y = (y-1) * cellHeight + 1,
  34.       width = cellWidth,
  35.       height = cellHeight,
  36.       active = false,
  37.       timeActive = 0
  38.     }
  39.   end
  40. end
  41.  
  42. -- Helper function to draw a button
  43. local function drawButton(x, y, w, h, text, bgColor, textColor)
  44.   monitor.setBackgroundColor(bgColor)
  45.   for i = 0, h - 1 do
  46.     monitor.setCursorPos(x, y + i)
  47.     monitor.write(string.rep(" ", w))
  48.   end
  49.  
  50.   monitor.setTextColor(textColor)
  51.   local textX = x + math.floor((w - #text) / 2)
  52.   local textY = y + math.floor(h / 2)
  53.   monitor.setCursorPos(textX, textY)
  54.   monitor.write(text)
  55. end
  56.  
  57. -- Helper function to check if a point is inside a rectangle
  58. local function isInside(px, py, x, y, w, h)
  59.   return px >= x and px < x + w and py >= y and py < y + h
  60. end
  61.  
  62. -- Draw welcome screen
  63. local function drawWelcomeScreen()
  64.   monitor.setBackgroundColor(colors.black)
  65.   monitor.clear()
  66.  
  67.   -- Title
  68.   monitor.setCursorPos(math.floor(width/2) - 6, math.floor(height/3))
  69.   monitor.setTextColor(colors.yellow)
  70.   monitor.write("WHACK-A-MOLE")
  71.  
  72.   -- Instructions
  73.   monitor.setCursorPos(math.floor(width/2) - 10, math.floor(height/3) + 2)
  74.   monitor.setTextColor(colors.white)
  75.   monitor.write("Tap the moles as they appear!")
  76.  
  77.   -- Start button
  78.   local buttonWidth = 12
  79.   local buttonHeight = 3
  80.   local buttonX = math.floor(width/2) - math.floor(buttonWidth/2)
  81.   local buttonY = math.floor(height/3) + 5
  82.  
  83.   drawButton(buttonX, buttonY, buttonWidth, buttonHeight, "Start Game", colors.green, colors.white)
  84.  
  85.   return {
  86.     startButton = {x = buttonX, y = buttonY, width = buttonWidth, height = buttonHeight}
  87.   }
  88. end
  89.  
  90. -- Main game function
  91. local function playGame(continuePrevious, prevScore, prevDifficulty)
  92.   -- Game state - reset based on whether continuing or new game
  93.   local gameState = {
  94.     score = continuePrevious and prevScore or 0,
  95.     timeLeft = GAME_DURATION,
  96.     activeMoles = {},
  97.     difficulty = continuePrevious and prevDifficulty or 1,
  98.     isGameOver = false,
  99.     startTime = os.epoch("local") / 1000,
  100.     lastSpawnTime = os.epoch("local") / 1000
  101.   }
  102.  
  103.   -- Clear all moles
  104.   for x = 1, GRID_WIDTH do
  105.     for y = 1, GRID_HEIGHT do
  106.       grid[x][y].active = false
  107.       grid[x][y].timeActive = 0
  108.     end
  109.   end
  110.  
  111.   print("GAME STARTED - Difficulty: " .. gameState.difficulty ..
  112.         (continuePrevious and " (Continued)" or " (New Game)"))
  113.  
  114.   -- Draw the game board
  115.   local function drawGame()
  116.     monitor.setBackgroundColor(colors.green)  -- Grass background
  117.     monitor.clear()
  118.    
  119.     -- Draw each mole hole
  120.     for x = 1, GRID_WIDTH do
  121.       for y = 1, GRID_HEIGHT do
  122.         local cell = grid[x][y]
  123.        
  124.         -- Draw the mole hole (brown circle)
  125.         local holeColor = colors.brown
  126.         local centerX = cell.x + math.floor(cell.width/2)
  127.         local centerY = cell.y + math.floor(cell.height/2)
  128.        
  129.         -- Draw a circular hole
  130.         for cy = -math.floor(cell.height/3), math.floor(cell.height/3) do
  131.           for cx = -math.floor(cell.width/3), math.floor(cell.width/3) do
  132.             if cx*cx + cy*cy <= (cell.width/3)*(cell.height/3) then
  133.               monitor.setCursorPos(centerX + cx, centerY + cy)
  134.               monitor.setBackgroundColor(holeColor)
  135.               monitor.write(" ")
  136.             end
  137.           end
  138.         end
  139.        
  140.         -- Draw mole if active
  141.         if cell.active then
  142.           -- Draw mole face (black with white eyes)
  143.           for cy = -math.floor(cell.height/4), math.floor(cell.height/4) do
  144.             for cx = -math.floor(cell.width/4), math.floor(cell.width/4) do
  145.               if cx*cx + cy*cy <= (cell.width/4)*(cell.height/4) then
  146.                 monitor.setCursorPos(centerX + cx, centerY + cy)
  147.                 monitor.setBackgroundColor(colors.black)
  148.                 monitor.write(" ")
  149.               end
  150.             end
  151.           end
  152.          
  153.           -- Draw eyes
  154.           monitor.setCursorPos(centerX - 2, centerY - 1)
  155.           monitor.setBackgroundColor(colors.white)
  156.           monitor.write(" ")
  157.           monitor.setCursorPos(centerX + 2, centerY - 1)
  158.           monitor.setBackgroundColor(colors.white)
  159.           monitor.write(" ")
  160.         end
  161.       end
  162.     end
  163.    
  164.     -- Draw score and time
  165.     monitor.setCursorPos(2, 2)
  166.     monitor.setBackgroundColor(colors.black)
  167.     monitor.setTextColor(colors.white)
  168.     monitor.write("Score: " .. gameState.score)
  169.    
  170.     monitor.setCursorPos(width - 15, 2)
  171.     monitor.setTextColor(colors.white)
  172.     monitor.write("Time: " .. gameState.timeLeft .. "s")
  173.    
  174.     -- Debug info
  175.     monitor.setCursorPos(2, height - 1)
  176.     monitor.setTextColor(colors.white)
  177.     monitor.write("Difficulty: " .. gameState.difficulty)
  178.   end
  179.  
  180.   -- Draw game over screen with two buttons
  181.   local function drawGameOver()
  182.     monitor.setBackgroundColor(colors.black)
  183.     monitor.clear()
  184.    
  185.     monitor.setCursorPos(math.floor(width/2) - 4, math.floor(height/4))
  186.     monitor.setTextColor(colors.yellow)
  187.     monitor.write("GAME OVER!")
  188.    
  189.     monitor.setCursorPos(math.floor(width/2) - 7, math.floor(height/4) + 2)
  190.     monitor.setTextColor(colors.white)
  191.     monitor.write("Final Score: " .. gameState.score)
  192.    
  193.     -- Continue button (with increased difficulty)
  194.     local continueButtonWidth = 16
  195.     local buttonHeight = 3
  196.     local continueButtonX = math.floor(width/2) - math.floor(continueButtonWidth/2)
  197.     local continueButtonY = math.floor(height/2)
  198.    
  199.     drawButton(continueButtonX, continueButtonY, continueButtonWidth, buttonHeight,
  200.               "Continue (Lvl " .. math.min(5, gameState.difficulty + 1) .. ")",
  201.               colors.blue, colors.white)
  202.    
  203.     -- New Game button
  204.     local newGameButtonWidth = 16
  205.     local newGameButtonX = math.floor(width/2) - math.floor(newGameButtonWidth/2)
  206.     local newGameButtonY = continueButtonY + buttonHeight + 2
  207.    
  208.     drawButton(newGameButtonX, newGameButtonY, newGameButtonWidth, buttonHeight,
  209.               "New Game (Lvl 1)",
  210.               colors.green, colors.white)
  211.    
  212.     return {
  213.       continueButton = {
  214.         x = continueButtonX,
  215.         y = continueButtonY,
  216.         width = continueButtonWidth,
  217.         height = buttonHeight
  218.       },
  219.       newGameButton = {
  220.         x = newGameButtonX,
  221.         y = newGameButtonY,
  222.         width = newGameButtonWidth,
  223.         height = buttonHeight
  224.       }
  225.     }
  226.   end
  227.  
  228.   -- Spawn a new mole
  229.   local function spawnMole()
  230.     if #gameState.activeMoles >= MAX_ACTIVE_MOLES then return end
  231.    
  232.     -- Find an empty hole
  233.     local attempts = 0
  234.     while attempts < 20 do
  235.       local x = math.random(1, GRID_WIDTH)
  236.       local y = math.random(1, GRID_HEIGHT)
  237.      
  238.       if not grid[x][y].active then
  239.         grid[x][y].active = true
  240.         grid[x][y].timeActive = 0
  241.         table.insert(gameState.activeMoles, {x=x, y=y})
  242.         gameState.lastSpawnTime = os.epoch("local") / 1000
  243.         break
  244.       end
  245.      
  246.       attempts = attempts + 1
  247.     end
  248.   end
  249.  
  250.   -- Update moles (make them disappear after a while)
  251.   local function updateMoles()
  252.     local i = 1
  253.     while i <= #gameState.activeMoles do
  254.       local mole = gameState.activeMoles[i]
  255.       grid[mole.x][mole.y].timeActive = grid[mole.x][mole.y].timeActive + 1
  256.      
  257.       -- Mole goes back in hole after some time (slower at lower difficulties)
  258.       if grid[mole.x][mole.y].timeActive > 25 - gameState.difficulty*2 then
  259.         grid[mole.x][mole.y].active = false
  260.         table.remove(gameState.activeMoles, i)
  261.       else
  262.         i = i + 1
  263.       end
  264.     end
  265.   end
  266.  
  267.   -- Handle player touch during gameplay
  268.   local function handleGameTouch(x, y)
  269.     -- Handle whacking moles
  270.     for gx = 1, GRID_WIDTH do
  271.       for gy = 1, GRID_HEIGHT do
  272.         local cell = grid[gx][gy]
  273.        
  274.         -- Check if touch is within this cell
  275.         if isInside(x, y, cell.x, cell.y, cell.width, cell.height) then
  276.           -- If there's a mole, whack it!
  277.           if cell.active then
  278.             cell.active = false
  279.             gameState.score = gameState.score + 1
  280.            
  281.             -- Remove from active moles
  282.             for i, mole in ipairs(gameState.activeMoles) do
  283.               if mole.x == gx and mole.y == gy then
  284.                 table.remove(gameState.activeMoles, i)
  285.                 break
  286.               end
  287.             end
  288.            
  289.             -- Increase difficulty more slowly (every 7 points)
  290.             -- And cap at a lower maximum (5 instead of 6)
  291.             gameState.difficulty = math.min(5, 1 + math.floor(gameState.score / 7))
  292.           end
  293.         end
  294.       end
  295.     end
  296.   end
  297.  
  298.   -- Game loop
  299.   local gameOverButtons = nil
  300.  
  301.   while true do
  302.     if gameState.isGameOver then
  303.       if not gameOverButtons then
  304.         gameOverButtons = drawGameOver()
  305.       end
  306.      
  307.       -- Check for touch events on game over screen
  308.       local event, side, touchX, touchY = os.pullEvent("monitor_touch")
  309.      
  310.       if isInside(touchX, touchY,
  311.                  gameOverButtons.continueButton.x,
  312.                  gameOverButtons.continueButton.y,
  313.                  gameOverButtons.continueButton.width,
  314.                  gameOverButtons.continueButton.height) then
  315.         -- Continue with increased difficulty
  316.         return "continue", gameState.score, math.min(5, gameState.difficulty + 1)
  317.       elseif isInside(touchX, touchY,
  318.                      gameOverButtons.newGameButton.x,
  319.                      gameOverButtons.newGameButton.y,
  320.                      gameOverButtons.newGameButton.width,
  321.                      gameOverButtons.newGameButton.height) then
  322.         -- Start a new game
  323.         return "new", 0, 1
  324.       end
  325.     else
  326.       -- Update time
  327.       local currentTime = os.epoch("local") / 1000
  328.       gameState.timeLeft = math.max(0, math.floor(GAME_DURATION - (currentTime - gameState.startTime)))
  329.      
  330.       -- Check if time's up
  331.       if gameState.timeLeft <= 0 then
  332.         gameState.isGameOver = true
  333.       else
  334.         -- Spawn moles at a reasonable rate based on difficulty
  335.         local timeSinceLastSpawn = currentTime - gameState.lastSpawnTime
  336.         local spawnDelay = math.max(2.0, 3.5 - (gameState.difficulty * 0.25))
  337.        
  338.         if timeSinceLastSpawn > spawnDelay and #gameState.activeMoles < MAX_ACTIVE_MOLES then
  339.           spawnMole()
  340.         end
  341.        
  342.         -- Update moles
  343.         updateMoles()
  344.        
  345.         -- Draw game
  346.         drawGame()
  347.       end
  348.      
  349.       -- Check for touch events during gameplay (non-blocking)
  350.       local timer = os.startTimer(0.1)
  351.       local event, param1, param2, param3 = os.pullEvent()
  352.      
  353.       if event == "monitor_touch" then
  354.         local side, touchX, touchY = param1, param2, param3
  355.         handleGameTouch(touchX, touchY)
  356.       end
  357.     end
  358.   end
  359. end
  360.  
  361. -- Main program loop
  362. local function main()
  363.   while true do
  364.     -- Show welcome screen
  365.     local welcomeButtons = drawWelcomeScreen()
  366.    
  367.     -- Wait for player to press start
  368.     while true do
  369.       local event, side, touchX, touchY = os.pullEvent("monitor_touch")
  370.      
  371.       if isInside(touchX, touchY,
  372.                  welcomeButtons.startButton.x,
  373.                  welcomeButtons.startButton.y,
  374.                  welcomeButtons.startButton.width,
  375.                  welcomeButtons.startButton.height) then
  376.         break
  377.       end
  378.     end
  379.    
  380.     -- Start the first game
  381.     local gameMode, score, difficulty = "new", 0, 1
  382.    
  383.     -- Game loop - handles multiple games
  384.     while true do
  385.       gameMode, score, difficulty = playGame(gameMode == "continue", score, difficulty)
  386.      
  387.       if gameMode == "new" then
  388.         -- If player chose "New Game", go back to welcome screen
  389.         break
  390.       end
  391.       -- If player chose "Continue", we'll loop back to playGame
  392.     end
  393.   end
  394. end
  395.  
  396. -- Create startup file to run this program automatically
  397. local function createStartupFile()
  398.   local startupPath = "/startup.lua"
  399.   local file = fs.open(startupPath, "w")
  400.   if file then
  401.     file.write('shell.run("whackamole")')
  402.     file.close()
  403.     print("Created startup file to auto-run the game")
  404.   else
  405.     print("Failed to create startup file")
  406.   end
  407. end
  408.  
  409. -- Create the startup file when this program is first run
  410. if not fs.exists("/startup.lua") then
  411.   createStartupFile()
  412. end
  413.  
  414. -- Start the main program
  415. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement