Advertisement
nonogamer9

OC-TETRIS : Tetris For OpenComputers

Aug 5th, 2024 (edited)
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.87 KB | Gaming | 0 0
  1. local component = require("component")
  2. local gpu = component.gpu
  3. local event = require("event")
  4. local keyboard = require("keyboard")
  5. local math = require("math")
  6.  
  7. -- Check GPU tier
  8. local maxResolution = {gpu.maxResolution()}
  9. local isTier3 = maxResolution[1] > 160
  10.  
  11. -- Set resolution based on tier
  12. local SCREEN_WIDTH, SCREEN_HEIGHT
  13. if isTier3 then
  14.   SCREEN_WIDTH, SCREEN_HEIGHT = 160, 50
  15. else
  16.   SCREEN_WIDTH, SCREEN_HEIGHT = 80, 25
  17. end
  18.  
  19. gpu.setResolution(SCREEN_WIDTH, SCREEN_HEIGHT)
  20.  
  21. -- Create double buffers for the screen
  22. local screenBuffer1 = {}
  23. local screenBuffer2 = {}
  24. for y = 1, SCREEN_HEIGHT do
  25.   screenBuffer1[y] = {}
  26.   screenBuffer2[y] = {}
  27.   for x = 1, SCREEN_WIDTH do
  28.     screenBuffer1[y][x] = {bg = 0x000000, fg = 0xFFFFFF, char = " "}
  29.     screenBuffer2[y][x] = {bg = 0x000000, fg = 0xFFFFFF, char = " "}
  30.   end
  31. end
  32. local currentScreenBuffer = screenBuffer1
  33. local nextScreenBuffer = screenBuffer2
  34.  
  35. -- Create double buffers for the plasma effect
  36. local plasmaBuffer1 = {}
  37. local plasmaBuffer2 = {}
  38. for y = 1, SCREEN_HEIGHT do
  39.   plasmaBuffer1[y] = {}
  40.   plasmaBuffer2[y] = {}
  41.   for x = 1, SCREEN_WIDTH do
  42.     plasmaBuffer1[y][x] = 0
  43.     plasmaBuffer2[y][x] = 0
  44.   end
  45. end
  46. local currentPlasmaBuffer = plasmaBuffer1
  47. local nextPlasmaBuffer = plasmaBuffer2
  48.  
  49. -- Create double buffers for the Tetris board
  50. local BOARD_WIDTH = 10
  51. local BOARD_HEIGHT = 20
  52. local boardBuffer1 = {}
  53. local boardBuffer2 = {}
  54. for y = 1, BOARD_HEIGHT do
  55.   boardBuffer1[y] = {}
  56.   boardBuffer2[y] = {}
  57.   for x = 1, BOARD_WIDTH do
  58.     boardBuffer1[y][x] = 0
  59.     boardBuffer2[y][x] = 0
  60.   end
  61. end
  62. local currentBoardBuffer = boardBuffer1
  63. local nextBoardBuffer = boardBuffer2
  64.  
  65. -- Define game constants
  66. local BLOCK_SIZE = isTier3 and 3 or 2
  67. local BOARD_OFFSET_X = math.floor((SCREEN_WIDTH - BOARD_WIDTH * BLOCK_SIZE) / 2)
  68. local BOARD_OFFSET_Y = math.floor((SCREEN_HEIGHT - BOARD_HEIGHT * BLOCK_SIZE) / 2)
  69.  
  70. -- Define tetromino shapes and colors
  71. local tetrominoes = {
  72.   {shape = {{{1,1,1,1}},{{1},{1},{1},{1}}}, color = 0x00FFFF}, -- I
  73.   {shape = {{{1,1},{1,1}}}, color = 0xFFFF00}, -- O
  74.   {shape = {{{0,1,0},{1,1,1}},{{1,0},{1,1},{1,0}},{{1,1,1},{0,1,0}},{{0,1},{1,1},{0,1}}}, color = 0x800080}, -- T
  75.   {shape = {{{0,1,1},{1,1,0}},{{1,0},{1,1},{0,1}}}, color = 0x00FF00}, -- S
  76.   {shape = {{{1,1,0},{0,1,1}},{{0,1},{1,1},{1,0}}}, color = 0xFF0000}, -- Z
  77.   {shape = {{{1,0,0},{1,1,1}},{{1,1},{1,0},{1,0}},{{1,1,1},{0,0,1}},{{0,1},{0,1},{1,1}}}, color = 0x0000FF}, -- J
  78.   {shape = {{{0,0,1},{1,1,1}},{{1,0},{1,0},{1,1}},{{1,1,1},{1,0,0}},{{1,1},{0,1},{0,1}}}, color = 0xFFA500}  -- L
  79. }
  80.  
  81. local currentPiece = {
  82.   shape = {},
  83.   x = 0,
  84.   y = 0,
  85.   rotation = 1,
  86.   color = 0xFFFFFF
  87. }
  88.  
  89. local score = 0
  90. local level = 1
  91. local linesCleared = 0
  92.  
  93. -- Function to spawn a new piece
  94. local function spawnPiece()
  95.   local pieceIndex = math.random(#tetrominoes)
  96.   currentPiece.shape = tetrominoes[pieceIndex].shape
  97.   currentPiece.color = tetrominoes[pieceIndex].color
  98.   currentPiece.rotation = 1
  99.   currentPiece.x = math.floor(BOARD_WIDTH / 2) - 1
  100.   currentPiece.y = 1
  101. end
  102.  
  103. -- Function to check for collisions
  104. local function checkCollision(pieceShape, pieceX, pieceY)
  105.   for y = 1, #pieceShape do
  106.     for x = 1, #pieceShape[1] do
  107.       if pieceShape[y][x] == 1 then
  108.         local boardX, boardY = pieceX + x - 1, pieceY + y - 1
  109.         if boardX < 1 or boardX > BOARD_WIDTH or boardY > BOARD_HEIGHT or (boardY > 0 and currentBoardBuffer[boardY][boardX] ~= 0) then
  110.           return true
  111.         end
  112.       end
  113.     end
  114.   end
  115.   return false
  116. end
  117.  
  118. -- Function to merge the piece with the board
  119. local function mergePiece()
  120.   for y = 1, #currentPiece.shape[currentPiece.rotation] do
  121.     for x = 1, #currentPiece.shape[currentPiece.rotation][1] do
  122.       if currentPiece.shape[currentPiece.rotation][y][x] == 1 then
  123.         local boardY = currentPiece.y + y - 1
  124.         if boardY > 0 then
  125.           nextBoardBuffer[boardY][currentPiece.x + x - 1] = currentPiece.color
  126.         end
  127.       end
  128.     end
  129.   end
  130.   currentBoardBuffer, nextBoardBuffer = nextBoardBuffer, currentBoardBuffer
  131. end
  132.  
  133. -- Function to clear completed lines
  134. local function clearLines()
  135.   local linesCleared = 0
  136.   for y = BOARD_HEIGHT, 1, -1 do
  137.     local fullLine = true
  138.     for x = 1, BOARD_WIDTH do
  139.       if currentBoardBuffer[y][x] == 0 then
  140.         fullLine = false
  141.         break
  142.       end
  143.     end
  144.     if fullLine then
  145.       table.remove(nextBoardBuffer, y)
  146.       table.insert(nextBoardBuffer, 1, {0,0,0,0,0,0,0,0,0,0})
  147.       linesCleared = linesCleared + 1
  148.     else
  149.       nextBoardBuffer[y] = currentBoardBuffer[y]
  150.     end
  151.   end
  152.   currentBoardBuffer, nextBoardBuffer = nextBoardBuffer, currentBoardBuffer
  153.   return linesCleared
  154. end
  155.  
  156. -- Plasma effect variables
  157. local time = 0
  158.  
  159. -- Function to update plasma effect
  160. local function updatePlasma()
  161.   time = time + 0.1
  162.   for y = 1, SCREEN_HEIGHT do
  163.     for x = 1, SCREEN_WIDTH do
  164.       local value = math.sin(x / 16.0 + time) + math.sin(y / 8.0 + time) +
  165.                     math.sin((x + y) / 16.0 + time) + math.sin(math.sqrt(x*x + y*y) / 8.0 + time)
  166.       value = (value + 4) * 32
  167.       nextPlasmaBuffer[y][x] = math.floor(value) % 256
  168.     end
  169.   end
  170.   currentPlasmaBuffer, nextPlasmaBuffer = nextPlasmaBuffer, currentPlasmaBuffer
  171. end
  172.  
  173. -- Function to render the game
  174. local function render()
  175.   updatePlasma()
  176.  
  177.   -- Render plasma background
  178.   for y = 1, SCREEN_HEIGHT do
  179.     for x = 1, SCREEN_WIDTH do
  180.       local color = currentPlasmaBuffer[y][x]
  181.       nextScreenBuffer[y][x].bg = color * 0x010101
  182.       nextScreenBuffer[y][x].char = " "
  183.     end
  184.   end
  185.  
  186.   -- Render board
  187.   for y = 1, BOARD_HEIGHT do
  188.     for x = 1, BOARD_WIDTH do
  189.       local screenX = BOARD_OFFSET_X + (x-1) * BLOCK_SIZE + 1
  190.       local screenY = BOARD_OFFSET_Y + (y-1) * BLOCK_SIZE + 1
  191.       for dy = 0, BLOCK_SIZE-1 do
  192.         for dx = 0, BLOCK_SIZE-1 do
  193.           if screenY+dy > 0 and screenY+dy <= SCREEN_HEIGHT and screenX+dx > 0 and screenX+dx <= SCREEN_WIDTH then
  194.             if currentBoardBuffer[y][x] ~= 0 then
  195.               nextScreenBuffer[screenY+dy][screenX+dx].bg = currentBoardBuffer[y][x]
  196.               nextScreenBuffer[screenY+dy][screenX+dx].char = " "
  197.             else
  198.               nextScreenBuffer[screenY+dy][screenX+dx].bg = 0x000000
  199.               nextScreenBuffer[screenY+dy][screenX+dx].char = "."
  200.             end
  201.           end
  202.         end
  203.       end
  204.     end
  205.   end
  206.  
  207.   -- Render current piece
  208.   if currentPiece and currentPiece.shape and currentPiece.shape[currentPiece.rotation] then
  209.     for y = 1, #currentPiece.shape[currentPiece.rotation] do
  210.       for x = 1, #currentPiece.shape[currentPiece.rotation][1] do
  211.         if currentPiece.shape[currentPiece.rotation][y][x] == 1 then
  212.           local screenX = BOARD_OFFSET_X + (currentPiece.x + x - 2) * BLOCK_SIZE + 1
  213.           local screenY = BOARD_OFFSET_Y + (currentPiece.y + y - 2) * BLOCK_SIZE + 1
  214.           for dy = 0, BLOCK_SIZE-1 do
  215.             for dx = 0, BLOCK_SIZE-1 do
  216.               if screenY+dy > 0 and screenY+dy <= SCREEN_HEIGHT and screenX+dx > 0 and screenX+dx <= SCREEN_WIDTH then
  217.                 nextScreenBuffer[screenY+dy][screenX+dx].bg = currentPiece.color
  218.                 nextScreenBuffer[screenY+dy][screenX+dx].char = " "
  219.               end
  220.             end
  221.           end
  222.         end
  223.       end
  224.     end
  225.   end
  226.  
  227.   -- Render score and level
  228.   local scoreText = "Score: " .. score
  229.   local levelText = "Level: " .. level
  230.   for i = 1, #scoreText do
  231.     nextScreenBuffer[1][i].fg = 0xFFFFFF
  232.     nextScreenBuffer[1][i].char = scoreText:sub(i,i)
  233.   end
  234.   for i = 1, #levelText do
  235.     nextScreenBuffer[2][i].fg = 0xFFFFFF
  236.     nextScreenBuffer[2][i].char = levelText:sub(i,i)
  237.   end
  238.  
  239.   -- Apply buffer to screen
  240.   for y = 1, SCREEN_HEIGHT do
  241.     for x = 1, SCREEN_WIDTH do
  242.       if currentScreenBuffer[y][x].bg ~= nextScreenBuffer[y][x].bg or
  243.          currentScreenBuffer[y][x].fg ~= nextScreenBuffer[y][x].fg or
  244.          currentScreenBuffer[y][x].char ~= nextScreenBuffer[y][x].char then
  245.         gpu.setBackground(nextScreenBuffer[y][x].bg)
  246.         gpu.setForeground(nextScreenBuffer[y][x].fg)
  247.         gpu.set(x, y, nextScreenBuffer[y][x].char)
  248.       end
  249.     end
  250.   end
  251.  
  252.   -- Swap buffers
  253.   currentScreenBuffer, nextScreenBuffer = nextScreenBuffer, currentScreenBuffer
  254. end
  255.  
  256. -- Function to handle user input
  257. local function handleInput()
  258.   local _, _, _, code = event.pull(0.05, "key_down")
  259.   if code then
  260.     if code == 203 and not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x - 1, currentPiece.y) then
  261.       -- Left arrow
  262.       currentPiece.x = currentPiece.x - 1
  263.     elseif code == 205 and not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x + 1, currentPiece.y) then
  264.       -- Right arrow
  265.       currentPiece.x = currentPiece.x + 1
  266.     elseif code == 200 then
  267.       -- Up arrow (rotate)
  268.       local newRotation = currentPiece.rotation % #currentPiece.shape + 1
  269.       if not checkCollision(currentPiece.shape[newRotation], currentPiece.x, currentPiece.y) then
  270.         currentPiece.rotation = newRotation
  271.       end
  272.     elseif code == 208 then
  273.       -- Down arrow (soft drop)
  274.       if not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y + 1) then
  275.         currentPiece.y = currentPiece.y + 1
  276.         score = score + 1
  277.       end
  278.     elseif code == 57 then
  279.       -- Spacebar (hard drop)
  280.       while not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y + 1) do
  281.         currentPiece.y = currentPiece.y + 1
  282.         score = score + 2
  283.       end
  284.     end
  285.   end
  286. end
  287.  
  288. -- Main game loop
  289. local function gameLoop()
  290.   spawnPiece()
  291.   local lastMoveTime = os.time()
  292.   local moveDelay = 1.0 -- Start with a 1 second delay
  293.  
  294.   while true do
  295.     render()
  296.     handleInput()
  297.    
  298.     -- Handle piece movement
  299.     if os.time() - lastMoveTime >= moveDelay then
  300.       if not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y + 1) then
  301.         currentPiece.y = currentPiece.y + 1
  302.       else
  303.         mergePiece()
  304.         local cleared = clearLines()
  305.         score = score + cleared * 100 * level
  306.         linesCleared = linesCleared + cleared
  307.         if linesCleared >= level * 10 then
  308.           level = level + 1
  309.           moveDelay = math.max(0.1, moveDelay - 0.05)
  310.         end
  311.         spawnPiece()
  312.         if checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y) then
  313.           -- Game over
  314.           gpu.setBackground(0x000000)
  315.           gpu.fill(1, 1, SCREEN_WIDTH, SCREEN_HEIGHT, " ")
  316.           gpu.set(SCREEN_WIDTH/2-4, SCREEN_HEIGHT/2, "GAME OVER")
  317.           gpu.set(SCREEN_WIDTH/2-8, SCREEN_HEIGHT/2+2, "Final Score: " .. score)
  318.           os.sleep(3)
  319.           return
  320.         end
  321.       end
  322.       lastMoveTime = os.time()
  323.     end
  324.   end
  325. end
  326.  
  327. -- Function to display the main menu
  328. local function mainMenu()
  329.   while true do
  330.     gpu.setBackground(0x000000)
  331.     gpu.fill(1, 1, SCREEN_WIDTH, SCREEN_HEIGHT, " ")
  332.     gpu.set(SCREEN_WIDTH/2-4, SCREEN_HEIGHT/2-2, "TETRIS")
  333.     gpu.set(SCREEN_WIDTH/2-8, SCREEN_HEIGHT/2, "Press ENTER to start")
  334.     gpu.set(SCREEN_WIDTH/2-7, SCREEN_HEIGHT/2+2, "Press Q to quit")
  335.    
  336.     local _, _, _, code = event.pull("key_down")
  337.     if code == 28 then -- Enter key
  338.       return true
  339.     elseif code == 16 then -- Q key
  340.       return false
  341.     end
  342.   end
  343. end
  344.  
  345. -- Main program
  346. local function main()
  347.   while true do
  348.     if mainMenu() then
  349.       score = 0
  350.       level = 1
  351.       linesCleared = 0
  352.       for y = 1, BOARD_HEIGHT do
  353.         for x = 1, BOARD_WIDTH do
  354.           currentBoardBuffer[y][x] = 0
  355.           nextBoardBuffer[y][x] = 0
  356.         end
  357.       end
  358.       gameLoop()
  359.     else
  360.       break
  361.     end
  362.   end
  363.  
  364.   -- Clean up
  365.   gpu.setBackground(0x000000)
  366.   gpu.setForeground(0xFFFFFF)
  367.   gpu.fill(1, 1, SCREEN_WIDTH, SCREEN_HEIGHT, " ")
  368. end
  369.  
  370. -- Run the main program
  371. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement