mirror of
https://github.com/blackboxprogramming/universal-computer.git
synced 2026-03-17 02:57:11 -05:00
Create utm.py
This commit is contained in:
committed by
GitHub
parent
25feb28fa1
commit
e7e8c32780
124
utm.py
Normal file
124
utm.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Universal Turing Machine simulator.
|
||||||
|
|
||||||
|
This script implements a simple universal Turing machine. Given a machine
|
||||||
|
description encoded in JSON and an input tape, it simulates the execution
|
||||||
|
of that machine and prints the resulting tape contents. It is designed
|
||||||
|
for educational purposes and is not optimised for performance.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
python3 utm.py machines/incrementer.json --tape "1111"
|
||||||
|
|
||||||
|
The machine description JSON must define the following keys:
|
||||||
|
|
||||||
|
* `states`: list of all states (strings)
|
||||||
|
* `alphabet`: list of symbols that may appear on the tape
|
||||||
|
* `blank`: the symbol representing a blank cell
|
||||||
|
* `transitions`: mapping from "state:symbol" to
|
||||||
|
[next_state, write_symbol, move_direction]
|
||||||
|
* `start`: the initial state
|
||||||
|
* `halt`: the halting state
|
||||||
|
|
||||||
|
Tape positions not present in the internal dictionary are assumed to hold
|
||||||
|
the blank symbol. The head starts at position 0 on the leftmost symbol
|
||||||
|
of the input tape (index 0).
|
||||||
|
|
||||||
|
This script stops either when the machine enters the halting state or when
|
||||||
|
the number of steps exceeds a configurable limit to avoid infinite loops.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
from typing import Dict, Tuple, List
|
||||||
|
|
||||||
|
|
||||||
|
class TuringMachine:
|
||||||
|
"""A simple Turing machine simulator."""
|
||||||
|
|
||||||
|
def __init__(self, description: Dict):
|
||||||
|
self.states: List[str] = description["states"]
|
||||||
|
self.alphabet: List[str] = description["alphabet"]
|
||||||
|
self.blank: str = description["blank"]
|
||||||
|
self.transitions: Dict[str, Tuple[str, str, str]] = {
|
||||||
|
key: tuple(value) # type: ignore
|
||||||
|
for key, value in description["transitions"].items()
|
||||||
|
}
|
||||||
|
self.start: str = description["start"]
|
||||||
|
self.halt: str = description["halt"]
|
||||||
|
|
||||||
|
def run(self, tape: Dict[int, str], max_steps: int = 10_000) -> Tuple[Dict[int, str], int, str]:
|
||||||
|
"""
|
||||||
|
Execute the Turing machine on the given tape.
|
||||||
|
|
||||||
|
:param tape: Dictionary representing the tape; keys are integer positions, values are symbols.
|
||||||
|
:param max_steps: Maximum number of steps to execute before stopping.
|
||||||
|
:return: A tuple of (final tape, steps executed, final state).
|
||||||
|
"""
|
||||||
|
head = 0
|
||||||
|
state = self.start
|
||||||
|
steps = 0
|
||||||
|
while state != self.halt and steps < max_steps:
|
||||||
|
symbol = tape.get(head, self.blank)
|
||||||
|
key = f"{state}:{symbol}"
|
||||||
|
if key not in self.transitions:
|
||||||
|
# No defined transition; halt prematurely
|
||||||
|
break
|
||||||
|
next_state, write_symbol, move = self.transitions[key]
|
||||||
|
# Write the symbol
|
||||||
|
if write_symbol == self.blank:
|
||||||
|
# Represent blank by removing the entry
|
||||||
|
tape.pop(head, None)
|
||||||
|
else:
|
||||||
|
tape[head] = write_symbol
|
||||||
|
# Move the head
|
||||||
|
if move == 'L':
|
||||||
|
head -= 1
|
||||||
|
elif move == 'R':
|
||||||
|
head += 1
|
||||||
|
# 'S' means stay; no change to head
|
||||||
|
# Update state
|
||||||
|
state = next_state
|
||||||
|
steps += 1
|
||||||
|
return tape, steps, state
|
||||||
|
|
||||||
|
|
||||||
|
def parse_tape(input_str: str, blank: str) -> Dict[int, str]:
|
||||||
|
"""Convert an input string into a tape dictionary."""
|
||||||
|
tape: Dict[int, str] = {}
|
||||||
|
for i, ch in enumerate(input_str):
|
||||||
|
if ch != blank:
|
||||||
|
tape[i] = ch
|
||||||
|
return tape
|
||||||
|
|
||||||
|
|
||||||
|
def tape_to_string(tape: Dict[int, str], blank: str) -> str:
|
||||||
|
"""Convert a tape dictionary back into a string for display."""
|
||||||
|
if not tape:
|
||||||
|
return ''
|
||||||
|
min_pos = min(tape.keys())
|
||||||
|
max_pos = max(tape.keys())
|
||||||
|
return ''.join(tape.get(i, blank) for i in range(min_pos, max_pos + 1))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description="Universal Turing Machine simulator")
|
||||||
|
parser.add_argument('machine', help='Path to the machine description JSON file')
|
||||||
|
parser.add_argument('--tape', default='', help='Initial tape contents (string of symbols)')
|
||||||
|
parser.add_argument('--max-steps', type=int, default=10_000, help='Maximum number of steps to execute')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
with open(args.machine, 'r', encoding='utf-8') as f:
|
||||||
|
description = json.load(f)
|
||||||
|
|
||||||
|
utm = TuringMachine(description)
|
||||||
|
tape = parse_tape(args.tape, utm.blank)
|
||||||
|
final_tape, steps, final_state = utm.run(tape, max_steps=args.max_steps)
|
||||||
|
print(f"Final state: {final_state}")
|
||||||
|
print(f"Steps executed: {steps}")
|
||||||
|
print(f"Final tape: {tape_to_string(final_tape, utm.blank)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user