From 857af059a7da37ced3150c6088df4eb48f85051d Mon Sep 17 00:00:00 2001 From: Matthew Grove Date: Sun, 30 Jan 2022 12:50:57 +0000 Subject: [PATCH] Add universal Turing machine (Node.JS) --- universal-turing-machine.js | 86 +++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 universal-turing-machine.js diff --git a/universal-turing-machine.js b/universal-turing-machine.js new file mode 100644 index 0000000..315f69a --- /dev/null +++ b/universal-turing-machine.js @@ -0,0 +1,86 @@ +// © Matthew Grove 2022 + +String.prototype.replaceAt = function(index, replacement) { + return this.substr(0, index) + replacement + this.substr(index + replacement.length); +} +const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + +let turingMachine = class { + /** + * Constructs a turing machine class object. + * @param {tape} String A string containing the contents of the tape. + * @param {transitions} String A string containing the transition functions for the machine. + * States are separated by / and transition functions are separated by , + * Each transition function must contain four characters: input character, output character, direction to move (l or r), and state to transition into + * States are numbered automatically by index, and accepting states must be defined with no transition functions + * @param {startPosition} Integer The starting index of the pointer on the tape. + * @param {startState} Integer The index of the machine's starting state. + * @param {delay} Integer The time period (in ms) to wait before each transition. + */ + constructor(tape, transitions, startPosition, startState, delay) { + this.position = startPosition; + this.tape = tape; + let exportedTransitions = {}; + transitions.split("/").map((state, stateIndex) => { + if (state.length > 0) { + exportedTransitions[stateIndex] = { + accepting: false, + }; + state.split(",").map(transition => { + exportedTransitions[stateIndex][transition.charAt(0)] = { + write: transition.charAt(1), + move: transition.charAt(2), + newState: transition.charAt(3), + }; + }, this); + } else { + exportedTransitions[stateIndex] = { + accepting: true, + }; + } + }, this); + this.transitions = exportedTransitions; + this.state = startState; + this.delay = delay; + } + + run = async () => { + console.log(`Machine is now running. Tape is currently: +${this.tape} +${" ".repeat(this.position)}^`); + while (!this.transitions[this.state].accepting) { + await delay(this.delay); + let transitionData = this.transitions[this.state][this.tape.charAt(this.position)]; // storing write, move, newState + let originalChar = this.tape.charAt(this.position); + this.tape = this.tape.replaceAt(this.position, transitionData.write); + // shift position + if (transitionData.move.toLowerCase() === "l") { + if (this.position === 0) { + this.tape = ` ${this.tape}`; + } else { + this.position--; + } + } else { + this.position++; + if (this.position == this.tape.length) { + this.tape = `${this.tape} `; + } + } + console.log(`Read ${originalChar} at position ${this.position} in state ${this.state}, and changed it to ${transitionData.write}. Machine has moved to state ${transitionData.newState} and tape is now: +${this.tape} +${" ".repeat(this.position)}^`) + this.state = transitionData.newState; + } + + if (this.transitions[this.state].accepting) { + console.log(`Machine is accepting in state ${this.state}. Tape is now: +${this.tape} +${" ".repeat(this.position)}^`) + } else { + console.log("error...") + } + } +} + +let machineOne = new turingMachine("01# ", "00r0,11r0,##r0, 0l2/00r1,11r1,##r1, 1l2/00l2,11l2,##l2,x0r3,y1r3/0xr0,1yr1,##r4/",0,3, 1000); +machineOne.run(); \ No newline at end of file