-
Notifications
You must be signed in to change notification settings - Fork 0
/
multifunge.py
175 lines (149 loc) · 5.67 KB
/
multifunge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# (c) 2022 Owen Bechtel
# License: MIT (see LICENSE file)
#!/usr/bin/env python3
import copy
import sys
def getch():
try:
import msvcrt
return msvcrt.getch()
except ImportError:
import sys, tty, termios
fd = sys.stdin.fileno()
oldSettings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
c = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, oldSettings)
return c
class Pointer:
printing = False
waiting = False
def __init__(self, row, column, value, direction, intMode):
self.row = row
self.column = column
self.value = value
self.direction = direction
self.intMode = intMode
def move(self):
if self.waiting: return
if self.direction == 'up': self.row -= 1
if self.direction == 'down': self.row += 1
if self.direction == 'left': self.column -= 1
if self.direction == 'right': self.column += 1
def horizontal(self):
return self.direction in ['left', 'right']
def toMatrix(file):
# Split file into 2d list of characters
program = [[char for char in line] for line in file.split('\n')]
# Calculate max line length
lengths = [len(line) for line in program]
lengths.sort()
maxLength = lengths[-1]
# Make all lines the same length by adding spaces to the end
newProgram = []
for line in program:
while len(line) < maxLength:
line.append(' ')
newProgram.append(line)
return newProgram
def createPointers(program):
# Add pointer every time the '@' character appears and return list of pointers
pointers = []
for row, line in enumerate(program):
for column, command in enumerate(line):
if command == '@':
pointers.append(Pointer(row, column, 0, 'right', True))
return pointers
def step(program, pointers):
newPointers = []
for pointer in pointers:
# Copy pointer and call it 'p' so that 'pointers' doesn't change mid-loop
p = copy.copy(pointer)
p.move()
# Delete pointer if it is outside of file
if p.row < 0 or p.row >= len(program) or p.column < 0 or p.column >= len(program[p.row]):
continue
c = program[p.row][p.column]
bracketed = program[p.row][p.column - 1] == '[' and program[p.row][p.column + 1] == ']'
# Set this to true to delete pointer
deleted = False
# If the pointer is in print mode, print the character unless it is a quote
if p.printing:
if c == '"': p.printing = False
else: print(c, end = '')
# If the pointer is waiting, search for other waiting pointers and do the operation
elif p.waiting:
for ptr in pointers:
# Continue searching if the other pointer isn't on the same space
if ptr.row != p.row or ptr.column != p.column or ptr.horizontal() == p.horizontal(): continue
# If the pointer is horizontal, execute the command
if p.horizontal():
p.waiting = False
if c == '+': p.value += ptr.value
elif c == '-': p.value -= ptr.value
elif c == '*': p.value *= ptr.value
elif c == '/': p.value //= ptr.value
elif c == '%': p.value %= ptr.value
elif c == '^': p.value **= ptr.value
elif c == '&': p.value = 1 if p.value and ptr.value != 0 else 0
elif c == '|': p.value = 1 if p.value or ptr.value != 0 else 0
elif c == '<': p.value = 1 if p.value < ptr.value else 0
elif c == '>': p.value = 1 if p.value > ptr.value else 0
elif c == '=': p.value = 1 if p.value == ptr.value else 0
elif c == '?' and ptr.value != 0: p.direction = ptr.direction
# If the pointer is vertical it gets deleted
else: deleted = True
break
# If the command is bracketed, wait for other pointer
elif bracketed: p.waiting = True
# Otherwise, execute the command
elif c == 'x': deleted = True
elif c == ';': return []
elif c == '^': p.direction = 'up'
elif c == 'v': p.direction = 'down'
elif c == '<': p.direction = 'left'
elif c == '>': p.direction = 'right'
elif c == '*':
directions = ['up', 'down'] if p.horizontal() else ['left', 'right']
newPointers.append(Pointer(p.row, p.column, p.value, directions[0], p.intMode))
newPointers.append(Pointer(p.row, p.column, p.value, directions[1], p.intMode))
elif c == '/':
direction = {'up':'right', 'right':'up', 'down':'left', 'left':'down'}[p.direction]
newPointers.append(Pointer(p.row, p.column, p.value, direction, p.intMode))
elif c == '\\':
direction = {'up':'left', 'left':'up', 'down':'right', 'right':'down'}[p.direction]
newPointers.append(Pointer(p.row, p.column, p.value, direction, p.intMode))
elif c == '+': p.value += 1
elif c == '-': p.value -= 1
elif c == '~': p.value *= -1
elif c == 'i': p.intMode = True
elif c == 'c': p.intMode = False
elif c == '?':
if p.intMode: p.value = int(input())
else: p.value = ord(getch())
elif c == '!':
if p.intMode: print(p.value, end = '')
else: print(chr(p.value), end = '')
elif c == '"': p.printing = True
elif c == '.': print()
elif c == '#': p.value = 0
elif c in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
p.value = p.value * 10 + int(c)
# Append pointer to new list of pointers if it hasn't been deleted
if not deleted: newPointers.append(p)
return newPointers
def run(file):
program = toMatrix(file)
pointers = createPointers(program)
while len(pointers) > 0:
pointers = step(program, pointers)
def main():
if len(sys.argv) == 2:
with open(sys.argv[1], 'r') as file:
run(file.read())
else:
print('incorrect number of arguments')
print('example: ' + sys.argv[0] + ' program.mfg')
if __name__ == '__main__': main()