Python GUI 039 – Transpo – Route Utils

Finally, I’m getting to the actual solvers. We’ll start with Route Transposition.

Route has some special requirements that justify breaking the program up into two files – Utils (which largely handles the details for creating the different routes within an array (an n x m list object), and the main file (which has the code for creating the GUI screens, and performing the actual auto-solution step). Note that a lot of the supporting code is spread across other files (crypt_utils.py, inc_utils.py, transpo_utils.py), and can’t be easily incorporated into the discussion of transpo_route.py and/or transpo_route_utils.py. I’ll try to link to the previous posts for things I’ve written about before, otherwise some of the details may need to wait until I’ve finished writing about all of the other transposition solvers, and can tackle the remaining utils files after that. If things seem to be really haphazard, it’s because they are (actually, it’s because the overall program is going to include possibly 40 different cipher types, and it’s just a massive undertaking for one person).

If you’re not familiar with Route Transposition, or you’ve forgotten what I’ve written about before, check out the Transpo Introduction post (it’s near the bottom). I’ve also previewed the data entry screens in the screen caps post. Finally, I’ve decided to implement the dict-based enum approach described in the last post, so check that out if you need a refresher.

##########################
# transpo_route_utils.py #
##########################

# Route Tramp functions
# make_box() ....... : Read message into box with OnBy option
# read_off_box() ... : Read message off box with OffBy option
# string_direction() : Flip string with Direction option
# mirror_box() ..... : Horizontal/Vertical mirror flips with Flip

# .................. : option
# show_box() ....... : Debugging function
# show_boxes() ..... : Debugging function

"""
get_route_enums()
This is the function that sets up the dict objects for use as "enum simulators." onby_dict and offby_dict are actually the same thing, but I'm keeping them separate because it's easier to understand what's going on when building up the transposition array (on-by) and peeling off the final message (off-by) if I can see the names for the operation at hand.
flip_dict is used for mirror flipping the array.
dir_dict allows for reversing the message string
"""

def get_route_enums():
... ret1 = {'ROWS': 0, 'COLS': 1, 'ALTROWS': 2, 'ALTCOLS': 3, \

........... 'DIAGS': 4, 'ALTDIAGS': 5, 'REVDIAGS': 6, \
........... 'ALTREVDIAGS': 7, 'SPIRAL': 8, 'CCWSPIRAL': 9}
... ret2 = {'NORMAL': 0, 'HORIZONTAL': 1, 'VERTICAL': 2, 'BOTH': 3}
... ret3 = {'NORMAL': 0, 'REVERSED': 1}
... return ret1, ret1, ret2, ret3

# Make the actual dict objects as globals

onby_dict, offby_dict, flip_dict, dir_dict = get_route_enums()

"""
make_box()
Create the n x m array that will contain our encrypted message letter positions, then populate it with the positions using the specified route (either user-specified, or forced by the auto-solver going through each route one at a time).

Note, I may need to change things to allow for using actual message strings when I get to Playfair for making Polybius alphabet tables.
"""

def make_box(msglen, width, height, route):

# Initialize our numbered list.

... msg = []

# Prep our output list (as a flat, one-dimensional array).

... ret = [0] * width * height

# Build up our raw numbered list

... for i in range(msglen):
....... msg.append(i)

# Start encrypting the numbered positions
# Note that all routes start in the upper left
# corner of the box. Any other starting points
# can be achieved by using flip and direction.
# ROWS is easy - just copy as-is.

... if route == onby_dict['ROWS']:
....... for i in range(msglen):
........... ret[i] = msg[i]

# Columns copies the positions in by-width steps.

... elif route == onby_dict['COLS']:
....... r, c = 0, 0
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... r += 1
........... if r >= height:
............... r = 0
............... c += 1

# Now we start getting into the ugly parts.
# Alternating Rows. Check out the introduction
# post for a visualization of what's
# happening here. (And for the following routes,
# too.)

... elif route == onby_dict['ALTROWS']:
....... r, c, d = 0, 0, 1
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... c += d
........... if c >= width:
............... d = -1
............... r += 1
............... c -= 1
........... elif c < 0:
............... d = 1
............... r += 1
............... c = 0

# Alternating Columns.

... elif route == onby_dict['ALTCOLS']:
....... r, c, d = 0, 0, 1
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... r += d
........... if r >= height:
............... d = -1
............... r -= 1
............... c += 1
........... elif r < 0:
............... d = 1
............... r = 0
............... c += 1

# Diagonals Clockwise.

... elif route == onby_dict['DIAGS']:
....... r, c, rmstr, cmstr = 0, 0, 0, 0
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... c -= 1
........... r += 1
........... if c < 0 or r == height:
............... if cmstr < width - 1:
................... cmstr += 1
............... else:
................... rmstr += 1
............... c = cmstr
............... r = rmstr

# Alternating Diagonals, Clockwise.

... elif route == onby_dict['ALTDIAGS']:
....... r, c, rmstr, cmstr, d = 0, 0, 0, 0, -1
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... if d == 1:
............... if c == 0:
................... d = -1
................... if r != height - 1:
....................... r += 1
................... else:
....................... c += 1
............... elif r == height - 1:
................... c += 1
................... d = -1
............... else:
................... r += 1
................... c -= 1
........... else:
............... if r == 0:
................... d = 1
................... if c != width - 1:
....................... c += 1
................... else:
....................... r += 1
............... elif c == width - 1:
................... r += 1
................... d = 1
............... else:
................... r -= 1
................... c += 1

# Diagonals, counterclockwise.

... elif route == onby_dict['REVDIAGS']:
....... r, c, rmstr, cmstr = 0, 0, 0, 0
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... c += 1
........... r -= 1
........... if r < 0 or c == width:
............... if rmstr < height - 1:
................... rmstr += 1
............... else:
................... cmstr += 1
............... c = cmstr
............... r = rmstr

# Alternating Diagonals, counterclockwise.

... elif route == onby_dict['ALTREVDIAGS']:
....... r, c, rmstr, cmstr, d = 0, 0, 0, 0, 1
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... if d == 1:
............... if c == 0:
................... d = -1
................... if r != height - 1:
....................... r += 1
................... else:
....................... c += 1
............... elif r == height - 1:
................... c += 1
................... d = -1
............... else:
................... r += 1
................... c -= 1
........... else:
............... if r == 0:
................... d = 1
................... if c != width - 1:
....................... c += 1
................... else:
....................... r += 1
............... elif c == width - 1:
................... r += 1
................... d = 1
............... else:
................... r -= 1
................... c += 1

# Clockwise Spiral.

... elif route == onby_dict['SPIRAL']:
....... r, c, rmin, rmax, cmin, cmax = 0, 0, 1, height - 1, 0, width - 1
....... d = 0
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... if d == 0:
............... c += 1
............... if c == cmax:
................... cmax -= 1
................... d += 1
........... elif d == 1:
............... r += 1
............... if r == rmax:
................... rmax -= 1
................... d += 1
........... elif d == 2:
............... c -= 1
............... if c == cmin:
................... cmin += 1
................... d += 1
........... elif d == 3:
............... r -= 1
............... if r == rmin:
................... rmin += 1
................... d = 0

# Counterclockwise Spiral.

... elif route == onby_dict['CCWSPIRAL']:
....... r, c, rmin, rmax, cmin, cmax = 0, 0, 0, height - 1, 1, width - 1
....... d = 0
....... for ch in msg:
........... ret[c + (r * width)] = ch
........... if d == 0:
............... r += 1
............... if r == rmax:
................... rmax -= 1
................... d += 1
........... elif d == 1:
............... c += 1
............... if c == cmax:
................... cmax -= 1
................... d += 1
........... elif d == 2:
............... r -= 1
............... if r == rmin:
................... rmin += 1
................... d += 1
........... elif d == 3:
............... c -= 1
............... if c == cmin:
................... cmin += 1
................... d = 0

... return ret

"""
read_off_box()
This is really just the reverse of make_box(). Taking the specified off-by route, read the message in the box and write it to a flat list.
"""

def read_off_box(box, width, height, route):
... ret = []

# Read off by rows.

... if route == offby_dict['ROWS']:
....... for i in range(height):
........... for j in range(width):
............... ret.append(box[i*width + j])

# Read off by columns.

... elif route == offby_dict['COLS']:
....... for j in range(width):
........... for i in range(height):
............... ret.append(box[i*width + j])

# Read off by alternating rows.

... elif route == offby_dict['ALTROWS']:
....... r, c, d = 0, 0, 1
....... for i in range(len(box)):
........... ret.append(box[c + (r * width)])
........... c += d
........... if c >= width:
............... d = -1
............... r += 1
............... c -= 1
........... elif c < 0:
............... d = 1
............... r += 1
............... c = 0

# Read off by alternating columns.

... elif route == offby_dict['ALTCOLS']:
....... r, c, d = 0, 0, 1
....... for i in range(len(box)):
........... ret.append(box[c + (r * width)])
........... r += d
........... if r >= height:
............... d = -1
............... r -= 1
............... c += 1
........... elif r < 0:
............... d = 1
............... r = 0
............... c += 1

# Read off by clockwise diagonals.

... elif route == offby_dict['DIAGS']:
....... r, c, rmstr, cmstr = 0, 0, 0, 0
....... for i in range(len(box)):
........... ret.append(box[c + (r * width)])
........... c -= 1
........... r += 1
........... if c < 0 or r == height:
............... if cmstr < width - 1:
................... cmstr += 1
............... else:
................... rmstr += 1
............... c = cmstr
............... r = rmstr

# Read off by clockwise alternating diagonals.

... elif route == offby_dict['ALTDIAGS']:
....... r, c, rmstr, cmstr, d = 0, 0, 0, 0, -1
....... for ch in range(len(box)):
........... ret.append(box[c + (r * width)])
........... if d == 1:
............... if c == 0:
................... d = -1
................... if r != height - 1:
....................... r += 1
................... else:
....................... c += 1
............... elif r == height - 1:
................... c += 1
................... d = -1
............... else:
................... r += 1
................... c -= 1
........... else:
............... if r == 0:
................... d = 1
................... if c != width - 1:
....................... c += 1
................... else:
....................... r += 1
............... elif c == width - 1:
................... r += 1
................... d = 1
............... else:
................... r -= 1
................... c += 1

# Read off by counterclockwise diagonals.

... elif route == offby_dict['REVDIAGS']:
....... r, c, rmstr, cmstr = 0, 0, 0, 0
....... for ch in range(len(box)):
........... ret.append(box[c + (r * width)])
........... c += 1
........... r -= 1
........... if r < 0 or c == width:
............... if rmstr < height - 1:
................... rmstr += 1
............... else:
................... cmstr += 1
............... c = cmstr
............... r = rmstr

# Read off by counterclockwise alternating diagonals.

... elif route == offby_dict['ALTREVDIAGS']:
....... r, c, rmstr, cmstr, d = 0, 0, 0, 0, 1
....... for ch in range(len(box)):
........... ret.append(box[c + (r * width)])
........... if d == 1:
............... if c == 0:
................... d = -1
................... if r != height - 1:
....................... r += 1
................... else:
....................... c += 1
............... elif r == height - 1:
................... c += 1
................... d = -1
............... else:
................... r += 1
................... c -= 1
........... else:
............... if r == 0:
................... d = 1
................... if c != width - 1:
....................... c += 1
................... else:
....................... r += 1
............... elif c == width - 1:
................... r += 1
................... d = 1
............... else:
................... r -= 1
................... c += 1

# Read off by clockwise spiral.

... elif route == offby_dict['SPIRAL']:
....... r, c, rmin, rmax, cmin, cmax = 0, 0, 1, height - 1, 0, width - 1
....... d = 0
....... for ch in range(len(box)):
........... ret.append(box[c + (r * width)])
........... if d == 0:
............... c += 1
............... if c == cmax:
................... cmax -= 1
................... d += 1
........... elif d == 1:
............... r += 1
............... if r == rmax:
................... rmax -= 1
................... d += 1
........... elif d == 2:
............... c -= 1
............... if c == cmin:
................... cmin += 1
................... d += 1
........... elif d == 3:
............... r -= 1
............... if r == rmin:
................... rmin += 1
................... d = 0

# Read off by counterclockwise spiral.

... elif route == offby_dict['CCWSPIRAL']:
....... r, c, rmin, rmax, cmin, cmax = 0, 0, 0, height - 1, 1, width - 1
....... d = 0
....... for ch in range(len(box)):
........... ret.append(box[c + (r * width)])
........... if d == 0:
............... r += 1
............... if r == rmax:
................... rmax -= 1
................... d += 1
........... elif d == 1:
............... c += 1
............... if c == cmax:
................... cmax -= 1
................... d += 1
........... elif d == 2:
............... r -= 1
............... if r == rmin:
................... rmin += 1
................... d += 1
........... elif d == 3:
............... c -= 1
............... if c == cmin:
................... cmin += 1
................... d = 0

... return ret

"""
show_box()
This is a debugging function that prints out the desired box (the array with the on-by message filled in, or the array after mirror flipping) as an actual n x m rectangle.
"""

def show_box(box, width, height):
... for i in range(0, len(box), width):
....... print('%s' % (''.join(box[i: i+ width])))

"""
show_boxes()
This is a debugging function that prints both desired boxes (the array with the on-by message filled in, and the array after mirror flipping) as actual n x m rectangles.
"""

def show_boxes(box1, box2, width, height):
... for i in range(0, len(box1), width):
....... print('%s %s' % (''.join(box1[i: i+ width]), ''.join(box2[i: i + width])))

"""
string_direction()
Either returns the message as-is, or reverses and returns it.
"""

def string_direction(msg, direction):
... ret = msg
... if direction == dir_dict['REVERSED']:
....... ret = ret[::-1]
... return ret

"""
mirror_box()
Takes the desired array (usually the first box with the message read in by the on-by route) and flips it (horizontally, vertically, or both).
"""

def mirror_box(box, width, height, t):

# Initialization.

... ret = [''] * width * height
... ret2 = [''] * width * height

# Return the original array as-is.

... if t == flip_dict['NORMAL']:
....... return box

# Perform a horizontal mirror flip.

... elif t == flip_dict['HORIZONTAL']:
....... for x in range(height):
........... for y in range(width):
............... ret[(x * width) + y] = box[(x * width) + (width - y - 1)]

# Perform a vertical mirror flip.

... elif t == flip_dict['VERTICAL']:
....... for y in range(width):
........... for x in range(height):
............... ret[(x * width) + y] = box[(height - x - 1) * width + y]

# I'm lazy. I copied both flips here.
# I guess I could have combined them in the above
# H and V flip sections.

... elif t == flip_dict['BOTH']:
....... for x in range(height):
........... for y in range(width):
............... ret2[(x * width) + y] = box[(x * width) + (width - y - 1)]

....... for y in range(width):
........... for x in range(height):
............... ret[(x * width) + y] = ret2[(height - x - 1) * width + y]

... return ret

Next up: No idea

Published by The Chief

Who wants to know?

Leave a comment

Design a site like this with WordPress.com
Get started