Add universal Turing machine (Node.JS)
This commit is contained in:
86
universal-turing-machine.js
Normal file
86
universal-turing-machine.js
Normal file
@@ -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();
|
||||
Reference in New Issue
Block a user