# This sample need ruby 1.8 or 1.6 with shim
# Thanks to Simon Strandgaard
require 'sdl'
if RUBY_VERSION < "1.7" then
require 'features/ruby18'
end
class Object
def deep_clone
Marshal::load(Marshal.dump(self))
end
end
class Array
def random
at(Kernel.rand(size))
end
end
class Pattern
def initialize(x, y, *data)
@x = x
@y = y
@data = data
end
attr_reader :data, :x, :y
alias width x
def rotate
@data = @data.reverse.transpose
@x, @y = @y, @x
end
PAT1 = Pattern.new(4, 1, [1, 1, 1, 1])
PAT2 = Pattern.new(3, 2, [1, 1, 1], [0, 1, 0])
PAT3 = Pattern.new(3, 2, [1, 1, 1], [1, 0, 0])
PAT4 = Pattern.new(3, 2, [1, 1, 1], [0, 0, 1])
PAT5 = Pattern.new(2, 2, [1, 1], [1, 1])
PAT6 = Pattern.new(3, 2, [1, 1, 0], [0, 1, 1])
PAT7 = Pattern.new(3, 2, [0, 1, 1], [1, 1, 0])
def Pattern::patterns
[PAT1, PAT2, PAT3, PAT4, PAT5, PAT6, PAT7]
end
end
class Level
def initialize(width=10, height=15)
@height = height
@width = width
@data = Array.new(height) { Array.new(width, 0) }
backup_background
end
attr_reader :width, :height
def test
@data[0][0] = 1
@data[0][@width-1] = 1
@data[@height-1][0] = 1
@data[@height-1][@width-1] = 1
end
def bg_is_collision(offset_x, offset_y, pattern)
return true if pattern.x + offset_x > @width
return true if pattern.y + offset_y > @height
y = offset_y
pattern.data.each do |row|
x = offset_x
row.each do |cell|
if (cell != 0) and (@bg[y][x] != 0)
return true
end
x += 1
end
y += 1
end
return false
end
def restore_background
@data = @bg
end
def backup_background
@bg = @data.deep_clone
end
def or_pattern(offset_x, offset_y, pattern)
backup_background
y = offset_y
pattern.data.each do |row|
x = offset_x
row.each do |cell|
@data[y][x] = cell if cell != 0
x += 1
end
y += 1
end
end
def find_filled_rows
res = []
@data.each_index do |y|
ok = true
@data[y].each do |cell|
if cell == 0
ok = false
break
end
end
res << y if ok
end
res
end
def remove_rows(*rows)
rows.uniq!
rows.each do |y|
@bg[y] = nil
end
@bg.compact!
extra = Array.new(rows.size) { Array.new(@width, 0) }
@bg = extra + @bg
raise "integrity error" if @bg.size != @height
end
end
class Level
def load_render_data
@image = SDL::Surface.loadBMP("icon.bmp")
@image.setColorKey( SDL::SRCCOLORKEY ,0)
@image = @image.displayFormat
@step_x = 32 # todo: image width
@step_y = 32 # todo: image height
# todo: raise exception is level does not fit to screen!
@offset_x = 100
@offset_y = 20
@fill_removal_dir = false
end
def render
y = @offset_y
@data.each do |row|
render_line(y, row)
y += @step_y
end
end
def render_line(y, cells)
x = @offset_x
cells.each do |cell|
i = (cell == 0) ? 63 : 255
@image.setAlpha(SDL::SRCALPHA,i)
$screen.put(@image,x,y)
x += @step_x
end
end
FILL = (20 * 256*256) + (0 * 256) + 10
def render_removal(rows)
$screen.fillRect(0,0,640,512,0)
render
rows.reverse_each do |row|
y = (row * @step_y) + @offset_y
if @fill_removal_dir
i = 0
x = @offset_x
while i < @width
$screen.fillRect(x,y,@step_x,@step_y,FILL)
$screen.flip
i += 1
x += @step_x
end
@fill_removal_dir = false
else
i = 0
x = @offset_x + ((@width-1)*@step_x)
while i < @width
$screen.fillRect(x,y,@step_x,@step_y,FILL)
$screen.flip
i += 1
x -= @step_x
end
@fill_removal_dir = true
end
end
end
end
SDL.init( SDL::INIT_VIDEO )
$screen = SDL::setVideoMode(640,512,24,SDL::SWSURFACE)
level = Level.new
level.test
level.load_render_data
pat = Pattern::PAT3.clone
pat_x = 5
pat_y = 0
time_step = 0.5
time = Time.now
launch_new_pattern = true
loop do
if launch_new_pattern
launch_new_pattern = false
time_step = 0.5
time = Time.now
pat = Pattern::patterns.random.clone
pat_x = 5
pat_y = 0
level.backup_background
if level.bg_is_collision(pat_x, pat_y, pat)
puts "Sorry you are game over"
sleep 3
raise "game over"
end
level.or_pattern(pat_x, pat_y, pat)
end
# timer events
while Time.now > time + time_step
pat_y += 1
if level.bg_is_collision(pat_x, pat_y, pat)
launch_new_pattern = true
rows = level.find_filled_rows
if rows.size > 0
puts "rows filled"
p rows
level.render_removal(rows)
level.backup_background
level.remove_rows(*rows)
level.restore_background
end
else
level.restore_background
level.or_pattern(pat_x, pat_y, pat)
end
time += time_step
end
old_pat_x = pat_x
old_pat_y = pat_y
rotate = false
# handle keystrokes
while event = SDL::Event2.poll
case event
when SDL::Event2::Quit then exit
when SDL::Event2::KeyDown
case event.sym
when SDL::Key::ESCAPE then exit
when SDL::Key::UP then rotate = true
when SDL::Key::DOWN then time_step = 0.1
when SDL::Key::LEFT
pat_x -= 1 if pat_x > 0
when SDL::Key::RIGHT
pat_x += 1 if pat_x < (level.width - pat.width)
end
end
end
SDL::Key.scan
# move pattern & do collition tests
if (pat_x <=> old_pat_x) or (pat_y <=> old_pat_y) or (rotate == true)
old_pat = pat.clone
pat.rotate if rotate
# if collision then restore last working-state
if level.bg_is_collision(pat_x, pat_y, pat)
pat_x = old_pat_x
pat_y = old_pat_y
pat = old_pat
else
# collision avoided.. therefore don't launch new pattern
launch_new_pattern = false
level.restore_background
level.or_pattern(pat_x, pat_y, pat)
end
end
# repaint screen
$screen.fillRect(0,0,640,512,0)
level.render
GC.start # release memory.. otherwise it would grow steady
$screen.flip
end
syntax highlighted by Code2HTML, v. 0.9.1