Subject: Ruby Tetris (Expert 23 Solution)



Since different language solutions are growing more common, I'll submit my Ruby solution.

I've been learning Ruby these past few weeks, so it's still probably rough around the edges. That would be my fault, not Ruby's, which is a fun language that I think is worth a look.

My solution only implements the first milestone, because that was a lot of work and I'm tired now. <laughs>

I built it with Ruby 1.8.2 preview 2, but I BELIEVE it will run on 1.6.x (standard in the mentioned Mac OS X testing environment). All you would have to do is:

% ruby unix_term_tetris.rb

WARNING: As the name suggests, the interface I built only runs on Unix. It would be trivial to convert it to Windows, but since I'm not a Windows guy it's someone else's triviality. <laughs>

My solution uses two files, which should be placed in the same directory.

Enjoy.

James Edward Gray II

=== unix_term_tetris.rb ===

#!/usr/bin/ruby -w

require "io/wait"

require "tetris"

system "stty raw -echo"

game = Tetris::Game.new

system "clear"
puts game.draw

game.start

loop do
if game.run
system "clear"
puts game.draw
end

break if game.over?

if STDIN.ready?
case STDIN.getc
when ?q, ?Q, ?\C-c
break
when ?z, ?Z
game.move_left
when ?x, ?X
game.move_right
when ?/, ??
game.move_drop
when ?', ?"
game.move_rotate
end
system "clear"
puts game.draw
end
end

END { system "stty -raw echo" }

__END__

=== tetris.rb ===

#!/usr/bin/ruby

module Tetris
class Block
@@blocks = [ [ ["#", "#"],
["#", "#"] ],
[ [" ", "@", "@"],
["@", "@", " "] ],
[ ["%", "%", " "],
[" ", "%", "%"] ],
[ ["*", "*", "*"],
["*", " ", " "] ],
[ ["&", "&", "&"],
[" ", " ", "&"] ],
[ ["$", "$", "$"],
[" ", "$", " "] ],
[ ["+", "+", "+", "+"] ] ]

attr_reader :x, :y

def initialize(x = nil, y = nil, type = rand(7))
@x = if x.nil?
if type == 6
3
else
4
end
else
x
end
@y = if x.nil?
if type == 6
0
else
-1
end
else
y
end
@dir = 1

@type = type
@block = @@blocks[@type]
end

def drop
@y += 1
end

def move_left
@x -= 1
end

def move_right
@x += 1
end

def rotate
copy = [ ]
@block[0].size.times { copy.push((" " * @block.size).split
"") }
@block.each_with_index do |row, y|
row.each_with_index do |square, x|
copy[x][copy[0].size - 1 - y] = square
end
end
@block = copy
@dir = if @dir == 4 then 1 else @dir + 1 end

case @type
when 6
case @dir
when 1, 3
@x -= 2
@y += 2
when 2, 4
@x += 2
@y -= 2
end
when 1, 2
case @dir
when 1, 3
@x -= 1
@y += 1
when 2, 4
@x += 1
@y -= 1
end
when 3, 4, 5
case @dir
when 1
@x -= 1
@y += 1
when 2
@y -= 1
when 4
@x += 1
end
end
end

def to_a
return Marshal.load(Marshal.dump(@block))
end

def to_s
str = ""
@block.each do |row|
str += row.join() + "\r\n"
end
return str
end
end

class Board
attr_reader :line_count, :touch_count

def initialize(block = Block.new, next_block = Block.new)
@board = [ ]
1.upto 20 do
row = (" " * 10).split ""
@board << row
end

@block = block
@next_block = next_block

@line_count = 0
@touch_count = 0
end

def block=(block)
@board = to_a

@line_count = 0
0.upto @board.size - 1 do |i|
unless @board[i].include? " "
@board.delete_at i
@board.unshift((" " * 10).split "")
@line_count += 1
end
end

@touch_count = 0
count_block = @block.to_a
count_block.each_with_index do |row, i|
next if @block.y + i < 0
row.each_with_index do |square, j|
if row[j] != " "
if j == 0 and ( @block.x - 1 ==
-1 or
@board[@block.y + i][@block.x - 1] !=
" " )
@touch_count += 1
end
if j == row.size - 1 and (
@block.x + 1 + j >= 10 or
@board[@block.y + i][@block.x + 1 + j]
!= " " )
@touch_count += 1
end
if i == 0 and ( @block.y - 1 ==
-1 or
@board[@block.y - 1][@block.x + j] !=
" " )
@touch_count += 1
end
if i == count_block.size - 1 and
( @block.y + 1 + i >= 20 or
@board[@block.y + 1 + i][@block.x + j]
!= " " )
@touch_count += 1
end
end
end
end

@block = @next_block
@next_block = block
end

def can_drop?
block = @block.to_a
block.each_with_index do |row, i|
next if @block.y + 1 + i < 0
return false if @block.y + 1 + i > 19
row.each_with_index do |square, j|
if row[j] != " " and
@board[@block.y + 1 + i][@block.x + j] !=
" "
return false
end
end
end

return true
end

def can_left?
block = @block.to_a
block.each_with_index do |row, i|
next if @block.y + i < 0
row.each_with_index do |square, j|
return false if @block.x - 1 + j < 0
if row[j] != " " and
@board[@block.y + i][@block.x - 1 + j] !=
" "
return false
end
end
end

return true
end

def can_right?
block = @block.to_a
block.each_with_index do |row, i|
next if @block.y + i < 0
row.each_with_index do |square, j|
return false if @block.x + 1 + j >= 10
if row[j] != " " and
@board[@block.y + i][@block.x + 1 + j] !=
" "
return false
end
end
end

return true
end

def can_rotate?
begin
@block.rotate
block = @block.to_a
block.each_with_index do |row, i|
next if @block.y + i < 0
return false if @block.y + i > 19
row.each_with_index do |square, j|
return false if @block.x + j < 0
return false if @block.x + j >=
10
if row[j] != " " and
@board[@block.y + i][@block.x + j] !=
" "
return false
end
end
end

return true
ensure
3.times { @block.rotate }
end
end

def drop
if can_drop?
@block.drop
return true
else
return false
end
end

def full?
if @board[0].join("") !~ /^ {10}$/
return true
else
return false
end
end

def move_left
if can_left?
@block.move_left
return true
else
return false
end
end

def move_right
if can_right?
@block.move_right
return true
else
return false
end
end

def rotate
if can_rotate?
@block.rotate
return true
else
return false
end
end

def to_a
copy = Marshal.load(Marshal.dump(@board))

block = @block.to_a
block.each_with_index do |row, i|
next if @block.y + i < 0
row.each_with_index do |square, j|
next if row[j] == " "
copy[@block.y + i][@block.x + j] =
square
end
end

return copy
end

def to_s(level, lines, score)
str = ""
to_a.each do |row|
str += "|" + row.join() + "|\r\n"
end
str += ("-" * 12) + "\r\n"

details = [ "Level #{level}", "Lines #{lines}", "Score
#{score}",
"", "Next", "" ]
@next_block.to_a.each do |row|
details.push(row.join(""))
end
details.push("")

display = str.split(/\r\n/).zip(details)
display.collect! do |e|
if e[1].nil?
e[0] + "\r\n"
else
e[0] + " " + e[1] + "\r\n"
end
end

return display.join("");
end
end

class Game
attr_reader :board

def initialize
@board = Board.new

@piece_floating = false

@level = 1
@lines = 0
@score = 0
end

def draw
return @board.to_s(@level, @lines, @score)
end

def move_drop
@board.drop
end

def move_left
@board.move_left
end

def move_right
@board.move_right
end

def move_rotate
@board.rotate
end

def over?
return @board.full?
end

def run
if (@last_drop + (1 / 1.3 ** (@level - 1))) - Time.now
<= 0
unless @board.drop
if @piece_floating
@board.block = Block.new
@lines += @board.line_count
@score += @board.touch_count
@score += @board.line_count *
10 * @level
@level += 1 if @lines > 0 and
@lines % 10 == 0
@piece_floating = false
else
@piece_floating = true
end
end
@last_drop = Time.now
return true
end
return false
end

def start
@last_drop = Time.now
end
end
end

__END__






Programming list archiving by: Enterprise Git Hosting