Hopefully the quiz isn't intimidating... It's a fairly simple language
to implement. It took me about 30 minutes to get a simple,
straightforward version working (my submission, shown below), and I
may do some revisions later to try shrinking the code and making it
more Ruby-ish. (Also to handle some conditions not currently handled
well...) I mostly want to revise the run and step methods to be less
case-y.
Feel free to steal this and make it better, or just to look at to get
an idea of how to implement your own.
#!/usr/bin/env ruby
class Befunge93
def self.load_and_run(filename)
self.new(File.read(filename)).run
end
def initialize(text)
rows = text.split(/[\r\n]+/) # split
input into rows
@hgt = rows.size # find
program height
@wid = rows.max { |a, b| a.size <=> b.size }.size # find
program width
@code = rows.map { |row| row + " " * (@wid - row.size) } # pad
rows, store code (r, c)
@pc, @dir = [0, 0], :east
@stack = []
@stringmode = false
end
def run
loop do
if @stringmode
case curr
when ?"
@stringmode = false
else
push curr
end
else
case curr
when ?0..?9
push (curr - ?0)
when ?+, ?-, ?*, ?/, ?%
b, a = pop, pop
push a.send(curr.to_sym, b)
when ?!
push (pop.zero? ? 1 : 0)
when ?`
b, a = pop, pop
push (a > b ? 1 : 0)
when ?.
puts pop
when ?>
@dir = :east
when ?<
@dir = :west
when ?^
@dir = :north
when ?v
@dir = :south
when ??
@dir = [:east, :west, :north, :south][rand(4)]
when ?_
@dir = pop.zero? ? :east : :west
when ?|
@dir = pop.zero? ? :south : :north
when ?"
@stringmode = true
when ?:
push top
when ?\\
a = pop # what about underflow?
b = pop
push a
push b
when ?$
pop
when ?.
print pop
when ?,
print pop.chr
when ?#
step
when ?g
r, c = pop, pop
push @code[r][c]
when ?p
r, c, v = pop, pop, pop
@code[r][c] = v
when ?&
print "int?> "
push gets.to_i
when ?~
print "chr?> "
push gets[0] # Ruby 1.8, maybe not 1.9?
when ?@
break
end
end
step # move program counter
end
end
private
def curr
@code[ @pc[0] ][ @pc[1] ]
end
def step
case @dir
when :north
@pc[0] = (@pc[0] - 1 + @hgt) % @hgt
when :south
@pc[0] = (@pc[0] + 1) % @hgt
when :east
@pc[1] = (@pc[1] + 1) % @wid
when :west
@pc[1] = (@pc[1] - 1 + @wid) % @wid
end
end
def push(val)
@stack.push(val)
end
def pop
@stack.pop || 0
end
def top
@stack.last || 0
end
end
if __FILE__ == $0
Befunge93.load_and_run(ARGV.shift)
end