# UTM simulator (complete, with quiet option)

import string

# loadTM takes the name of a file containing a Turing machine description
# and returns a list of rules representing that machine

def loadTM(filename):
    TMfile = open(filename)
    ruleList = []
    for line in TMfile:
        # ignore blank lines and comments
        if line == '\n' or line[0] == '#':
            pass
        else:
            rule = string.split(line)
            ruleList.append(rule)
    TMfile.close()
    return ruleList

# utm simulates the behavior of a Turing machine TM on a given input string

def utm(TM, tapeContents, quiet=False):
    # TM is a list of Turing machine rules, where each rule is a list of the
    # form [<old State>, <old Symbol>, <new State>, <new Symbol>, <move>].
    # tapeContents is a string that specifies the contents of the input tape.

    # set up the tape
    tape = []
    for symbol in tapeContents:
        tape.append(symbol)
    headPosition = 0

    if quiet:
        printTape(tape, headPosition)

    state = 'A'
    steps = 0
    while True:
        # print out the tape
        if not quiet:
            printTape(tape, headPosition)

        # determine the next action to perform, if any
        symbol = tape[headPosition]
        action = determineAction(state, symbol, TM)
        if action == None: break
        (newState, newSymbol, move) = action

        # write the new symbol on the tape
        tape[headPosition] = newSymbol
        # change to the new state
        state = newState
        # update read/write head position
        if move == 'right':
            if headPosition == len(tape) - 1:
                tape = expandRight(tape)
            headPosition = headPosition + 1
        elif move == 'left':
            if headPosition == 0:
                tape = expandLeft(tape)
                headPosition = len(tape) / 2
            headPosition = headPosition - 1
        else:
            print "unknown move encountered"
            break

        steps = steps + 1
        if steps % 1000000 == 0:
            print "reached", steps, "steps"

    if quiet:
        print '...'
        printTape(tape, headPosition, trim=True)
    print "halted after", steps, "steps"
    print "%d ones left on tape" % tape.count('1')

def expandRight(tape):
    blanks = ['_'] * len(tape)
    return tape + blanks

def expandLeft(tape):
    blanks = ['_'] * len(tape)
    return blanks + tape

# determineAction takes a state, a symbol, and a list of Turing machine rules TM,
# and returns a tuple (newState, newSymbol, move) indicating what action to take
# in response to the given state and symbol

def determineAction(state, symbol, TM):
    for rule in TM:
        (currentState, currentSymbol, newState, newSymbol, move) = rule
        # see if this is the rule we're looking for
        if currentState == state and currentSymbol == symbol:
            return (newState, newSymbol, move)
    # no appropriate rule found
    return None

# printTape prints out the tape with the read/write head indicated by []'s

def printTape(tape, headPosition, trim=False):
    if trim:
        i = 0
        while tape[i] == '_' and i < headPosition:
            i = i + 1
        tape = tape[i:]
        headPosition = headPosition - i
    s = ''
    for i in range(len(tape)):
        if i == headPosition:
            s = s + '[' + tape[i] + ']'
        elif i > 30:
            s = s + ' ...'
            break
        else:
            s = s + ' ' + tape[i] + ' '
    print s

