One of the foundational mathematical constructs behind computer science
is the universal Turing Machine.



(Alan Turing introduced the idea of such a machine in 1936–1937.)

Indeed one way to definitively prove that a language
is turing-complete
is to implement a universal Turing machine in it.



Task

Simulate such a machine capable
of taking the definition of any other Turing machine and executing it.

Of course, you will not have an infinite tape,
but you should emulate this as much as is possible.

The three permissible actions on the tape are "left", "right" and "stay".

To test your universal Turing machine (and prove your programming language
is Turing complete!), you should execute the following two Turing machines
based on the following definitions.


Simple incrementer

 States: q0, qf
 Initial state: q0
 Terminating states: qf
 Permissible symbols: B, 1
 Blank symbol: B
 Rules:
 (q0, 1, 1, right, q0)
 (q0, B, 1, stay, qf)


The input for this machine should be a tape of 1 1 1


Three-state busy beaver

 States: a, b, c, halt
 Initial state: a
 Terminating states: halt
 Permissible symbols: 0, 1
 Blank symbol: 0
 Rules:
 (a, 0, 1, right, b)
 (a, 1, 1, left, c)
 (b, 0, 1, left, a)
 (b, 1, 1, right, b)
 (c, 0, 1, left, b)
 (c, 1, 1, stay, halt)


The input for this machine should be an empty tape.


Bonus:

5-state, 2-symbol probable Busy Beaver machine from Wikipedia

 States: A, B, C, D, E, H
 Initial state: A
 Terminating states: H
 Permissible symbols: 0, 1
 Blank symbol: 0
 Rules:
 (A, 0, 1, right, B)
 (A, 1, 1, left, C)
 (B, 0, 1, right, C)
 (B, 1, 1, right, B)
 (C, 0, 1, right, D)
 (C, 1, 0, left, E)
 (D, 0, 1, left, A)
 (D, 1, 1, left, D)
 (E, 0, 1, stay, H)
 (E, 1, 0, left, A)


The input for this machine should be an empty tape.

This machine runs for more than 47 millions steps.




using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
public class TuringMachine
{
    public static async Task Main() {
        var fiveStateBusyBeaver = new TuringMachine("A", '0', "H").WithTransitions(
            ("A", '0', '1', Right, "B"),
            ("A", '1', '1', Left,  "C"),
            ("B", '0', '1', Right, "C"),
            ("B", '1', '1', Right, "B"),
            ("C", '0', '1', Right, "D"),
            ("C", '1', '0', Left,  "E"),
            ("D", '0', '1', Left,  "A"),
            ("D", '1', '1', Left,  "D"),
            ("E", '0', '1', Stay,  "H"),
            ("E", '1', '0', Left,  "A")
        );
        var busyBeaverTask = fiveStateBusyBeaver.TimeAsync();
 
        var incrementer = new TuringMachine("q0", 'B', "qf").WithTransitions(
            ("q0", '1', '1', Right, "q0"),
            ("q0", 'B', '1', Stay,  "qf")
        )
        .WithInput("111");
        foreach (var _ in incrementer.Run()) PrintLine(incrementer);
        PrintResults(incrementer);
 
        var threeStateBusyBeaver = new TuringMachine("a", '0', "halt").WithTransitions(
            ("a", '0', '1', Right, "b"),
            ("a", '1', '1', Left,  "c"),
            ("b", '0', '1', Left,  "a"),
            ("b", '1', '1', Right, "b"),
            ("c", '0', '1', Left,  "b"),
            ("c", '1', '1', Stay,  "halt")
        );
        foreach (var _ in threeStateBusyBeaver.Run()) PrintLine(threeStateBusyBeaver);
        PrintResults(threeStateBusyBeaver);
 
        var sorter = new TuringMachine("A", '*', "X").WithTransitions(
            ("A", 'a', 'a', Right, "A"),
            ("A", 'b', 'B', Right, "B"),
            ("A", '*', '*', Left,  "E"),
            ("B", 'a', 'a', Right, "B"),
            ("B", 'b', 'b', Right, "B"),
            ("B", '*', '*', Left,  "C"),
            ("C", 'a', 'b', Left,  "D"),
            ("C", 'b', 'b', Left,  "C"),
            ("C", 'B', 'b', Left,  "E"),
            ("D", 'a', 'a', Left,  "D"),
            ("D", 'b', 'b', Left,  "D"),
            ("D", 'B', 'a', Right, "A"),
            ("E", 'a', 'a', Left,  "E"),
            ("E", '*', '*', Right, "X")
        )
        .WithInput("babbababaa");
        sorter.Run().Last();
        Console.WriteLine("Sorted: " + sorter.TapeString);
        PrintResults(sorter);
 
        sorter.Reset().WithInput("bbbababaaabba");
        sorter.Run().Last();
        Console.WriteLine("Sorted: " + sorter.TapeString);
        PrintResults(sorter);
 
        Console.WriteLine(await busyBeaverTask);
        PrintResults(fiveStateBusyBeaver);
 
        void PrintLine(TuringMachine tm) => Console.WriteLine(tm.TapeString + "\tState " + tm.State);
 
        void PrintResults(TuringMachine tm) {
            Console.WriteLine($"End state: {tm.State} = {(tm.Success ? "Success" : "Failure")}");
            Console.WriteLine(tm.Steps + " steps");
            Console.WriteLine("tape length: " + tm.TapeLength);
            Console.WriteLine();
        }
    }
 
    public const int Left = -1, Stay = 0, Right = 1;
    private readonly Tape tape;
    private readonly string initialState;
    private readonly HashSet<string> terminatingStates;
    private Dictionary<(string state, char read), (char write, int move, string toState)> transitions;
 
    public TuringMachine(string initialState, char blankSymbol, params string[] terminatingStates) {
        State = this.initialState = initialState;
        tape = new Tape(blankSymbol);
        this.terminatingStates = terminatingStates.ToHashSet();
    }
 
    public TuringMachine WithTransitions(
        params (string state, char read, char write, int move, string toState)[] transitions)
    {
        this.transitions = transitions.ToDictionary(k => (k.state, k.read), k => (k.write, k.move, k.toState));
        return this;
    }
 
    public TuringMachine Reset() {
        State = initialState;
        Steps = 0;
        tape.Reset();
        return this;
    }
 
    public TuringMachine WithInput(string input) {
        tape.Input(input);
        return this;
    }
 
    public int Steps { get; private set; }
    public string State { get; private set; }
    public bool Success => terminatingStates.Contains(State);
    public int TapeLength => tape.Length;
    public string TapeString => tape.ToString();
 
    public IEnumerable<string> Run() {
        yield return State;
        while (Step()) yield return State;
    }
 
    public async Task<TimeSpan> TimeAsync(CancellationToken cancel = default) {
        var chrono = Stopwatch.StartNew();
        await RunAsync(cancel);
        chrono.Stop();
        return chrono.Elapsed;
    }
 
    public Task RunAsync(CancellationToken cancel = default)
        => Task.Run(() => {
            while (Step()) cancel.ThrowIfCancellationRequested();
        });
 
    private bool Step() {
        if (!transitions.TryGetValue((State, tape.Current), out var action)) return false;
        tape.Current = action.write;
        tape.Move(action.move);
        State = action.toState;
        Steps++;
        return true;
    }
 
 
    private class Tape
    {
        private List<char> forwardTape = new List<char>(), backwardTape = new List<char>();
        private int head = 0;
        private char blank;
 
        public Tape(char blankSymbol) => forwardTape.Add(blank = blankSymbol);
 
        public void Reset() {
            backwardTape.Clear();
            forwardTape.Clear();
            head = 0;
            forwardTape.Add(blank);
        }
 
        public void Input(string input) {
            Reset();
            forwardTape.Clear();
            forwardTape.AddRange(input);
        }
 
        public void Move(int direction) {
            head += direction;
            if (head >= 0 && forwardTape.Count <= head) forwardTape.Add(blank);
            if (head < 0 && backwardTape.Count <= ~head) backwardTape.Add(blank);
        }
 
        public char Current {
            get => head < 0 ? backwardTape[~head] : forwardTape[head];
            set {
                if (head < 0) backwardTape[~head] = value;
                else forwardTape[head] = value;
            }
        }
 
        public int Length => backwardTape.Count + forwardTape.Count;
 
        public override string ToString() {
            int h = (head < 0 ? ~head : backwardTape.Count + head) * 2 + 1;
            var builder = new StringBuilder(" ", Length * 2 + 1);
            if (backwardTape.Count > 0) {
                builder.Append(string.Join(" ", backwardTape)).Append(" ");
                if (head < 0) (builder[h + 1], builder[h - 1]) = ('(', ')');
                for (int l = 0, r = builder.Length - 1; l < r; l++, r--) (builder[l], builder[r]) = (builder[r], builder[l]);
            }
            builder.Append(string.Join(" ", forwardTape)).Append(" ");
            if (head >= 0) (builder[h - 1], builder[h + 1]) = ('(', ')');
            return builder.ToString();
        }
 
    }
 
}