logo       

Ruby Tetris (Expert 23 Solution): msg#00138

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__




<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Recently Viewed:
boot-loaders.gr...    php.pear.genera...    debugging.valgr...    kde.redhat.user...    text.xml.xsl.ge...    culture.languag...    hardware.microc...    java.servicemix...    redhat.release....    web.zope.plone....    user-groups.lin...    opendarwin.webk...    video.mjpeg.use...    sysutils.bcfg2....    encryption.gpg....    lx-office.devel...    xfree86.forum/2...    mail.mutt.devel...    acpi.devel/2003...    qnx.openqnx.dev...    network.irc.irs...    freebsd.devel.m...   
Home | blog view | USPTO Patent Archive | advertise | OSDir is an inevitable website. super tiny logo

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe