From: "Einar Magnús Boson" Date: 2008-11-26T13:56:40+09:00 Subject: Re: [QUIZ] Befunge (#184) --Apple-Mail-38-260377491 Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes Content-Transfer-Encoding: 7bit My take on it (in ruby 1.8.6 on leopard), please tell me of unrubyesqe (that a word?) idioms I've used. It's less terse than it could have been but I like how it let me define the operations (see below) and it's fun to watch in debug mode! My definitions: Directions = { ?^ => :up, ?> => :right, ?< => :left, ?v => :down } [?+, ?-, ?*, ?/, ?%].each do |ch| mk_i(ch) { push arg2.send(ch.chr.to_sym, arg1) } end Directions.each do |ch, dir| mk_i(ch) { turn dir } end (?0..?9).each do |i| mk_i(i) { push i.chr.to_i } end mk_i(?!) { push arg1.zero? ? 1 : 0 } mk_i(??) { turn Directions.values[rand(4)] } mk_i(?_) { turn(arg1 == 0 ? :right : :left) } mk_i(?|) { turn(arg1 == 0 ? :down : :up) } mk_i(?") { toggle_stringmode } mk_i(?:) { push arg1, arg1 } mk_i(?\\) { push arg1, arg2 } mk_i(?$) { arg1 } # discard mk_i(?.) { output arg1.to_s + " " } mk_i(?,) { output arg1.chr } mk_i(?#) { step } mk_i(?g) { push instruction_get(arg2, arg1) } mk_i(?p) { instruction_set(arg2, arg1, arg3) } mk_i(?&) { push get_int } mk_i(?~) { push get_chr } mk_i(?@) { stop } mk_i(?\ ) { } #nop mk_i(?`) { push(arg2 > arg1 ? 1 : 0) } Full solution attached, run with `ruby -d befunge2.rb [input_file]` to see what's going on, `ruby befunge.rb [input_file]` to just run normally. Requires the highline gem, `sudo install highline`. That was the only way I found to get characters from STDIN without the user having to press enter after. The paging I use is very primitive though, is there a way to make a console-app more usable, like non-blocking input without manual threads, and printing at specific co-ordinates of the terminal without having to send weird control sequences? How would you write `less` in ruby? is there an example somewhere? Having an exception signal normal halt is not very nice, I agree, but this is a hack. --Apple-Mail-38-260377491 Content-Disposition: attachment; filename=befunge2.rb Content-Type: text/x-ruby-script; x-mac-creator=54784D74; x-unix-mode=0644; name="befunge2.rb" Content-Transfer-Encoding: 7bit require 'rubygems' require 'highline' require "highline/import" include HighLine::SystemExtensions class Instruction attr_reader :cmd def initialize chr, &b @cmd = chr @command = b (@@all ||= {}).update @cmd => self end def invoke state @args = [] @state = state instance_eval &@command end def self.get(ch); @@all[ch]; end def to_s; @cmd.chr; end private # works because all instructions can be expressed as # one function call and arguments are evaluated first. def method_missing name, *args if /^arg(\d)$/ =~ name.to_s then arg = $1.to_i - 1 while not @args[arg] @args << @state.pop end @args[arg] else @state.send(name, *args) end end #make instructions def self.mk_i chr, &b Instruction.new chr, &b end Directions = { ?^ => :up, ?> => :right, ?< => :left, ?v => :down } [?+, ?-, ?*, ?/, ?%].each do |ch| mk_i(ch) { push arg2.send(ch.chr.to_sym, arg1) } end Directions.each do |ch, dir| mk_i(ch) { turn dir } end (?0..?9).each do |i| mk_i(i) { push i.chr.to_i } end mk_i(?!) { push arg1.zero? ? 1 : 0 } mk_i(??) { turn Directions.values[rand(4)] } mk_i(?_) { turn(arg1 == 0 ? :right : :left) } mk_i(?|) { turn(arg1 == 0 ? :down : :up) } mk_i(?") { toggle_stringmode } mk_i(?:) { push arg1, arg1 } mk_i(?\\) { push arg1, arg2 } mk_i(?$) { arg1 } # discard mk_i(?.) { output arg1.to_s + " " } mk_i(?,) { output arg1.chr } mk_i(?#) { step } mk_i(?g) { push instruction_get(arg2, arg1) } mk_i(?p) { instruction_set(arg2, arg1, arg3) } mk_i(?&) { push get_int } mk_i(?~) { push get_chr } mk_i(?@) { stop } mk_i(?\ ) { } #nop mk_i(?`) { push(arg2 > arg1 ? 1 : 0) } end class State attr_reader :stack, :result, :steps def initialize source @steps = 0 @stack = [] @result = "" @stringmode = false @instructions = Array.new(25) { i = Instruction.get(?\ ); (1..80).map { i } } source.each_with_index do |line, y| x=0 line.each_byte do |b| instruction_set x, y, b x += 1 end end @PC_x, @PC_y = 0, 0 turn :right end def toggle_stringmode @stringmode ^= true end def instruction_set x, y, ch i = Instruction.get(ch) || Instruction.mk_i(ch) @instructions[y][x] = i end def instruction_get x, y @instructions[y][x].cmd end def push *ch @stack.push *ch end def pop # is this really right? defaulting to 0 at underflow..? # a lot of programs seem to depend on it though @stack.pop || 0 end def output str if $DEBUG @result << str else print str end end def turn direction @direction = direction end class Done e puts "#{$/}Done in #{befunge.steps} steps." ensure puts befunge.result if not $DEBUG end end __END__ v >v"Please enter a number n, for n! "0< ,: >$*99g1-:99p#v_.25*,@ ^_&:1-99p>:1-:!|10 < ^ < --Apple-Mail-38-260377491 Content-Type: text/plain; charset=US-ASCII; format=flowed Content-Transfer-Encoding: 7bit einarmagnus --Apple-Mail-38-260377491--