Maximilian Wagner 2c6c21b761 Copied
2024-01-30 20:04:56 +01:00

189 lines
5.8 KiB
Python

class TuringMachine():
def __init__(self, turing_machine_def: str) -> None:
# if a placeholder is needed, create it with empty string
if not turing_machine_def:
return
# STATE VARIABLES
self.state_start: str = ''
self.state_accept: str = ''
self.state_reject: str = ''
self.state_current: str = ''
# BAND VARIABLES
self.band: list = []
self.band_num: int = 0
self.band_current: int = 0
# RULE VARIABLES
self.rules: list = []
self.rule_used: str = ''
# ERR Variables
self.errored: bool = False
# PARSE BANDS
def_split = turing_machine_def.replace(' ', '').replace('\n', '').lower().split(';')
self.band_num = int(def_split[0])
# PARSE STATES
self.state_start = def_split[1]
self.state_current = def_split[1]
self.state_accept = def_split[2]
self.state_reject = def_split[3]
def_split_truncated = def_split[4:]
# PARSE RULES
for x in def_split_truncated:
if not x: continue
rule_current = x.split(',')
tmp = rule_current[1].split('->')
rule_current[1] = tmp[0]
rule_current.insert(2, tmp[1])
self.rules.append(rule_current)
def turing_run(self) -> None:
'''Runs the TM without giving the option to step through manually.'''
while not self.turing_finished():
self.turing_step()
if self.errored: return
def turing_step(self) -> bool:
'''Steps through the TM one rule at a time. A step is changing
the value of the current element and moving the band by one.
Returns whether the TM has finished running or has an empty band.'''
if len(self.band) == 0:
return True
# Add blank if current location is out of bounds and adjust location
if self.band_current < 0:
self.band.insert(0, '_')
self.band_current = 0
if self.band_current == len(self.band):
self.band.append('_')
for rule in self.rules:
# Skip if rule does not apply to current state
if not rule[0] == self.state_current: continue
# Skip if rule does not apply to current band element
if not rule[1] == self.band[self.band_current]: continue
# Set band element according to rule
self.band[self.band_current] = rule[3]
# Set current state according to rule
self.state_current = rule[2]
# Move band according to rule
if rule[4] == '<':
self.band_current -= 1;
if rule[4] == '>':
self.band_current += 1;
self.rule_used = ( rule[0] + ', ' +
rule[1] + ' -> ' +
rule[2] + ', ' +
rule[3] + ', ' +
rule[4])
break
return self.turing_finished()
def turing_finished(self) -> bool:
'''Returns whether the TM is in accepting state, rejecting state or has
halted because no applicable rules are left.'''
return ( self.state_current == self.state_accept or
self.state_current == self.state_reject or
self.errored )
def turing_accepted(self) -> bool:
'''Returns whether the TM, with the given input, has reached accepted state.'''
return self.state_current == self.state_accept
def turing_reset(self) -> None:
'''Resets current state to start state and band location to zero.'''
self.state_current = self.state_start
self.band_current = 0
def get_rules(self) -> list:
return self.rules
def get_rule_used(self) -> str:
return self.rule_used
def get_band(self) -> str:
return (''.join(self.band)).replace('_', '')
def get_band_context(self, context: int) -> str:
'''Returns context chars to the left and right of the current element as well as the element.'''
ret = ""
for i in range(self.band_current - context, self.band_current + context + 1):
if i < 0:
ret += "_ "
elif i+1 > len(self.band):
ret += "_ "
else:
ret += f"{self.band[i]} "
return ret.rstrip()
def get_band_current_element(self) -> str:
if self.band_current < 0 or self.band_current == len(self.band):
return '_'
return self.band[self.band_current]
def get_state_start(self) -> str:
return self.state_start
def get_state_current(self) -> str:
return self.state_current
def get_state_accept(self) -> str:
return self.state_accept
def get_state_reject(self) -> str:
return self.state_reject
def get_has_errored(self) -> bool:
return self.errored
def set_band(self, input: str):
self.band = list(input)
def redefine(self, turing_machine_def: str):
self.__init__(turing_machine_def)
def test():
tm = TuringMachine(''' 1; q0; q3; q4;
q0, 0 -> q0, 0, > ;
q0, 1 -> q0, 1, > ;
q0, _ -> q1, _, < ;
q1, 0 -> q2, 1, < ;
q1, 1 -> q1, 0, < ;
q1, _ -> q3, 1, - ;
q2, 1 -> q2, 1, < ;
q2, 0 -> q2, 0, < ;
q2, _ -> q3, _, >
''')
tm.set_band("1011")
tm.turing_run()
if tm.get_band() == "1100":
print("Successfull")
else:
print("Failed")