Turing Machine

In the example below, we implement a Turing machine for checking balanced parentheses. Its initial state is A and it contains one accepted state. The transition function says, for instance, if the machine is at state A and its head reads symbol “)”, it should write “X” in that cell and move left, transitioning to state B.

Structs can be defined like the following:

import "serializer.scrypt";
import "arrayUtil.scrypt";


// Turing machine state
type State = bytes;
// alphabet symbol in each cell, 1 byte long each
type Symbol = bytes;

// contract state as a struct
struct StateStruct {
    int headPos;
    bytes tape;
    // current machine state
    State curState;
}

struct Input {
    State oldState;
    Symbol read;
}

struct Output {
    State newState;
    Symbol write;
    // move left or right
    bool moveLeft;
}

// transition function entry: input -> output
struct TransitionFuncEntry {
    Input input;
    Output output;
}

/*
 * A Turing Machine checking balanced parentheses
 */
contract TuringMachine {

    @state
    StateStruct states;
    // states
    static const State STATE_A = b'00'; // initial state
    static const State STATE_B = b'01';
    static const State STATE_C = b'02';
    static const State STATE_ACCEPT = b'03';

    // symbols
    static const Symbol BLANK = b'00';
    static const Symbol OPEN = b'01';
    static const Symbol CLOSE = b'02';
    static const Symbol X = b'03';

    static const bool LEFT = true;
    static const bool RIGHT = false;

    // number of rules in the transition function
    static const int N = 8;
    // transition function table
    static const TransitionFuncEntry[N] transitionFuncTable = [
        { {STATE_A, OPEN},   {STATE_A, OPEN, RIGHT} },
        { {STATE_A, X},      {STATE_A, X, RIGHT} },
        { {STATE_A, CLOSE},  {STATE_B, X, LEFT} },
        { {STATE_A, BLANK},  {STATE_C, BLANK, LEFT} },
        
        { {STATE_B, OPEN},   {STATE_A, X, RIGHT} },
        { {STATE_B, X},      {STATE_B, X, LEFT} },

        { {STATE_C, X},      {STATE_C, X, LEFT} },
        { {STATE_C, BLANK},  {STATE_ACCEPT, BLANK, RIGHT} }
    ];
    
    public function transit(SigHashPreimage txPreimage) {
        // read/deserialize contract state
        SigHashType sigHashType = SigHash.ANYONECANPAY | SigHash.SINGLE | SigHash.FORKID;
        require(Tx.checkPreimageSigHashType(txPreimage, sigHashType));
        // transition
        Symbol head = ArrayUtil.getElemAt(this.states.tape, this.states.headPos);
        // look up in transition table
        bool found = false;
        loop (N) : i {
            if (!found) {
                auto entry = transitionFuncTable[i];
                if (entry.input == { this.states.curState, head }) {
                    auto output = entry.output;
                    // update state
                    this.states.curState = output.newState;
                    // write tape head
                    this.states.tape = ArrayUtil.setElemAt(this.states.tape, this.states.headPos, output.write);
                    // move head
                    this.states.headPos += output.moveLeft ? -1 : 1;
                    // extend tape if out of bound
                    if (this.states.headPos < 0) {
                        // add 1 blank cell to the left
                        this.states.tape = BLANK + this.states.tape;
                        this.states.headPos = 0;
                    }
                    else if (this.states.headPos >= len(this.states.tape)) {
                        // add 1 blank cell to the right
                        this.states.tape = this.states.tape + BLANK;
                    }

                    if (this.states.curState == STATE_ACCEPT) {
                        // accept
                        exit(true);
                    }

                    found = true;
                }
            }
        }
        // reject if no transition rule found
        require(found);

        // otherwise machine goes to the next step
        bytes stateScript = this.getStateScript();
        bytes output = Utils.buildOutput(stateScript, SigHash.value(txPreimage));
        require(hash256(output) == SigHash.hashOutputs(txPreimage));

    }
}