Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local gpu = component.gpu
- local event = require("event")
- local keyboard = require("keyboard")
- local math = require("math")
- -- Check GPU tier
- local maxResolution = {gpu.maxResolution()}
- local isTier3 = maxResolution[1] > 160
- -- Set resolution based on tier
- local SCREEN_WIDTH, SCREEN_HEIGHT
- if isTier3 then
- SCREEN_WIDTH, SCREEN_HEIGHT = 160, 50
- else
- SCREEN_WIDTH, SCREEN_HEIGHT = 80, 25
- end
- gpu.setResolution(SCREEN_WIDTH, SCREEN_HEIGHT)
- -- Create double buffers for the screen
- local screenBuffer1 = {}
- local screenBuffer2 = {}
- for y = 1, SCREEN_HEIGHT do
- screenBuffer1[y] = {}
- screenBuffer2[y] = {}
- for x = 1, SCREEN_WIDTH do
- screenBuffer1[y][x] = {bg = 0x000000, fg = 0xFFFFFF, char = " "}
- screenBuffer2[y][x] = {bg = 0x000000, fg = 0xFFFFFF, char = " "}
- end
- end
- local currentScreenBuffer = screenBuffer1
- local nextScreenBuffer = screenBuffer2
- -- Create double buffers for the plasma effect
- local plasmaBuffer1 = {}
- local plasmaBuffer2 = {}
- for y = 1, SCREEN_HEIGHT do
- plasmaBuffer1[y] = {}
- plasmaBuffer2[y] = {}
- for x = 1, SCREEN_WIDTH do
- plasmaBuffer1[y][x] = 0
- plasmaBuffer2[y][x] = 0
- end
- end
- local currentPlasmaBuffer = plasmaBuffer1
- local nextPlasmaBuffer = plasmaBuffer2
- -- Create double buffers for the Tetris board
- local BOARD_WIDTH = 10
- local BOARD_HEIGHT = 20
- local boardBuffer1 = {}
- local boardBuffer2 = {}
- for y = 1, BOARD_HEIGHT do
- boardBuffer1[y] = {}
- boardBuffer2[y] = {}
- for x = 1, BOARD_WIDTH do
- boardBuffer1[y][x] = 0
- boardBuffer2[y][x] = 0
- end
- end
- local currentBoardBuffer = boardBuffer1
- local nextBoardBuffer = boardBuffer2
- -- Define game constants
- local BLOCK_SIZE = isTier3 and 3 or 2
- local BOARD_OFFSET_X = math.floor((SCREEN_WIDTH - BOARD_WIDTH * BLOCK_SIZE) / 2)
- local BOARD_OFFSET_Y = math.floor((SCREEN_HEIGHT - BOARD_HEIGHT * BLOCK_SIZE) / 2)
- -- Define tetromino shapes and colors
- local tetrominoes = {
- {shape = {{{1,1,1,1}},{{1},{1},{1},{1}}}, color = 0x00FFFF}, -- I
- {shape = {{{1,1},{1,1}}}, color = 0xFFFF00}, -- O
- {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
- {shape = {{{0,1,1},{1,1,0}},{{1,0},{1,1},{0,1}}}, color = 0x00FF00}, -- S
- {shape = {{{1,1,0},{0,1,1}},{{0,1},{1,1},{1,0}}}, color = 0xFF0000}, -- Z
- {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
- {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
- }
- local currentPiece = {
- shape = {},
- x = 0,
- y = 0,
- rotation = 1,
- color = 0xFFFFFF
- }
- local score = 0
- local level = 1
- local linesCleared = 0
- -- Function to spawn a new piece
- local function spawnPiece()
- local pieceIndex = math.random(#tetrominoes)
- currentPiece.shape = tetrominoes[pieceIndex].shape
- currentPiece.color = tetrominoes[pieceIndex].color
- currentPiece.rotation = 1
- currentPiece.x = math.floor(BOARD_WIDTH / 2) - 1
- currentPiece.y = 1
- end
- -- Function to check for collisions
- local function checkCollision(pieceShape, pieceX, pieceY)
- for y = 1, #pieceShape do
- for x = 1, #pieceShape[1] do
- if pieceShape[y][x] == 1 then
- local boardX, boardY = pieceX + x - 1, pieceY + y - 1
- if boardX < 1 or boardX > BOARD_WIDTH or boardY > BOARD_HEIGHT or (boardY > 0 and currentBoardBuffer[boardY][boardX] ~= 0) then
- return true
- end
- end
- end
- end
- return false
- end
- -- Function to merge the piece with the board
- local function mergePiece()
- for y = 1, #currentPiece.shape[currentPiece.rotation] do
- for x = 1, #currentPiece.shape[currentPiece.rotation][1] do
- if currentPiece.shape[currentPiece.rotation][y][x] == 1 then
- local boardY = currentPiece.y + y - 1
- if boardY > 0 then
- nextBoardBuffer[boardY][currentPiece.x + x - 1] = currentPiece.color
- end
- end
- end
- end
- currentBoardBuffer, nextBoardBuffer = nextBoardBuffer, currentBoardBuffer
- end
- -- Function to clear completed lines
- local function clearLines()
- local linesCleared = 0
- for y = BOARD_HEIGHT, 1, -1 do
- local fullLine = true
- for x = 1, BOARD_WIDTH do
- if currentBoardBuffer[y][x] == 0 then
- fullLine = false
- break
- end
- end
- if fullLine then
- table.remove(nextBoardBuffer, y)
- table.insert(nextBoardBuffer, 1, {0,0,0,0,0,0,0,0,0,0})
- linesCleared = linesCleared + 1
- else
- nextBoardBuffer[y] = currentBoardBuffer[y]
- end
- end
- currentBoardBuffer, nextBoardBuffer = nextBoardBuffer, currentBoardBuffer
- return linesCleared
- end
- -- Plasma effect variables
- local time = 0
- -- Function to update plasma effect
- local function updatePlasma()
- time = time + 0.1
- for y = 1, SCREEN_HEIGHT do
- for x = 1, SCREEN_WIDTH do
- local value = math.sin(x / 16.0 + time) + math.sin(y / 8.0 + time) +
- math.sin((x + y) / 16.0 + time) + math.sin(math.sqrt(x*x + y*y) / 8.0 + time)
- value = (value + 4) * 32
- nextPlasmaBuffer[y][x] = math.floor(value) % 256
- end
- end
- currentPlasmaBuffer, nextPlasmaBuffer = nextPlasmaBuffer, currentPlasmaBuffer
- end
- -- Function to render the game
- local function render()
- updatePlasma()
- -- Render plasma background
- for y = 1, SCREEN_HEIGHT do
- for x = 1, SCREEN_WIDTH do
- local color = currentPlasmaBuffer[y][x]
- nextScreenBuffer[y][x].bg = color * 0x010101
- nextScreenBuffer[y][x].char = " "
- end
- end
- -- Render board
- for y = 1, BOARD_HEIGHT do
- for x = 1, BOARD_WIDTH do
- local screenX = BOARD_OFFSET_X + (x-1) * BLOCK_SIZE + 1
- local screenY = BOARD_OFFSET_Y + (y-1) * BLOCK_SIZE + 1
- for dy = 0, BLOCK_SIZE-1 do
- for dx = 0, BLOCK_SIZE-1 do
- if screenY+dy > 0 and screenY+dy <= SCREEN_HEIGHT and screenX+dx > 0 and screenX+dx <= SCREEN_WIDTH then
- if currentBoardBuffer[y][x] ~= 0 then
- nextScreenBuffer[screenY+dy][screenX+dx].bg = currentBoardBuffer[y][x]
- nextScreenBuffer[screenY+dy][screenX+dx].char = " "
- else
- nextScreenBuffer[screenY+dy][screenX+dx].bg = 0x000000
- nextScreenBuffer[screenY+dy][screenX+dx].char = "."
- end
- end
- end
- end
- end
- end
- -- Render current piece
- if currentPiece and currentPiece.shape and currentPiece.shape[currentPiece.rotation] then
- for y = 1, #currentPiece.shape[currentPiece.rotation] do
- for x = 1, #currentPiece.shape[currentPiece.rotation][1] do
- if currentPiece.shape[currentPiece.rotation][y][x] == 1 then
- local screenX = BOARD_OFFSET_X + (currentPiece.x + x - 2) * BLOCK_SIZE + 1
- local screenY = BOARD_OFFSET_Y + (currentPiece.y + y - 2) * BLOCK_SIZE + 1
- for dy = 0, BLOCK_SIZE-1 do
- for dx = 0, BLOCK_SIZE-1 do
- if screenY+dy > 0 and screenY+dy <= SCREEN_HEIGHT and screenX+dx > 0 and screenX+dx <= SCREEN_WIDTH then
- nextScreenBuffer[screenY+dy][screenX+dx].bg = currentPiece.color
- nextScreenBuffer[screenY+dy][screenX+dx].char = " "
- end
- end
- end
- end
- end
- end
- end
- -- Render score and level
- local scoreText = "Score: " .. score
- local levelText = "Level: " .. level
- for i = 1, #scoreText do
- nextScreenBuffer[1][i].fg = 0xFFFFFF
- nextScreenBuffer[1][i].char = scoreText:sub(i,i)
- end
- for i = 1, #levelText do
- nextScreenBuffer[2][i].fg = 0xFFFFFF
- nextScreenBuffer[2][i].char = levelText:sub(i,i)
- end
- -- Apply buffer to screen
- for y = 1, SCREEN_HEIGHT do
- for x = 1, SCREEN_WIDTH do
- if currentScreenBuffer[y][x].bg ~= nextScreenBuffer[y][x].bg or
- currentScreenBuffer[y][x].fg ~= nextScreenBuffer[y][x].fg or
- currentScreenBuffer[y][x].char ~= nextScreenBuffer[y][x].char then
- gpu.setBackground(nextScreenBuffer[y][x].bg)
- gpu.setForeground(nextScreenBuffer[y][x].fg)
- gpu.set(x, y, nextScreenBuffer[y][x].char)
- end
- end
- end
- -- Swap buffers
- currentScreenBuffer, nextScreenBuffer = nextScreenBuffer, currentScreenBuffer
- end
- -- Function to handle user input
- local function handleInput()
- local _, _, _, code = event.pull(0.05, "key_down")
- if code then
- if code == 203 and not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x - 1, currentPiece.y) then
- -- Left arrow
- currentPiece.x = currentPiece.x - 1
- elseif code == 205 and not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x + 1, currentPiece.y) then
- -- Right arrow
- currentPiece.x = currentPiece.x + 1
- elseif code == 200 then
- -- Up arrow (rotate)
- local newRotation = currentPiece.rotation % #currentPiece.shape + 1
- if not checkCollision(currentPiece.shape[newRotation], currentPiece.x, currentPiece.y) then
- currentPiece.rotation = newRotation
- end
- elseif code == 208 then
- -- Down arrow (soft drop)
- if not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y + 1) then
- currentPiece.y = currentPiece.y + 1
- score = score + 1
- end
- elseif code == 57 then
- -- Spacebar (hard drop)
- while not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y + 1) do
- currentPiece.y = currentPiece.y + 1
- score = score + 2
- end
- end
- end
- end
- -- Main game loop
- local function gameLoop()
- spawnPiece()
- local lastMoveTime = os.time()
- local moveDelay = 1.0 -- Start with a 1 second delay
- while true do
- render()
- handleInput()
- -- Handle piece movement
- if os.time() - lastMoveTime >= moveDelay then
- if not checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y + 1) then
- currentPiece.y = currentPiece.y + 1
- else
- mergePiece()
- local cleared = clearLines()
- score = score + cleared * 100 * level
- linesCleared = linesCleared + cleared
- if linesCleared >= level * 10 then
- level = level + 1
- moveDelay = math.max(0.1, moveDelay - 0.05)
- end
- spawnPiece()
- if checkCollision(currentPiece.shape[currentPiece.rotation], currentPiece.x, currentPiece.y) then
- -- Game over
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, SCREEN_WIDTH, SCREEN_HEIGHT, " ")
- gpu.set(SCREEN_WIDTH/2-4, SCREEN_HEIGHT/2, "GAME OVER")
- gpu.set(SCREEN_WIDTH/2-8, SCREEN_HEIGHT/2+2, "Final Score: " .. score)
- os.sleep(3)
- return
- end
- end
- lastMoveTime = os.time()
- end
- end
- end
- -- Function to display the main menu
- local function mainMenu()
- while true do
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, SCREEN_WIDTH, SCREEN_HEIGHT, " ")
- gpu.set(SCREEN_WIDTH/2-4, SCREEN_HEIGHT/2-2, "TETRIS")
- gpu.set(SCREEN_WIDTH/2-8, SCREEN_HEIGHT/2, "Press ENTER to start")
- gpu.set(SCREEN_WIDTH/2-7, SCREEN_HEIGHT/2+2, "Press Q to quit")
- local _, _, _, code = event.pull("key_down")
- if code == 28 then -- Enter key
- return true
- elseif code == 16 then -- Q key
- return false
- end
- end
- end
- -- Main program
- local function main()
- while true do
- if mainMenu() then
- score = 0
- level = 1
- linesCleared = 0
- for y = 1, BOARD_HEIGHT do
- for x = 1, BOARD_WIDTH do
- currentBoardBuffer[y][x] = 0
- nextBoardBuffer[y][x] = 0
- end
- end
- gameLoop()
- else
- break
- end
- end
- -- Clean up
- gpu.setBackground(0x000000)
- gpu.setForeground(0xFFFFFF)
- gpu.fill(1, 1, SCREEN_WIDTH, SCREEN_HEIGHT, " ")
- end
- -- Run the main program
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement