Mice in a maze/mice.py
A Mice in a maze interpreter in python, written by User:Koen in 2018.
About
This interpreter was tested on the Hello, world! program from Mice_in_a_maze#Example_programs.
Padding
The interpreter automatically pads shorter lines with spaces, and adds four extra walls of 'W' around the code, so the mice cannot escape. These extra walls were added for convenience. In particular, I did not bother stripping the '\n' character at the end of each line, so the right outer wall is actually two spaces to the right of the farthest instruction.
Code flow
At each step, every mouse executes the instruction it's standing on, then moves if it can. Instructions 'C' and 'A' immediately cause the mouse to rotate, which might or might not be what the language author intended.
A mouse moves immediately after executing its instruction, or doesn't move if it is surrounded by four walls. Rotating does not constitute a move; mice rotate clockwise until they are not facing a wall, then move one step forward.
This timing issue might be relevant if a mouse is standing next to a conditional wall 'K', as this is the only way mice ever interact. If a mouse is currently standing on a 'K', it does not care whether this 'K' is a wall or not; only adjacent walls matter.
Mice are ordered according to their initial position in the maze (top to bottom, left to right). At every step, mice are activated in that order. The digit used to declare the mouse is ignored by the interpreter. In particular, two mice can share the same digit, and it is possible to declare more than 10 mice.
Execution halts when there are no mice left. If a mouse is never destroyed, execution loops.
Brainfuck memory
The tape is unbounded to the right. The data pointer starts at cell 0. Executing instruction '<' while at cell 0 has no effect.
Each cell holds a python int
and is initialised to 0.
Input and output
Output is flushed after each execution of the instruction '.'. Cells are converted into chars using python function chr
.
The input instruction ',' calls the python function sys.stdin.read(1)
.
Usage
Assuming the source code is in file interpreter.py
:
python3 interpreter.py maze.txt
Reads the Mice in a maze code from file maze.txt
.
python3 interpreter.py
Reads the code from stdin.
Source code
import sys def get_sourcecode(argv): if (len(sys.argv) == 1): sourcecode = sys.stdin.readlines() else: with open(sys.argv[1], 'r') as sourcefile: sourcecode = sourcefile.readlines() return(sourcecode) def add_padding(code): maxlen = max([len(line) for line in code]) maze = [] maze.append("\n".rjust(maxlen+2, 'W')) for line in code: maze.append("W" + line.ljust(maxlen) + "W") maze.append("\n".rjust(maxlen+2, 'W')) return(maze) def get_mice(sourcecode): mice = [] for y in range(len(sourcecode)): for x in range(len(sourcecode[y])): if str.isdigit(sourcecode[y][x]): mice.append((x,y,0,-1)) return(mice) def facing_wall(x, y, dx, dy, maze, cell_value): return( (maze[y+dy][x+dx] == 'W') or (cell_value != 0 and maze[y+dy][x+dx] == 'K') ) def rotate(dx, dy, clockwise = True): directions = [(0, -1), (1, 0), (0, 1), (-1, 0)] old_i = directions.index((dx, dy)) new_i = ((old_i+1) % 4) if clockwise else ((old_i-1) % 4) return (directions[new_i]) def move_mouse(x, y, dx, dy, maze, cell_value): count_rotation = 0 while (count_rotation < 4 and facing_wall(x, y, dx, dy, maze, cell_value)): dx, dy = rotate(dx, dy) count_rotation = count_rotation + 1 if (count_rotation < 4): return (x+dx, y+dy, dx, dy) def execute_instruction(instr, data, ptr, dx, dy): destroy_mouse = False if (instr == '>'): ptr = ptr + 1 if ptr >= len(data): data.extend([0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0]) elif (instr == '<'): ptr = max(ptr-1, 0) elif (instr == '+'): data[ptr] = data[ptr] + 1 elif (instr == '-'): data[ptr] = data[ptr] - 1 elif (instr == '.'): sys.stdout.write(chr(data[ptr])) sys.stdout.flush() elif (instr == ','): data[ptr] = sys.stdin.read(1) elif (instr == 'E'): destroy_mouse = True elif (instr == 'C'): dx, dy = rotate(dx, dy) elif (instr == 'A'): dx, dy = rotate(dx, dy, clockwise = False) return (ptr, dx, dy, destroy_mouse) def main(): code = get_sourcecode(sys.argv) maze = add_padding(code) data = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0] ptr = 0 mice = get_mice(maze) next_round_mice = [] while (mice): for i, (x, y, dx, dy) in enumerate(mice): (ptr, dx, dy, destroyed) = execute_instruction(maze[y][x], data, ptr, dx, dy) (x, y, dx, dy) = move_mouse(x, y, dx, dy, maze, data[ptr]) if (not destroyed): next_round_mice.append((x, y, dx, dy)) mice = next_round_mice next_round_mice = [] main()
Comments
A mouse is a quadruple (x, y, dx, dy)
, where (x, y) is its position in the maze, and (dx, dy) the cardinal direction it's facing: (0, -1)
is up, (1, 0)
is right.
mice
is the list of mice in the maze, in order. Once a mouse has executed its instruction and moved, it is added to next_round_mice
, unless it was destroyed. Execution loops until the list mice
is empty.
The tape is a python list called data
, initially containing 32 cells, and extended with 32 new cells every time the data pointer is too big.