Last active
November 1, 2019 21:24
-
-
Save ohmree/a37c0c492f0463b8fbc80d13adc45b55 to your computer and use it in GitHub Desktop.
HERE BE DRAGONS
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 | |
# The main befunge interpreter class. | |
class Interpreter | |
def initialize(code = nil) | |
# TODO: figure out a good starting size 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. | |
@instructions = { | |
' ' => ->(_) {}, | |
'0' => ->(s) { s.stack.push(0) }, | |
'1' => ->(s) { s.stack.push(1) }, | |
'2' => ->(s) { s.stack.push(2) }, | |
'3' => ->(s) { s.stack.push(3) }, | |
'4' => ->(s) { s.stack.push(4) }, | |
'5' => ->(s) { s.stack.push(5) }, | |
'6' => ->(s) { s.stack.push(6) }, | |
'7' => ->(s) { s.stack.push(7) }, | |
'8' => ->(s) { s.stack.push(8) }, | |
'9' => ->(s) { s.stack.push(9) }, | |
'"' => lambda do |s| | |
until @cursor == '"' | |
s.move | |
s.stack.push(s.cursor.ord) | |
end | |
end, | |
',' => ->(s) { putc s.stack.pop }, | |
# FIXME: these four are the problematic ones | |
'>' => ->(s) { s.direction = Directions::RIGHT }, | |
'<' => ->(s) { s.direction = Directions::LEFT }, | |
'v' => ->(s) { s.direction = Directions::DOWN }, | |
'^' => ->(s) { s.direction = Directions::UP }, | |
'@' => ->(s) { s.quit }, | |
'*' => ->(s) { s.stack.push(s.stack.pop * s.stack.pop) } | |
} | |
# Might need to add more exit behavior later. | |
def quit | |
exit 0 | |
end | |
# Step once in the direction `@direction`. | |
# | |
# If the char at the instruction pointer | |
# is a valid instruction, call it. | |
# | |
# We pass self as a parameter because class instance | |
# vars can't know about the current instance. | |
def step | |
if self.class.instructions.include? @cursor | |
self.class.instructions[@cursor].call(self) | |
end | |
move | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment