SHOW:
|
|
- or go back to the newest paste.
| 1 | - | ''' |
| 1 | + | |
| 2 | - | Created on Jun 13, 2013 |
| 2 | + | |
| 3 | ||
| 4 | - | @author: RSHELDON |
| 4 | + | |
| 5 | def __init__(self,a=[]): | |
| 6 | - | NOTE: currently windows only because of the msvcrt module |
| 6 | + | |
| 7 | - | linux solution for immediate command line interpretation should be implemented |
| 7 | + | |
| 8 | self.list.append(val) | |
| 9 | - | import sys |
| 9 | + | |
| 10 | - | import tty |
| 10 | + | |
| 11 | - | tty.setcbreak(sys.stdin) |
| 11 | + | |
| 12 | - | while True: |
| 12 | + | |
| 13 | - | print ord(sys.stdin.read(1)) |
| 13 | + | |
| 14 | - | ''' |
| 14 | + | |
| 15 | else: | |
| 16 | self.list.pop(n)#will error if you try to pop something that doesnt exist. good idea? | |
| 17 | def __getitem__(self,i): | |
| 18 | return self.list[i] | |
| 19 | def __repr__(self): | |
| 20 | return str(self.list) | |
| 21 | ||
| 22 | class BefungeInterpreter: | |
| 23 | def __init__(self): | |
| 24 | self.program = {}
| |
| 25 | self.tick = 0 | |
| 26 | self.stack = deque([Stack()]) | |
| 27 | self.exitStateFound = False | |
| 28 | self.pointerPosition = (0,0) | |
| 29 | self.exitValue = 0 | |
| 30 | self.storageOffset = (0,0) | |
| 31 | self.stringMode = False; | |
| 32 | self.stringCharacter = '"' | |
| 33 | self.jumpOverMode = False; | |
| 34 | self.jumpOverCharacter = ';' | |
| 35 | self.functionDictionary = {\
| |
| 36 | '+' : self.add, '-' : self.subtract, '*' : self.multiply, '/' : self.divide,\ | |
| 37 | '%' : self.modulus, '!' : self.logicalNot, '`' : self.greaterThan,\ | |
| 38 | '>' : self.east, '<' : self.west, '^' : self.north, 'V' : self.south, 'v' : self.south, '?' : self.randomDirection,\ | |
| 39 | '_' : self.westOrEast, '|' : self.northOrSouth,\ | |
| 40 | ':' : self.duplicate, '\\' : self.swap, '$' : self.pop, '.' : self.intPrint, ',' : self.strPrint,\ | |
| 41 | '#' : self.jump, 'p' : self.put, 'g' : self.get,\ | |
| 42 | '&' : self.inputNumber, '~' : self.inputChar, '@' : self.end, "noop" : self.noop,\ | |
| 43 | self.stringCharacter : self.string, 'r' : self.reverse, 'x' : self.popVector,\ | |
| 44 | 'j' : self.jumpForward, 'q' : self.quit, 'k' : self.iterate,\ | |
| 45 | '[' : self.turnLeft, ']' : self.turnRight, 'w' : self.compare, | |
| 46 | '{' : self.beginBlock, '}' : self.endBlock, "'" : self.fetchCharacter,
| |
| 47 | 'n' : self.clearStack, 'u' : self.stackUnderStack | |
| 48 | } | |
| 49 | self.EAST = (1,0) | |
| 50 | self.WEST = (-1,0) | |
| 51 | self.NORTH = (0,1) | |
| 52 | self.SOUTH = (0,-1) | |
| 53 | ||
| 54 | self.delta = self.EAST | |
| 55 | ||
| 56 | if __name__ == "__main__": | |
| 57 | if '-f' in sys.argv: | |
| 58 | self.loadASCIIFile(sys.argv[sys.argv.index('-f')+1])
| |
| 59 | if '-c' in sys.argv: | |
| 60 | self.loadCSVFile(sys.argv[sys.argv.index('-c')+1])
| |
| 61 | ||
| 62 | if '-v' in sys.argv: | |
| 63 | self.verbose = True | |
| 64 | ||
| 65 | if len(self.program): | |
| 66 | self.run() | |
| 67 | ||
| 68 | def clearProgram(self): | |
| 69 | self.program = {}
| |
| 70 | ||
| 71 | def loadASCIIFile(self,filePath): | |
| 72 | file = open(filePath) | |
| 73 | charinput = file.readlines() | |
| 74 | self.clearProgram() | |
| 75 | for y in range(0,len(charinput)): | |
| 76 | for x in range(0,len(charinput[y])-1):#1 to cut off newlines! check spec on this, we might need to account for /r/n | |
| 77 | self.program[(x,y)] = charinput[y][x] | |
| 78 | ||
| 79 | def loadCSVFile(self,filePath): | |
| 80 | self.clearProgram() | |
| 81 | y = 0 | |
| 82 | width = 0 | |
| 83 | with open(filePath, newline='') as csvfile: | |
| 84 | reader = csv.reader(csvfile, delimiter="\t", quotechar='"') | |
| 85 | for row in reader: | |
| 86 | if self.verbose: | |
| 87 | print(row) | |
| 88 | width = max(len(row),width) | |
| 89 | for x in range(0,len(row)): | |
| 90 | char = row[x] | |
| 91 | if char == '': | |
| 92 | char = ' ' | |
| 93 | self.program[(x,y)] = char | |
| 94 | y += 1 | |
| 95 | ||
| 96 | ||
| 97 | def run(self): | |
| 98 | self.pointerPosition = (0,0) | |
| 99 | ||
| 100 | while not self.exitStateFound:# and self.tick < 300: | |
| 101 | self.tick +=1 | |
| 102 | if self.verbose: | |
| 103 | currentCommand = self.getCommand() | |
| 104 | print(self.stack) | |
| 105 | #print("pointer position: " + str(self.pointerPosition))
| |
| 106 | #print("direction: " +str(self.delta))
| |
| 107 | print("current command: " + currentCommand)
| |
| 108 | #print('tick: '+ str(self.tick))
| |
| 109 | print('-'*20)
| |
| 110 | self.processCommand() | |
| 111 | ||
| 112 | return self.exitValue | |
| 113 | ||
| 114 | def getCommand(self): | |
| 115 | if self.stringMode: | |
| 116 | if self.program.get(self.pointerPosition," ") == " ": | |
| 117 | #return space and set program counter to place before the next value | |
| 118 | while self.program.get(self.pointerPosition," ") == " ": | |
| 119 | self.advance() | |
| 120 | self.retreat() | |
| 121 | return " " | |
| 122 | else: | |
| 123 | return self.program[self.pointerPosition] | |
| 124 | else: | |
| 125 | if self.program.get(self.pointerPosition," ") == ";": | |
| 126 | self.advance() | |
| 127 | while self.program.get(self.pointerPosition," ") != ";": | |
| 128 | self.advance() | |
| 129 | self.advance() | |
| 130 | return self.getCommand() | |
| 131 | elif self.program.get(self.pointerPosition," ") == " ": | |
| 132 | while self.program.get(self.pointerPosition," ") == " ": | |
| 133 | self.advance() | |
| 134 | return self.getCommand() | |
| 135 | else: | |
| 136 | return self.program[self.pointerPosition] | |
| 137 | ||
| 138 | def processCommand(self): | |
| 139 | currentCommand = self.getCommand() | |
| 140 | if self.stringMode: | |
| 141 | self.string() | |
| 142 | elif currentCommand in set("1234567890abcdef"):#TODO: make these into functions cuz its faster
| |
| 143 | self.push(int(currentCommand,16))#pretty sure we should do int here | |
| 144 | else: | |
| 145 | self.functionDictionary.get(currentCommand,self.error)() | |
| 146 | self.advance() | |
| 147 | ||
| 148 | def error(self): | |
| 149 | print("unexpected character encountered, '" + str(self.getCommand()) + "' at " + str(self.pointerPosition))
| |
| 150 | exit() | |
| 151 | ||
| 152 | def advance(self):#TODO: implement lahey-space wraparound | |
| 153 | screenspaceDelta = (self.delta[0],-self.delta[1])#delta is stored as coordinate delta. in screenspace positive y is flipped. this affects the turning functions | |
| 154 | self.pointerPosition = tuple(map(lambda x,y: x+y,self.pointerPosition,screenspaceDelta)) | |
| 155 | ||
| 156 | def retreat(self):#technically I dont have to implement the wraparound for this function because its internal | |
| 157 | self.pointerPosition = tuple(map(lambda x,y: x-y,self.pointerPosition,self.delta)) | |
| 158 | ||
| 159 | def pop(self,n=""):#TODO: figure out how they do default n | |
| 160 | return self.stack[0].pop(n) | |
| 161 | def push(self,value): | |
| 162 | self.stack[0].push(value) | |
| 163 | ||
| 164 | #command functions | |
| 165 | def add(self): | |
| 166 | a,b = self.pop(),self.pop() | |
| 167 | self.push(a+b) | |
| 168 | def subtract(self): | |
| 169 | a,b = self.pop(),self.pop() | |
| 170 | self.push(b-a) | |
| 171 | def multiply(self): | |
| 172 | a,b = self.pop(),self.pop() | |
| 173 | self.push(a*b) | |
| 174 | def divide(self): | |
| 175 | a,b = self.pop(),self.pop() | |
| 176 | self.push(b//a)#will error on 0 currently | |
| 177 | def modulus(self): | |
| 178 | a,b = self.pop(),self.pop() | |
| 179 | self.push(b%a)#will error on 0 currently | |
| 180 | def logicalNot(self):#not is reserved lol | |
| 181 | a = self.pop() | |
| 182 | if a==0: | |
| 183 | a = 1 | |
| 184 | else: | |
| 185 | a = 0 | |
| 186 | self.push(a) | |
| 187 | def greaterThan(self): | |
| 188 | a,b = self.pop(),self.pop() | |
| 189 | if b>a: | |
| 190 | self.push(1) | |
| 191 | else: | |
| 192 | self.push(0) | |
| 193 | def west(self): | |
| 194 | self.delta = self.WEST | |
| 195 | def east(self): | |
| 196 | self.delta = self.EAST | |
| 197 | def north(self): | |
| 198 | self.delta = self.NORTH | |
| 199 | def south(self): | |
| 200 | self.delta = self.SOUTH | |
| 201 | def randomDirection(self): | |
| 202 | self.delta = random.choice([self.WEST,self.EAST,self.NORTH,self.SOUTH]) | |
| 203 | def westOrEast(self): | |
| 204 | a = self.pop() | |
| 205 | if a==0: | |
| 206 | self.delta = self.EAST | |
| 207 | else: | |
| 208 | self.delta = self.WEST | |
| 209 | def northOrSouth(self): | |
| 210 | a = self.pop() | |
| 211 | if a==0: | |
| 212 | self.delta = self.SOUTH | |
| 213 | else: | |
| 214 | self.delta = self.NORTH | |
| 215 | def string(self): | |
| 216 | if not self.stringMode: | |
| 217 | self.stringMode = True; | |
| 218 | else: | |
| 219 | currentCommand = self.getCommand() | |
| 220 | if currentCommand == self.stringCharacter: | |
| 221 | self.stringMode = False; | |
| 222 | else: | |
| 223 | self.push(ord(currentCommand)) | |
| 224 | def duplicate(self): | |
| 225 | a = self.pop() | |
| 226 | self.push(a) | |
| 227 | self.push(a) | |
| 228 | def swap(self): | |
| 229 | x,y = self.pop(),self.pop() | |
| 230 | self.push(x) | |
| 231 | self.push(y) | |
| 232 | def intPrint(self): | |
| 233 | print(self.pop(),end="") | |
| 234 | def strPrint(self): | |
| 235 | print(chr(self.pop()),end="") | |
| 236 | def jump(self): | |
| 237 | self.advance() | |
| 238 | def put(self): | |
| 239 | y,x,v = self.pop(),self.pop(),self.pop() | |
| 240 | x,y = x+self.storageOffset[0],y+self.storageOffset[1] | |
| 241 | ||
| 242 | self.program[(x,y)] = chr(v) | |
| 243 | def get(self): | |
| 244 | y,x = self.pop(),self.pop() | |
| 245 | x,y = x+self.storageOffset[0],y+self.storageOffset[1] | |
| 246 | ||
| 247 | self.push(ord(self.program[(x,y)])) | |
| 248 | def inputNumber(self):#spec says to extract first contiguous base 10 number from input | |
| 249 | #msvcrt.getch() #wai wont u work | |
| 250 | m = re.search('\d+',input())
| |
| 251 | self.push(int(m.group(0)))#currently errors if not found. maybe reflect()? | |
| 252 | ||
| 253 | def inputChar(self): | |
| 254 | #self.push(ord(msvcrt.getch())) | |
| 255 | self.push(ord(input())) | |
| 256 | def end(self): | |
| 257 | self.exitStateFound = True | |
| 258 | def noop(self): | |
| 259 | "" | |
| 260 | ||
| 261 | def turnLeft(self): | |
| 262 | x,y = self.delta | |
| 263 | y *= -1 | |
| 264 | self.delta = (y,x) | |
| 265 | def turnRight(self): | |
| 266 | x,y = self.delta | |
| 267 | x *= -1 | |
| 268 | self.delta = (y,x) | |
| 269 | ||
| 270 | def reverse(self):#todo: hooray, I can do lambdas. they'll be the first to go when I start optomizing | |
| 271 | self.delta = tuple(map(lambda x: x*-1,self.delta)) | |
| 272 | ||
| 273 | def popVector(self): | |
| 274 | y,x = self.pop(),self.pop() | |
| 275 | self.delta = (x,y) | |
| 276 | ||
| 277 | def jumpOver(self):#TODO: incorporate this and space into getCommand. they take 0 ticks, and certain instructions require getCommand to return the next actual valid character | |
| 278 | if not self.jumpOverMode: | |
| 279 | self.jumpOverMode = True | |
| 280 | else: | |
| 281 | if self.getCommand() == self.jumpOverCharacter: | |
| 282 | self.jumpOverMode = False | |
| 283 | ||
| 284 | def jumpForward(self): | |
| 285 | num = self.pop() | |
| 286 | if num > 0: | |
| 287 | for i in range(0,num): | |
| 288 | self.advance() | |
| 289 | else: | |
| 290 | for i in range(0,num*-1): | |
| 291 | self.retreat() | |
| 292 | ||
| 293 | def quit(self): | |
| 294 | self.exitStateFound = True | |
| 295 | self.exitValue = self.pop() | |
| 296 | ||
| 297 | def iterate(self): | |
| 298 | num = self.pop() | |
| 299 | self.advance() | |
| 300 | command = self.getCommand() | |
| 301 | for i in range(0,num): | |
| 302 | self.functionDictionary[command]() | |
| 303 | ||
| 304 | def compare(self): | |
| 305 | b,a = self.pop(),self.pop() | |
| 306 | if a < b: | |
| 307 | self.turnLeft() | |
| 308 | elif a == b: | |
| 309 | self.noop() | |
| 310 | else: | |
| 311 | self.turnRight() | |
| 312 | ||
| 313 | #TODO: do pushthrough functions. 0-9 and a-f | |
| 314 | #TODO: in string mode, spaces are NOT ignored, but are truncated to a single space | |
| 315 | def fetchCharacter(self): | |
| 316 | self.advance() | |
| 317 | self.push(ord(self.getCommand())) | |
| 318 | ||
| 319 | def store(self): | |
| 320 | self.advance() | |
| 321 | self.program[self.pointerPosition] = chr(self.pop()) | |
| 322 | ||
| 323 | def clearStack(self): | |
| 324 | self.stack[0]= Stack() | |
| 325 | ||
| 326 | def beginBlock(self): | |
| 327 | n = self.pop() | |
| 328 | if n < 0: | |
| 329 | self.stack.prepend(Stack([0]*n)) | |
| 330 | else: | |
| 331 | self.stack.appendleft(Stack(self.stack[0].list[-n:]))#have to use list here for now | |
| 332 | self.stack[1].push(self.storageOffset[0]) | |
| 333 | self.stack[1].push(self.storageOffset[1]) | |
| 334 | self.storageOffset = tuple(map(lambda x,y: x+y,self.pointerPosition,self.delta)) | |
| 335 | ||
| 336 | def endBlock(self): | |
| 337 | if len(self.stack) > 1: | |
| 338 | n = self.pop() | |
| 339 | y,x = self.stack[1].pop(),self.stack[1].pop() | |
| 340 | self.storageOffset = (x,y) | |
| 341 | if n < 0: | |
| 342 | for i in range(0,n): | |
| 343 | self.stack[1].pop() | |
| 344 | else: | |
| 345 | self.stack[1].list += self.stack[0].list[-n:]#using list here too | |
| 346 | self.stack.popleft() | |
| 347 | else: | |
| 348 | self.reverse(); | |
| 349 | ||
| 350 | def stackUnderStack(self): | |
| 351 | if len(self.stack) == 1: | |
| 352 | self.reverse() | |
| 353 | else: | |
| 354 | n = self.pop() | |
| 355 | if n > 0: | |
| 356 | for i in range(0,n): | |
| 357 | self.push(self.stack[1].pop()) | |
| 358 | elif n < 0: | |
| 359 | for i in range(0,-n): | |
| 360 | self.stack[1].push(self.pop())#TODO: check if push is valid for python lists and get rid of all appends | |
| 361 | #more TODO: need to create class for stacks. pop() needs to return 0 if nothing is on the stack, which it wont right now | |
| 362 | ||
| 363 | ||
| 364 | #TO BE CONTINUED | |
| 365 | ||
| 366 | ||
| 367 | b = BefungeInterpreter() | |
| 368 | b.verbose = False#True | |
| 369 | b.loadCSVFile('dice.csv')
| |
| 370 | #b.loadASCIIFile('befunge.txt')
| |
| 371 | ||
| 372 | b.run() |