import defaultIO import field import stackStack import math import os import random import times randomize() type Pointer* = object x, y: int # The pointer's positiont dx, dy: int # The pointer's traveling delta sox, soy: int # The storage offset stringMode, lastCharWasSpace: bool # The string mode flags ss: ref StackStack next*: ref Pointer # the next element for multi-threaded funge-98 characterInput*: proc(): int decimalInput*: proc(): int characterOutput*: proc(v: int) decimalOutput*: proc(v: int) argv: seq[string] func NewPointer*(next: ref Pointer = nil, argv: seq[string]): ref Pointer = new(result) result.dx = 1 result.ss = NewStackStack() result.next = next result.characterInput = defaultCharacterInput result.decimalInput = defaultDecimalInput result.characterOutput = defaultCharacterOutput result.decimalOutput = defaultDecimalOutput result.argv = argv #func Split(p: var Pointer) = # # TODO check the spec, maybe we need to duplicate the stack? # p.next = NewPointer(p.next) func Step(p: var Pointer, f: Field) = (p.x, p.y) = f.Step((p.x, p.y), (p.dx, p.dy)) func StepAndGet(p: var Pointer, f: Field): int = p.Step(f) return f.Get(p.x, p.y) func Reverse(p: var Pointer) = p.dx = -p.dx; p.dy = -p.dy proc Redirect(p: var Pointer, c: int): bool = ## Redirects the pointer according to c's value. ## Returns true if a redirect has been performed case c: of int('^'): p.dx = 0; p.dy = -1 of int('>'): p.dx = 1; p.dy = 0 of int('v'): p.dx = 0; p.dy = 1 of int('<'): p.dx = -1; p.dy = 0 of int('?'): const directions = [0, -1, 1, 0, 0, 1, -1, 0] let r = 2 * rand(3) p.dx = directions[r]; p.dy = directions[r+1] of int('['): (p.dx, p.dy) = (p.dy, -p.dx) of int(']'): (p.dx, p.dy) = (-p.dy, p.dx) of int('r'): p.Reverse() of int('x'): p.dy = p.ss[].Pop(); p.dx = p.ss[].Pop() else: return false return true proc Eval(p: var Pointer, f: var Field, c: int): (bool, ref int) = ## Executes the instruction on the field ## Returns true if the pointer terminated, and a return code if ## the program should terminate completely case c: of int('@'): return (true, nil) of int('z'): discard of int('#'): p.Step(f) of int('j'): let n = p.ss[].Pop() if n > 0: for j in 0.. 0: p.x = x; p.y = y if v != int(' ') and v != int(';'): if v == int('q') or v == int('@'): return p.Eval(f, v) for i in 0.. b: p.ss[].Push(1) else: p.ss[].Push(0) of int('_'): if p.ss[].Pop() == 0: p.dx = 1 else: p.dx = -1 p.dy = 0 of int('|'): p.dx = 0 if p.ss[].Pop() == 0: p.dy = 1 else: p.dy = -1 of int('w'): let (a, b) = p.ss[].PopVector() if a < b: (p.dx, p.dy) = (p.dy, -p.dx) elif a > b: (p.dx, p.dy) = (-p.dy, p.dx) of int('+'): let (a, b) = p.ss[].PopVector() p.ss[].Push(a+b) of int('*'): let (a, b) = p.ss[].PopVector() p.ss[].Push(a*b) of int('-'): let (a, b) = p.ss[].PopVector() p.ss[].Push(a-b) of int('/'): let (a, b) = p.ss[].PopVector() if b == 0: p.ss[].Push(0) else: p.ss[].Push(a div b) of int('%'): let (a, b) = p.ss[].PopVector() if b == 0: p.ss[].Push(0) else: p.ss[].Push(a mod b) of int('"'): p.stringMode = true of int('\''): p.ss[].Push(p.StepAndGet(f)) of int('s'): p.Step(f) f.Set(p.x, p.y, p.ss[].Pop()) of int('$'): discard p.ss[].Pop() of int(':'): p.ss[].Duplicate() of int('\\'): p.ss[].Swap() of int('n'): p.ss[].Clear() of int('{'): p.ss[].Begin((p.sox, p.soy)) p.sox = p.x + p.dx p.soy = p.y + p.dy of int('}'): var v: tuple[x, y: int] if p.ss[].End(v): p.Reverse() p.sox = v.x; p.soy = v.y of int('u'): if p.ss[].Under(): p.Reverse() of int('g'): let (x, y) = p.ss[].PopVector() p.ss[].Push(f.Get(x+p.sox, y+p.soy)) of int('p'): let (x, y) = p.ss[].PopVector() let v = p.ss[].Pop() f.Set(x+p.sox, y+p.soy, v) of int('.'): p.decimalOutput(p.ss[].Pop()) of int(','): p.characterOutput(p.ss[].Pop()) of int('&'): p.ss[].Push(p.decimalInput()) of int('~'): p.ss[].Push(p.characterInput()) of int('y'): let n = p.ss[].Pop() let now = now() let (x, y, lx, ly) = f.GetSize() let (height, heights) = p.ss[].GetHeights() # 20 for key, value in envPairs(): case key of "LC_ALL": discard of "PWD": discard of "PATH": discard of "DISPLAY": discard of "USER": discard of "TERM": discard of "LANG": discard of "HOME": discard of "EDITOR": discard of "SHELL": discard else: continue p.ss[].Push(0) for i in value.len-1..0: p.ss[].Push(int(value[i])) # 19 p.ss[].PushVector((0, 0)) for i in 0.. 0: p.ss[].YCommandPick(n, heights[0]) of int('('): let n = p.ss[].Pop() var v = 0 for i in 0..= int('0') and c <= int('9'): p.ss[].Push(c - int('0')) elif c >= int('a') and c <= int('f'): p.ss[].Push(c - int('a') + 10) else: p.Reverse() return (false, nil) proc Exec*(p: var Pointer, f: var Field): (bool, ref int) = ## Advances to the next instruction of the field and executes it ## Returns true if the pointer terminated, and a return code if ## the program should terminate completely var c = f.Get(p.x, p.y) if p.stringMode: if p.lastCharWasSpace: while c == int(' '): c = p.StepAndGet(f) p.lastCharWasSpace = false if c == int('"'): p.stringMode = false else: if c == int(' '): p.lastCharWasSpace = true p.ss[].Push(c) else: var jumpingMode = false while jumpingMode or c == int(' ') or c == int(';'): if c == int(';'): jumpingMode = not jumpingMode c = p.StepAndGet(f) result = p.Eval(f, c) p.Step(f) return result