Here is my very simple solution, you controll the player using WASD for  
moving up, left, down and right and R for restarting the game. It should  
run on any Ruby 1.8.

class Level
   def initialize(level)
     @level = level
   end

   def play
     while count_free_crates > 0
       printf "\n%s\n\n> ", self
       c = gets
       c.each_byte do |command|
         case command
           when ?w
             move(0, -1)
           when ?a
             move(-1, 0)
           when ?s
             move(0, 1)
           when ?d
             move(1, 0)
           when ?r
             return false
         end
       end
     end
     printf "\n%s\nCongratulations, on to the next level!\n", self
     return true
   end

private

   def move(dx, dy)
     x, y = find_player
     dest = self[x+dx, y+dy]
     case dest
       when ?#
         return
       when ?o, ?*
         dest2 = self[x+dx*2, y+dy*2]
         if dest2 == 32
           self[x+dx*2, y+dy*2] = ?o
         elsif dest2 == ?.
           self[x+dx*2, y+dy*2] = ?*
         else
           return
         end
         dest = (dest == ?o) ? 32 : ?.
     end
     self[x+dx, y+dy] = (dest == 32) ? ?@ : ?+
     self[x, y] = (self[x, y] == ?@) ? 32 : ?.
   end

   def count_free_crates
     @level.scan(/o/).size
   end

   def find_player
     pos = @level.index(/@|\+/)
     return pos % 19, pos / 19
   end

   def [](x, y)
     @level[x + y * 19]
   end

   def []=(x, y, v)
     @level[x + y * 19] = v
   end

   def to_s
     (0...16).map {|i| @level[i * 19, 19]}.join("\n")
   end
end

levels = File.readlines('sokoban_levels.txt')
levels = levels.map {|line| line.chomp.ljust(19)}.join("\n")
levels = levels.split(/\n {19}\n/).map{|level| level.gsub(/\n/, '')}

levels.each do |level|
   redo unless Level.new(level.ljust(19*16)).play
end
-- 
exoticorn/farbrausch