diff --git a/Enigma_Week0_assignment b/Enigma_Week0_assignment new file mode 100644 index 0000000..8ffbfa6 --- /dev/null +++ b/Enigma_Week0_assignment @@ -0,0 +1,368 @@ + const ROTOR_PERMUTATIONS = [ + "EKMFLGDQVZNTOWYHXUSPAIBRCJ", /* Permutation for slow rotor */ + "AJDKSIRUXBLHWTMCQGZNPYFVOE", /* Permutation for medium rotor */ + "BDFHJLCPRTXVZNYEIWGAKMUSQO" /* Permutation for fast rotor */ +]; + +/* Constants that control the display of the current rotor setting */ + +const ROTOR_BGCOLOR = "#BBAA77"; /* Background color for the rotor */ +const ROTOR_WIDTH = 24; /* Width of the setting indicator */ +const ROTOR_HEIGHT = 26; /* Height of the setting indicator */ +const ROTOR_COLOR = "Black"; /* Text color of the rotor */ +const ROTOR_LABEL_DY = 9; /* Offset from center to baseline */ +const ROTOR_FONT = "Helvetica Neue-24"; + +/* This array specifies the coordinates of each rotor display */ + +const ROTOR_LOCATIONS = [ + { x: 244, y: 95 }, + { x: 329, y: 95 }, + { x: 412, y: 95 } +]; + +/* + * To the left of the slow rotor, the Enigma machine includes a + * component called the "reflector," which implements a fixed + * permutation that remains unchanged as the rotors advance. The + * constant REFLECTOR_PERMUTATION defines the mapping of the reflector. + * Note that the reflector is symmetric. If A is transformed to I, + * then I is transformed to A. + */ + +const REFLECTOR_PERMUTATION = "IXUHFEZDAOMTKQJWNSRLCYPBVG"; + +/* Constants that define the keys on the Enigma keyboard */ + +const KEY_RADIUS = 24; /* Outer radius of a key in pixels */ +const KEY_BORDER = 3; /* Width of the key border */ +const KEY_BORDER_COLOR = "#CCCCCC"; /* Fill color of the key border */ +const KEY_BGCOLOR = "#666666"; /* Background color of the key */ +const KEY_UP_COLOR = "#CCCCCC"; /* Text color when the key is up */ +const KEY_DOWN_COLOR = "#CC3333"; /* Text color when the key is down */ +const KEY_LABEL_DY = 10; /* Offset from center to baseline */ +const KEY_FONT = "Helvetica Neue-Bold-28"; + +/* This array determines the coordinates of a key for each letter index */ + +const KEY_LOCATIONS = [ + { x: 140, y: 566 } /* A */, + { x: 471, y: 640 } /* B */, + { x: 319, y: 639 } /* C */, + { x: 294, y: 567 } /* D */, + { x: 268, y: 495 } /* E */, + { x: 371, y: 567 } /* F */, + { x: 448, y: 567 } /* G */, + { x: 523, y: 567 } /* H */, + { x: 650, y: 496 } /* I */, + { x: 598, y: 567 } /* J */, + { x: 674, y: 567 } /* K */, + { x: 699, y: 641 } /* L */, + { x: 624, y: 641 } /* M */, + { x: 547, y: 640 } /* N */, + { x: 725, y: 497 } /* O */, + { x: 92, y: 639 } /* P */, + { x: 115, y: 494 } /* Q */, + { x: 345, y: 495 } /* R */, + { x: 217, y: 566 } /* S */, + { x: 420, y: 496 } /* T */, + { x: 574, y: 496 } /* U */, + { x: 395, y: 639 } /* V */, + { x: 192, y: 494 } /* W */, + { x: 242, y: 639 } /* X */, + { x: 168, y: 639 } /* Y */, + { x: 497, y: 496 } /* Z */ +]; + +/* Constants that define the lamps above the Enigma keyboard */ + +const LAMP_RADIUS = 23; /* Radius of a lamp in pixels */ +const LAMP_BORDER_COLOR = "#111111"; /* Line color of the lamp border */ +const LAMP_BGCOLOR = "#333333"; /* Background color of the lamp */ +const LAMP_OFF_COLOR = "#666666"; /* Text color when the lamp is off */ +const LAMP_ON_COLOR = "#FFFF99"; /* Text color when the lamp is on */ +const LAMP_LABEL_DY = 9; /* Offset from center to baseline */ +const LAMP_FONT = "Helvetica Neue-Bold-24"; + +/* This array determines the coordinates of a lamp for each letter index */ + +const LAMP_LOCATIONS = [ + { x: 144, y: 332 } /* A */, + { x: 472, y: 403 } /* B */, + { x: 321, y: 402 } /* C */, + { x: 296, y: 333 } /* D */, + { x: 272, y: 265 } /* E */, + { x: 372, y: 333 } /* F */, + { x: 448, y: 334 } /* G */, + { x: 524, y: 334 } /* H */, + { x: 650, y: 266 } /* I */, + { x: 600, y: 335 } /* J */, + { x: 676, y: 335 } /* K */, + { x: 700, y: 403 } /* L */, + { x: 624, y: 403 } /* M */, + { x: 549, y: 403 } /* N */, + { x: 725, y: 267 } /* O */, + { x: 94, y: 401 } /* P */, + { x: 121, y: 264 } /* Q */, + { x: 347, y: 265 } /* R */, + { x: 220, y: 332 } /* S */, + { x: 423, y: 265 } /* T */, + { x: 574, y: 266 } /* U */, + { x: 397, y: 402 } /* V */, + { x: 197, y: 264 } /* W */, + { x: 246, y: 402 } /* X */, + { x: 170, y: 401 } /* Y */, + { x: 499, y: 265 } /* Z */ +]; + + + + +/* + * File: Enigma.js + * --------------- + * This program implements a graphical simulation of the Enigma machine. + */ + +import "graphics"; + + + +/* Main program */ + +function Enigma() { + var enigmaImage = GImage("EnigmaTopView.png"); + var gw = GWindow(enigmaImage.getWidth(), enigmaImage.getHeight()); + gw.add(enigmaImage); + runEnigmaSimulation(gw); +} + +function runEnigmaSimulation(gw) { + var enigma = { + keys: [], + lamps: [], + rotors: [] + }; + + // Add keys and lamps + for (var i = 0; i < 26; i++) { + var ch = String.fromCharCode("A".charCodeAt(0) + i); + + var center = KEY_LOCATIONS[i]; + var key = createKey(ch); + key.setLocation(center.x - KEY_RADIUS, center.y - KEY_RADIUS); + gw.add(key); + enigma.keys.push(key); + + var center2 = LAMP_LOCATIONS[i]; + var lamp = createLamp(ch); + lamp.setLocation(center2.x - LAMP_RADIUS, center2.y - LAMP_RADIUS); + gw.add(lamp); + enigma.lamps.push(lamp); + } + + // Add rotors + for (var i = 0; i < 3; i++) { + var loc = ROTOR_LOCATIONS[i]; + var rotor = createRotor("A"); + rotor.setLocation(loc.x - ROTOR_WIDTH / 2, loc.y - ROTOR_HEIGHT / 2); + gw.add(rotor); + enigma.rotors.push(rotor); + } + + // Mouse event listeners + gw.addEventListener("mousedown", function (e) { + var obj = gw.getElementAt(e.getX(), e.getY()); + if (obj !== null && obj.mousedownAction !== undefined) { + obj.mousedownAction(enigma); + } + }); + + gw.addEventListener("mouseup", function (e) { + var obj = gw.getElementAt(e.getX(), e.getY()); + if (obj !== null && obj.mouseupAction !== undefined) { + obj.mouseupAction(enigma); + } + }); +} + +function createKey(letter) { + var key = GCompound(); + + var border = GOval(0, 0, 2 * KEY_RADIUS, 2 * KEY_RADIUS); + border.setFilled(true); + border.setFillColor(KEY_BORDER_COLOR); + key.add(border); + + var inner = GOval(KEY_BORDER, KEY_BORDER, + 2 * (KEY_RADIUS - KEY_BORDER), + 2 * (KEY_RADIUS - KEY_BORDER)); + inner.setFilled(true); + inner.setFillColor(KEY_BGCOLOR); + key.add(inner); + + var label = GLabel(letter); + label.setFont(KEY_FONT); + label.setColor(KEY_UP_COLOR); + label.setLocation(KEY_RADIUS - label.getWidth() / 2, + KEY_RADIUS + KEY_LABEL_DY); + key.add(label); + + var index = letter.charCodeAt(0) - "A".charCodeAt(0); + + key.mousedownAction = function(enigma) { + label.setColor(KEY_DOWN_COLOR); + advanceRotors(enigma); + var step1 = encryptforward(letter, enigma); + var step2 = reflect(step1); + var step3 = encryptbackward(step2, enigma); + lightLamp(step3, enigma); +}; + + key.mouseupAction = function (enigma) { + label.setColor(KEY_UP_COLOR); + // Turn off all lamps on key release + for (var i = 0; i < enigma.lamps.length; i++) { + enigma.lamps[i].label.setColor(LAMP_OFF_COLOR); + } + }; + + return key; +} + +function createLamp(letter) { + var lamp = GCompound(); + + var border = GOval(0, 0, 2 * LAMP_RADIUS, 2 * LAMP_RADIUS); + border.setFilled(true); + border.setFillColor(LAMP_BORDER_COLOR); + lamp.add(border); + + var label = GLabel(letter); + label.setFont(LAMP_FONT); + label.setColor(LAMP_OFF_COLOR); + label.setLocation(LAMP_RADIUS - label.getWidth() / 2, + LAMP_RADIUS + LAMP_LABEL_DY); + lamp.add(label); + + lamp.label = label; // Attach label so you can change color later + + return lamp; +} + +function createRotor(letter) { + var rotor = GCompound(); + + var border = GRect(0, 0, ROTOR_WIDTH, ROTOR_HEIGHT); + border.setFilled(true); + border.setFillColor(ROTOR_BGCOLOR); + rotor.add(border); + + var label = GLabel(letter); + label.setFont(ROTOR_FONT); + label.setColor(ROTOR_COLOR); + label.setLocation((ROTOR_WIDTH - label.getWidth()) / 2, + ROTOR_HEIGHT - ROTOR_LABEL_DY); + rotor.add(label); + + rotor.label = label; + + rotor.mouseupAction = function (enigma) { + var current = rotor.label.getLabel(); + var next = String.fromCharCode((current.charCodeAt(0) - 65 + 1) % 26 + 65); + rotor.label.setLabel(next); + }; + + return rotor; +} + +// Basic single rotor encryption function +function encryptforward(letter, enigma) { + var index = letter.charCodeAt(0) - "A".charCodeAt(0); + var mappedChar = ""; + var mappedIndex = 0; + + for (var i = 2; i >= 0; i--) { + var rotor = ROTOR_PERMUTATIONS[i]; + + // Get the rotor offset from the label + var rotorLetter = enigma.rotors[i].label.getLabel(); + var offset = rotorLetter.charCodeAt(0) - "A".charCodeAt(0); + + var shiftedIndex = (index + offset) % 26; + mappedChar = rotor.charAt(shiftedIndex); + index = mappedChar.charCodeAt(0) - "A".charCodeAt(0); + } + + mappedIndex = index; + return mappedChar; +} + +function reflect(letter) { + var index = letter.charCodeAt(0) - "A".charCodeAt(0); + return REFLECTOR_PERMUTATION.charAt(index); +} + +function encryptbackward(letter, enigma) { + var index = letter.charCodeAt(0) - "A".charCodeAt(0); + var mappedChar = ""; + var mappedIndex = 0; + + for (var i = 0; i < 3; i++) { + var rotor = ROTOR_PERMUTATIONS[i]; + + // Get the rotor offset from the label + var rotorLetter = enigma.rotors[i].label.getLabel(); + var offset = rotorLetter.charCodeAt(0) - "A".charCodeAt(0); + + var shiftedIndex = (index + offset) % 26; + mappedChar = rotor.charAt(shiftedIndex); + index = mappedChar.charCodeAt(0) - "A".charCodeAt(0); + } + + mappedIndex = index; + return mappedChar; +} + +function lightLamp(letter, enigma) { + var index = letter.charCodeAt(0) - "A".charCodeAt(0); + var lamp = enigma.lamps[index]; + + if (lamp !== undefined && lamp !== null) { + var label = lamp.label; + if (label !== undefined && label !== null) { + label.setColor(LAMP_ON_COLOR); + } else { + println("Lamp label is missing at index " + index); + } + } else { + println("Lamp is missing at index " + index); + } +} + +function advanceRotors(enigma) { + var r2 = enigma.rotors[2]; // fast + var r1 = enigma.rotors[1]; // medium + var r0 = enigma.rotors[0]; // slow + + // Advance fast rotor + var fastLetter = r2.label.getLabel(); + var fastNext = String.fromCharCode((fastLetter.charCodeAt(0) - 65 + 1) % 26 + 65); + r2.label.setLabel(fastNext); + + // If fast rotor wraps from Z → A, step middle + if (fastLetter === "Z") { + var midLetter = r1.label.getLabel(); + var midNext = String.fromCharCode((midLetter.charCodeAt(0) - 65 + 1) % 26 + 65); + r1.label.setLabel(midNext); + + // If middle wraps, step slow + if (midLetter === "Z") { + var slowLetter = r0.label.getLabel(); + var slowNext = String.fromCharCode((slowLetter.charCodeAt(0) - 65 + 1) % 26 + 65); + r0.label.setLabel(slowNext); + } + } +} + +