A .NET 10 library for building and running automatons like Turing machines and Markov algorithms. You can try pre-made algorithms or create your own in this demo application (или версия на русском).
- Basic abstract interface for all formal algorithms. Each algorithm can be executed step-by-step (using
IEnumerable) or with one action (from start to the end). - Algorithm snapshots carry debug information about each step of the algoritm run.
- Fluent builders for Turing machines and Markov algorithms.
- Turing machine definition is expanded. Machine can scan special symbols like
empty,non-emptyorany. - Several example algorithms.
.NET 10 for main projects, xUnit and FluentAssertions for unit tests.
- Solution layout
- Core concepts
- Quick start: Turing machine
- Quick start: Markov algorithm
- Execution results
- Built-in algorithms
- Printing and parsing
| Project | Description |
|---|---|
PrettyMachines.Algorithms |
Core library: abstract algorithm model, Turing, Markov and Utils (printing/parsing). |
PrettyMachines.Implementations |
Ready-to-use algorithms built on the core library. |
PrettyMachines.BlazorUI |
Blazor WebAssembly app for building and executing algorithms. |
PrettyMachines.Tests |
xUnit tests for the core library and implementations. |
An algorithm is any well-defined set of instructions that, when followed, terminates after a finite number of steps and comprises a solution to a given computational problem. This project represents algorithms as effective methods that satisfy the following:
- They consist of a finite number of exact, finite instructions.
- Applied to a problem from their class, they always terminate and always produce a correct answer.
- Their instructions can be followed rigorously, without requiring ingenuity.
Optionally, an algorithm may be required never to return a result as if it were an answer for inputs from outside its class. Adding this requirement reduces the set of classes that have an effective method.
All algorithms implement IAlgorithm (text in, text out) and optionally the strongly-typed
IAlgorithm<TInput, TOutput>. Instances are immutable once built, so their instructions cannot change
during execution.
| Member | Purpose |
|---|---|
Name |
Optional algorithm name. |
ValidateInput(input) |
Checks whether an input belongs to the algorithm's class. |
Run(input, cancellation, verbose) |
Lazily yields the initial snapshot followed by one snapshot per step. |
Execute(input, cancellation, verbose) |
Consumes Run and returns the final AlgorithmResult<T>. |
Convenience extension: algorithm.Execute(input, verbose: true) uses AlgorithmCancellation.Default
(1000 steps).
AlgorithmCancellation bounds execution with a maximum step count and/or an external CancellationToken.
AlgorithmCancellation.Default allows 1000 steps; a step limit of 0 means unlimited.
var cancellation = new AlgorithmCancellation(10_000);
var result = machine.Execute("101", cancellation, verbose: true);| Status | Meaning |
|---|---|
Unknown |
Execution is still running (Run only). |
Success |
A terminal state or terminal rule was reached. |
Stuck |
No instruction was applicable. |
Aborted |
Cancelled from outside or trapped in a loop past the step limit. |
InvalidInput |
Input is outside the algorithm's class. |
Run also lets you inspect intermediate states, which is useful for stepping debuggers and UIs:
foreach (var snapshot in machine.Run("101", new AlgorithmCancellation(10_000)))
Console.WriteLine($"step {snapshot.Steps}: {snapshot.Output} [{snapshot.Termination}]");A Turing machine is a mathematical model of computation describing an abstract machine that manipulates symbols on a strip of tape according to a table of rules.[ Despite the model's simplicity, it is capable of implementing any computer algorithm. Machine is defined by an alphabet (optionally strict), a blank symbol, a set of states with one initial state, and a transition table.
using PrettyMachines.Abstract;
using PrettyMachines.Turing;
using PrettyMachines.Utils.Printing;
var machine = TuringMachine.Create("Toggle first bit")
.WithAlphabet("0", "1") // strict alphabet; unknown symbols fail the machine
.WithBlankSymbol("_")
.AddInitialState("scan", out var q0)
.AddTerminalState("done", out var qDone)
.BuildRules(rules => rules
.AddRule(q0, "0", qDone, "1", TapeMovement.Right)
.AddRule(q0, "1", qDone, "0", TapeMovement.Right));
var result = machine.Execute("101", new AlgorithmCancellation(10_000), verbose: true);
Console.WriteLine($"{result.Output} ({result.Termination} after {result.Steps} steps)");
Console.WriteLine(InstructionTablePrinter.PrintTable(machine));Rules can also reference states by name (rules.AddRule("scan", "0", "done", ...)), and a rule that reads
SymbolMatch.Empty, SymbolMatch.NotEmpty or SymbolMatch.Any matches a whole class of cells:
using PrettyMachines.Turing;
rules.AddRule(q0, SymbolMatch.NotEmpty, q0, null, TapeMovement.Right)
.AddHalt(q0, SymbolMatch.Empty, "1"); // AddHalt targets TuringMachineState.HaltThe same machine typed over the tape (no input mutation of the caller's tape):
var tape = new MachineTape(new[] { "1", "0", "1" }, blankSymbol: "_");
AlgorithmResult<IReadOnlyTape> tapeResult = machine.Execute(tape, new AlgorithmCancellation(10_000));A Markov algorithm is a string rewriting system that uses grammar-like rules to operate on strings of symbols. Markov algorithms have been shown to be Turing-complete, which means that they are suitable as a general model of computation and can represent any mathematical expression from its simple notation. Markov algorithms are named after the Soviet mathematician Andrey Markov, Jr. Algorithm applies the first matching substitution rule, replacing the leftmost occurrence of its pattern. A rule marked terminal stops the algorithm after it is applied.
using PrettyMachines.Abstract;
using PrettyMachines.Markov;
var algorithm = MarkovAlgorithm.Create("Capitalize")
.WithAlphabet('a', 'b', 'c')
.WithMarkers('*')
.AddRule("a", "A").WithComment("uppercase a")
.AddRule("b", "B").WithComment("uppercase b")
.AddRule("c", "C", isTerminal: true).WithComment("uppercase c and stop")
.Build();
var result = algorithm.Execute("abc", new AlgorithmCancellation(1000));- An empty pattern prepends the replacement (
AddRule("", "$")). - An empty replacement deletes the pattern.
- The order of rules matters: the first matching rule wins.
WithAlphabetrestricts input; symbols outside the alphabet yieldInvalidInput.WithMarkersdeclares special symbols that rules may produce but input must not contain.
AlgorithmResult<T> is the outcome of Execute:
| Member | Description |
|---|---|
Termination |
Final TerminationStatus. |
Output |
Final output (string or IReadOnlyTape depending on the overload). |
Steps |
Number of steps executed. |
Trace |
Step-by-step trace lines (populated when verbose: true). |
AppliedInstructions |
Numbers of the instructions applied on each step, when available. |
PrettyMachines.Implementations ships ready-made machines. Both TuringMachines and MarkovAlgorithms
expose static factories with a common naming scheme.
| Concept | Turing machine | Markov algorithm |
|---|---|---|
| Brackets grammar | Create_BracketsGrammar |
Create_BracketsGrammar |
| Binary increment | Create_BinaryIncrementMachine |
Create_BinaryIncrement |
| Binary decrement | Create_BinaryDecrementMachine |
Create_BinaryDecrement |
| Binary addition | Create_BinaryAdditionMachine |
Create_BinaryAddition |
| Binary subtraction | Create_BinarySubtractionMachine |
Create_BinarySubtraction |
| String concatenation | Create_StringConcatenationMachine |
Create_StringConcatenation |
| String reversal | Create_StringReversalMachine |
Create_StringReversal |
| Busy beaver | Create_BusyBeaver |
Create_BusyBeaver |
| Binary → unary | Create_BinaryToUnaryConverterMachine |
Create_BinaryToUnaryConverter |
| Unary → binary | Create_UnaryToBinaryConverterMachine |
Create_UnaryToBinaryConverter |
| Unary → ternary | Create_UnaryToTernaryConverterMachine |
Create_UnaryToTernaryConverter |
| Decimal → binary | Create_DecimalToBinaryConverterMachine |
Create_DecimalToBinaryConverter |
| Binary → decimal | Create_BinaryToDecimalConverterMachine |
Create_BinaryToDecimalConverter |
| Leading zeros trim | — | Create_LeadingZerosTrim |
Operand-based algorithms read input in the form A+B / A-B; the brackets grammar accepts a string and
outputs the accepted or rejected symbol.
using PrettyMachines.Implementations;
var adder = TuringMachines.Create_BinaryAdditionMachine();
var sum = adder.Execute("101+11", new AlgorithmCancellation(100_000)).Output; // "1000"PrettyMachines.Algorithms.Utils provides text/CSV output and a rule parser.
InstructionTablePrinter.PrintTable(machine)/PrintList/PrintCsv— render a Turing machine and its instruction table.MarkovAlgorithmPrinter.PrintFormatted(algorithm)/PrintCsv— render Markov rules with comments.MachineTapePrinter.Print(tape)— render the non-blank tape cells.MarkovSubstitutionParser.ParseQuoted("'a' -> 'b'")andParseUnquoted("a=>b")— createSubstitutionrules;=>marks a terminal rule.
Each printer overload accepts a StringBuilder, a Stream, or returns a string.