def __init__(self, left_rotor, middle_rotor, right_rotor, reflector, menu_link='ZZZ', conx_in={}, conx_out={}): """rotors must be strings referring to either ['I','II','III','IV','V'] reflector must be string, one of either ['B','C']""" self.right_rotor = right_rotor self.middle_rotor = middle_rotor self.left_rotor = left_rotor self.reflector = reflectors[reflector] self.menu_link = menu_link self.middle_notch = entry.index( notches[self.middle_rotor] ) ## point if right rotor reaches will trigger middle rotor to step self.left_notch = entry.index( notches[self.left_rotor] ) ## point if middle rotor reaches will trigger left rotor to step self.current_position = menu_link self.pos_left_rotor, self.pos_mid_rotor, self.pos_rgt_rotor = ( ascii_uppercase.index(m) for m in menu_link.upper()) self.status = {} self.status['in'] = {char: 0 for char in entry} self.status['out'] = {char: 0 for char in entry} self.conxns = {'in': conx_in, 'out': conx_out}
def __init__(self, left_rotor: str, middle_rotor: str, right_rotor: str, reflector: str, menu_link: str = 'ZZZ'): """rotors must be strings referring to either ['I','II','III','IV','V'] reflector must be string, one of either ['B','C']""" assert all([ r in raw_rotors.keys() for r in (left_rotor, middle_rotor, right_rotor) ]) assert reflector in reflectors.keys() self.right_rotor = right_rotor self.middle_rotor = middle_rotor self.left_rotor = left_rotor self.reflector = reflectors[reflector] self.menu_link = menu_link self.middle_notch = entry.index( notches[self.middle_rotor] ) ## point if right rotor reaches will trigger middle rotor to step self.left_notch = entry.index( notches[self.left_rotor] ) ## point if middle rotor reaches will trigger left rotor to step self.pos_left_rotor, self.pos_mid_rotor, self.pos_rgt_rotor = ( ascii_uppercase.index(m) for m in menu_link.upper()) self.in_status = {char: 0 for char in entry} self.out_status = {char: 0 for char in entry} self.current_position = menu_link self.record = {}
def once_thru_scramble(self, start_character, direction, first_rotor, pos1, second_rotor, pos2, third_rotor, pos3): """ start_character must be single ASCII character A-Z direction is either 'forward' or 'back' """ if direction == 'forward': usedict = {k: v for k, v in forward_rotors.items()} elif direction == 'back': usedict = {k: v for k, v in rev_rotors.items()} else: print("direction can only be 'forward' or 'back'") return 'wtf' start_character = start_character.upper() entry_pos = entry.index(start_character) fst_pos_modifier = (26 + pos1 - 0) % 26 fst_in = (entry_pos + fst_pos_modifier) % 26 fst_out = usedict[first_rotor][fst_in] ch1o = entry[fst_out] scd_pos_modifier = (26 + pos2 - pos1) % 26 scd_in = (fst_out + scd_pos_modifier) % 26 ch2i = entry[scd_in] scd_out = usedict[second_rotor][scd_in] ch2o = entry[scd_out] thd_pos_modifier = (26 + pos3 - pos2) % 26 thd_in = (scd_out + thd_pos_modifier) % 26 ch3i = entry[thd_in] thd_out = usedict[third_rotor][thd_in] ch3o = entry[thd_out] return ch3o
def full_scramble(self, in_ch): in_ch = in_ch.upper() left_rotor = self.left_rotor middle_rotor = self.middle_rotor right_rotor = self.right_rotor rflector = self.reflector # # first run right to left through scrambler forward_run = self.once_thru_scramble(in_ch, direction='forward', first_rotor=right_rotor, pos1=self.pos_rgt_rotor, second_rotor=middle_rotor, pos2=self.pos_mid_rotor, third_rotor=left_rotor, pos3=self.pos_left_rotor) # # reflector back around for return rfi_pos_mod = ( 26 + 0 - self.pos_left_rotor ) % 26 ## the '0' is there to matching formatting of other position modifiers - reflector is not moved so it will always be 0 rf_in = (entry.index(forward_run) + rfi_pos_mod) % 26 chri = entry[rf_in] mirrored = rflector[chri] # print(f"{forward_run} -> {chri} (into reflector) -> {mirrored} (reflected out)") # # second run back left to right thru scrambler back_run = self.once_thru_scramble(mirrored, direction='back', first_rotor=left_rotor, pos1=self.pos_left_rotor, second_rotor=middle_rotor, pos2=self.pos_mid_rotor, third_rotor=right_rotor, pos3=self.pos_rgt_rotor) bk_out = entry.index(back_run) bko_pos_mod = ( 26 + 0 - self.pos_rgt_rotor ) % 26 ## as above, '0' just reflects that the entry interface doesn't move bk_final = (bk_out + bko_pos_mod) % 26 final = entry[bk_final] # print('RR back out: ', back_run, '-->', final) # print(in_ch,"-->",final) return final
def once_thru_scramble(self, start_character, direction, first_rotor, pos1, second_rotor, pos2, third_rotor, pos3): """ start_character must be single ASCII character A-Z direction is either 'forward' or 'back' """ if direction == 'forward': usedict = {k: v for k, v in forward_rotors.items()} elif direction == 'back': usedict = {k: v for k, v in rev_rotors.items()} else: print('only forward or back for direction') return 'wtf' # problem is confusion around left/middle/right rotors vs first/second/third rotors and forward/back # this currently works as if first = left, middle=second, third = right. If in 'forward'. Is this desired? start_character = start_character.upper() entry_pos = entry.index(start_character) fst_pos_modifier = (26 + pos1 - 0) % 26 fst_in = (entry_pos + fst_pos_modifier) % 26 fst_out = usedict[first_rotor][fst_in] ch1o = entry[fst_out] scd_pos_modifier = (26 + pos2 - pos1) % 26 scd_in = (fst_out + scd_pos_modifier) % 26 ch2i = entry[scd_in] scd_out = usedict[second_rotor][scd_in] ch2o = entry[scd_out] thd_pos_modifier = (26 + pos3 - pos2) % 26 thd_in = (scd_out + thd_pos_modifier) % 26 ch3i = entry[thd_in] thd_out = usedict[third_rotor][thd_in] ch3o = entry[thd_out] if direction == 'forward': print( f"{start_character} -> (RR out) {ch1o} -> (MR in) {ch2i} -> (MR out) {ch2o} -> (LR in) {ch3i} -> (LR out) {ch3o}" ) elif direction == 'back': print( f"{start_character} -> (LR out) {ch1o} -> (MR in) {ch2i} -> (MR out) {ch2o} -> (RR in) {ch3i} -> (RR out) {ch3o}" ) return ch3o
def nx_setup(self, scale=1, figsize=(15, 10), width_of_scrambler=0.1, height_of_scrambler=0.06): """For creating NetworkX graphs. Setting up base graph (BG), detailed graph (TG), figure and axes for displaying scrambler connections Will also effectively reset the nx Graphs""" self.BG = nx.Graph() scramblers_in_menu = [ k if type(k) == int else 'REG' for k in menu.keys() ] self.BG.add_nodes_from(scramblers_in_menu) base_edges = set() for scr_id, descriptor_dict in self.menu.items(): if scr_id == 'config': scr_id = 'REG' for inorout in ['in', 'out']: for connected_scrambler, ior in descriptor_dict['conxns'][ inorout].items(): ## the dictionary of connections that is the value for each 'conx_in/out' keys base_edges.add(frozenset([ scr_id, connected_scrambler ])) # set of sets so not to double up base_edges = [tuple(be) for be in base_edges ] # turn set of frozensets into list of tuples self.BG.add_edges_from(base_edges) # self.base_pos_for_nx = nx.circular_layout(self.BG,scale=scale) self.base_pos_for_nx = nx.spring_layout(self.BG, scale=scale) self.base_pos_for_nx['REG'] = np.array([0, -0.9]) ## This section for the detailed graph (TG) self.TG = nx.Graph() ## this for-loop adds all nodes to the graph for scr_id in scramblers_in_menu: ## for each scrambler in the menu for ch in entry: ## for each letter A-Z for i in [ 'I', 'O' ]: ## for each end (in/out) of the double-ended scrambler if scr_id == 'REG': ## i = X (not I or O) if it's the register i = 'X' this_node_label = f"{scr_id}-{i}-{ch}" self.TG.add_node( this_node_label) ## add a node to the graph self.TG.nodes[this_node_label]["color"] = grey ## this for-loop adds edges for scrambler connections to the graph self.inter_scr_edges = set() for scr_id, descriptor_dict in self.menu.items( ): # for each scrambler (scr_id) and its spec dict for inorout in ['in', 'out']: # go thru the conx_in and conx_out dicts if scr_id == 'config': # this just for dealing with the register first_node = 'REG-X-' else: first_node = f"{scr_id}-{iomap[inorout]}-" # label the start of the 1st node, with either I/O for connected_scrambler, ior in descriptor_dict['conxns'][ inorout].items(): # go thru the in/out connections second_node = f"{connected_scrambler}-{iomap[ior]}-" # label the start of the 2nd node for ch in entry: # for each letter A-Z self.inter_scr_edges.add( frozenset([first_node + ch, second_node + ch]) ) # create 26 nodes with each of 1st/2nd node plus letter ## bit of data reformatting for the edges self.inter_scr_edges = [list(fs) for fs in self.inter_scr_edges] for edge in self.inter_scr_edges: edge.append({'color': grey}) self.inter_scr_edges = [tuple(fs) for fs in self.inter_scr_edges] self.TG.add_edges_from(self.inter_scr_edges) wrange_of_letters = list( np.linspace(-0.5 * width_of_scrambler, 0.5 * width_of_scrambler, 26)) self.manual_pos = {} for node in self.TG.nodes(): scr_id, io, ch = node.split('-') try: scr_id = int(scr_id) except: pass x, y = self.base_pos_for_nx[scr_id] if io == 'I': ## spacing apart the in from the out nodes y += -0.5 * height_of_scrambler ## 'in' (I) is below else: y += 0.5 * height_of_scrambler ## 'out' (O) is above x += wrange_of_letters[entry.index(ch)] self.manual_pos[node] = np.array([x, y]) self.colors = [self.TG[u][v]['color'] for u, v in self.TG.edges()] fig, ax = plt.subplots(figsize=figsize) nx.draw_networkx_nodes(self.BG, pos=self.base_pos_for_nx) nx.draw_networkx_labels(self.BG, pos=self.base_pos_for_nx) nx.draw_networkx_edges(self.TG, pos=self.manual_pos, edge_color=self.colors)