I recently decided to try to code "yet another" brainfuck interpreter, but I have a problem. It prints out the wrong numbers, when it should put our hello,world!. Does anyone know why this is happening? Thanks in advance! I cannot figure out why it's doing this. Please help! I am only a beginner. Sorry for the bad coding style, but please don't change the whole thing. I would rather recieve tips.
from copy import copy
import sys
def brainfuckintepreter(program):
# find pairs of brackets
brackets = {}
bbrackets = {}
def findmatchingclosingbracket(ctr):
level = 0
if program[ctr] == '[':
while True:
if program[ctr] == '[':
level += 1
elif program[ctr] == ']':
level -= 1
if level == 0:
return ctr
break
ctr += 1
def findmatchingopeningbracket(ctr):
level = 0
if program[ctr] == ']':
while True:
if program[ctr] == '[':
level -= 1
elif program[ctr] == ']':
level += 1
if level == 0:
return ctr
break
ctr -= 1
"""
ctr = 0
for a in program:
if a == '[':
f = copy(findmatchingclosingbracket(ctr))
brackets[ctr] = f
bbrackets[f] = ctr
ctr += 1
print(brackets)
print(bbrackets)
"""
# running the program
tape = [0] * 3000
pointer = 1500
counter = 0
results = ""
valid = True
while counter != len(program) and valid:
a = program[counter]
# move right
if a == '>':
if pointer == len(tape) - 1:
tape.append(0)
pointer += 1
# move left
elif a == '<':
if pointer == 0:
raise ValueError("On index ", counter, ", the program tried to move to -1 on the tape")
valid = False
return valid
else:
pointer -= 1
# increment
elif a == '+':
if tape[pointer] == 255:
tape[pointer] = 0
else:
tape[pointer] += 1
# decrement
elif a == '-':
if tape[pointer] == 0:
tape[pointer] = 255
else:
tape[pointer] -= 1
# output character
elif a == '.':
t = chr(tape[pointer])
results += t
print(t, end='')
# input character
elif a == ',':
tape[pointer] = ord(sys.stdin.read(1))
# opening bracket
elif a == '[':
if tape[pointer] == 0:
pointer = findmatchingclosingbracket(pointer)
# closing bracket
elif a == ']':
if tape[pointer] != 0:
pointer = findmatchingopeningbracket(counter)
counter += 1
"""
for b in tape:
if b != 0:
print(b)
"""
brainfuckintepreter('++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.')
Edit:
I changed my code after some revisions, but same problem.
Your loops are the problem.
level = 0
while level > 0:
...
That loop will never be entered. The condition is immediately false (you're setting level to 0, right before checking if it is greater than 0).
You could change that to a do..while loop instead of a while loop (make a while true loop and check the condition at the end to decide whether to break out of the loop or not) and start checking at the current pointer position (include the current [ or ]), not at the next character after it.
Also here:
if tape[pointer] == 0:
pointer = findmatchingclosingbracket(pointer)
you should be passing and updating the program counter, not the tape pointer on the second line. And likewise for the other one just below it.
Related
I am rather new to python and am attempting to create a terminal based minesweeper clone. Everything has been going well other than implementing the flood fill algorithm. Whenever I run the flood fill it just gets stuck looping indefinitely. If anyone could help with this it would be greatly appreciated.
import random
from queue import Queue
'⚑'
fieldY = 10
fieldX = 5
validFloodFills = ['1','2','3','4','5','6','7','8','◼']
visibleField = []
internalField = []
#creates blank fields with the ◼ character
def generateField(grid, y , x):
for i in range(y):
tempList = []
for i in range(x):
tempList.append("◼")
grid.append(tempList)
#places bombs into the internal field according to the numOfBombs argument
def placeBombs(numOfBombs):
generateField(internalField, fieldY, fieldX)
placed = 0
#while loop is use becuase you cannot step an iter back with a for loop
while placed != numOfBombs:
y = random.randint(0, fieldY-1)
x = random.randint(0, fieldX-1)
if internalField[y][x] == 'X':
continue
else:
internalField[y][x] = 'X'
placed += 1
#loops through all tiles and numbers them according to the number of adjacent bombs
def placeInternalNumbers():
global fieldY
global fieldX
#loop through all tiles and check all 8 neighbours
#if neighbour is a bomb then add 1 to int
#after checking all neighbours the value of that int is assigned to that tile
for idxY, y in enumerate(internalField):
for idxX, x in enumerate(y):
#makes indexes in range
fieldY -= 1
fieldX -= 1
idxY -= 1
idxX -= 1
adjacentBomb = 0
if internalField[idxY][idxX] == 'X':
continue
#look down one
if idxY != fieldY:
if internalField[idxY+1][idxX] == 'X':
adjacentBomb += 1
#look up one
if idxY != 0:
if internalField[idxY-1][idxX] == 'X':
adjacentBomb += 1
#look right one
if idxX != fieldX:
if internalField[idxY][idxX+1] == 'X':
adjacentBomb += 1
#look left one
if idxX != 0:
if internalField[idxY][idxX-1] == 'X':
adjacentBomb += 1
#look down right
if idxY != fieldY and idxX != fieldX:
if internalField[idxY+1][idxX+1] == 'X':
adjacentBomb += 1
#look down left
if idxY != fieldY and idxX != 0:
if internalField[idxY+1][idxX-1] == 'X':
adjacentBomb += 1
#look up right
if idxY != 0 and idxX != fieldX:
if internalField[idxY-1][idxX+1] == 'X':
adjacentBomb += 1
#look up left
if idxY != 0 and idxX != 0:
if internalField[idxY-1][idxX-1] == 'X':
adjacentBomb += 1
#checks if the adjacent bombs are zero and does not update the tile if there are no adjacent bombs
if adjacentBomb == 0:
continue
internalField[idxY][idxX] = str(adjacentBomb)
#prints the field parsed to the function
def printField(field):
for i in field:
print(i)
#floodFill
def floodFill(y, x):
#TODO needs to read and compare with the internal board and then write changes to the visible board
n = len(visibleField)
m = len(visibleField[0])
if y < 0 or y >= n or x < 0 or x >= m or internalField[y][x] not in validFloodFills:
return
else:
visibleField[y][x] = internalField[y][x]
floodFill(y+1, x)
floodFill(y-1, x)
floodFill(y, x+1)
floodFill(y, x-1)
#main loop
def mainLoop():
cont = True
while cont:
printField(visibleField)
print()
printField(internalField)
print("To select a tile type 'S' then the x coordinate and y coordinate all seperated by spaces\nTo flag a tile type 'F' then the x coordinate and y coordinate all seperated by spaces")
select = input().split(' ') #TODO input validation, use length to check for multiple spaces
operator = select[0]
x = int(select[1])
y = int(select[2])
if operator == 'S':
if internalField[y][x] == 'X':
break
else:
floodFill(y, x)
continue
elif operator == 'F':
pass
#TODO more stuff on fail eg printing revealed minefield
print("You hit a bomb, better luck next time")
generateField(visibleField, fieldY, fieldX)
placeBombs(5)
placeInternalNumbers()
mainLoop()
printField(visibleField)
print()
printField(internalField)
Trying to make a brainfuck interpreter in python from scratch just for fun, I'm almost done with the code however this small error is coming up whenever I try interpreting ",[>++++<]" Which is supposed to input a number, and multiply it by 4. It stops and gets this error
File ".\bfInterpreter.py", line 42, in endWhile
if self.stack[self.pointer]: self.place = self.inWhile.pop(-1)
IndexError: pop from empty list
Any ideas?
here is the code that the error is in:
import sys
import time
class Operator:
def __init__(self) -> None:
self.pointer = 0
self.place = 0
self.inWhile = []
self.stack = [0]
def plus(self):
self.stack[self.pointer]+=1
def minus(self):
self.stack[self.pointer]-=1
def left(self):
if self.pointer > 0: self.pointer -= 1
else: raise IndexError
def right(self):
self.pointer += 1
try: self.stack[self.pointer] = 0
except: self.stack.append(0)
def output(self):
print(ascii(self.stack[self.pointer]))
def inp(self):
val = input("Num: ")
try: self.stack[self.pointer] = int(val)
except:
print("Please input a number")
self.inp()
def startWhile(self):
self.inWhile.append(self.place)
print(self.inWhile)
def endWhile(self):
print(self.inWhile)
if self.stack[self.pointer]: self.place = self.inWhile.pop(-1)
def interpret(self, bf):
self.place = 0
while self.place < len(bf):
if bf[self.place] == "+": self.plus()
elif bf[self.place] == "-": self.minus()
elif bf[self.place] == "<": self.left()
elif bf[self.place] == ">": self.right()
elif bf[self.place] == "[": self.startWhile(bf)
elif bf[self.place] == "]": self.endWhile()
elif bf[self.place] == ",": self.inp()
elif bf[self.place] == ".": self.output()
else: raise SyntaxError
self.place += 1
print(self.stack)
def main():
start = time.time()
op = Operator()
op.interpret(",[>++++<]")
print(f"Ended in {time.time() - start} second(s)")
if __name__ == "__main__": main()
Edit: self.stack is basically the memory that brainfuck is allowed to access and edit, so the "[" starts a while loop, checking if a certain spot in the stack has reached 0 every cycle, and if it's not 0, it repeats the code in the brackets
The main problem is self.inWhile.pop(-1) in command ] - it removes value from list and in next loop this list is empty - so pop(-1) raises your error message.
But there is more mistakes and problems in [ and ].
All commands runs self.place += 1 but ] shouldn't do this because this way it jump to first command after [ but it should run again [. Maybe if it would run again [ then it could add again value to inWhile and self.inWhile.pop(-1) whouldn't make problem.
But there is other problem with [ - it should check if value is 0 and jump to first command after ]. It would need another list to remember position of ] but it seems more complicated - because if at start value is 0 then it couldn't yet run ] and it couldn't add position to list. Better is to search matching ] in string with commands. The same method I used to search [ (instead of using inWhile).
Other problem is > you always try to set stack[place] = 0 but if you run > and next < and again > then stack[place] may already exists and it may have some value and stack[place] = 0 may remove this value. I rather use while loop with pointer >= len(stack) to append values only if stack is really too short.
Other problem is that program in branfuck runs loop forever because it doesn't have - to decreate inputed value. It needs also >. after loop to display result.
,[->++++<]>.
My version without inWhile but with loop which search matching [ or matching ].
import sys
import time
class Operator:
def __init__(self):
self.place = 0 # command place
self.pointer = 0 # stack pointer
self.stack = [0]
def input(self):
#print('[DEBUG] input')
while True:
val = input("Number: ")
try:
self.stack[self.pointer] = int(val)
break
except:
print("Please input a integer number")
def output(self):
#print('[DEBUG] output')
print(ascii(self.stack[self.pointer]))
def plus(self):
#print('[DEBUG] plus')
self.stack[self.pointer] += 1
def minus(self):
#print('[DEBUG] minus')
self.stack[self.pointer] -= 1
def left(self):
#print('[DEBUG] left')
if self.pointer > 0:
self.pointer -= 1
else:
raise IndexError
def right(self):
#print('[DEBUG] right')
self.pointer += 1
while len(self.stack) <= self.pointer:
self.stack.append(0)
def start_loop(self):
#print('[DEBUG] start_loop')
#print('self.place:', self.place)
if self.stack[self.pointer] == 0:
# find matching `]` to jump to next command after `]`
counter = 1
while counter > 0:
self.place += 1
char = self.bf[self.place]
if char == '[':
counter += 1
elif char == ']':
counter -= 1
#print('counter:', counter)
def end_loop(self):
#print('[DEBUG] end_loop')
#print('self.place:', self.place)
# find matching `[` to run again `[` and all loop
counter = 1
while counter > 0:
self.place -= 1
char = self.bf[self.place]
if char == '[':
counter -= 1
elif char == ']':
counter += 1
#print('counter:', counter)
def interpret(self, bf):
self.bf = bf
self.place = 0
while self.place < len(bf):
char = self.bf[self.place]
#print('[DEBUG] interpret:', self.place, bf[self.place])
if char == ",":
self.input()
self.place += 1
elif char == ".":
self.output()
self.place += 1
elif char == "+":
self.plus()
self.place += 1
elif char == "-":
self.minus()
self.place += 1
elif char == "<":
self.left()
self.place += 1
elif char == ">":
self.right()
self.place += 1
elif char == "[":
self.start_loop()
self.place += 1
elif char == "]":
self.end_loop()
# it has to be without `self.place += 1`
else:
raise SyntaxError
print('stack:', self.stack)
def main():
start = time.time()
op = Operator()
op.interpret(",[->++++<]>.") # input*4
#op.interpret(",[->+>++>+++>++++<<<<]>.>.>.>.") # input*1, input*2, input*3, input*4
#op.interpret(",[->+<>>++<<>>>+++<<<>>>>++++<<<<]>.>.>.>.") # input*1, input*2, input*3, input*4
end = time.time()
print(f"Ended in {end - start:.2f} second(s)")
if __name__ == "__main__":
main()
the problem I'm trying to solve is this Codewars kata: https://www.codewars.com/kata/58678d29dbca9a68d80000d7/train/python
I'm passing 123 out of 124 tests, but the one I'm failing is related to how I'm handling nested loops. I just can't seem to figure out a way to replicate the problem because they don't show you the test input. Any help is appreciated; here's what I have for the problem so far:
import re
def interpreter(code, tape):
#Filter out non-command characters
code_pure = re.findall(r"(\>|\<|\*|\[|\])", code)
sf = "".join(code_pure)
#Convert tape to list so that elements are mutable
tape_list = []
for bit in tape:
tape_list.append(bit)
#Keep track of pointer and instruction position
pointer = 0
instr = 0
brack_pos = [] #Contains positions of "[" brackets
while instr < len(sf):
#If pointer goes out of bounds then end the program
if pointer >= len(tape_list) or pointer < 0:
return "".join(tape_list)
#"*" flips the current bit
if sf[instr] == "*":
if tape_list[pointer] == "1":
tape_list[pointer] = "0"
elif tape_list[pointer] == "0":
tape_list[pointer] = "1"
instr += 1
#Move right one bit
elif sf[instr] == ">":
pointer += 1
instr += 1
#Move left one bit
elif sf[instr] == "<":
pointer -= 1
instr += 1
elif sf[instr] == "[":
#If pointer is on 0, skip the loop
if tape_list[pointer] == "0":
brack_cnt = 1
while brack_cnt != 0:
instr += 1
if sf[instr] == "[":
brack_cnt += 1
elif sf[instr] == "]":
brack_cnt -= 1
instr += 1
#If pointer is 1, step into the loop
elif tape_list[pointer] != "0":
brack_pos.append(instr)
instr += 1
elif sf[instr] == "]":
if tape_list[pointer] == "0":
instr += 1
elif tape_list[pointer] != "0":
instr = brack_pos[-1]
brack_pos.pop()
return "".join(tape_list)
The problem is in the last if-block checking for sf[instr] == "]" where brack_pos.pop() is executed only when looping back to the opening bracket, not when moving past the closing bracket. I.e. brack_pos.pop() needs to be unconditional.
Seems like a fun exercise btw., here's what I came up with:
def interpreter(code, tape):
# Implement your interpreter here
tape = list(tape)
# Find matching brackets
jump = [0]*len(code)
open_bracket_stack = []
for i,c in enumerate(code):
if c == '[':
open_bracket_stack.append(i)
elif c == ']':
matching = open_bracket_stack.pop()
jump[i] = matching
jump[matching] = i
code_ptr = 0
tape_ptr = 0
while(code_ptr < len(code) and tape_ptr < len(tape) and tape_ptr >= 0):
c = code[code_ptr]
if c == '>':
tape_ptr += 1
elif c == '<':
tape_ptr -= 1
elif c == '*':
tape[tape_ptr] = '1' if tape[tape_ptr] == '0' else '0'
elif c == '[' and tape[tape_ptr] == '0':
code_ptr = jump[code_ptr]
elif c == ']' and tape[tape_ptr] == '1':
code_ptr = jump[code_ptr]
code_ptr += 1
return "".join(tape)
A friend of mine told me that she needs help with some homework, I owe her a favor so I said fine, why not. she needed help with a program that checks a sequence, if the sequence is made of the same 2 chars one after the other it will print "yes" (for example "ABABABAB" or "3$3$3$3:)
The program works fine with even length strings (for example "abab") but not with odd length one ("ububu")
I made the code messy and "bad" in purpose, computers is her worst subject so I don't want it to look obvious that someone else wrote the code
the code -
def main():
StringInput = input('your string here - ')
GoodOrBad = True
L1 = StringInput[0]
L2 = StringInput[1]
i = 0
while i <= len(StringInput):
if i % 2 == 0:
if StringInput[i] == L1:
i = i + 1
else:
GoodOrBad = False
break
if i % 2 != 0:
if StringInput[i] == L2:
i = i + 1
else:
GoodOrBad = False
break
if GoodOrBad == True:
print("yes")
elif GoodOrBad != True:
print("no")
main()
I hope someone will spot the problem, thanks you if you read everything :)
How about (assuming s is your string):
len(set(s[::2]))==1 & len(set(s[1::2]))==1
It checks that there is 1 char in the even locations, and 1 char in the odd locations.
a) Showing your friend bad and messy code makes her hardly a better programmer. I suggest that you explain to her in a way that she can improve her programming skills.
b) If you check for the character at the even position and find that it is good, you increment i. After that, you check if i is odd (which it is, since you found a valid character at the even position), you check if the character is valid. Instead of checking for odd position, an else should do the trick.
You can do this using two methods->
O(n)-
def main():
StringInput = input('your string here - ')
GoodOrBad = True
L1 = StringInput[0]
L2 = StringInput[1]
i = 2
while i < len(StringInput):
l=StringInput[i]
if(l==StringInput[i-2]):
GoodOrBad=True
else:
GoodOrBad=False
i+=1
if GoodOrBad == True:
print("yes")
elif GoodOrBad == False:
print("no")
main()
Another method->
O(1)-
def main():
StringInput = input('your string here - ')
GoodOrBad = True
L1 = set(StringInput[0::2])
L2 = set(StringInput[1::2])
if len(L1)==len(L2):
print("yes")
else:
print("no")
main()
There is a lot in this that I would change, but I am just showing the minimal changes to get it to work. There are 2 issues.
You have an off by one error in the code:
i = 0
while i <= len(StringInput):
# in the loop you index into StringInput
StringInput[i]
Say you have 5 characters in StringInput. Because your while loop is going from i = 0 to i < = len(StringInput), it is going to go through the values [0, 1, 2, 3, 4, 5]. That last index is a problem since it is off the end off StringInput.
It will throw a 'string index out of range' exception.
You need to use:
while i < len(StringInput)
You also need to change the second if to an elif (actually it could just be an else, but...) so you do not try to test both in the same pass of the loop. If you go into the second if after the last char has been tested in the first if it will go out of range again.
elif i % 2 != 0:
So the corrected code would be:
def main():
StringInput = input('your string here - ')
GoodOrBad = True
L1 = StringInput[0]
L2 = StringInput[1]
i = 0
while i < len(StringInput):
if i % 2 == 0:
if StringInput[i] == L1:
i = i + 1
else:
GoodOrBad = False
break
elif i % 2 != 0:
if StringInput[i] == L2:
i = i + 1
else:
GoodOrBad = False
break
if GoodOrBad == True:
print("yes")
elif GoodOrBad != True:
print("no")
main()
def main():
StringInput = input('your string here - ')
MaxLength = len(StringInput) // 2 + (len(StringInput) % 2 > 0)
start = StringInput[:2]
chained = start * MaxLength
GoodOrBad = chained[:len(StringInput)] == StringInput
if GoodOrBad == True:
print("yes")
elif GoodOrBad != True:
print("no")
I believe this does what you want. You can make it messier if this isn't bad enough.
I wrote this Python interpreter for a language called Self-modifying Brainf*** (SMBF). Today I discovered a bug where if the program dynamically creates code at the initial cell or after on the tape, it will not be executed. I wrote this interpreter to look as close as possible to the Ruby interpreter on the linked page. Note that this bug may exist in the original Ruby interpreter, too. I don't know, I haven't used it.
The way SMBF is different from normal BF is that the source code is placed on the tape to the left of the cell that the pointer starts at. So the program <. would print the last character of the source (a period). This works.
Note that I trimmed some code out so it's still runnable but takes less space in this post.
The interpreter:
from __future__ import print_function
import os, sys
class Tape(bytearray):
def __init__(self):
self.data = bytearray(b'\0' * 1000)
self.center = len(self.data) // 2
def __len__(self):
return len(self.data)
def __getitem__(self, index):
try:
return self.data[index + self.center]
except:
return 0
def __setitem__(self, index, val):
i = index + self.center
if i < 0 or i >= len(self.data):
# resize the data array to be large enough
new_size = len(self.data)
while True:
new_size *= 2
test_index = index + (new_size // 2)
if test_index >= 0 and test_index < new_size:
# array is big enough now
break
# generate the new array
new_data = bytearray(b'\0' * new_size)
new_center = new_size // 2
# copy old data into new array
for j in range(0, len(self.data)):
new_data[j - self.center + new_center] = self.data[j]
self.data = new_data
self.center = new_center
self.data[index + self.center] = val & 0xff
class Interpreter():
def __init__(self, data):
self.tape = Tape()
# copy the data into the tape
for i in range(0, len(data)):
self.tape[i - len(data)] = data[i]
# program start point
self.entrypoint = -len(data)
def call(self):
pc = self.entrypoint
ptr = 0
# same as -len(self.tape) // 2 <= pc + self.tape.center < len(self.tape) // 2
while -len(self.tape) <= pc < 0: # used to be "while pc < 0:"
c = chr(self.tape[pc])
if c == '>':
ptr += 1
elif c == '<':
ptr -= 1
elif c == '+':
self.tape[ptr] += 1
elif c == '-':
self.tape[ptr] -= 1
elif c == '.':
print(chr(self.tape[ptr]), end="")
elif c == ',':
sys.stdin.read(1)
elif c == '[':
if self.tape[ptr] == 0:
# advance to end of loop
loop_level = 1
while loop_level > 0:
pc += 1
if chr(self.tape[pc]) == '[': loop_level += 1
elif chr(self.tape[pc]) == ']': loop_level -= 1
elif c == ']':
# rewind to the start of the loop
loop_level = 1
while loop_level > 0:
pc -= 1
if chr(self.tape[pc]) == '[': loop_level -= 1
elif chr(self.tape[pc]) == ']': loop_level += 1
pc -= 1
pc += 1
# DEBUG
#print(pc, self.tape.data.find(b'.'))
def main():
# Working "Hello, World!" program.
#data = bytearray(b'<[.<]>>>>>>>>+\x00!dlroW ,olleH')
# Should print a period, but doesn't.
data = bytearray(b'>++++++++++++++++++++++++++++++++++++++++++++++')
intr = Interpreter(data)
intr.call()
#print(intr.tape.data.decode('ascii').strip('\0'))
if __name__ == "__main__":
main()
The problem:
This line is how I set the program (so I can run this on Ideone.com):
data = bytearray(b'++++++++++++++++++++++++++++++++++++++++++++++')
The program adds to the cell until it is 46, which is the decimal value for an ASCII ., which should print the current cell (a period). But for some reason, the program counter pc never gets to that cell. I want the program to run all code it finds until it hits the end of the tape, but I'm having a hard time getting the program counter to take into account the center of the tape, and ensure that it's still correct if the tape is resized in __setitem__.
The relevant line is (what I was trying out):
while -len(self.tape) <= pc < 0:
which was originally this:
while pc < 0:
So I think that the while line either needs to be adjusted, or I need to change it to while True: and just use a try/except while getting chr(self.tape[pc]) to determine if I've hit the end of the tape.
Does anyone see what is wrong or how to fix it?
Found a solution thanks to Sp3000.
self.end = 0 in Tape.__init__, self.end = max(self.end, index+1) in Tape.__setitem__ and replace the while in Interpreter.call with while pc < self.tape.end:.