This is truly excellent, I frankly didn't expect a tetris in 3k, great job. We have pure gold in this thread, it will be hard to choose which one to publish first. Warm thanks to you all!
@rrraptor Unbelievable! I still reading to find out why these 114 lines of code can have a complicated "rotate clamp in the boundary" feature! Thank you so much!
@rrraptor Unbelievable! I still reading to find out why these 114 lines of code can have a complicated "rotate clamp in the boundary" feature! Thank you so much!
At the bottom I hardcoded a wall. For left & right boundaries the trick is that I copy the whole board into a temporary array (the "trMv" function aka "tryMove"), then I "erase" the current piece from it and check for a collision with something (but in a new position), there is also a check for out of bounds by checking for "nil" (however in the code I wrote it as "not bv" aka "boardValue ~= nil") in "over" function (aka "overlap")
Here is uncut version (with imgui):
require"ImGui"-- Constants --
BOARD_W =10
BOARD_H =20
TILE =32-- Global variables ---- values for 'GameState' variable
States ={
GAME_OVER =0,
SPAWN =1,
REMOVE_LINES =2}
GameState =0
GameSpeed =0.4-- lower -> faster
SpeedUPFactor =0.05-- GameSpeed multiplier when holding 'Down' (lower -> faster)
Score =0
MaxScore =0
Timer =0
TimerScale =1
CanMove =false
ScoreTable ={[0]=0,40,100,300,1200}-- how many scores player get for burning N lines-- initialize simple matrix
Board =table.create(BOARD_H + 1)for i=1, BOARD_H do
Board[i]=table.create(BOARD_W, 0)end
Board[BOARD_H + 1]=table.create(BOARD_W,8)-- Floor
Tetrominos ={{-- I
color = 0x00F0F0, data ={{0,0,0,0},
{1,1,1,1},
{0,0,0,0},
{0,0,0,0}}},
{-- J
color = 0x0000F0, data ={{1,0,0},
{1,1,1},
{0,0,0}}},
{-- L
color = 0xF0A000, data ={{0,0,1},
{1,1,1},
{0,0,0}}},
{-- O
color = 0xF0F000, data ={{1,1},
{1,1}}},
{-- S
color = 0x00F000, data ={{0,1,1},
{1,1,0},
{0,0,0}}},
{-- T
color = 0xA000F0, data ={{0,1,0},
{1,1,1},
{0,0,0}}},
{-- Z
color = 0xF00000, data ={{1,1,0},
{0,1,1},
{0,0,0}}},
-- Floor color{0x323232}}
CurrentTetromino ={
index =0, -- index in 'Tetrominos' table
x =4, -- position
y =1,
data =0-- data from 'Tetrominos' table}
ui=ImGui.new()
IO = ui:getIO()
stage:addChild(ui)function forEachTetromino(tetData, callback)local h = #tetData
local w = #tetData[1]for y=1, h dofor x=1, w doif tetData[y][x]>0thenlocal returnValue = callback(x, y)if returnValue ~=nilthenreturn returnValue
endendendendendfunction overlap(board, tx, ty, tetData)return forEachTetromino(tetData, function(x, y)local boardValue = board[ty + y - 1][tx + x - 1]if boardValue ==nilor boardValue >0thenreturntrueendend)endfunction setBoard(board, tx, ty, tetData, value)
forEachTetromino(tetData, function(x, y)
board[ty + y - 1][tx + x - 1]= value
end)endfunction tryMove(dstX, dstY, tetData)-- copy 'Board' to temp tablelocal tempBoard ={}for i=1,BOARD_H + 1do
tempBoard[i]={}for j =1, BOARD_W do
tempBoard[i][j]= Board[i][j]endend-- remove current piece from temp board
setBoard(tempBoard, CurrentTetromino.x, CurrentTetromino.y, CurrentTetromino.data, 0)-- check for collisions at destination pointifnot overlap(tempBoard, dstX, dstY, tetData)then-- erase data at old position
setBoard(Board, CurrentTetromino.x, CurrentTetromino.y, CurrentTetromino.data, 0)-- add data at new position
setBoard(Board, dstX, dstY, tetData, CurrentTetromino.index)returntrueendendfunction move(dx, dy)local nx = CurrentTetromino.x + dx
local ny = CurrentTetromino.y + dy
if tryMove(nx, ny, CurrentTetromino.data)then
CurrentTetromino.x = nx
CurrentTetromino.y = ny
returntrueendendfunction onAppResize()local minX, minY, maxX, maxY = application:getLogicalBounds()local screenW = maxX - minX
local screenH = maxY - minY
BoardOffsetX =(screenW - TILE * BOARD_W)*0.5
BoardOffsetY =(screenH - TILE * BOARD_H)*0.5
IO:setDisplaySize(screenW, screenH)endfunction onKeyDown(e)if e.keyCode == KeyCode.SPACE and GameState == States.GAME_OVER then
GameState = States.SPAWN
endifnot CanMove thenreturnendif e.keyCode == KeyCode.DOWN then
TimerScale = SpeedUPFactor
elseif e.keyCode == KeyCode.UP then-- roatate current piecelocal rotatedData ={}local h = #CurrentTetromino.data
local w = #CurrentTetromino.data[1]for i = w, 1, -1do
rotatedData[i]={}for j =1, h do
rotatedData[i][j]= CurrentTetromino.data[h - j + 1][i]endend-- if there is no collision between rotated piece and something else, update current dataif tryMove(CurrentTetromino.x, CurrentTetromino.y, rotatedData)then
CurrentTetromino.data = rotatedData
endelseif e.keyCode == KeyCode.LEFT then
move(-1, 0)elseif e.keyCode == KeyCode.RIGHT then
move(1, 0)endendfunction onKeyUp(e)if e.keyCode == KeyCode.DOWN then
TimerScale =1endendfunction onEnterFrame(e)local dt = e.deltaTime
Timer += dt
if Timer > GameSpeed * TimerScale then
Timer =0if GameState == States.SPAWN then
CurrentTetromino.x =math.round(BOARD_W /2)
CurrentTetromino.y =1
CurrentTetromino.index =math.random(#Tetrominos - 1)
CurrentTetromino.data = Tetrominos[CurrentTetromino.index].data
if overlap(Board, CurrentTetromino.x, CurrentTetromino.y, CurrentTetromino.data)thenfor i =1, BOARD_H dofor j=1, BOARD_W do
Board[i][j]=0endend
GameState = States.GAME_OVER
Score =0
CanMove =falseelse
setBoard(Board, CurrentTetromino.x, CurrentTetromino.y, CurrentTetromino.data, CurrentTetromino.index)
GameState = States.REMOVE_LINES
CanMove =trueendelseif GameState == States.REMOVE_LINES thenifnot move(0, 1)thenlocal rowsRemoved =0for y =1, BOARD_H dolocal columnsFilled =0for x =1, BOARD_W doif Board[y][x]>0then
columnsFilled +=1endendif columnsFilled == BOARD_W thentable.remove(Board, y)table.insert(Board, 1, table.create(BOARD_W, 0))
rowsRemoved +=1endend
Score += ScoreTable[rowsRemoved]
MaxScore = MaxScore <> Score
GameState = States.SPAWN
CanMove =falseendendend-- rendering with ImGui
ui:newFrame(dt)local list = ui:getForegroundDrawList()local text ="Press 'Space' to start"if GameState == States.GAME_OVER then
text=`Score: {Score} Max: {MaxScore}\n{text}`
else
text=`Score: {Score} Max: {MaxScore}`
end
list:addText(10, 3, 0, 1, text)for i, row inipairs(Board)dofor j, cell inipairs(row)doif cell >0thenlocal sx = BoardOffsetX + (j - 1)* TILE
local sy = BoardOffsetY + (i - 1)* TILE
local ex = BoardOffsetX + j * TILE
local ey = BoardOffsetY + i * TILE
list:addRectFilled(sx, sy, ex, ey, Tetrominos[cell].color or0)
list:addRect(sx, sy, ex, ey,0, 0.5, 2)endendend
list:addRect(BoardOffsetX, BoardOffsetY, BoardOffsetX + TILE * BOARD_W, BoardOffsetY + TILE * BOARD_H, 0, 1)
ui:render()
ui:endFrame()end
onAppResize()
stage:addEventListener("applicationResize", onAppResize)
stage:addEventListener("keyDown", onKeyDown)
stage:addEventListener("keyUp", onKeyUp)
stage:addEventListener("enterFrame", onEnterFrame)
I've had a go at making a first single sheet short gideros code publication. For the first one, I choosed @PaulH plane game, I'll publish it on the internet and see if that gets some attention. If so, maybe we could make a poll to choose next publications. Here is the first link: https://wiki.gideros.rocks/images/d/d1/Short-Issue1-Overheat.pdf
Comments
Likes: MoKaLux
Likes: PaulH, MoKaLux
Likes: pie, keszegh, vitalitymobile, MoKaLux, hgy29
Here is uncut version (with imgui):
Likes: MoKaLux, vitalitymobile, pie, hgy29
Imho it is less scary (easier to explain) than all the funky code you guys posted.
So what is the goal, oneliners or short elegant Gideros games?
My two cents given I haven't produced anything yet
Likes: MoKaLux, pie, PaulH, hgy29
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Here is the first link: https://wiki.gideros.rocks/images/d/d1/Short-Issue1-Overheat.pdf
Likes: pie
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Fragmenter - animated loop machine and IKONOMIKON - the memory game
Fragmenter - animated loop machine and IKONOMIKON - the memory game