from tabidir import TaBiDir

class Configuration:
    """configuration d'une machine de Turing, ruban bi-infini"""
    def __init__(self, etat, entree):
        self.etat = etat
        self.pos = 0
        self.ruban = TaBiDir([], entree)

    def __str__(self):
        return str([str(self.ruban[i]) + '(' + self.etat + ')'
                    if i == self.pos
                    else str(self.ruban[i])
                    for i in range(self.ruban.imin(),
                                   self.ruban.imax()+1)])
        # return 'État : ' + str(self.etat) + '\n' \
        #     + 'Position : ' + str(self.pos) + '\n' \
        #     + str(self.ruban)

    def lit(self):
        return self.ruban[self.pos]
        
    def evolue(self, e, d, s):
        assert -1 <= d <= 1
        self.ruban[self.pos] = e
        self.pos += d
        if self.pos < self.ruban.imin():
            self.ruban.prepend(None)
        if self.pos > self.ruban.imax():
            self.ruban.append(None)
        self.etat = s
        
    
class MachineDeTuring:
    """description d'une machine de Turing"""
    def __init__(self, etat_initial, etat_final, regles):
        self.etat_initial = etat_initial
        self.etat_final = etat_final
        self.regles = regles

# def action(machine, config):
#     assert config.etat != machine.etat_final
#     assert config.ruban.imin() <= config.pos <= config.ruban.imax()
#     e, d, s = machine.regles[config.etat][config.lit()]
#     config.evolue(e, d, s)

def calcule(machine, entree):
    config = Configuration(machine.etat_initial, entree)
    print(config)
    while config.etat != machine.etat_final:
        e, d, s = machine.regles[config.etat][config.lit()]
        config.evolue(e, d, s)
        # action(machine, config)
        print(config)
    print('Calcul terminé')

regles_incr = {
    'A': {'0': ('0', 1, 'A'),
          '1': ('1', 1, 'A'),
          None: (None, -1, 'B')},
    'B': {'0': ('1', 0, 'F'),
          '1': ('0', -1, 'B'),
          None: ('1', 0, 'F')}
}
machine_incr = MachineDeTuring('A', 'F', regles_incr)
# calcule(machine_incr, ['1', '0', '0', '1', '1'])
# calcule(machine_incr, ['1', '1', '1'])
# calcule(machine_incr, [None])

regles_decalage = {
    'I': {'0': (None, 1, 'D0'),
          '1': (None, 1, 'D1'),
          None: (None, 0, 'F')},
    'D0': {'0': ('0', 1, 'D0'),
           '1': ('0', 1, 'D1'),
           None: ('0', 0, 'F')},
    'D1': {'0': ('1', 1, 'D0'),
           '1': ('1', 1, 'D1'),
           None: ('1', 0, 'F')}
}
machine_decalage = MachineDeTuring('I', 'F', regles_decalage)
# calcule(machine_decalage, ['1', '0', '0', '1', '1'])
# calcule(machine_decalage, ['1', '1', '1'])
# calcule(machine_decalage, [None])

regles_palindrome = {
    'P': { '0': (None, 1, 'D0'),
           '1': (None, 1, 'D1'),
           None: (1, 0, 'F')},
    'D0': { '0': ('0', 1, 'D0'),
            '1': ('1', 1, 'D0'),
            None: (None, -1, 'V0')},
    'D1': { '0': ('0', 1, 'D1'),
            '1': ('1', 1, 'D1'),
            None: (None, -1, 'V1')},
    'V0': { '0': (None, -1, 'R'),
            '1': (None, -1, 'E'),
            None: ('1', 0, 'F')},
    'V1': { '0': (None, -1, 'E'),
            '1': (None, -1, 'R'),
            None: ('1', 0, 'F')},
    'R': { '0': ('0', -1, 'R'),
           '1': ('1', -1, 'R'),
           None: (None, 1, 'P')},
    'E': { '0': (None, -1, 'E'),
           '1': (None, -1, 'E'),
           None: ('0', 0, 'F')}
}
machine_palindrome = MachineDeTuring('P', 'F', regles_palindrome)
# calcule(machine_palindrome, ['1', '0', '0', '1', '1'])
# calcule(machine_palindrome, ['1', '0', '0', '1'])
# calcule(machine_palindrome, ['1', '0', '1'])
# calcule(machine_palindrome, [None])

regles_palindrome_bis = {
    'P': { '0': ('M0', 1, 'D0'),
           '1': ('M1', 1, 'D1'),
           'M0': (0, -1, 'O'),
           'M1': (1, -1, 'O'),
           None: (None, -1, 'O')},
    'D0': { '0': ('0', 1, 'D0'),
            '1': ('1', 1, 'D0'),
            'M0': ('0', -1, 'V0'),
            'M1': ('1', -1, 'V0'),
            None: (None, -1, 'V0')},    
    'D1': { '0': ('0', 1, 'D1'),
            '1': ('1', 1, 'D1'),
            'M0': ('0', -1, 'V1'),
            'M1': ('1', -1, 'V1'),
            None: (None, -1, 'V1')},
    'V0': { '0': ('M0', -1, 'R'),
            '1': ('1', -1, 'N'),
            'M0': ('0', -1, 'O')},
    'V1': { '0': ('0', -1, 'N'),
            '1': ('M1', -1, 'R'),
            'M1': ('1', -1, 'O')},
    'R': { '0': ('0', -1, 'R'),
           '1': ('1', -1, 'R'),
           'M0': ('0', 1, 'P'),
           'M1': ('1', 1, 'P')},
    'N': { '0': ('0', -1, 'N'),
           '1': ('1', -1, 'N'),
           'M0': ('0', -1, 'N'),
           'M1': ('1', -1, 'N'),
           None: ('0', 0, 'F')},
    'O': { '0': ('0', -1, 'O'),
           '1': ('1', -1, 'O'),
           'M0': ('0', -1, 'O'),
           'M1': ('1', -1, 'O'),
           None: ('1', 0, 'F')}
}
machine_palindrome = MachineDeTuring('P', 'F', regles_palindrome_bis)
calcule(machine_palindrome, ['1', '0', '0', '1', '1'])
calcule(machine_palindrome, ['1', '0', '0', '1'])
calcule(machine_palindrome, ['1', '0', '1'])
calcule(machine_palindrome, [None])


regles_duplication = {
    'A': {'0': ('M0', 1, 'B0'),
          '1': ('M1', 1, 'B1'),
          None: (None, 0, 'F')},
    'B0': {'0': ('0', 1, 'B0'),
           '1': ('1', 1, 'B0'),
           None: (None, 1, 'C0')},
    'B1': {'0': ('0', 1, 'B1'),
           '1': ('1', 1, 'B1'),
           None: (None, 1, 'C1')},
    'C0': {'0': ('0', 1, 'C0'),
           '1': ('1', 1, 'C0'),
           None: ('0', -1, 'D')},
    'C1': {'0': ('0', 1, 'C1'),
           '1': ('1', 1, 'C1'),
           None: ('1', -1, 'D')},
    'D': {'0': ('0', -1, 'D'),
          '1': ('1', -1, 'D'),
          'M0': ('0', 1, 'A'),
          'M1': ('1', 1, 'A'),
          None: (None, -1, 'D')}
}
# Aucun des états A/B/C n'est censé voir de case marquée
machine_duplication = MachineDeTuring('A', 'F', regles_duplication)
# calcule(machine_duplication, ['1', '0', '0', '1', '1'])
# calcule(machine_duplication, ['1'])
# calcule(machine_duplication, [None])

regles_zero = {
    'C': {'0': ('0', -1, 'P'),
          '1': ('1', 1, 'C'),
          None: (None, -1, 'A')},
    'P': {'0': ('0', -1, 'P'), # assert false
          '1': ('1', -1, 'P'),
          None: ('1', 0, 'F')},
    'A': {'0': ('0', -1, 'A'), # assert false
          '1': ('1', -1, 'A'),
          None: ('0', 0, 'F')}
}
machine_zero = MachineDeTuring('C', 'F', regles_zero)
# calcule(machine_zero, ['1', '1', '1', '0', '1'])
# calcule(machine_zero, ['1', '1', '1'])
# calcule(machine_zero, ['0'])
# calcule(machine_zero, [None])

regles_zerozerozero = {
    'C3': {'0': ('0', 1, 'C2'),
           '1': ('1', 1, 'C3'),
           None: (None, -1, 'A')},
    'C2': {'0': ('0', 1, 'C1'),
           '1': ('1', 1, 'C3'),
           None: (None, -1, 'A')},
    'C1': {'0': ('0', -1, 'P'),
           '1': ('1', 1, 'C3'),
           None: (None, -1, 'A')},
    'P': {'0': ('0', -1, 'P'),
          '1': ('1', -1, 'P'),
          None: ('1', 0, 'F')},
    'A': {'0': ('0', -1, 'A'),
          '1': ('1', -1, 'A'),
          None: ('0', 0, 'F')}
}
machine_zerozerozero = MachineDeTuring('C3', 'F', regles_zerozerozero)
# calcule(machine_zerozerozero, ['1', '0', '0', '1', '0', '0', '0', '1'])
# calcule(machine_zerozerozero, ['1', '0', '0', '1', '0', '0', '1'])
# calcule(machine_zerozerozero, ['1', '0', '0', '1', '0', '0'])
# calcule(machine_zerozerozero, ['0', '0'])

regles_exploration = {
    'D': {'0': ('0', 0, 'F'),
          '*': ('*', 1, 'D'),
          None: ('*', -1, 'G')},
    'G': {'0': ('0', 0, 'F'),
          '*': ('*', -1, 'G'),
          None: ('*', 1, 'D')}
}
machine_exploration = MachineDeTuring('G', 'F', regles_exploration)
# calcule(machine_exploration, [None, None, None, '0'])