Python Forum
Enigma Machine, I need help!
Thread Rating:
  • 3 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Enigma Machine, I need help!
#11
To keep you up to date, here is what I have written so far.. But still cannot manage to find where the error lies... :(

I hope you can help!

from tkinter import *

#create window
Start = Tk()
Start.title("Enigma Machine")
Start.geometry("400x360+500+200")
Start.resizable(width=False, height=False)

def base():
    """Machine module opening a window with Tk inter to recreate the
    Enigma Machine which was used in WWII by the german army."""
    global PAD1,PAD2,PAD3,text,counter,ecounter,enclist
    Rotors = Frame(bg='dark grey')
    Rotors.pack(side=TOP)

    #in these Frames we will pack the keys for each key of the Keyboard
    Keyrow1 = Frame(pady=5)
    Keyrow1.pack(side=TOP)
    Keyrow2 = Frame(pady=3)
    Keyrow2.pack(side=TOP)
    Keyrow3 = Frame()
    Keyrow3.pack(side=TOP)
    Keyrow4 = Frame(pady=10)
    Keyrow4.pack(side=TOP)

    #this will be the first text field, containing the entered message
    NONEncryptF = Frame()
    NONEncryptF.pack(side=TOP)
    #This Frame contains the second text field with the encripted message
    EncryptF = Frame()
    EncryptF.pack(side=TOP)

    #First text field, M being Message, T for text and L for label
    NONEncryptM = "Inputted Text will be written here."
    NONEncryptT = StringVar()
    NONEncryptL = Label(NONEncryptF,width=50,height=4,textvariable=NONEncryptT,bg="black",fg="white")
    NONEncryptL.pack()
    NONEncryptT.set(str(NONEncryptM))

    #same is done for the encripted text box
    EncryptM = "Encrypted Text will be written here."
    EncryptT = StringVar()
    EncryptL = Label(EncryptF,width=50,height=4,textvariable=EncryptT,bg="grey55",fg="black")
    EncryptL.pack()
    EncryptT.set(str(EncryptM))

    PAD1=1          #these are the three rotors, counters for us
    PAD2=1
    PAD3=1

    counter = 0          #these both counters add a paragraph if the line exceeds 40 characters (see later)
    ecounter = 0

    #Keyboard
    LR1 = ["q","w","e","r","t","z","u","i","o","p"]
    LR2 = ["a","s","d","f","g","h","j","k","l"]
    LR3 = ["y","x","c","v","b","n","m",".",]
    LR4 = ["space"]

    text=[]         #these lists will be the containers for the both texts
    enclist = []

    #First Part: having the GUI of the machine work
    def GUI():
        def ROTORMOVER():
            """for each pressed key, we will augment the counter,
            counter moves back to 0 if it reaches 26,
            moving the one to its right"""
            global PAD1,PAD2,PAD3,counter,ecounter
            PAD3+=1
            if PAD3>26:
                PAD3=1
                PAD2+=1
                if PAD2>26:
                    PAD2=1
                    PAD1+=1
                    if PAD1>26:PAD1=1
            #increases counters
            counter+=1
            ecounter+=1
            ROTOR3.set(str(PAD3))
            ROTOR2.set(str(PAD2))
            ROTOR1.set(str(PAD1))

        def printer(l):
            """this pastes the normal, inputted text in the Message"""
            global text,NONEncryptM,counter
            ROTORMOVER()
            text+=[l]
            if counter==40:         # if characters exceed 40, we append a paragraph
                text.append("\n")
                counter=0
            NONEncryptM = "".join(text)
            NONEncryptT.set(str(NONEncryptM))
            encrypt()            #we launch encrypt() to encript the message that was just given

        #We create Labels and Stringvars for each row of keys in the keyboard
        ROTOR1 = StringVar()
        ROTOR2 = StringVar()
        ROTOR3 = StringVar()
        LabelPad1 = Label(Rotors, textvariable = ROTOR1 , bg ="grey")
        ROTOR1.set(str(PAD1))
        LabelPad1.pack(padx=5,pady=10,side=LEFT)
        LabelPad2 = Label(Rotors,textvariable = ROTOR2, bg = "grey")
        ROTOR2.set(str(PAD2))
        LabelPad2.pack(padx=5,pady=10,side=LEFT)
        LabelPad3 = Label(Rotors, textvariable = ROTOR3 , bg ="grey")
        ROTOR3.set(str(PAD3))
        LabelPad3.pack(padx=5,pady=10)

        for char in LR1:        #creating the keyboard
            Button(Keyrow1,text=char,width=3,pady=5,command=lambda q=char:printer(q)).pack(side=LEFT)
        for char in LR2:
            Button(Keyrow2,text=char,width=3,pady=5,command=lambda q=char:printer(q)).pack(side=LEFT)
        for char in LR3:
            Button(Keyrow3,text=char,width=3,pady=5,command=lambda q=char:printer(q)).pack(side=LEFT)
        Button(Keyrow4,text="Space",width=3,padx=85,pady=5,command=lambda:printer(" ")).pack()
    GUI()    #Executing code above

    def encrypt():
        """In this second section, we take what was written in the text field and let it "pass through" the rotors"""
        global EncryptM,text,PAD3,PAD2,PAD1,enclist,c,d,e,ENCMESS,x,ecounter,Rotor1,Rotor2,Rotor3,Mirror
        char = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"," "]
        c = PAD3-2          #c,d,e are variables that act as counters: for each pressed key they increase, meaning pressing twice 'a' will not exit the same key
        if c==-1:c=26
        d = PAD2-1
        e = PAD1-1
        ENCMESS=0

        #The modifications for the 3 rotors and mirror, I did not insert a plugboard yet in the program
        Rotor1 = [7,1,13,23,6,17,14,10,26,2,5,19,12,18,21,3,25,9,15,24,4,11,20,8,22,16]
        Rotor2 = [19,17,6,3,25,16,7,11,23,4,1,20,9,5,26,22,14,2,18,10,24,15,8,12,21,13]
        Rotor3 = [24,13,9,21,10,5,17,2,12,25,4,11,15,18,7,20,1,14,23,6,16,19,22,3,26,8]
        Mirror = [21,7,17,18,11,3,1,23,15,12,8,25,13,16,9,22,2,26,20,14,6,19,24,4,10,5]

        def starter():
            """Converts our last character of the inputted text into a cipher"""
            global ENCMESS,text,x
            x = text[len(text)-1]
            for cha in char:
                ENCMESS+=1
                if x==cha: break

        def coder(seq,rev=0,m=0):
            """Acts as our rotors"""
            global ENCMESS
            if rev==0 or rev==2:
                ENCMESS =(ENCMESS+m)%27
                ENCMESS = seq[ENCMESS-1]
                ENCMESS =(ENCMESS-m)%27
            elif rev==1:
                lst=["empty"]*26
                ENCMESS =(ENCMESS+m)%27
                for i,v in enumerate(seq):
                    lst[v-1] = i
                ENCMESS = lst[ENCMESS-1]
                ENCMESS =(ENCMESS-m)%27

        def letters():
            """Converts the cipher back to a letter"""
            global ENCMESS
            ENCMESS = char[ENCMESS-1]

        starter()
        coder(Rotor1,0,c)
        coder(Rotor2,0,d)
        coder(Rotor3,0,e)
        coder(Mirror,2)
        coder(Rotor3,1,e)
        coder(Rotor2,1,d)
        coder(Rotor1,1,c)
        #appends parapgraph if the characters are over 40
        for l in enclist:
            if ecounter==40:
                enclist.append("\n")
                ecounter=0
        if x==" ": enclist.append(" ")      #If the last charcater (a.k.a. inputted charcter is a space or dot, it inserts respectively space or dot)
        elif x==".":enclist.append(".")
        elif x=="\n":pass
        else:       # If not, it appends our encripted character
            letters()
            enclist.append(ENCMESS)
        EncryptM = "".join(enclist)
        EncryptT.set(str(EncryptM))

base()
Start.mainloop()        #keeps the window open
Reply
#12
I have been re-oreding your code for two reasons,
to get rid on all inspection errors, and to put into a class,
thus eliminating the need for globals.
The underlying code is still entirely yours, will not change names
beyond making them PEP8 compatible, for example:
LabelPad1
# changed to:
self.label_pad_1
Original inspection errors attached

.txt   Original Errors.txt (Size: 3.17 KB / Downloads: 346)

If this is disagreeable with you, please advise.
Reply
#13
(Nov-14-2017, 05:47 PM)Larz60+ Wrote: I have been re-oreding your code for two reasons,
to get rid on all inspection errors, and to put into a class,
thus eliminating the need for globals.
The underlying code is still entirely yours, will not change names
beyond making them PEP8 compatible, for example:
LabelPad1
# changed to:
self.label_pad_1
Original inspection errors attached


If this is disagreeable with you, please advise.

Thank you very much for the hard work, but where do I find the code now?
Reply
#14
It's not done.
I wanted to get a response first. I wasn't going to continue if there was
protest.
I'll post the code, (before and after) errors are located.

... edited comment ...
There are a lot more issues than I originally  thought. To do this properly,
i do have to simplify some of the code.
I will continue to do so, you can chuck the results if you wish.
Since this is not my only task, will run a bit longer.
Reply
#15
While Larz is working on that, I'll point out that the Enigma machines had no numbers or punctuation (including 'space' and 'period'). The letter 'X' was often used in place of a period and as a 'filler'. The keyboard looked like:

Q W E R T Z U I O
A S D F G H J K
P Y X C V B N M L

The message was entered as an unbroken string of letters, so a sentence like "Pizza party at 1 o'clock." would be entered as "PIZZAPARTYATONEOCLOCKX". Say the encoded string was "ABXDGPRMWQCZSLZPEMVTHJ", this message would be sent via morse code, either as 4 or 5 letter 'words', i.e.: "ABXDG PRMWQ CZSLZ PEMVT HJXXX" (note how last word is padded). At the receiving end, the message is again entered into an Enigma machine with the exact same settings as one long string of letters which results in the decoded message.  Afterwards, both machines are returned to the days default settings, to await the next message.

I mention this because if you include numbers or punctuation in your message you throw off the 'rotors' limit of 26 and the message will encode/decode incorrectly.

I also seem to recall reading some time ago that when the 3rd rotor (far left) initially tripped, it would set rotor 2 back 1 position.  As I said, it was some time ago so I would have to try and verify that, or perhaps, you were already aware of it.

Great project as well as a great look into history.
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
Reply
#16
I've been reading up on the details, and fear this might take more time that I have available.
I just found out yesterday that my 78 yr old sister passed suddenly of a heart attack. Family
matters will take a lot though not all of my time for a few days. I will still be on the forum, as
I thoroughly enjoy my time spent here, and I think it's my way of coping with the loss.

Since I plan to be programming until at least 90, it scares me that she was so young (others
may disagree), but my family has a history of longevity, with many living over 100, and the
oldest that I recall was 110.

I may not be as active for the next few days.
Reply
#17
Sorry to hear of your lose, it's never easy, regardless of the age.

(Nov-16-2017, 06:08 AM)Larz60+ Wrote: I've been reading up on the details, and fear this might take more time that I have available.

Yes, amazingly, something so rudimentary in it's construction (by today's standards) is so complex when it comes to replicating it via code. Given there were many versions of the Enigma, I think the OP should specify which model his/her code applies to.

Despite the obstacles, I still think it is a good project for someone to sink their teeth into and hope the OP continues the effort.
If it ain't broke, I just haven't gotten to it yet.
OS: Windows 10, openSuse 42.3, freeBSD 11, Raspian "Stretch"
Python 3.6.5, IDE: PyCharm 2018 Community Edition
Reply
#18
I have to thank you both very much for the work you have already provided. Larz60+, I can understand that you will not be her efor as long as you usually would, take your time and don't worry about it please.
My only ask would be, if you have already made some progress on it, could you send me the code as it is, so that I can see in what direction you were moving in, to maybe finalize it myself?
I thank you a lot too sparkz_alot, for your supply in helpful information about the machine itself, many things I already knew of, but many others I didn't also! So thank you for that. And to be honest, I am not replicating an exact Enigma Machine as you may have noticed, For me, it is more interesting to have a functioning machine, that works with the same principles. So de facto I can add my own tweaks and changes (like spacebar and dot). Thank you!

I am grateful for the time you both have invested already in my project, it will probably help me along further, in my still long way to discover in programming :) Thank you very much.
Reply
#19
Let me finish what I have this evening and tomorrow morning.
It will just be the GUI shell, same graphics layout, but using classes, and some background changes
I gave it a raised panel, and black background to look more like a black box.

You will need to add the functionality, or wait a few days.
Reply
#20
Here's a teaser for you, a Rotor class.
class Rotor:
    def __init__(self):
        self.iterno = 1
        self.maxiter = 26

    def __iter__(self):
        return self

    def next(self):
        try:
            if self.iterno > self.maxiter:
                raise StopIteration
            else:
                self.iterno += 1
                return self.iterno - 1
        except StopIteration:
            self.iterno = 2
            return self.iterno - 1

class TestRotor:
    def __init__(self):
        self.rotor_1 = Rotor()

    def click(self, who):
        return who.next()

    def tryme(self):
        print(self.click(self.rotor_1))
        for a_click in range(30):
            print(self.click(self.rotor_1))

if __name__ == '__main__':
    testit = TestRotor()
    testit.tryme()
looking at the code in TestRotor,
self.rotor_1 = Rotor()
creates a new rotor object,
x = self.rotor_1.next()
will give you the next click. it resets to 1 after the 26th click

run code to test.
to use the rotor class from a function:
rotor1 = Rotor()
for x in range(10):
    print(rotor1.next())
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Numeric Enigma Machine idev 9 485 Mar-29-2024, 06:15 PM
Last Post: idev
  Enigma Decoding Problem krisarmstrong 4 722 Dec-14-2023, 10:42 AM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020