197 lines
4.7 KiB
Ruby
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
|
||
|
|