Created
November 1, 2019 23:29
-
-
Save ohmree/2ee77e6a2133e14303ed68b950626cd0 to your computer and use it in GitHub Desktop.
Problematic lines are 109-113
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# frozen_string_literal: true | |
# A direction (y,x). | |
# The order follows the curses convention. | |
module Directions | |
RIGHT = { y: 0, x: 1 }.freeze | |
LEFT = { y: 0, x: -1 }.freeze | |
UP = { y: 1, x: 0 }.freeze | |
DOWN = { y: -1, x: 0 }.freeze | |
end | |
# These labels corrospond to method names on the `Interpreter` class | |
INSTRUCTIONS = { | |
' ' => :instruction_noop, | |
'0' => :instruction_0, | |
'1' => :instruction_1, | |
'2' => :instruction_2, | |
'3' => :instruction_3, | |
'4' => :instruction_4, | |
'5' => :instruction_5, | |
'6' => :instruction_6, | |
'7' => :instruction_7, | |
'8' => :instruction_8, | |
'9' => :instruction_9, | |
'"' => :instruction_start_string, | |
',' => :instruction_putc, | |
'>' => :instruction_right, | |
'<' => :instruction_left, | |
'v' => :instruction_down, | |
'^' => :instruction_up, | |
'@' => :instruction_quit, | |
'+' => :instruction_add, | |
'-' => :instruction_sub, | |
'*' => :instruction_mul, | |
'/' => :instruction_div | |
}.freeze | |
# The main befunge interpreter class. | |
class Interpreter | |
def initialize(code = nil) | |
# TODO: figure out a good starting value for the stack. | |
@stack = Array.new(500) | |
@direction = Directions::RIGHT | |
# If no code string given, initialize with an empty | |
# array of 25 lines and 80 columns. | |
@grid = if code | |
self.class.grid_from_string(code) | |
else | |
Array.new(25) { Array.new(80) { ' ' } } | |
end | |
@instruction_pointer = { y: 0, x: 0 } | |
# A shortcut for "the character at the instruction pointer". | |
@cursor = @grid[0][0] | |
end | |
# Move once in the direction `@direction`. | |
def move | |
# Wrap around if exceeding 25 y or 80 x. | |
# From my understanding this is the | |
# correct behavior in Befunge 93 | |
# but not in Befunge 98. | |
y_result = @instruction_pointer[:y] + @direction[:y] | |
y_result = 0 if y_result >= 25 | |
x_result = @instruction_pointer[:x] + @direction[:x] | |
x_result = 0 if x_result >= 80 | |
@instruction_pointer = { y: y_result, x: x_result } | |
@cursor = @grid[y_result][x_result] | |
end | |
# The `s` parameter is the current instance. | |
# Might need to add more exit behavior later. | |
def quit | |
exit | |
end | |
def instruction_noop; end | |
# Here be some meta-fuckery. | |
# We define methods to push each number. | |
# Might be better to just handle numbers | |
# as an edge case in `step` but IDK. | |
'0'.upto('9') do |c| | |
define_method "instruction_#{c}".to_sym do | |
@stack.push(c.to_i) | |
end | |
end | |
def instruction_start_string | |
until @cursor == '"' | |
move | |
@stack.push(@cursor.ord) | |
end | |
end | |
def instruction_putc | |
putc @stack.pop | |
end | |
# Meta-fuckery pt. 2. | |
# These instructions change `@direction`. | |
Directions.constants.each do |name| | |
define_method "instruction_#{name.to_s.downcase}".to_sym do | |
@direction = Directions.const_get(name) | |
end | |
end | |
def instruction_quit | |
quit | |
end | |
def instruction_add | |
@stack.push(@stack.pop + @stack.pop) | |
end | |
def instruction_sub | |
a = @stack.pop | |
b = @stack.pop | |
@stack.push(b - a) | |
end | |
def instruction_mul | |
@stack.push(@stack.pop * @stack.pop) | |
end | |
def instruction_div | |
a = @stack.pop | |
b = @stack.pop | |
@stack.push(b / a) | |
end | |
# Step once through the program. | |
# | |
# If the thing at instruction pointer | |
# is a valid instruction, call it. | |
def step | |
instruction_name = INSTRUCTIONS.fetch(@cursor, :noop) | |
public_send(instruction_name, self) | |
move | |
rescue NoMethodError | |
abort <<~ERROR | |
Instructions table refers to instruction #{instruction_name}, | |
but it doesn't exist in class #{self.class}. | |
Aborting | |
ERROR | |
end | |
def print | |
@grid.each do |ary| | |
puts ary.join | |
end | |
puts | |
puts "stack: #{@stack.join(' ')}" | |
end | |
attr_reader :direction, :grid, :stack, :cursor, :instruction_pointer | |
class << self | |
attr_reader :instructions | |
# Split a string of befunge code into a 2d array for `@grid`. | |
# TODO: pad with spaces so that lines = 25, cols = 80. | |
# (this is part of the befunge specification) | |
def grid_from_string(str) | |
str.split("\n").map(&:chars) | |
end | |
end | |
private | |
# We don't want the outside world | |
# to be able to modify the direction. | |
attr_writer :direction | |
end | |
code = <<~CODE.lstrip | |
> v | |
v ,,,,,"Hello"< | |
>48*, v | |
v,,,,,,"World!"< | |
>25*,@ | |
CODE | |
# Example usage: | |
i = Interpreter.new(code) | |
i.print | |
i.step | |
i.print |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is the error I'm getting when I try to run the program | |
$ ./befunge.rb | |
> v | |
v ,,,,,"Hello"< | |
>48*, v | |
v,,,,,,"World!"< | |
>25*,@ | |
stack: | |
Traceback (most recent call last): | |
3: from ./befunge.rb:194:in `<main>' | |
2: from ./befunge.rb:144:in `step' | |
1: from ./befunge.rb:144:in `public_send' | |
./befunge.rb:109:in `block (2 levels) in <class:Interpreter>': wrong number of arguments (given 1, expected 0) (ArgumentError) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment