Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
368 changes: 368 additions & 0 deletions Enigma_Week0_assignment
Original file line number Diff line number Diff line change
@@ -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);
}
}
}