SFML/ruby/test/tetris.rb

197 lines
4.7 KiB
Ruby

require 'RubySFML'
include SFML
# A simple class to handle a single puzzle piece
class Piece
# Each shape is made up of 4 blocks:
# * * * * ** ** **
# * ** ** ** * * **
# * * * * * *
# *
SHAPES = [ # Negative y is down
[[0, 0], [0, 1], [0, -1], [0, -2]],
[[0, 0], [0, 1], [1, 0], [0, -1]],
[[0, 0], [0, 1], [1, 0], [1, -1]],
[[1, 0], [1, 1], [0, 0], [0, -1]],
[[0, 0], [0, 1], [1, 1], [0, -1]],
[[1, 0], [1, 1], [0, 1], [1, -1]],
[[0, 0], [0, 1], [1, 1], [1, 0]],
]
# Each shape has its own color
COLORS = [Color.Red, Color.Yellow, Color.Cyan, Color.Magenta, Color.Green, Color.Blue, Color.new(255, 128, 64)]
attr_accessor :x, :y, :r
def initialize(n, x, y)
@n, @x, @y = n, x, y
@r = 0
end
def shape(); SHAPES[@n]; end
def color(); COLORS[@n]; end
def rleft(); @r = (@r+1) % 4; end
def rright(); @r = (@r-1) % 4; end
def each() # Iterate each block coordinate (corrected for rotation)
case @r
when 0 then shape.each {|c| yield([c[0], c[1]])}
when 1 then shape.each {|c| yield([-c[1], c[0]])}
when 2 then shape.each {|c| yield([-c[0], -c[1]])}
when 3 then shape.each {|c| yield([c[1], -c[0]])}
end
end
end
class Grid
HEIGHT = 20
WIDTH = 10
BLOCK = 18
BORDER = 1
BSIZE = BLOCK + BORDER
def initialize()
@rows = []
HEIGHT.times { @rows.push Array.new(WIDTH) }
@background_image = Image.new(WIDTH*(BSIZE)+BORDER, HEIGHT*BSIZE+BORDER, Color.White)
@background = Sprite.new(@background_image, (640-WIDTH*BSIZE)/2-BORDER, (480-HEIGHT*BSIZE)/2-BORDER)
@block_image = Image.new(BLOCK, BLOCK, Color.White)
@block = Sprite.new(@block_image)
@score = 0
@last_down = $clock.to_f
@piece = Piece.new(rand(7), WIDTH/2-1, HEIGHT-1)
@next_piece = Piece.new(rand(7), WIDTH/2-1, HEIGHT-1)
end
def [](y)
return @rows[y]
end
def draw_block(win, x, y, color)
@block.left = x*BSIZE + @background.left+BORDER
@block.top = ((HEIGHT-1)-y)*BSIZE + @background.top+BORDER
@block.color = color
win.draw(@block)
end
def solid?(x, y)
return true if x < 0 or y < 0 or x >= WIDTH or y >= HEIGHT
return @rows[y][x]
end
def left
x = @piece.x
@piece.x -= 1
@piece.each {|c| @piece.x = x if solid?(@piece.x+c[0], @piece.y+c[1])}
end
def right
x = @piece.x
@piece.x += 1
@piece.each {|c| @piece.x = x if solid?(@piece.x+c[0], @piece.y+c[1])}
end
def up
r = @piece.r
@piece.rright
@piece.each {|c| @piece.r = r if solid?(@piece.x+c[0], @piece.y+c[1])}
end
def down
@last_down = $clock.to_f
y = @piece.y
@piece.y -= 1
done = false
@piece.each {|c| done = true if solid?(@piece.x+c[0], @piece.y+c[1])}
if done
@piece.y = y
@piece.each {|c| @rows[@piece.y+c[1]][@piece.x+c[0]] = @piece.color}
@piece = @next_piece
@next_piece = Piece.new(rand(7), WIDTH/2-1, HEIGHT-1)
end
end
def update(win)
speed = 0.5 - 0.1 * (@score/1000)
down if $clock.to_f - @last_down >= speed
collapsed = 0
while y = find_full_row()
collapse_row(y)
collapsed += 1
end
@score += 5 * 2**collapsed if collapsed > 0
end
def render(win)
win.draw(@background)
HEIGHT.times {|y|
WIDTH.times {|x|
c = @rows[y][x] || Color.Black
draw_block(win, x, y, c)
}
}
@piece.each {|c|
x, y, c = @piece.x+c[0], @piece.y+c[1], @piece.color
next if y < 0 or y > HEIGHT-1
draw_block(win, x, y, c)
}
score = Text.new("Score: #{@score}", "", 20)
#score.left, score.top = 10, 10
win.draw(score)
text = Text.new("Coming Next:", "", 20)
text.left, text.top = 450, 50
win.draw(text)
@next_piece.each {|c|
x, y, c = 14+c[0], 16+c[1], @next_piece.color
next if y < 0 or y > HEIGHT-1
draw_block(win, x, y, c)
}
end
def collapse_row(i)
while i < HEIGHT-1
@rows[i] = @rows[i+1]
i += 1
end
@rows[HEIGHT-1] = Array.new(WIDTH, nil)
end
def find_full_row()
HEIGHT.times {|y| return y unless @rows[y].index(nil) }
return nil
end
end
$clock = Clock.new
mode = VideoMode.new(640, 480, 32)
win = RenderWindow.new(mode, "RubySFML Test", 0)
win.showMouseCursor(false)
win.useVerticalSync(true)
grid = Grid.new()
# Simple game loop
done = false
while !done
while e = win.getEvent()
case e.type
when Event::Closed then done = true
when Event::KeyReleased then done = true if e.code == Key::Escape
when Event::KeyPressed
case e.code
when Key::Up then grid.up
when Key::Left then grid.left
when Key::Right then grid.right
when Key::Down then grid.down
end
end
end
grid.update(win)
grid.render(win)
win.display()
sleep(0.01)
end