Skip to content

Instantly share code, notes, and snippets.

@lynn
Last active November 19, 2020 04:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save lynn/914087eb06580608d34ca1b83a214baf to your computer and use it in GitHub Desktop.
Save lynn/914087eb06580608d34ca1b83a214baf to your computer and use it in GitHub Desktop.
Cardinal esolang interpreter
#!/usr/bin/env python3
# usage:
# python3 cardinal.py code.cardinal < input.txt
# to show program execution, pass -d and a time delta:
# python3 cardinal.py -d 0.1 code.cardinal < input.txt
import sys
import time
DIRS = [(0, -1), (0, +1), (-1, 0), (+1, 0)]
class IP:
def __init__(self, pos, direction):
self.pos = pos
self.direction = direction
self.quit = False
self.alive = True
self.a = self.i = 0
self.wait = 0
self.remove_after = False
self.output_mode = False
def go(self):
if self.wait > 0:
self.wait -= 1
return
command = code.get(self.pos, ' ')
x, y = self.pos
dx, dy = self.direction
if command == '"':
self.output_mode = not self.output_mode
elif self.output_mode:
sys.stdout.write(command)
elif command == ' ':
pass
elif command == '#':
self.alive = False
for d in DIRS:
if d == (-dx, -dy):
continue
copy = IP((x + d[0], y + d[1]), d)
copy.a = self.a
copy.i = self.i
copy.remove_after = self.remove_after
ips.append(copy)
elif command == '^':
self.direction = (0, -1)
elif command == 'v':
self.direction = (0, +1)
elif command == '<':
self.direction = (-1, 0)
elif command == '>':
self.direction = (+1, 0)
elif command == 'x':
self.alive = False
elif command == '@':
self.quit = True
elif command == '?' and self.a == 0:
self.alive = False
elif command == '!' and self.a != 0:
self.alive = False
elif command == '/':
code[x, y] = '\\'
self.direction = (-dy, -dx)
elif command == '\\':
code[x, y] = '/'
self.direction = (dy, dx)
elif command == '~':
self.a, self.i = self.i, self.a
elif command == '+':
self.a += 1
elif command == '-':
self.a -= 1
elif command == 't':
self.a *= self.i
elif command == 'd':
self.a /= self.i
elif command == '*':
self.a += self.i
elif command == "'":
self.a -= self.i
elif command == 'M':
self.a %= self.i
elif command == '&':
self.a = 1 if self.a and self.i else 0
elif command == '|':
self.a = 1 if self.a or self.i else 0
elif command == 'X':
self.a = 1 if (self.a == 0) != (self.i == 0) else 0
elif command == 'O':
self.direction = (-dy, dx)
elif command == 'o':
self.direction = (dy, -dx)
elif command == ':':
self.a = int(input())
elif command == '.':
sys.stdout.write(str(self.a))
elif command == ',':
sys.stdout.write(chr(self.a))
elif command == ';':
sys.stdout.write('\n')
elif command == '=':
self.i = self.a
elif command in '`':
self.remove_after = not self.remove_after
elif command in 'nu()':
pdx, pdy = DIRS['nu()'.index(command)]
code[x + pdx, y + pdy] = chr(self.a)
self.a = ord(code[x - pdx, y - pdy])
if self.remove_after:
self.alive = False
elif command in 'VA}{':
if self.direction == DIRS['VA}{'.index(command)]:
self.alive = False
elif command == '0':
self.a = 0
elif command == 'N':
self.direction = (-dx, dy)
elif command == 'Z':
self.direction = (dx, -dy)
elif command == 'I':
self.direction = (-dx, -dy)
elif command in 'UDLR':
if self.a > 0:
self.direction = DIRS['UDLR'.index(command)]
elif command == '8':
self.wait = 3
elif command == '$':
x, y = self.a, self.i
elif command == 'J' and self.a > 0 or command == 'j' and self.a == 0:
x += dx
y += dy
# Move the pointer
dx, dy = self.direction
x += dx
y += dy
self.pos = x, y
if x < 0 or x > max_x or y < 0 or y > max_y:
self.alive = False
if len(sys.argv) < 2:
sys.stderr.write('usage: {0} code\n'.format(sys.argv[0]))
sys.exit(1)
debug = False
if sys.argv[1] == '-d':
time_delta = float(sys.argv[2])
debug = True
del sys.argv[1:3]
ips = []
code = {}
max_x = max_y = 0
with open(sys.argv[1]) as f:
for y, line in enumerate(f):
for x, char in enumerate(line.rstrip('\n')):
code[x, y] = char
if char == '%':
for d in DIRS:
ips.append(IP((x, y), d))
max_x = max(max_x, x)
max_y = max(max_y, y)
while ips:
if debug:
sys.stderr.write('\n' * 5)
rows = []
for y in range(max_y):
row = []
for x in range(max_x):
row.append(code.get((x, y), ' '))
rows.append(row)
for ip in ips:
x, y = ip.pos
if 0 <= y < max_y and 0 <= x < max_x:
rows[y][x] = '\x1b[31m@\x1b[0m'
for row in rows:
sys.stderr.write(''.join(row) + '\n')
time.sleep(time_delta)
for ip in ips[:]:
ip.go()
if any(ip.quit for ip in ips):
break
ips = [ip for ip in ips if ip.alive]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment