I was doing some CodeFights and had alot of fun with this Befunge challange
Problem statement
While exploring the ruins of a golden lost city, you discovered an ancient manuscript containing series of strange symbols. Thanks to your profound knowledge of dead languages, you realized that the text was written in one of the dialects of Befunge-93. Looks like the prophecy was true: you are the one who can find the answer to the Ultimate Question of Life! Of course you brought your futuristic laptop with you, so now you just need a function that will run the encrypted message and make you the all-knowing human being.
Befunge-93 is a stack-based programming language, the programs for which are arranged in a two-dimensional torus grid. The program execution sequence starts at the top left corner and proceeds to the right until the first direction instruction is met (which can appear in the very first cell). The torus adjective means that the program never leaves the grid: when it encounters a border, it simply goes to the next command at the opposite side of the grid.
You need to write a function that will be able to execute the given
Befunge-93 program
. Unfortunately your laptop, futuristic that it is, can't handle more than10^5
instructions and will probably catch on fire if you try to execute more, so the function should exit after10^5
commands. The good news is, the prophesy said that the answer to the Ultimate Question of Life contains no more than100
symbols, so the function should return the program output once it contains100
symbols.The dialect of Befunge-93 in the manuscript consists of the following commands:
direction instructions:
>
: start moving right<
: start moving leftv
: start moving down^
: start moving up#
: bridge; skip next cellconditional instructions:
_
: pop a value; move rightif value = 0
, left otherwise|
: pop a value; move downif value = 0
, up otherwisemath operators:
+
: addition; popa
, popb
, then pusha + b
-
: subtraction; popa
, popb
, then pushb - a
*
: multiplication; popa
, popb
, then pusha * b
/
: integer division; popa
, popb
, then pushb / a
%
: modulo operation; popa
, popb
, then pushb % a
logical operators:
!
: logical NOT; pop a value,if the value = 0
, push 1, otherwise push0
`
: greater than; pop a and b, then push 1if b > a
, otherwise0
stack instructions:
:
: duplicate value on top of the stack\
: swap the top stack value with the second to the top$
: pop value from the stack and discard itoutput instructions:
.
: pop value and output it as an integer followed by a space,
: pop value and output it as ASCII character digitsOther instructions:
0-9
: push the encountered number on the stack"
: start string mode; push each character's ASCII value all the way up to the next ": (whitespace character): empty instruction; does nothing
@
: end program; the program output should be returned then?
: start moving in a random cardinal directionIf the stack is empty and it is necessary to pop a value, no exception is raised; instead, 0 is produced.
My code
import random
def befunge93(program):
height = len(program)
weigth = len(program[0])
prog_count = 0
output = ''
stack = []
current = program[0][0]
strmode = False
location = [0, 0]
direction = (0, 1)
step = ()
def safepop():
if stack:
return stack.pop()
return 0
while prog_count < 100000 and len(output) < 100:
if strmode:
if current == '"':
strmode = False
else:
stack.append(ord(current))
else:
if current == '>':
direction = (0, 1)
elif current == '<':
direction = (0, -1)
elif current == 'v':
direction = (1, 0)
elif current == '^':
direction = (-1, 0)
elif current == '#':
step = (direction[0] * 2, direction[1] * 2)
elif current == '_':
direction = (0, 1) if safepop() == 0 else (0, -1)
elif current == '|':
direction = (1, 0) if safepop() == 0 else (-1, 0)
elif current == '+':
stack.append(safepop() + safepop())
elif current == '-':
a, b = safepop(), safepop()
stack.append(b - a)
elif current == '*':
stack.append(safepop() * safepop())
elif current == '/':
a, b = safepop(), safepop()
stack.append(b // a)
elif current == '%':
a, b = safepop(), safepop()
stack.append(b % a)
elif current == '!':
stack.append(int(safepop() == 0))
elif current == '`':
stack.append(int(safepop() < safepop()))
elif current == ':':
stack += [safepop()] * 2
elif current == '\\':
stack += [safepop(), safepop()]
elif current == '$':
safepop()
elif current == '.':
output += str(safepop()) + ' '
elif current == ',':
a = safepop()
output += chr(a)
elif current.isdigit():
stack.append(int(current))
elif current == '"':
strmode = True
elif current == '?':
direction = random.choice([(0, 1), (1, 0), (0, -1), (-1, 0)])
elif current == '@':
break
if step:
location[0] += step[0]
location[1] += step[1]
step = ()
else:
location[0] += direction[0]
location[1] += direction[1]
location = [(height + location[0]) % height, (weigth + location[1]) % weigth]
current = program[location[0]][location[1]]
prog_count += 1
return output
Note
Missing standard befunge-93 operations are:
(Added for completeness sake but not needed for tests)
p
: aput
call. Popa
,b
, andv
, then change the character at (x,y) in the program to the character withASCII value v
g
: aget
call. Popa
andb
, then push ASCII value of the character at that position in the program&
: ask user for a number and push it~
: ask user for a character and push its ASCII value
Tests
This link does not contain correct test data (codefights and wiki does), if need be I'll add a few more examples.
print(befunge93([" v",
"v ,,,,,\"Hello\"<",
">48*, v",
"\"!dlroW\",,,,,,v>",
"25*,@ > "]))
# >>> Hello world\n
Any review would be welcome. :)