Skip to content

Instantly share code, notes, and snippets.

@icculus
Last active June 21, 2023 10:29
Show Gist options
  • Save icculus/a5dd97bb6862e62943e7fa9266481850 to your computer and use it in GitHub Desktop.
Save icculus/a5dd97bb6862e62943e7fa9266481850 to your computer and use it in GitHub Desktop.
$gtk.reset
class TetrisGame
# This is just to get GTK to not complain in the console for now.
def serialize ; { args: '' } ; end
def inspect ; serialize.to_s ; end
def to_s ; serialize.to_s ; end
def render_cube_pixelpos x, y, r, g, b, a=255
@args.outputs.solids << [ x, y, @boxsize, @boxsize, r, g, b, a ]
@args.outputs.borders << [ x, y, @boxsize, @boxsize, 255, 255, 255, a ]
end
def render_cube x, y, color
return if color == 0
render_cube_pixelpos @grid_x + (x * @boxsize), (720 - @grid_y) - (y * @boxsize), *@color_index[color]
end
def render_cube_framing x, y, w, h
for i in x..(x+w)-1 do
render_cube i, y, 7
render_cube i, (y+h)-1, 7
end
for i in y..(y+h)-1 do
render_cube x, i, 7
render_cube (x+w)-1, i, 7
end
end
def render_grid_border
render_cube_framing -1, -1, @grid_w + 2, @grid_h + 2
end
def render_piece piece, x, y
xpos = x
for xi in piece do
ypos = y
for yi in xi do
render_cube xpos, ypos, yi
ypos += 1
end
xpos += 1
end
end
def render_current_piece
render_piece @current_piece, @current_piece_x, @current_piece_y
end
def render_next_piece
@args.outputs.labels << [935, 610, "Next piece", 10, 255, 255, 255, 255 ]
render_cube_framing @grid_w + 4, 3, 8, 8
centerx = ((6 - @next_piece.length) / 2)
centery = ((6 - @next_piece[0].length) / 2)
render_piece @next_piece, @grid_w + 5 + centerx, 4 + centery
end
def render_game_grid
for y in 0..@grid_h-1 do
for x in 0..@grid_w-1 do
color = @grid[x][y]
render_cube x, y, color
end
end
end
def render
@args.outputs.solids << [ 0, 0, 1280, 720, 0, 0, 0, 255 ]
@args.outputs.labels << [550, 726, "Dragontris", 10, 255, 255, 255, 255 ]
@args.outputs.labels << [75, 70, "Score: #{@score}", 10, 255, 255, 255, 255 ]
@args.outputs.sprites << [75, 300, 300, 300, 'console-logo.png']
render_grid_border
render_game_grid
render_current_piece
render_next_piece
end
def select_next_piece
@current_piece = @next_piece
X = rand(6) + 1
@next_piece = case X
when 1 then [ [ 0, X ], [ 0, X ], [ X, X ] ] # L shape
when 2 then [ [ X, X ], [ 0, X ], [ 0, X ] ] # other L shape
when 3 then [ [ X, X, X, X ] ] # straight line
when 4 then [ [ X, X ], [ X, X ] ] # box shape
when 5 then [ [ 0, X ], [ X, X ], [ X, 0 ] ] # zigzag shape
when 6 then [ [ X, 0 ], [ X, X ], [ X, 0 ] ] # T shape
end
@current_piece_x = 5
@current_piece_y = 0
end
def current_piece_colliding
xpos = @current_piece_x
for x in @current_piece do
ypos = @current_piece_y
for y in x do
if (y != 0) && ((ypos >= @grid_h) || (@grid[xpos][ypos+1] != 0))
return true
end
ypos += 1
end
xpos += 1
end
return false
end
def rotate_piece_left
@current_piece = @current_piece.transpose.map(&:reverse)
if (@current_piece_x + @current_piece.length) >= @grid_w
@current_piece_x = @grid_w - @current_piece.length
end
end
def rotate_piece_right
# !!! FIXME: is there a way to transpose once to accomplish this?
@current_piece = @current_piece.transpose.map(&:reverse)
@current_piece = @current_piece.transpose.map(&:reverse)
@current_piece = @current_piece.transpose.map(&:reverse)
if (@current_piece_x + @current_piece.length) >= @grid_w
@current_piece_x = @grid_w - @current_piece.length
end
end
def plant_current_piece
# make this piece part of the landscape
xpos = @current_piece_x
for x in @current_piece do
ypos = @current_piece_y
for y in x do
@grid[xpos][ypos] = y if y != 0
ypos += 1
end
xpos += 1
end
# Check the grid for full lines to remove.
for y in 0..@grid_h-1
full = true
for x in 0..@grid_w-1
if @grid[x][y] == 0
full = false
break
end
end
if full # nuke this line!
@score += 1
for i in y.downto(1) do
for j in 0..@grid_w-1
@grid[j][i] = @grid[j][i-1]
end
end
for i in 0..@grid_w-1
@grid[i][0] = 0
end
end
end
select_next_piece
@current_piece_x = 5
@current_piece_y = 0
if current_piece_colliding # Collision at start? Game over.
@gameover = true
end
end
def iterate
c = @args.inputs.controller_one
k = @args.inputs.keyboard
if @gameover
@args.outputs.labels << [200, 450, "GAME OVER", 100, 255, 255, 255, 255 ]
if k.key_down.space || c.key_down.start
$gtk.reset
end
return
end
if c.key_down.left || k.key_down.left
@current_piece_x -= 1 if @current_piece_x > 0
end
if c.key_down.right || k.key_down.right
@current_piece_x += 1 if (@current_piece_x + @current_piece.length) < @grid_w
end
if c.key_down.down || c.key_held.down ||
k.key_down.down || k.key_held.down ||
k.key_down.space || k.key_held.space
@next_move -= 10 # drop faster
end
if c.key_down.a || k.key_down.a
rotate_piece_left
end
if c.key_down.b || k.key_down.s
rotate_piece_right
end
@next_move -= 1
if @next_move <= 0 # drop the piece!
# decide if any piece of this is touching the ground...
if current_piece_colliding
plant_current_piece
else
@current_piece_y += 1 # let it click down one space.
end
@next_move = @move_ticks # reset the move timer
end
end
def initialize args
@args = args
@score = 0
@gameover = false
@move_ticks ||= 30
@next_move ||= @move_ticks
@boxsize ||= 30
@grid_w ||= 10
@grid_h ||= 20
@grid_x ||= (1280 - (@grid_w * @boxsize)) / 2
@grid_y ||= (720 - ((@grid_h-2) * @boxsize)) / 2
@grid = []
@color_index = [
[ 0, 0, 0 ],
[ 255, 0, 0 ],
[ 0, 255, 0 ],
[ 0, 0, 255 ],
[ 255, 255, 0 ],
[ 255, 0, 255 ],
[ 0, 255, 255 ],
[ 127, 127, 127 ]
]
for x in 0..@grid_w-1 do
@grid[x] = []
for y in 0..@grid_h-1 do
@grid[x][y] = 0
end
end
select_next_piece # queues up an initial piece.
select_next_piece # puts one in play
end
def tick
iterate
render
end
end
# This is what DragonRuby calls 60 times a second.
def tick args
args.state.game ||= TetrisGame.new args
args.state.game.tick
end
# end of main.rb ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment