-- Tetris Game for SimpleOS
-- CC: Tweaked compatible
local w, h = term.getSize()
-- Game board dimensions
local boardWidth = 10
local boardHeight = 18
local boardX = math.floor((w - boardWidth * 2) / 2)
local boardY = 2
-- Tetromino shapes
local shapes = {
{ -- I
{{1,1,1,1}},
{{1},{1},{1},{1}}
},
{ -- O
{{1,1},{1,1}}
},
{ -- T
{{0,1,0},{1,1,1}},
{{1,0},{1,1},{1,0}},
{{1,1,1},{0,1,0}},
{{0,1},{1,1},{0,1}}
},
{ -- S
{{0,1,1},{1,1,0}},
{{1,0},{1,1},{0,1}}
},
{ -- Z
{{1,1,0},{0,1,1}},
{{0,1},{1,1},{1,0}}
},
{ -- J
{{1,0,0},{1,1,1}},
{{1,1},{1,0},{1,0}},
{{1,1,1},{0,0,1}},
{{0,1},{0,1},{1,1}}
},
{ -- L
{{0,0,1},{1,1,1}},
{{1,0},{1,0},{1,1}},
{{1,1,1},{1,0,0}},
{{1,1},{0,1},{0,1}}
}
}
local shapeColors = {
colors.cyan,
colors.yellow,
colors.purple,
colors.green,
colors.red,
colors.blue,
colors.orange
}
-- Game state
local board = {}
local currentShape = nil
local currentRotation = 1
local currentX, currentY = 1, 1
local currentType = 1
local score = 0
local level = 1
local lines = 0
local gameOver = false
local speed = 0.5
-- Initialize board
local function initBoard()
board = {}
for y = 1, boardHeight do
board[y] = {}
for x = 1, boardWidth do
board[y][x] = 0
end
end
end
-- Get current shape data
local function getShape()
local rotations = shapes[currentType]
return rotations[((currentRotation - 1) % #rotations) + 1]
end
-- Check collision
local function checkCollision(shape, px, py)
for y, row in ipairs(shape) do
for x, cell in ipairs(row) do
if cell == 1 then
local bx = px + x - 1
local by = py + y - 1
if bx < 1 or bx > boardWidth or by > boardHeight then
return true
end
if by >= 1 and board[by][bx] ~= 0 then
return true
end
end
end
end
return false
end
-- Spawn new piece
local function spawnPiece()
currentType = math.random(1, #shapes)
currentRotation = 1
currentX = math.floor(boardWidth / 2) - 1
currentY = 1
if checkCollision(getShape(), currentX, currentY) then
gameOver = true
end
end
-- Lock piece to board
local function lockPiece()
local shape = getShape()
for y, row in ipairs(shape) do
for x, cell in ipairs(row) do
if cell == 1 then
local by = currentY + y - 1
local bx = currentX + x - 1
if by >= 1 and by <= boardHeight and bx >= 1 and bx <= boardWidth then
board[by][bx] = currentType
end
end
end
end
end
-- Clear completed lines
local function clearLines()
local cleared = 0
local y = boardHeight
while y >= 1 do
local full = true
for x = 1, boardWidth do
if board[y][x] == 0 then
full = false
break
end
end
if full then
table.remove(board, y)
local newRow = {}
for x = 1, boardWidth do
newRow[x] = 0
end
table.insert(board, 1, newRow)
cleared = cleared + 1
else
y = y - 1
end
end
if cleared > 0 then
local points = {40, 100, 300, 1200}
score = score + (points[cleared] or 1200) * level
lines = lines + cleared
level = math.floor(lines / 10) + 1
speed = math.max(0.05, 0.5 - (level - 1) * 0.05)
end
end
-- Draw game
local function draw()
term.setBackgroundColor(colors.black)
term.clear()
-- Draw border
term.setBackgroundColor(colors.gray)
for y = 0, boardHeight + 1 do
term.setCursorPos(boardX - 1, boardY + y)
term.write(" ")
term.setCursorPos(boardX + boardWidth * 2, boardY + y)
term.write(" ")
end
for x = -1, boardWidth * 2 do
term.setCursorPos(boardX + x, boardY + boardHeight + 1)
term.write(" ")
end
-- Draw board
for y = 1, boardHeight do
for x = 1, boardWidth do
term.setCursorPos(boardX + (x - 1) * 2, boardY + y - 1)
if board[y][x] ~= 0 then
term.setBackgroundColor(shapeColors[board[y][x]])
term.write(" ")
else
term.setBackgroundColor(colors.black)
term.write(" ")
end
end
end
-- Draw current piece
if not gameOver then
local shape = getShape()
term.setBackgroundColor(shapeColors[currentType])
for y, row in ipairs(shape) do
for x, cell in ipairs(row) do
if cell == 1 then
local dy = currentY + y - 1
local dx = currentX + x - 1
if dy >= 1 then
term.setCursorPos(boardX + (dx - 1) * 2, boardY + dy - 1)
term.write(" ")
end
end
end
end
end
-- Draw info
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.setCursorPos(2, 2)
term.write("TETRIS")
term.setCursorPos(2, 4)
term.write("Score:")
term.setCursorPos(2, 5)
term.write(tostring(score))
term.setCursorPos(2, 7)
term.write("Level:")
term.setCursorPos(2, 8)
term.write(tostring(level))
term.setCursorPos(2, 10)
term.write("Lines:")
term.setCursorPos(2, 11)
term.write(tostring(lines))
term.setCursorPos(2, h - 1)
term.write("[Q] Quit")
end
-- Game over screen
local function showGameOver()
term.setBackgroundColor(colors.black)
term.setTextColor(colors.red)
term.setCursorPos(boardX + 2, boardY + 7)
term.write("GAME OVER")
term.setTextColor(colors.white)
term.setCursorPos(boardX, boardY + 9)
term.write("ENTER=Restart")
end
-- Move piece
local function movePiece(dx, dy)
local shape = getShape()
if not checkCollision(shape, currentX + dx, currentY + dy) then
currentX = currentX + dx
currentY = currentY + dy
return true
end
return false
end
-- Rotate piece
local function rotatePiece()
local oldRotation = currentRotation
currentRotation = currentRotation + 1
local shape = getShape()
if checkCollision(shape, currentX, currentY) then
currentRotation = oldRotation
end
end
-- Drop piece
local function dropPiece()
while movePiece(0, 1) do end
lockPiece()
clearLines()
spawnPiece()
end
-- Main game loop
local function run()
initBoard()
spawnPiece()
score = 0
level = 1
lines = 0
gameOver = false
speed = 0.5
while true do
draw()
if gameOver then
showGameOver()
while true do
local event, key = os.pullEvent("key")
if key == keys.enter then
initBoard()
spawnPiece()
score = 0
level = 1
lines = 0
gameOver = false
speed = 0.5
break
elseif key == keys.q then
return
end
end
end
local timer = os.startTimer(speed)
while true do
local event, p1 = os.pullEvent()
if event == "key" then
if p1 == keys.left then
movePiece(-1, 0)
draw()
elseif p1 == keys.right then
movePiece(1, 0)
draw()
elseif p1 == keys.down then
movePiece(0, 1)
draw()
elseif p1 == keys.up then
rotatePiece()
draw()
elseif p1 == keys.space then
dropPiece()
break
elseif p1 == keys.q then
return
end
elseif event == "timer" and p1 == timer then
break
end
end
if not gameOver then
if not movePiece(0, 1) then
lockPiece()
clearLines()
spawnPiece()
end
end
end
end
run()