# ===================================================================
# Enigma Machine Simulation - read/process configuration file
# ===================================================================
from datetime import date
import user_interface as ui
import copy
import re
import sys
# --------------------------------------------------------------------
# ---- namespace for enigma simulation runtime configuration
# --------------------------------------------------------------------
class EnigmaConfig():
def __init__(self,filename):
# ---- rotor dictionary and keys
self.rotors = { 'I':[], 'II':[], 'III':[], 'IV':[],
'V':[], 'VI':[], 'VII':[], 'VIII':[] }
self.rotorkeys = list(self.rotors.keys())
self.abc = [] # alphabet list
self.abclen = 0 # abc list length
self.abcidx = [] # list of alphabet
# indexes
self.configfile = filename # configuration
# file
self.line_count = 0 # line count read
# from config file
self.plugs = [] # plugboard pairs
self.pglst = [] # plugboard list
self.reflst = [] # reflector list
self.date = date.today() # config file date
# ---- default enigma machine rotor configuration
# ---- [left,middle,right]
self.default_rotor_names = ['III','II','I']
self.default_rotor_starts= [2,1,0]
# ---- create an empty configuration
if not filename:
return
# ---- read/process configuration file
fh = open(filename,'r')
for line in fh:
self.line_count += 1
line = line.strip()
if not line: # empty string (line)
continue
if re.match('^#',line): # comment
continue
x = line.split(',')
# --- date file created
if x[0] =='date': # date
self.date = x[1]
continue
# --- alphabet length?
if x[0] == 'len': # alphabet length
tf,i = ui.is_int(x[1])
if tf:
if i >= 0:
self.abclen = i
continue
if x[0] == 'unk': # substitute for unknown
# character
self.abcunk = x[1]
continue
# --- alphabet?
if x[0] == 'abc': # alphabet
self.abc.append(x[2])
continue
# --- plug?
if x[0] == 'plug': # plugboard
self.plugs.append(x[1])
continue
# ---- reflector?
if x[0] == 'ref': # reflector
self.reflst.append(int(x[2]))
continue
# ---- rotor?
if x[0] in self.rotors.keys(): # rotor
tf,i = ui.is_int(x[2])
if tf:
if i >= 0:
self.rotors[x[0]].append(int(x[2]))
continue
# ---- what?
print()
print('unknown line in config file ' +
f'line={line_count})\n({line})')
sys.exit()
fh.close()
# ---- verify config file data
for r in self.rotors:
if len(self.rotors[r]) != self.abclen:
print()
print(f'bad rotor list length (r)')
sys.exit()
if len(self.abc) != self.abclen:
print()
print('bad abc list length\n(alphabet)')
sys.exit()
if len(self.reflst) != self.abclen:
print()
print('bad reflector list length\n(reflector)')
sys.exit()
for i in range(self.abclen):
j = self.reflst[i]
if self.reflst[j] != i:
print(f'error in reflector pair i={i} j={j}')
sys.exit()
# ---- now set abcidx
self.abcidx = list(range(self.abclen))
# ---- now create plugboard
self.create_plugboard()
# ---------------------------------------------------------------
# ---- helper function: convert character to index
# ---------------------------------------------------------------
def _char_to_index(self,c):
for i in range(len(self.abc)):
if c == self.abc[i]:
return i
print()
print(f'character c not found in alphabet')
sys.exit()
# ---------------------------------------------------------------
# ---- create plugboard (list)
# ---------------------------------------------------------------
def create_plugboard(self):
used_chars = []
self.pglst = list(range(self.abclen))
for cc in self.plugs:
# --- error check
if len(cc) != 2 or cc[0] == cc[1] or \
cc[0] in used_chars or \
cc[1] in used_chars or \
cc[0] not in self.abc or \
cc[1] not in self.abc:
print()
print(f' error in plugboard pair ({cc})')
sys.exit()
used_chars.append(cc[0])
used_chars.append(cc[1])
idx0 = self._char_to_index(cc[0])
idx1 = self._char_to_index(cc[1])
self.pglst[idx0] = idx1
self.pglst[idx1] = idx0
# ---------------------------------------------------------------
# ---- display EnigmaConfig internals
# ---------------------------------------------------------------
def display_config(self):
self.display_stats()
def display_stats(self):
print()
print(f'config file: {self.configfile}')
print(f'date : {self.date}')
print(f'lines read : {self.line_count}')
print(f'alphabet len: {len(self.abc)}')
print(f' abclen : {self.abclen}')
print(f' abcunk : {self.abcunk}')
print(f'rotor count: {len(self.rotors)}')
for r in self.rotors.keys():
print(f' {r:5} len: {len(self.rotors[r])}')
print(f'reflector len: {len(self.reflst)}')
print(f'plugs : {len(self.plugs)}')
for p in self.plugs:
print(f' ({p})')
##for idx in range(len(self.pglst)):
## print(f' {idx:>2} --> {self.pglst[idx]:<2} i' +
## f'({self.abc[idx]})')
print()