Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ python3 utm.py machines/incrementer.json --tape "1101"

This will increment the binary number `1101` (13) to `1110` (14).

To observe each step that the machine performs, pass `--trace`:

```
python3 utm.py machines/incrementer.json --tape "1101" --trace
```

## Directory structure

- `utm.py` – the universal Turing machine simulator.
Expand Down
22 changes: 20 additions & 2 deletions utm.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,18 @@ def __init__(self, description: Dict):
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]:
def run(
self,
tape: Dict[int, str],
max_steps: int = 10_000,
trace: bool = False,
) -> 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.
:param trace: Whether to print a step-by-step trace of execution.
:return: A tuple of (final tape, steps executed, final state).
"""
head = 0
Expand All @@ -66,6 +72,11 @@ def run(self, tape: Dict[int, str], max_steps: int = 10_000) -> Tuple[Dict[int,
# No defined transition; halt prematurely
break
next_state, write_symbol, move = self.transitions[key]
if trace:
print(
f"[step {steps}] state={state} head={head} read={symbol} "
f"-> write={write_symbol} move={move} next={next_state}"
)
# Write the symbol
if write_symbol == self.blank:
# Represent blank by removing the entry
Expand Down Expand Up @@ -107,14 +118,21 @@ def main() -> None:
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')
parser.add_argument(
'--trace',
action='store_true',
help='Print a step-by-step trace of the machine execution',
)
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)
final_tape, steps, final_state = utm.run(
tape, max_steps=args.max_steps, trace=args.trace
)
print(f"Final state: {final_state}")
print(f"Steps executed: {steps}")
print(f"Final tape: {tape_to_string(final_tape, utm.blank)}")
Expand Down