4 "cell_type": "markdown",
7 "# Machine interpreter\n",
11 "Four registers, `a`, `b`, `c`, and `d`.\n",
12 "Program counter, `pc`.\n",
14 "Each register can hold 8-byte integers (Java `long`s).\n",
16 "Machine carries out the instruction at location `pc`. After it's executed, `pc` increments by 1.\n",
18 "`jmp` and `jpz` override this normal change in `pc`.\n",
20 "| Instruction | Description |\n",
21 "|:------------|:------------|\n",
22 "| `inc r` | increment contents of register `r` |\n",
23 "| `dec r` | decrement contents of register `r` |\n",
24 "| `set r i` | set contents of register `r` to literal value `i` |\n",
25 "| `cpy r s` | copy contents of register `r` into register `s` | \n",
26 "| `jmp i` | jump to instruction `i` places forward |\n",
27 "| `jpz r i` | jump to instruction `i` places forward if<br>register `r` contains zero, otherwise continue to next instruction |\n",
29 "For `jmp` and `jpz`, `i` is relative to the current instruction. `i` can be negative to jump to earlier places in the program. `i`=1 is a no-op, `i`=0 causes an infinite loop."
38 "def new_machine():\n",
39 " return {'pc': 0, \n",
44 " 'instructions': []}"
55 "def show_machine(machine):\n",
56 " return ', '.join('{}: {}'.format(sk, machine[int(sk) if sk.isnumeric() else sk]) \n",
57 " for sk in sorted(str(k) for k in machine)\n",
58 " if sk != 'instructions')"
69 "def inc(reg, machine):\n",
70 " machine[reg] += 1\n",
82 "def dec(reg, machine):\n",
83 " machine[reg] -= 1\n",
95 "def jmp(addr, machine):\n",
96 " machine['pc'] += addr"
101 "execution_count": 9,
107 "def jpz(reg, addr, machine):\n",
108 " if machine[reg] == 0:\n",
109 " machine['pc'] += addr\n",
111 " machine['pc'] += 1"
116 "execution_count": 10,
122 "def set_literal(reg, literal, machine):\n",
123 " machine[reg] = literal\n",
124 " machine['pc'] += 1"
129 "execution_count": 11,
135 "def cpy(from_reg, to_reg, machine):\n",
136 " machine[to_reg] = machine[from_reg]\n",
137 " machine['pc'] += 1"
142 "execution_count": 12,
148 "instruction_table = {'inc': inc, 'dec': dec, 'jmp': jmp,\n",
149 " 'jpz': jpz, 'set': set_literal, 'cpy': cpy}\n",
150 "numeric_args_table = {'jmp': [0], 'jpz': [1], 'set': [1], 'sto': [1], 'ld': [1]}"
155 "execution_count": 13,
161 "def parse(instruction):\n",
162 " words = instruction.split()\n",
163 " instr = words[0]\n",
164 " args = words[1:]\n",
165 " if instr in numeric_args_table:\n",
166 " for p in numeric_args_table[instr]:\n",
167 " args[p] = int(args[p])\n",
168 " return instruction_table[instr], args"
173 "execution_count": 14,
179 "{'a': 2, 'b': 1, 'c': 0, 'd': 0, 'instructions': [], 'pc': 3}"
182 "execution_count": 14,
184 "output_type": "execute_result"
188 "m = new_machine()\n",
190 "cargs = ['a', 'b']\n",
198 "execution_count": 15,
204 "def program_from_instructions(prog, machine):\n",
205 " machine['instructions'] = [parse(instr) for instr in prog]"
210 "execution_count": 16,
216 "def program_from_listing(listing, machine):\n",
217 " labelled_instructions = [i.strip() for i in listing.split('\\n') \n",
219 " if not i.strip().startswith('#')]\n",
220 " instructions = replace_labels(labelled_instructions)\n",
221 " program_from_instructions(instructions, machine)"
226 "execution_count": 17,
232 "def replace_labels(listing):\n",
234 " for n, i in enumerate(listing):\n",
236 " locations[i.split(':')[0]] = n\n",
238 " unlabelled_listing = []\n",
239 " for n, i in enumerate(listing):\n",
240 " instr = i.split()\n",
242 " instr = i.split(':')[1].split()\n",
244 " instr = i.split()\n",
246 " for term in instr:\n",
247 " if term in locations:\n",
248 " terms += [str(locations[term] - n)]\n",
250 " terms += [term]\n",
251 " transformed_instr = ' '.join(terms)\n",
252 " unlabelled_listing += [transformed_instr]\n",
254 " return unlabelled_listing "
259 "execution_count": 18,
268 "execution_count": 18,
270 "output_type": "execute_result"
274 "'fred: inc a'.split(':')[1].split()"
279 "execution_count": 19,
284 "output_type": "stream",
295 "program = \"\"\"\n",
304 "labelled_instructions = [i.strip() for i in program.split('\\n') if i.strip() if not i.strip().startswith('#')]\n",
305 "instructions = replace_labels(labelled_instructions)\n",
306 "print('\\n'.join(instructions))"
311 "execution_count": 20,
317 "def run(machine, initial_state=None, trace=False):\n",
318 " if initial_state:\n",
319 " machine.update(initial_state)\n",
320 " while machine['pc'] < len(machine['instructions']):\n",
322 " print(show_machine(machine))\n",
323 " cmd, args = machine['instructions'][machine['pc']]\n",
324 " cmd(*args, machine)"
329 "execution_count": 21,
335 "def execute(listing, initial_state=None, trace=False):\n",
336 " m = new_machine()\n",
337 " program_from_listing(listing, m)\n",
338 " run(m, initial_state=initial_state, trace=trace)\n",
344 "execution_count": 22,
354 " 'instructions': [(<function __main__.inc>, ['a']),\n",
355 " (<function __main__.inc>, ['a']),\n",
356 " (<function __main__.cpy>, ['a', 'b']),\n",
357 " (<function __main__.inc>, ['a'])],\n",
361 "execution_count": 22,
363 "output_type": "execute_result"
367 "program = \"\"\"\n",
378 "execution_count": 24,
388 " 'instructions': [(<function __main__.set_literal>, ['a', 10]),\n",
389 " (<function __main__.dec>, ['a']),\n",
390 " (<function __main__.inc>, ['b']),\n",
391 " (<function __main__.jpz>, ['a', 2]),\n",
392 " (<function __main__.jmp>, [-3])],\n",
396 "execution_count": 24,
398 "output_type": "execute_result"
402 "program = \"\"\"\n",
409 "# m = new_machine()\n",
410 "# program_from_listing(program, m)\n",
412 "execute(program, initial_state={'c': 20})"
417 "execution_count": 25,
427 " 'instructions': [(<function __main__.set_literal>, ['a', 10]),\n",
428 " (<function __main__.dec>, ['a']),\n",
429 " (<function __main__.inc>, ['b']),\n",
430 " (<function __main__.jpz>, ['a', 2]),\n",
431 " (<function __main__.jmp>, [-3])],\n",
435 "execution_count": 25,
437 "output_type": "execute_result"
441 "program = \"\"\"\n",
448 "execute(program, initial_state={'c': 20})"
453 "execution_count": 26,
463 " 'instructions': [(<function __main__.cpy>, ['c', 'a']),\n",
464 " (<function __main__.set_literal>, ['b', 0]),\n",
465 " (<function __main__.dec>, ['a']),\n",
466 " (<function __main__.jpz>, ['b', 3]),\n",
467 " (<function __main__.dec>, ['b']),\n",
468 " (<function __main__.jmp>, [2]),\n",
469 " (<function __main__.inc>, ['b']),\n",
470 " (<function __main__.jpz>, ['a', 3]),\n",
471 " (<function __main__.jmp>, [-6])],\n",
475 "execution_count": 26,
477 "output_type": "execute_result"
481 "program = \"\"\"\n",
492 "# m = new_machine()\n",
493 "# program_from_listing(program, m)\n",
495 "execute(program, initial_state={'c': 5})"
500 "execution_count": 27,
510 " 'instructions': [(<function __main__.cpy>, ['c', 'a']),\n",
511 " (<function __main__.set_literal>, ['b', 0]),\n",
512 " (<function __main__.dec>, ['a']),\n",
513 " (<function __main__.jpz>, ['b', 3]),\n",
514 " (<function __main__.dec>, ['b']),\n",
515 " (<function __main__.jmp>, [2]),\n",
516 " (<function __main__.inc>, ['b']),\n",
517 " (<function __main__.jpz>, ['a', 2]),\n",
518 " (<function __main__.jmp>, [-6])],\n",
522 "execution_count": 27,
524 "output_type": "execute_result"
528 "# b holds parity of number in c: (c % 2)\n",
529 "program = \"\"\"\n",
540 "# m = new_machine()\n",
541 "# program_from_listing(program, m)\n",
543 "execute(program, initial_state={'c': 5})"
548 "execution_count": 28,
554 "'a: 0, b: 1, c: 8, d: 0, pc: 10'"
557 "execution_count": 28,
559 "output_type": "execute_result"
563 "# c holds floor(a/2)\n",
564 "program = \"\"\"\n",
576 "# m = new_machine()\n",
577 "# program_from_listing(program, m)\n",
579 "show_machine(execute(program, initial_state={'a': 17}))"
584 "execution_count": 29,
590 "'a: 4, b: 0, c: 12, d: 0, pc: 9'"
593 "execution_count": 29,
595 "output_type": "execute_result"
600 "program = \"\"\"\n",
603 " # start of main loop\n",
611 " # end of program \n",
615 "# m = new_machine()\n",
616 "# program_from_listing(program, m)\n",
618 "show_machine(execute(program, initial_state={'a': 4}))"
623 "execution_count": 38,
629 "'a: 0, b: 0, c: 27, d: 0, pc: 11'"
632 "execution_count": 38,
634 "output_type": "execute_result"
639 "program = \"\"\"\n",
642 "loop: jpz b end \n",
645 "smul: jpz a emul\n",
653 "m = new_machine()\n",
654 "program_from_listing(program, m)\n",
656 "show_machine(execute(program, initial_state={'a': 9, 'b': 3}))"
661 "execution_count": 39,
666 "output_type": "stream",
683 "labelled_instructions = [i.strip() for i in program.split('\\n') if i.strip() if not i.strip().startswith('#')]\n",
684 "instructions = replace_labels(labelled_instructions)\n",
685 "print('\\n'.join(instructions))"
690 "execution_count": 31,
696 "'a: 2, b: 0, c: 10, d: 2, pc: 13'"
699 "execution_count": 31,
701 "output_type": "execute_result"
706 "program = \"\"\"\n",
711 "loop: jpz b end \n",
714 "smul: jpz a emul\n",
722 "m = new_machine()\n",
723 "program_from_listing(program, m)\n",
725 "show_machine(execute(program, initial_state={'a': 9, 'b': 3}))"
730 "execution_count": 32,
735 "output_type": "stream",
754 "labelled_instructions = [i.strip() for i in program.split('\\n') \n",
756 " if not i.strip().startswith('#')]\n",
757 "instructions = replace_labels(labelled_instructions)\n",
758 "print('\\n'.join(instructions))"
763 "execution_count": 33,
769 "'a: 52, b: 0, c: 0, d: 0, pc: 48'"
772 "execution_count": 33,
774 "output_type": "execute_result"
778 "# Collatz. a initially holds value, but location 1 is used to store the current value as we're going along.\n",
779 "# Location 2 holds number of steps taken\n",
780 "# Location 3 holds max value reached\n",
781 "program = \"\"\"\n",
786 " # if a is one, finish, \n",
791 " # find parity of a\n",
802 " # b == 0 means a even; b == 1 means a odd\n",
805 " # c holds a * 3 + 1\n",
832 "maxc: jpz c this\n",
840 " # end of program \n",
846 "# m = new_machine()\n",
847 "# program_from_listing(program, m)\n",
849 "show_machine(execute(program, initial_state={'a': 7}))"
854 "execution_count": 32,
863 "execution_count": 32,
865 "output_type": "execute_result"
874 "execution_count": 33,
880 "def max_collatz(start):\n",
895 "execution_count": 34,
904 "execution_count": 34,
906 "output_type": "execute_result"
915 "execution_count": 35,
924 "execution_count": 35,
926 "output_type": "execute_result"
930 "max([(max_collatz(i), i) for i in range(1, 1000)])"
935 "execution_count": 36,
1004 " (62, 0, 9232),\n",
1005 " (63, 0, 9232),\n",
1013 " (71, 0, 9232),\n",
1015 " (73, 0, 9232),\n",
1024 " (82, 0, 9232),\n",
1025 " (83, 0, 9232),\n",
1033 " (91, 0, 9232),\n",
1036 " (94, 0, 9232),\n",
1037 " (95, 0, 9232),\n",
1039 " (97, 0, 9232),\n",
1044 "execution_count": 36,
1046 "output_type": "execute_result"
1051 "for i in range(1, 100):\n",
1052 " m = execute(program, initial_state={'a': i})\n",
1053 " c = max_collatz(i)\n",
1055 " ans += [(i, m[1], c)]\n",
1060 "cell_type": "code",
1061 "execution_count": 34,
1067 "'a: 250504, b: 0, c: 0, d: 0, pc: 48'"
1070 "execution_count": 34,
1072 "output_type": "execute_result"
1076 "show_machine(execute(program, initial_state={'a': 937}))"
1080 "cell_type": "code",
1081 "execution_count": 35,
1086 "output_type": "stream",
1144 "execution_count": 35,
1146 "output_type": "execute_result"
1150 "labelled_instructions = [i.strip() for i in program.split('\\n') \n",
1152 " if not i.strip().startswith('#')]\n",
1153 "instructions = replace_labels(labelled_instructions)\n",
1154 "print('\\n'.join(instructions))\n",
1155 "open('07-program.txt', 'w').write('\\n'.join(instructions))"
1159 "cell_type": "code",
1160 "execution_count": null,
1170 "display_name": "Python 3",
1171 "language": "python",
1175 "codemirror_mode": {
1179 "file_extension": ".py",
1180 "mimetype": "text/x-python",
1182 "nbconvert_exporter": "python",
1183 "pygments_lexer": "ipython3",